mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-31 11:59:10 +00:00
ccdfb64921
An entry revision advances on every change to that entry, which makes it a cheap marker for what has moved on in a rundown. The client did not maintain it: an optimistic entry carried the old revision, so the entry the server returned always differed from ours by that one field. That difference is enough to defeat structural sharing. A new entries object recomputes the rundown metadata, which fires the effect behind the list, which re-renders every visible row. Editing one title on a fourteen entry rundown cost eight structural and sixty-one attribute mutations, twice over. - optimistic patches apply the same revision bump the server does, so an unchanged entry keeps its identity and a refetch costs nothing to render - the rundown metadata memo keys on the fields the derivation reads, so a change of rundown revision alone no longer churns the list - editing an entry now reads the server response, which is the authority on what was applied, and only writes when it differs from what we predicted Measured on the same edit: eight to four structural, sixty-one to forty-one attribute mutations. Both changes are needed, neither helps on its own. Two bugs found on the way. A swap stored the pre increment revision, so swapped entries were indistinguishable from untouched ones. The optimistic batch edit spread the request envelope onto the entry rather than the patch, so it never applied the edit it was predicting. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011a5cbVjNC5XXF88b2PkUCa
71 lines
2.2 KiB
TypeScript
71 lines
2.2 KiB
TypeScript
import { EntryId, OntimeEntry } from 'ontime-types';
|
|
import { useMemo } from 'react';
|
|
|
|
import { useRundownScope } from '../context/RundownScopeContext';
|
|
import { useSelectedEventId } from '../hooks/useSocket';
|
|
import { getFlatRundownMetadata, getRundownMetadata } from '../utils/rundownMetadata';
|
|
import { flattenRundown, useRundownById } from './useRundownById';
|
|
|
|
/**
|
|
* Normalised rundown data for the rundown of the enclosing scope
|
|
*/
|
|
export default function useRundown() {
|
|
const { rundownId } = useRundownScope();
|
|
return useRundownById(rundownId);
|
|
}
|
|
|
|
/**
|
|
* Runtime state only describes the loaded rundown,
|
|
* a scope pointed elsewhere must not show a playing event
|
|
*/
|
|
export function useScopedSelectedEventId(): EntryId | null {
|
|
const { isLoaded } = useRundownScope();
|
|
const selectedEventId = useSelectedEventId();
|
|
return isLoaded ? selectedEventId : null;
|
|
}
|
|
|
|
export function useRundownWithMetadata() {
|
|
const { data, status } = useRundown();
|
|
const selectedEventId = useScopedSelectedEventId();
|
|
// key on the fields the derivation reads, a revision only change must not churn the list
|
|
const { entries, flatOrder } = data;
|
|
const rundownMetadata = useMemo(
|
|
() => getRundownMetadata({ entries, flatOrder }, selectedEventId),
|
|
[entries, flatOrder, selectedEventId],
|
|
);
|
|
return { data, status, rundownMetadata };
|
|
}
|
|
|
|
/**
|
|
* Provides access to a flat rundown
|
|
* built from the order and rundown fields
|
|
*/
|
|
export function useFlatRundown() {
|
|
const { data, status } = useRundown();
|
|
const flatRundown = useMemo(() => flattenRundown(data), [data]);
|
|
|
|
return { data: flatRundown, rundownId: data.id, status };
|
|
}
|
|
|
|
export function useFlatRundownWithMetadata() {
|
|
const { data, status } = useRundown();
|
|
const selectedEventId = useScopedSelectedEventId();
|
|
|
|
const { entries, flatOrder } = data;
|
|
const rundownWithMetadata = useMemo(
|
|
() => getFlatRundownMetadata({ entries, flatOrder }, selectedEventId),
|
|
[entries, flatOrder, selectedEventId],
|
|
);
|
|
return { data: rundownWithMetadata, status };
|
|
}
|
|
|
|
/**
|
|
* Hook to get a specific entry by ID from the rundown
|
|
*/
|
|
export function useEntry(entryId: EntryId | null): OntimeEntry | null {
|
|
const { data: rundown } = useRundown();
|
|
|
|
if (entryId === null) return null;
|
|
return rundown.entries[entryId] ?? null;
|
|
}
|