import type { GroupModel } from "../group" import { GroupFieldType } from "../group" import type { NestableModel } from "../nestable" import type { SharedSliceModelVariation } from "../slice" import { type DiffChange, DiffOperation } from "./changes" import { isNotEmpty, zipObjects } from "./utils" import type { GroupWidgetWithFieldsDiff, NestableWidgetDiff, SlicePrimaryWidgetDiff, } from "./widgets" export type VariationMetadata = Omit export type VariationDiff = DiffChange< SharedSliceModelVariation, Partial & { primary?: Record items?: Record } > function compareVariationMeta( variationA?: SharedSliceModelVariation, variationB?: SharedSliceModelVariation, ): Partial { const zippedVariations = zipObjects(variationA, variationB) return Object.entries(zippedVariations).reduce((acc, [key, value]) => { if (key === "primary" || key === "items") return acc if (value?.left === value?.right) return acc return { ...acc, [key]: value?.right } }, {}) } function compareWidgets( widgetsA?: { [key: string]: TWidget }, widgetsB?: { [key: string]: TWidget }, ): | Record | undefined { const zippedWidgets = zipObjects(widgetsA, widgetsB) const diffWidgets = Object.entries(zippedWidgets).reduce((acc, [widgetKey, widgetValue]) => { if (JSON.stringify(widgetValue?.left) === JSON.stringify(widgetValue?.right)) return acc const changes = (() => { if (!widgetValue?.left && !widgetValue?.right) return if (widgetValue?.left && !widgetValue.right) { return { op: DiffOperation.Removed, } } if (!widgetValue?.left && widgetValue?.right) { if (widgetValue.right?.type === GroupFieldType) { const group: GroupWidgetWithFieldsDiff = { ...widgetValue.right, config: { ...widgetValue.right.config, fields: {}, }, } // On added groups, this basically marks all fields in the group as added group.config.fields = compareWidgets(undefined, widgetValue.right.config?.fields) || {} return { op: DiffOperation.Added, value: group, } } return { op: DiffOperation.Added, value: widgetValue.right, } } if (widgetValue.right?.type === GroupFieldType) { const group: GroupWidgetWithFieldsDiff = { ...widgetValue.right, config: { ...widgetValue.right.config, fields: {}, }, } // On updated groups, this also compares the fields inside the group group.config.fields = compareWidgets( widgetValue.left?.type === GroupFieldType ? widgetValue.left?.config?.fields : undefined, widgetValue.right.config?.fields, ) || {} return { op: DiffOperation.Updated, value: group, } } return { op: DiffOperation.Updated, value: widgetValue.right, } })() if (!changes) return acc return { ...acc, [widgetKey]: changes, } }, {}) return isNotEmpty(diffWidgets) ? diffWidgets : undefined } export const VariationComparator = { compare( variationA?: SharedSliceModelVariation, variationB?: SharedSliceModelVariation, ): VariationDiff | undefined { if (variationA && !variationB) return { op: DiffOperation.Removed, } if (!variationA && variationB) { return { op: DiffOperation.Added, value: variationB, } } const diffMeta = compareVariationMeta(variationA, variationB) const diffPrimary = compareWidgets(variationA?.primary, variationB?.primary) const diffItems = compareWidgets(variationA?.items, variationB?.items) const diffVariation = { ...diffMeta, ...(diffPrimary ? { primary: diffPrimary } : {}), ...(diffItems ? { items: diffItems } : {}), } if (isNotEmpty(diffVariation)) { return { op: DiffOperation.Updated, value: diffVariation, } } return undefined }, }