Skip to main content

Switching to 360°

Every camera tile can show two different pictures of the same mountain: the standard 60° frame (16:9), or the 360° stitched panorama — a strip several times wider that the grid expands across the row. There are two ways to flip that switch, and they meet in one piece of state.

One Set to rule them

The source of truth is disarmingly small: panoCameraIds, a Set<string> of camera ids in CameraListComponent's local state. A tile renders as pano if and only if its id is in the Set. Everything below is just different ways of writing to it.

The per-tile toggle. Each tile's 360° button calls handlePanoToggle(cameraId, isPano), which adds or removes one id. computeGridPositions re-runs, the tile expands to the end of its row, and its image component switches to the pano retrieval function.

The global switch. The Camera Options menu offers a View Mode: 60° or 360° for everything. That writes viewAllMode into cameraOptionsStore, and a sync effect in CameraListComponent translates it into the Set — all visible ids, or none:

setViewAllMode('360')
→ sync effect → setPanoCameraIds(new Set(all camera ids))
→ computeGridPositions recalculates every tile as pano
→ useGetLatestPanoForCameras batch-fetches the images

The sync effect guards with a prevViewAllModeRef compare so it skips the initial mount — without that, every remount would stomp per-tile choices back to the global mode.

The batch fetch

Here's the cost problem the global mode creates: 50 visible tiles each firing their own pano request is 50 round trips at once. So when viewAllMode === '360', the per-tile fetches are short-circuited by one batch query:

const { data: batchPanoMap } = useGetLatestPanoForCameras(
viewAllMode === '360' ? pollingCameraIds : [],
viewAllMode === '360' && isTileView,
);

The result is a Map<cameraId, imageUrl> passed down as batchPanoImageUrl; a tile that receives one skips its own request entirely. Per-tile toggles (a handful of panos, not fifty) still fetch individually — the batch path only exists for the all-at-once case.

What changes inside the tile

When showPano is true, CameraDetails renders the image in a scrollable pano container, turns on the compass tick overlays, enables the pan-degree readout on the mouse tracker, and points IImage.imageRetrievalFunc at GetLatestPanoForCamera (or the batch URL). The full mechanics of how that strip scrolls — spoiler: it isn't a scroll container — are in Rendering the camera view.

Wrapping up

A single Set of ids decides which tiles are panos; the per-tile button edits one entry, the global mode rewrites the whole Set through a mount-guarded sync effect, and a batch query keeps the all-360° mode from stampeding the backend.

Next: the inline details panel — the other thing that bends the grid.