fix: MEDIUM issues #23-#30 — stale deps, export modal, cache race, StrictMode dedup, SeatingService ref, SVG import persist, group-as-block multi-select, session expiry

This commit is contained in:
A0 Orchestrator
2026-06-30 13:15:54 +02:00
parent d09b1dfc2e
commit 172f933456
4 changed files with 147 additions and 29 deletions
+26 -5
View File
@@ -349,12 +349,20 @@ export async function createBlockTyped(token: string, drawingId: string, block:
return dbBlockToFrontend(raw);
}
const projectLoadCache = new Map<string, Promise<ProjectData>>();
const projectLoadCache = new Map<string, { promise: Promise<ProjectData>; requestId: number }>();
const projectLoadRequestCounter = new Map<string, number>();
export async function loadProjectDataTyped(token: string, projectId: string): Promise<ProjectData> {
// Dedup concurrent calls (React StrictMode double-render)
// Generate a new request ID to track the latest request for this project
const currentRequestId = (projectLoadRequestCounter.get(projectId) ?? 0) + 1;
projectLoadRequestCounter.set(projectId, currentRequestId);
// Dedup concurrent calls (React StrictMode double-render): if there is an in-flight
// request with the same ID, reuse it. Otherwise start a fresh one.
const existing = projectLoadCache.get(projectId);
if (existing) return existing;
if (existing && existing.requestId === currentRequestId - 1) {
return existing.promise;
}
const promise = (async () => {
const drawings = await getDrawings(token, projectId);
@@ -373,6 +381,13 @@ export async function loadProjectDataTyped(token: string, projectId: string): Pr
getBlocks(token, drawing.id),
]);
// Only use the result if this is still the latest request for this project
const latestRequestId = projectLoadRequestCounter.get(projectId);
if (latestRequestId !== currentRequestId) {
// A newer request superseded this one — reject to prevent stale data
throw new Error('Superseded by a newer load request');
}
return {
project,
drawing,
@@ -382,8 +397,14 @@ export async function loadProjectDataTyped(token: string, projectId: string): Pr
};
})();
projectLoadCache.set(projectId, promise);
promise.finally(() => projectLoadCache.delete(projectId));
projectLoadCache.set(projectId, { promise, requestId: currentRequestId });
promise.finally(() => {
// Only clear cache if this is still the latest request
const cached = projectLoadCache.get(projectId);
if (cached && cached.requestId === currentRequestId) {
projectLoadCache.delete(projectId);
}
});
return promise;
}