Skip to content

Commit e1af66b

Browse files
mattdawkinsclaude
andcommitted
Fix annotations not displaying after switching view modes
When switching between side and bottom view modes, the annotator component is destroyed and recreated. Previously, the old media controller remained in the subControllers array, causing getController() to return stale references to destroyed geoViewers. - Clean up old controller state when re-initializing the same camera - Add resizeTrigger to notify LayerManager to redraw after resize Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 97af826 commit e1af66b

3 files changed

Lines changed: 50 additions & 2 deletions

File tree

client/src/components/LayerManager.vue

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@ export default defineComponent({
8989
return trackStyleManager.typeStyling.value;
9090
});
9191
92-
const annotator = injectAggregateController().value.getController(props.camera);
92+
const aggregateController = injectAggregateController();
93+
const annotator = aggregateController.value.getController(props.camera);
9394
const frameNumberRef = annotator.frame;
9495
const flickNumberRef = annotator.flick;
9596
@@ -433,6 +434,23 @@ export default defineComponent({
433434
);
434435
});
435436
437+
/** Watch for resize events to redraw layers after view mode changes */
438+
watch(
439+
() => aggregateController.value.resizeTrigger.value,
440+
() => {
441+
updateLayers(
442+
frameNumberRef.value,
443+
editingModeRef.value,
444+
selectedTrackIdRef.value,
445+
multiSeletListRef.value,
446+
enabledTracksRef.value,
447+
visibleModesRef.value,
448+
selectedKeyRef.value,
449+
props.colorBy,
450+
);
451+
},
452+
);
453+
436454
const Clicked = (trackId: number, editing: boolean, modifiers?: {ctrl: boolean}) => {
437455
// If the camera isn't selected yet we ignore the click
438456
if (selectedCamera.value !== props.camera) {

client/src/components/annotators/mediaControllerType.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ export interface AggregateMediaController {
1515
volume: Readonly<Ref<number>>;
1616
cameras: Readonly<Ref<string[]>>;
1717
cameraSync: Readonly<Ref<boolean>>;
18+
/** Incremented when the viewer is resized, used to trigger layer redraws */
19+
resizeTrigger: Readonly<Ref<number>>;
1820

1921
pause: () => void;
2022
play: () => void;

client/src/components/annotators/useMediaController.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export function useMediaController() {
8383
let state: Record<string, UnwrapRef<MediaControllerReactiveData>> = {};
8484
let cameraControllerSymbols: Record<string, symbol> = {};
8585
const synchronizeCameras: Ref<boolean> = ref(false);
86+
const resizeTrigger: Ref<number> = ref(0);
8687
function clear() {
8788
geoViewers = {};
8889
containers = {};
@@ -111,6 +112,7 @@ export function useMediaController() {
111112
* onResize resets the zoom of a camera when its window size changes.
112113
*/
113114
function onResize() {
115+
let resized = false;
114116
subControllers.forEach((mc) => {
115117
const camera = cameraControllerSymbols[mc.cameraName.value].toString();
116118
const geoViewerRef = geoViewers[camera];
@@ -121,12 +123,19 @@ export function useMediaController() {
121123
const size = containerRef.value.getBoundingClientRect();
122124
const mapSize = geoViewerRef.value.size();
123125
if (size.width !== mapSize.width || size.height !== mapSize.height) {
126+
resized = true;
124127
window.requestAnimationFrame(() => {
125128
geoViewerRef.value.size(size);
126129
mc.resetZoom();
127130
});
128131
}
129132
});
133+
// Trigger layer redraw after resize
134+
if (resized) {
135+
window.requestAnimationFrame(() => {
136+
resizeTrigger.value += 1;
137+
});
138+
}
130139
}
131140

132141
function toggleSynchronizeCameras(val: boolean) {
@@ -172,6 +181,24 @@ export function useMediaController() {
172181
setVolume(level: number): void;
173182
setSpeed(level: number): void;
174183
}) {
184+
// Clean up existing controller for this camera if it exists (e.g., when view mode switches)
185+
const existingIndex = subControllers.findIndex((c) => c.cameraName.value === cameraName);
186+
if (existingIndex !== -1) {
187+
subControllers.splice(existingIndex, 1);
188+
const existingSymbol = cameraControllerSymbols[cameraName];
189+
if (existingSymbol) {
190+
const existingCamera = existingSymbol.toString();
191+
const cameraIndex = cameras.value.indexOf(existingSymbol);
192+
if (cameraIndex !== -1) {
193+
cameras.value.splice(cameraIndex, 1);
194+
}
195+
delete geoViewers[existingCamera];
196+
delete containers[existingCamera];
197+
delete imageCursors[existingCamera];
198+
delete state[existingCamera];
199+
}
200+
}
201+
175202
const cameraSymbol = Symbol(`media-controller-${cameraName}`);
176203
cameraControllerSymbols[cameraName] = cameraSymbol;
177204
const camera = cameraSymbol.toString();
@@ -423,7 +450,7 @@ export function useMediaController() {
423450
resetMapDimensions,
424451
toggleSynchronizeCameras,
425452
cameraSync: synchronizeCameras,
426-
453+
resizeTrigger,
427454
};
428455

429456
subControllers.push(mediaController);
@@ -461,6 +488,7 @@ export function useMediaController() {
461488
getController,
462489
toggleSynchronizeCameras,
463490
cameraSync: synchronizeCameras,
491+
resizeTrigger,
464492
};
465493
});
466494

0 commit comments

Comments
 (0)