fix(frontend): layers/blocks local-first merge + push effects (quality review fixes)

This commit is contained in:
2026-06-30 00:09:22 +02:00
parent baba0cbb38
commit 4a5377cb53
+34 -26
View File
@@ -334,42 +334,36 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
setElements(merged); setElements(merged);
} }
// Never overwrite local layers with empty remote layers (prevents losing initialLayers) // Merge layers: local-first, update changed from remote, append remote-only
if (collab.layers.length > 0) { if (collab.layers.length > 0) {
// Compare by ID set, not by index ordering. Update if any layer properties differ.
const localLayerMap = new Map(layers.map(l => [l.id, l])); const localLayerMap = new Map(layers.map(l => [l.id, l]));
let layersChanged = false; const remoteLayerMap = new Map(collab.layers.map(l => [l.id, l]));
const mergedLayers = collab.layers.map(rl => { const mergedLayers = layers.map(ll => {
const local = localLayerMap.get(rl.id); const remote = remoteLayerMap.get(ll.id);
if (!local || JSON.stringify(local) !== JSON.stringify(rl)) { return remote ? (JSON.stringify(remote) !== JSON.stringify(ll) ? remote : ll) : ll;
layersChanged = true;
}
return rl;
}); });
// Also check if local has layers not in remote collab.layers.forEach(rl => {
if (!layersChanged && layers.length !== collab.layers.length) { if (!localLayerMap.has(rl.id)) mergedLayers.push(rl);
layersChanged = true; });
if (mergedLayers.length !== layers.length || mergedLayers.some((ml, i) => ml !== layers[i])) {
setLayers(mergedLayers);
} }
if (layersChanged) setLayers(mergedLayers);
} }
// Never overwrite local blocks with empty remote blocks (prevents losing defaultBlocks) // Merge blocks: local-first, update changed from remote, append remote-only
if (collab.blocks.length > 0) { if (collab.blocks.length > 0) {
// Compare by ID set, not by index ordering. Update if any block properties differ.
const localBlockMap = new Map(blocks.map(b => [b.id, b])); const localBlockMap = new Map(blocks.map(b => [b.id, b]));
let blocksChanged = false; const remoteBlockMap = new Map(collab.blocks.map(b => [b.id, b]));
const mergedBlocks = collab.blocks.map(rb => { const mergedBlocks = blocks.map(lb => {
const local = localBlockMap.get(rb.id); const remote = remoteBlockMap.get(lb.id);
if (!local || JSON.stringify(local) !== JSON.stringify(rb)) { return remote ? (JSON.stringify(remote) !== JSON.stringify(lb) ? remote : lb) : lb;
blocksChanged = true;
}
return rb;
}); });
// Also check if local has blocks not in remote collab.blocks.forEach(rb => {
if (!blocksChanged && blocks.length !== collab.blocks.length) { if (!localBlockMap.has(rb.id)) mergedBlocks.push(rb);
blocksChanged = true; });
if (mergedBlocks.length !== blocks.length || mergedBlocks.some((mb, i) => mb !== blocks[i])) {
setBlocks(mergedBlocks);
} }
if (blocksChanged) setBlocks(mergedBlocks);
} }
// Sync groups from remote → local // Sync groups from remote → local
@@ -409,6 +403,20 @@ const CADEditor: React.FC<CADEditorProps> = ({ projectId, token, onNavigateBack
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [bgConfig, collab.status]); }, [bgConfig, collab.status]);
// Sync local → remote: push local layers to Yjs when layers change
React.useEffect(() => {
if (collab.status !== 'connected') return;
for (const layer of layers) collab.setLayer(layer);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [layers, collab.status]);
// Sync local → remote: push local blocks to Yjs when blocks change
React.useEffect(() => {
if (collab.status !== 'connected') return;
for (const block of blocks) collab.setBlock(block);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [blocks, collab.status]);
const handleElementCreated = useCallback((el: CADElement) => { const handleElementCreated = useCallback((el: CADElement) => {
const elWithLayer = el.layerId ? el : { ...el, layerId: activeLayerId }; const elWithLayer = el.layerId ? el : { ...el, layerId: activeLayerId };
setElements((prev) => { setElements((prev) => {