import React, { useCallback } from 'react'; import type { DiffCalculatedData, DiffContext, DiffEvent, } from './diff-context'; import { diffContext } from './diff-context'; import { generateDiff, getDiffMapKey, } from '@/lib/domain/diff/diff-check/diff-check'; import type { Diagram } from '@/lib/domain/diagram'; import { useEventEmitter } from 'ahooks'; import type { DBField } from '@/lib/domain/db-field'; import type { DataType } from '@/lib/data/data-types/data-types'; import type { DBRelationship } from '@/lib/domain/db-relationship'; import type { ChartDBDiff, DiffMap } from '@/lib/domain/diff/diff'; export const DiffProvider: React.FC = ({ children, }) => { const [newDiagram, setNewDiagram] = React.useState(null); const [originalDiagram, setOriginalDiagram] = React.useState(null); const [diffMap, setDiffMap] = React.useState( new Map() ); const [tablesChanged, setTablesChanged] = React.useState< Map >(new Map()); const [fieldsChanged, setFieldsChanged] = React.useState< Map >(new Map()); const [isSummaryOnly, setIsSummaryOnly] = React.useState(false); const events = useEventEmitter(); const generateFieldsToAddMap = useCallback( ({ diffMap, newDiagram, }: { diffMap: DiffMap; newDiagram: Diagram; }) => { const newFieldsMap = new Map(); diffMap.forEach((diff) => { if (diff.object === 'field' && diff.type === 'added') { const field = newDiagram?.tables ?.find((table) => table.id === diff.tableId) ?.fields.find((f) => f.id === diff.newField.id); if (field) { newFieldsMap.set(diff.tableId, [ ...(newFieldsMap.get(diff.tableId) ?? []), field, ]); } } }); return newFieldsMap; }, [] ); const findRelationshipsToAdd = useCallback( ({ diffMap, newDiagram, }: { diffMap: DiffMap; newDiagram: Diagram; }) => { const relationships: DBRelationship[] = []; diffMap.forEach((diff) => { if (diff.object === 'relationship' && diff.type === 'added') { const relationship = newDiagram?.relationships?.find( (rel) => rel.id === diff.newRelationship.id ); if (relationship) { relationships.push(relationship); } } }); return relationships; }, [] ); const generateDiffCalculatedData = useCallback( ({ newDiagram, diffMap, }: { newDiagram: Diagram; diffMap: DiffMap; }): DiffCalculatedData => { return { tablesToAdd: newDiagram?.tables?.filter((table) => { const tableKey = getDiffMapKey({ diffObject: 'table', objectId: table.id, }); return ( diffMap.has(tableKey) && diffMap.get(tableKey)?.type === 'added' ); }) ?? [], fieldsToAdd: generateFieldsToAddMap({ diffMap: diffMap, newDiagram: newDiagram, }), relationshipsToAdd: findRelationshipsToAdd({ diffMap: diffMap, newDiagram: newDiagram, }), }; }, [findRelationshipsToAdd, generateFieldsToAddMap] ); const calculateDiff: DiffContext['calculateDiff'] = useCallback( ({ diagram, newDiagram: newDiagramArg, options }) => { const { diffMap: newDiffs, changedTables: newChangedTables, changedFields: newChangedFields, } = generateDiff({ diagram, newDiagram: newDiagramArg }); setDiffMap(newDiffs); setTablesChanged(newChangedTables); setFieldsChanged(newChangedFields); setNewDiagram(newDiagramArg); setOriginalDiagram(diagram); setIsSummaryOnly(options?.summaryOnly ?? false); events.emit({ action: 'diff_calculated', data: generateDiffCalculatedData({ diffMap: newDiffs, newDiagram: newDiagramArg, }), }); return { foundDiff: !!newDiffs.size }; }, [setDiffMap, events, generateDiffCalculatedData] ); const getTableNewName = useCallback( ({ tableId }) => { const tableNameKey = getDiffMapKey({ diffObject: 'table', objectId: tableId, attribute: 'name', }); if (diffMap.has(tableNameKey)) { const diff = diffMap.get(tableNameKey); if (diff?.type === 'changed') { return { new: diff.newValue as string, old: diff.oldValue as string, }; } } return null; }, [diffMap] ); const getTableNewColor = useCallback( ({ tableId }) => { const tableColorKey = getDiffMapKey({ diffObject: 'table', objectId: tableId, attribute: 'color', }); if (diffMap.has(tableColorKey)) { const diff = diffMap.get(tableColorKey); if (diff?.type === 'changed') { return { new: diff.newValue as string, old: diff.oldValue as string, }; } } return null; }, [diffMap] ); const checkIfTableHasChange = useCallback< DiffContext['checkIfTableHasChange'] >(({ tableId }) => tablesChanged.get(tableId) ?? false, [tablesChanged]); const checkIfNewTable = useCallback( ({ tableId }) => { const tableKey = getDiffMapKey({ diffObject: 'table', objectId: tableId, }); return ( diffMap.has(tableKey) && diffMap.get(tableKey)?.type === 'added' ); }, [diffMap] ); const checkIfTableRemoved = useCallback( ({ tableId }) => { const tableKey = getDiffMapKey({ diffObject: 'table', objectId: tableId, }); return ( diffMap.has(tableKey) && diffMap.get(tableKey)?.type === 'removed' ); }, [diffMap] ); const checkIfFieldHasChange = useCallback< DiffContext['checkIfFieldHasChange'] >( ({ fieldId }) => { return fieldsChanged.get(fieldId) ?? false; }, [fieldsChanged] ); const checkIfFieldRemoved = useCallback( ({ fieldId }) => { const fieldKey = getDiffMapKey({ diffObject: 'field', objectId: fieldId, }); return ( diffMap.has(fieldKey) && diffMap.get(fieldKey)?.type === 'removed' ); }, [diffMap] ); const checkIfNewField = useCallback( ({ fieldId }) => { const fieldKey = getDiffMapKey({ diffObject: 'field', objectId: fieldId, }); return ( diffMap.has(fieldKey) && diffMap.get(fieldKey)?.type === 'added' ); }, [diffMap] ); const getFieldNewName = useCallback( ({ fieldId }) => { const fieldKey = getDiffMapKey({ diffObject: 'field', objectId: fieldId, attribute: 'name', }); if (diffMap.has(fieldKey)) { const diff = diffMap.get(fieldKey); if (diff?.type === 'changed') { return { old: diff.oldValue as string, new: diff.newValue as string, }; } } return null; }, [diffMap] ); const getFieldNewType = useCallback( ({ fieldId }) => { const fieldKey = getDiffMapKey({ diffObject: 'field', objectId: fieldId, attribute: 'type', }); if (diffMap.has(fieldKey)) { const diff = diffMap.get(fieldKey); if (diff?.type === 'changed') { return { old: diff.oldValue as DataType, new: diff.newValue as DataType, }; } } return null; }, [diffMap] ); const getFieldNewPrimaryKey = useCallback< DiffContext['getFieldNewPrimaryKey'] >( ({ fieldId }) => { const fieldKey = getDiffMapKey({ diffObject: 'field', objectId: fieldId, attribute: 'primaryKey', }); if (diffMap.has(fieldKey)) { const diff = diffMap.get(fieldKey); if (diff?.type === 'changed') { return { old: diff.oldValue as boolean, new: diff.newValue as boolean, }; } } return null; }, [diffMap] ); const getFieldNewNullable = useCallback( ({ fieldId }) => { const fieldKey = getDiffMapKey({ diffObject: 'field', objectId: fieldId, attribute: 'nullable', }); if (diffMap.has(fieldKey)) { const diff = diffMap.get(fieldKey); if (diff?.type === 'changed') { return { old: diff.oldValue as boolean, new: diff.newValue as boolean, }; } } return null; }, [diffMap] ); const getFieldNewCharacterMaximumLength = useCallback< DiffContext['getFieldNewCharacterMaximumLength'] >( ({ fieldId }) => { const fieldKey = getDiffMapKey({ diffObject: 'field', objectId: fieldId, attribute: 'characterMaximumLength', }); if (diffMap.has(fieldKey)) { const diff = diffMap.get(fieldKey); if (diff?.type === 'changed') { return { old: diff.oldValue as string, new: diff.newValue as string, }; } } return null; }, [diffMap] ); const getFieldNewScale = useCallback( ({ fieldId }) => { const fieldKey = getDiffMapKey({ diffObject: 'field', objectId: fieldId, attribute: 'scale', }); if (diffMap.has(fieldKey)) { const diff = diffMap.get(fieldKey); if (diff?.type === 'changed') { return { old: diff.oldValue as number, new: diff.newValue as number, }; } } return null; }, [diffMap] ); const getFieldNewPrecision = useCallback< DiffContext['getFieldNewPrecision'] >( ({ fieldId }) => { const fieldKey = getDiffMapKey({ diffObject: 'field', objectId: fieldId, attribute: 'precision', }); if (diffMap.has(fieldKey)) { const diff = diffMap.get(fieldKey); if (diff?.type === 'changed') { return { old: diff.oldValue as number, new: diff.newValue as number, }; } } return null; }, [diffMap] ); const checkIfNewRelationship = useCallback< DiffContext['checkIfNewRelationship'] >( ({ relationshipId }) => { const relationshipKey = getDiffMapKey({ diffObject: 'relationship', objectId: relationshipId, }); return ( diffMap.has(relationshipKey) && diffMap.get(relationshipKey)?.type === 'added' ); }, [diffMap] ); const checkIfRelationshipRemoved = useCallback< DiffContext['checkIfRelationshipRemoved'] >( ({ relationshipId }) => { const relationshipKey = getDiffMapKey({ diffObject: 'relationship', objectId: relationshipId, }); return ( diffMap.has(relationshipKey) && diffMap.get(relationshipKey)?.type === 'removed' ); }, [diffMap] ); const resetDiff = useCallback(() => { setDiffMap(new Map()); setTablesChanged(new Map()); setFieldsChanged(new Map()); setNewDiagram(null); setOriginalDiagram(null); setIsSummaryOnly(false); }, []); return ( 0, isSummaryOnly, calculateDiff, resetDiff, // table diff getTableNewName, checkIfNewTable, checkIfTableRemoved, checkIfTableHasChange, getTableNewColor, // field diff checkIfFieldHasChange, checkIfFieldRemoved, checkIfNewField, getFieldNewName, getFieldNewType, getFieldNewPrimaryKey, getFieldNewNullable, getFieldNewCharacterMaximumLength, getFieldNewScale, getFieldNewPrecision, // relationship diff checkIfNewRelationship, checkIfRelationshipRemoved, events, }} > {children} ); };