refactor(rundown): address review on the rundown scope

- extract getRundownCacheKey, the fallback to the bootstrap alias for an
  unresolved rundown id was spelled out in two readers and missing in the
  selection store, which could not resolve shift/ctrl ranges during startup
- an unresolved target is no longer reported as the loaded rundown, it was
  briefly enabling run mode and playback following before a rundown existed
- only the reader which bootstrapped drops the /current alias, a reader on
  another rundown was evicting a cache others still relied on
- the selection store returns a new Set on toggle and unselect, components
  selecting the set by reference never saw the change
- useSelectAndRevealEntry takes its rundown from the scope, callers were
  threading an id for no other reason

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011a5cbVjNC5XXF88b2PkUCa
This commit is contained in:
Claude
2026-08-29 17:21:24 +00:00
parent 802521c706
commit bc8407c0e6
10 changed files with 64 additions and 39 deletions
@@ -1,9 +1,9 @@
import { useQuery, useQueryClient } from '@tanstack/react-query';
import { EntryId, OntimeEntry, Rundown } from 'ontime-types';
import { useEffect, useMemo } from 'react';
import { useEffect, useMemo, useRef } from 'react';
import { queryRefetchIntervalSlow } from '../../ontimeConfig';
import { CURRENT_RUNDOWN_QUERY_KEY, getRundownQueryKey } from '../api/constants';
import { CURRENT_RUNDOWN_QUERY_KEY, getRundownCacheKey, getRundownQueryKey } from '../api/constants';
import { fetchCurrentRundown, fetchRundown } from '../api/rundown';
import { useRundownScope } from '../context/RundownScopeContext';
import { useSelectedEventId } from '../hooks/useSocket';
@@ -32,7 +32,7 @@ export function useRundownById(rundownId: string | null | undefined) {
const isBootstrap = id === '';
const { data, status, isError, refetch, isFetching } = useQuery<Rundown>({
queryKey: isBootstrap ? CURRENT_RUNDOWN_QUERY_KEY : getRundownQueryKey(id),
queryKey: getRundownCacheKey(id),
queryFn: ({ signal }) => (isBootstrap ? fetchCurrentRundown({ signal }) : fetchRundown(id, { signal })),
placeholderData: (previousData, _previousQuery) => previousData,
refetchInterval: queryRefetchIntervalSlow,
@@ -44,9 +44,12 @@ export function useRundownById(rundownId: string | null | undefined) {
queryClient.setQueryData(getRundownQueryKey(data.id), data);
}, [data, isBootstrap, queryClient]);
// Once we have the ID, drop the temporary current cache
// Once we have the ID, drop the temporary current cache.
// Only the reader which bootstrapped may do so, others are still relying on it.
const didBootstrap = useRef(isBootstrap);
useEffect(() => {
if (isBootstrap) return;
if (isBootstrap || !didBootstrap.current) return;
didBootstrap.current = false;
queryClient.removeQueries({ queryKey: CURRENT_RUNDOWN_QUERY_KEY, exact: true });
}, [isBootstrap, queryClient]);