idbdatabase
This commit is contained in:
@@ -24,6 +24,7 @@ import {
|
|||||||
parseTagTransferPayload,
|
parseTagTransferPayload,
|
||||||
writeTagTransferData,
|
writeTagTransferData,
|
||||||
} from './documents/tagTransfer';
|
} from './documents/tagTransfer';
|
||||||
|
import { fetchLayoutRecords, upsertLayoutRecords } from './desk/db';
|
||||||
import './DesktopWorkspace.css';
|
import './DesktopWorkspace.css';
|
||||||
|
|
||||||
const CANVAS_PADDING = 24;
|
const CANVAS_PADDING = 24;
|
||||||
@@ -628,58 +629,7 @@ const DesktopWorkspace = ({
|
|||||||
documentLookupRef.current = documentLookup;
|
documentLookupRef.current = documentLookup;
|
||||||
}, [documentLookup]);
|
}, [documentLookup]);
|
||||||
|
|
||||||
const storageKey = useMemo(() => {
|
const persistedLayoutRef = useRef(new Map());
|
||||||
if (!tenantId || !viewId) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const normalizedViewId = encodeURIComponent(String(viewId));
|
|
||||||
return `papercrate.desk-layout.${tenantId}.${normalizedViewId}`;
|
|
||||||
}, [tenantId, viewId]);
|
|
||||||
|
|
||||||
const initialPersistedLayout = useMemo(() => {
|
|
||||||
if (!storageKey || typeof window === 'undefined') {
|
|
||||||
return new Map();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const raw = window.localStorage.getItem(storageKey);
|
|
||||||
if (!raw) {
|
|
||||||
return new Map();
|
|
||||||
}
|
|
||||||
const parsed = JSON.parse(raw);
|
|
||||||
if (!parsed || typeof parsed !== 'object') {
|
|
||||||
return new Map();
|
|
||||||
}
|
|
||||||
const map = new Map();
|
|
||||||
Object.entries(parsed).forEach(([docId, value]) => {
|
|
||||||
if (!value || typeof value !== 'object') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const centerX = Number(value.centerX);
|
|
||||||
const centerY = Number(value.centerY);
|
|
||||||
if (!Number.isFinite(centerX) || !Number.isFinite(centerY)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const rotation = Number.isFinite(Number(value.rotation)) ? Number(value.rotation) : 0;
|
|
||||||
const z = Number.isFinite(Number(value.z)) ? Number(value.z) : undefined;
|
|
||||||
map.set(String(docId), {
|
|
||||||
centerX,
|
|
||||||
centerY,
|
|
||||||
rotation,
|
|
||||||
z,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return map;
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('[desk] Failed to parse persisted layout', error);
|
|
||||||
return new Map();
|
|
||||||
}
|
|
||||||
}, [storageKey]);
|
|
||||||
|
|
||||||
const persistedLayoutRef = useRef(initialPersistedLayout);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
persistedLayoutRef.current = initialPersistedLayout;
|
|
||||||
}, [initialPersistedLayout]);
|
|
||||||
|
|
||||||
const markLayoutDirty = useCallback(() => {
|
const markLayoutDirty = useCallback(() => {
|
||||||
layoutDirtyRef.current = true;
|
layoutDirtyRef.current = true;
|
||||||
@@ -1167,8 +1117,8 @@ const recalcVisibleDocIds = useCallback(() => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const persistLayoutSnapshot = useCallback(
|
const persistLayoutSnapshot = useCallback(
|
||||||
(snapshot, force = false) => {
|
async (snapshot, force = false) => {
|
||||||
if (!storageKey || typeof window === 'undefined') {
|
if (!tenantId || !viewId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!force && !layoutDirtyRef.current) {
|
if (!force && !layoutDirtyRef.current) {
|
||||||
@@ -1190,21 +1140,25 @@ const recalcVisibleDocIds = useCallback(() => {
|
|||||||
merged.set(docId, { centerX, centerY, rotation, z });
|
merged.set(docId, { centerX, centerY, rotation, z });
|
||||||
});
|
});
|
||||||
|
|
||||||
const payload = {};
|
persistedLayoutRef.current = merged;
|
||||||
|
|
||||||
|
const records = [];
|
||||||
merged.forEach((entry, docId) => {
|
merged.forEach((entry, docId) => {
|
||||||
if (!docId || !entry) {
|
if (!docId || !entry) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
payload[docId] = entry;
|
records.push({
|
||||||
|
documentId: docId,
|
||||||
|
centerX: entry.centerX,
|
||||||
|
centerY: entry.centerY,
|
||||||
|
rotation: entry.rotation ?? 0,
|
||||||
|
zIndex: entry.z ?? 0,
|
||||||
});
|
});
|
||||||
try {
|
});
|
||||||
window.localStorage.setItem(storageKey, JSON.stringify(payload));
|
|
||||||
persistedLayoutRef.current = merged;
|
await upsertLayoutRecords({ tenantId, viewId, entries: records });
|
||||||
} catch (error) {
|
|
||||||
console.warn('[desk] Failed to persist desk layout', error);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
[storageKey],
|
[tenantId, viewId],
|
||||||
);
|
);
|
||||||
|
|
||||||
const syncLayoutSnapshot = useCallback((force = false) => {
|
const syncLayoutSnapshot = useCallback((force = false) => {
|
||||||
@@ -1250,6 +1204,47 @@ const recalcVisibleDocIds = useCallback(() => {
|
|||||||
observer.observe(container);
|
observer.observe(container);
|
||||||
return () => observer.disconnect();
|
return () => observer.disconnect();
|
||||||
}, []);
|
}, []);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!tenantId || !viewId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
const loadLayouts = async () => {
|
||||||
|
const records = await fetchLayoutRecords({ tenantId, viewId });
|
||||||
|
if (cancelled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const map = new Map();
|
||||||
|
records.forEach((record) => {
|
||||||
|
if (!record || !record.documentId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
map.set(String(record.documentId), {
|
||||||
|
centerX: Number(record.centerX) || 0,
|
||||||
|
centerY: Number(record.centerY) || 0,
|
||||||
|
rotation: Number(record.rotation) || 0,
|
||||||
|
z: Number(record.zIndex) || 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
persistedLayoutRef.current = map;
|
||||||
|
layoutDirtyRef.current = false;
|
||||||
|
if (records.length) {
|
||||||
|
zCounterRef.current = Math.max(
|
||||||
|
zCounterRef.current,
|
||||||
|
...records.map((r) => Number(r.zIndex) || 0),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setDocSizeVersion((value) => value + 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
loadLayouts();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, [tenantId, viewId]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!containerRef.current || !canvasSize.width || !canvasSize.height) {
|
if (!containerRef.current || !canvasSize.width || !canvasSize.height) {
|
||||||
return;
|
return;
|
||||||
@@ -1291,9 +1286,7 @@ const recalcVisibleDocIds = useCallback(() => {
|
|||||||
const persisted = docKey ? persistedLayoutRef.current.get(docKey) : null;
|
const persisted = docKey ? persistedLayoutRef.current.get(docKey) : null;
|
||||||
let existing = previous.get(doc.id) || null;
|
let existing = previous.get(doc.id) || null;
|
||||||
if (persisted) {
|
if (persisted) {
|
||||||
existing = existing
|
existing = existing ? { ...existing, ...persisted } : { ...persisted };
|
||||||
? { ...existing, ...persisted }
|
|
||||||
: { ...persisted };
|
|
||||||
}
|
}
|
||||||
if (existing) {
|
if (existing) {
|
||||||
const defaultCenterX = (minCenterX + maxCenterX) / 2;
|
const defaultCenterX = (minCenterX + maxCenterX) / 2;
|
||||||
@@ -1349,7 +1342,6 @@ const recalcVisibleDocIds = useCallback(() => {
|
|||||||
syncLayoutSnapshot,
|
syncLayoutSnapshot,
|
||||||
recalcVisibleDocIds,
|
recalcVisibleDocIds,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
recalcVisibleDocIds();
|
recalcVisibleDocIds();
|
||||||
}, [recalcVisibleDocIds, items.length, canvasSize.width, canvasSize.height, docSizeVersion]);
|
}, [recalcVisibleDocIds, items.length, canvasSize.width, canvasSize.height, docSizeVersion]);
|
||||||
|
|||||||
Reference in New Issue
Block a user