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);
}
// 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) {
// Compare by ID set, not by index ordering. Update if any layer properties differ.
const localLayerMap = new Map(layers.map(l => [l.id, l]));
let layersChanged = false;
const mergedLayers = collab.layers.map(rl => {
const local = localLayerMap.get(rl.id);
if (!local || JSON.stringify(local) !== JSON.stringify(rl)) {
layersChanged = true;
}
return rl;
const remoteLayerMap = new Map(collab.layers.map(l => [l.id, l]));
const mergedLayers = layers.map(ll => {
const remote = remoteLayerMap.get(ll.id);
return remote ? (JSON.stringify(remote) !== JSON.stringify(ll) ? remote : ll) : ll;
});
// Also check if local has layers not in remote
if (!layersChanged && layers.length !== collab.layers.length) {
layersChanged = true;
collab.layers.forEach(rl => {
if (!localLayerMap.has(rl.id)) mergedLayers.push(rl);
});
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) {
// Compare by ID set, not by index ordering. Update if any block properties differ.
const localBlockMap = new Map(blocks.map(b => [b.id, b]));
let blocksChanged = false;
const mergedBlocks = collab.blocks.map(rb => {
const local = localBlockMap.get(rb.id);
if (!local || JSON.stringify(local) !== JSON.stringify(rb)) {
blocksChanged = true;
}
return rb;
const remoteBlockMap = new Map(collab.blocks.map(b => [b.id, b]));
const mergedBlocks = blocks.map(lb => {
const remote = remoteBlockMap.get(lb.id);
return remote ? (JSON.stringify(remote) !== JSON.stringify(lb) ? remote : lb) : lb;
});
// Also check if local has blocks not in remote
if (!blocksChanged && blocks.length !== collab.blocks.length) {
blocksChanged = true;
collab.blocks.forEach(rb => {
if (!localBlockMap.has(rb.id)) mergedBlocks.push(rb);
});
if (mergedBlocks.length !== blocks.length || mergedBlocks.some((mb, i) => mb !== blocks[i])) {
setBlocks(mergedBlocks);
}
if (blocksChanged) setBlocks(mergedBlocks);
}
// 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
}, [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 elWithLayer = el.layerId ? el : { ...el, layerId: activeLayerId };
setElements((prev) => {