Add defensive guards and queueing for annotation imports to handle race conditions

This commit is contained in:
Ross
2026-03-23 11:02:03 +00:00
parent 6cd9e33e65
commit be16e3554f
+49 -8
View File
@@ -2545,27 +2545,68 @@ function App({ container_id, imageStacks, autoCacheStack, annotationJson, viewer
};
// Import and restore all annotations using cornerstoneTools.annotation.state.restoreAnnotations()
// Add defensive guards and queueing so callers can call importAnnotations before the
// viewer/annotation state is fully initialized in rare race conditions.
// @ts-expect-error window augmentation
window.__pendingImportAnnotations = window.__pendingImportAnnotations || {};
const flushPending = (key: string) => {
try {
// @ts-expect-error global
const pending = window.__pendingImportAnnotations && window.__pendingImportAnnotations[key];
if (!pending || !pending.length) return;
const annotationState = cornerstoneTools?.annotation?.state;
if (!annotationState || typeof annotationState.addAnnotation !== 'function') return;
// Drain the queue
while ((window.__pendingImportAnnotations[key] || []).length) {
const annotationData = (window.__pendingImportAnnotations[key] || []).shift();
if (!annotationData) continue;
if (Array.isArray(annotationData)) {
annotationData.forEach(ann => {
try { annotationState.addAnnotation(ann, ann.annotationUID); } catch (err) { console.error('Failed to add queued annotation', err, ann); }
});
}
}
if (renderingEngineRef.current) renderingEngineRef.current.render();
} catch (err) {
console.error('Error flushing pending annotations:', err);
}
};
// @ts-expect-error window augmentation
window[`importAnnotations_${apiKey}`] = (json: string) => {
try {
const annotationData = typeof json === "string" ? JSON.parse(json) : json;
// Remove all existing annotations if needed
if (cornerstoneTools.annotation.state.removeAllAnnotations) {
cornerstoneTools.annotation.state.removeAllAnnotations();
const annotationState = cornerstoneTools?.annotation?.state;
if (!annotationState || typeof annotationState.addAnnotation !== 'function') {
// Queue the annotations until the annotation state is ready
// @ts-expect-error window augmentation
window.__pendingImportAnnotations[apiKey] = window.__pendingImportAnnotations[apiKey] || [];
window.__pendingImportAnnotations[apiKey].push(annotationData);
console.warn('importAnnotations: annotation state not ready, queued import', apiKey);
// schedule a flush attempt
setTimeout(() => flushPending(apiKey), 500);
return;
}
// Add each annotation from the imported data
// Remove all existing annotations if needed
if (annotationState.removeAllAnnotations) {
try { annotationState.removeAllAnnotations(); } catch (err) { console.warn('removeAllAnnotations failed', err); }
}
if (Array.isArray(annotationData)) {
// For now just add them to a new group
//const group = new cornerstoneTools.annotation.AnnotationGroup();
annotationData.forEach(ann => {
cornerstoneTools.annotation.state.addAnnotation(ann, ann.annotationUID);
try { annotationState.addAnnotation(ann, ann.annotationUID); } catch (err) { console.error('Failed to add annotation', err, ann); }
});
}
if (renderingEngineRef.current) {
renderingEngineRef.current.render();
}
} catch (e) {
alert("Failed to import annotations: " + e);
console.error("Failed to import annotations:", e, { apiKey, cornerstoneTools: !!cornerstoneTools, renderingEngine: !!renderingEngineRef.current });
try { alert("Failed to import annotations: " + e); } catch (ignored) {}
}
// Attempt to flush any queued imports (in case multiple imports were queued)
try { flushPending(apiKey); } catch (ignored) {}
};
window[`importLegacyAnnotations_${apiKey}`] = (json: string) => {