mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-19 14:14:17 +00:00
refactor: invalidate changed rundown
This commit is contained in:
committed by
Carlos Valente
parent
ab8da07518
commit
3bebbf9ce9
@@ -40,6 +40,7 @@ export default function useRundown() {
|
|||||||
queryClient.setQueryData(getRundownQueryKey(data.id), data);
|
queryClient.setQueryData(getRundownQueryKey(data.id), data);
|
||||||
}, [data, loadedRundownId, queryClient]);
|
}, [data, loadedRundownId, queryClient]);
|
||||||
|
|
||||||
|
// Once we have the ID, drop the temporary current cache
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!loadedRundownId) return;
|
if (!loadedRundownId) return;
|
||||||
queryClient.removeQueries({ queryKey: CURRENT_RUNDOWN_QUERY_KEY, exact: true });
|
queryClient.removeQueries({ queryKey: CURRENT_RUNDOWN_QUERY_KEY, exact: true });
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ import {
|
|||||||
Log,
|
Log,
|
||||||
MaybeNumber,
|
MaybeNumber,
|
||||||
MessageTag,
|
MessageTag,
|
||||||
ProjectRundownsList,
|
|
||||||
RefetchKey,
|
RefetchKey,
|
||||||
Rundown,
|
|
||||||
RuntimeStore,
|
RuntimeStore,
|
||||||
WsPacketToClient,
|
WsPacketToClient,
|
||||||
WsPacketToServer,
|
WsPacketToServer,
|
||||||
@@ -17,15 +15,16 @@ import {
|
|||||||
CLIENT_LIST,
|
CLIENT_LIST,
|
||||||
CSS_OVERRIDE,
|
CSS_OVERRIDE,
|
||||||
CUSTOM_FIELDS,
|
CUSTOM_FIELDS,
|
||||||
PROJECT_DATA,
|
|
||||||
CURRENT_RUNDOWN_QUERY_KEY,
|
CURRENT_RUNDOWN_QUERY_KEY,
|
||||||
PROJECT_RUNDOWNS,
|
PROJECT_DATA,
|
||||||
REPORT,
|
REPORT,
|
||||||
|
RUNDOWN,
|
||||||
RUNTIME,
|
RUNTIME,
|
||||||
TRANSLATION,
|
TRANSLATION,
|
||||||
URL_PRESETS,
|
URL_PRESETS,
|
||||||
VIEW_SETTINGS,
|
VIEW_SETTINGS,
|
||||||
getRundownQueryKey,
|
getRundownQueryKey,
|
||||||
|
PROJECT_RUNDOWNS,
|
||||||
} from '../api/constants';
|
} from '../api/constants';
|
||||||
import { invalidateAllCaches } from '../api/utils';
|
import { invalidateAllCaches } from '../api/utils';
|
||||||
import { ontimeQueryClient } from '../queryClient';
|
import { ontimeQueryClient } from '../queryClient';
|
||||||
@@ -181,7 +180,7 @@ export const connectSocket = () => {
|
|||||||
}
|
}
|
||||||
case MessageTag.Refetch: {
|
case MessageTag.Refetch: {
|
||||||
// the refetch message signals that the rundown has changed in the server side
|
// the refetch message signals that the rundown has changed in the server side
|
||||||
const { target, revision } = payload;
|
const { target, revision, rundownId } = payload;
|
||||||
switch (target) {
|
switch (target) {
|
||||||
case RefetchKey.All:
|
case RefetchKey.All:
|
||||||
invalidateAllCaches();
|
invalidateAllCaches();
|
||||||
@@ -196,7 +195,7 @@ export const connectSocket = () => {
|
|||||||
ontimeQueryClient.invalidateQueries({ queryKey: REPORT });
|
ontimeQueryClient.invalidateQueries({ queryKey: REPORT });
|
||||||
break;
|
break;
|
||||||
case RefetchKey.Rundown: {
|
case RefetchKey.Rundown: {
|
||||||
maybeInvalidateRundownCache(revision);
|
maybeInvalidateRundownCache(revision, rundownId);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case RefetchKey.UrlPresets:
|
case RefetchKey.UrlPresets:
|
||||||
@@ -232,34 +231,28 @@ export const connectSocket = () => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
export function maybeInvalidateRundownCache(revision: MaybeNumber, rundownId?: string) {
|
||||||
* When we receive a refetch message for the rundown
|
if (!rundownId) {
|
||||||
* check which rundown needs to be invalidated
|
// we omit rundownId to signify invalidate all rundowns
|
||||||
*/
|
ontimeQueryClient.invalidateQueries({ queryKey: RUNDOWN });
|
||||||
export function maybeInvalidateRundownCache(revision: MaybeNumber) {
|
ontimeQueryClient.invalidateQueries({ queryKey: CURRENT_RUNDOWN_QUERY_KEY, exact: true });
|
||||||
const loadedRundownId: string | undefined = (ontimeQueryClient.getQueryData(PROJECT_RUNDOWNS) as ProjectRundownsList)
|
|
||||||
?.loaded;
|
|
||||||
|
|
||||||
const activeRundownQueryKey = loadedRundownId ? getRundownQueryKey(loadedRundownId) : CURRENT_RUNDOWN_QUERY_KEY;
|
|
||||||
const cachedRundown = ontimeQueryClient.getQueryData<Rundown>(activeRundownQueryKey);
|
|
||||||
if (revision === cachedRundown?.revision) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ontimeQueryClient.invalidateQueries({ queryKey: activeRundownQueryKey, exact: true });
|
// skip if we dont recognise the ID the revision is lower
|
||||||
|
const queryKey = getRundownQueryKey(rundownId);
|
||||||
if (loadedRundownId) {
|
const cachedRundown = ontimeQueryClient.getQueryData<{ revision: number }>(queryKey);
|
||||||
// Keep bootstrap alias in sync with the ID-based cache
|
if (revision !== null && revision === cachedRundown?.revision) {
|
||||||
ontimeQueryClient.invalidateQueries({ queryKey: CURRENT_RUNDOWN_QUERY_KEY, exact: true });
|
return;
|
||||||
} else {
|
|
||||||
// During bootstrap, loadedRundownId is not yet known.
|
|
||||||
// Invalidate any ID-based rundown caches that may have been seeded early.
|
|
||||||
ontimeQueryClient.invalidateQueries({
|
|
||||||
predicate: (query) => query.queryKey[0] === 'rundown' && query.queryKey[1] !== 'current',
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ontimeQueryClient.invalidateQueries({ queryKey: CUSTOM_FIELDS });
|
ontimeQueryClient.invalidateQueries({ queryKey, exact: true });
|
||||||
|
|
||||||
|
// keep current alias in sync with the ID-based cache
|
||||||
|
const loadedRundownId = ontimeQueryClient.getQueryData<{ loaded: string }>(PROJECT_RUNDOWNS)?.loaded;
|
||||||
|
if (!loadedRundownId || loadedRundownId === rundownId) {
|
||||||
|
ontimeQueryClient.invalidateQueries({ queryKey: CURRENT_RUNDOWN_QUERY_KEY, exact: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function sendSocket<T extends MessageTag | ApiActionTag>(
|
export function sendSocket<T extends MessageTag | ApiActionTag>(
|
||||||
|
|||||||
@@ -262,6 +262,6 @@ export const socket = new SocketServer();
|
|||||||
/**
|
/**
|
||||||
* Utility function to notify clients that the REST data is stale
|
* Utility function to notify clients that the REST data is stale
|
||||||
*/
|
*/
|
||||||
export function sendRefetch(target: RefetchKey, revision: MaybeNumber = null) {
|
export function sendRefetch(target: RefetchKey, revision: MaybeNumber = null, rundownId?: string) {
|
||||||
socket.sendAsJson(MessageTag.Refetch, { target, revision });
|
socket.sendAsJson(MessageTag.Refetch, { target, revision, rundownId });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ let rundownMetadata: RundownMetadata = {
|
|||||||
let projectCustomFields: CustomFields = {};
|
let projectCustomFields: CustomFields = {};
|
||||||
|
|
||||||
export const getCurrentRundown = (): Readonly<Rundown> => cachedRundown;
|
export const getCurrentRundown = (): Readonly<Rundown> => cachedRundown;
|
||||||
|
export const getCurrentRundownId = (): string => cachedRundown.id;
|
||||||
export const getRundownMetadata = (): Readonly<RundownMetadata> => rundownMetadata;
|
export const getRundownMetadata = (): Readonly<RundownMetadata> => rundownMetadata;
|
||||||
export const getProjectCustomFields = (): Readonly<CustomFields> => projectCustomFields;
|
export const getProjectCustomFields = (): Readonly<CustomFields> => projectCustomFields;
|
||||||
export const getEntryWithId = (entryId: EntryId): OntimeEntry | undefined => cachedRundown.entries[entryId];
|
export const getEntryWithId = (entryId: EntryId): OntimeEntry | undefined => cachedRundown.entries[entryId];
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import { updateRundownData } from '../../stores/runtimeState.js';
|
|||||||
import {
|
import {
|
||||||
createTransaction,
|
createTransaction,
|
||||||
customFieldMutation,
|
customFieldMutation,
|
||||||
getCurrentRundown,
|
getCurrentRundownId,
|
||||||
rundownCache,
|
rundownCache,
|
||||||
rundownMutation,
|
rundownMutation,
|
||||||
updateBackgroundRundown,
|
updateBackgroundRundown,
|
||||||
@@ -74,7 +74,7 @@ export async function addEntry(eventData: EventPostPayload): Promise<OntimeEntry
|
|||||||
updateRuntimeOnChange(rundownMetadata);
|
updateRuntimeOnChange(rundownMetadata);
|
||||||
|
|
||||||
// notify timer and external services of change
|
// notify timer and external services of change
|
||||||
notifyChanges(rundownMetadata, revision, { timer: [newEntry.id], external: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, { timer: [newEntry.id], external: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
return newEntry;
|
return newEntry;
|
||||||
@@ -118,7 +118,10 @@ export async function editEntry(patch: PatchWithId): Promise<OntimeEntry> {
|
|||||||
updateRuntimeOnChange(rundownMetadata);
|
updateRuntimeOnChange(rundownMetadata);
|
||||||
|
|
||||||
// notify timer and external services of change
|
// notify timer and external services of change
|
||||||
notifyChanges(rundownMetadata, revision, { timer: didInvalidate ? true : [entry.id], external: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, {
|
||||||
|
timer: didInvalidate ? true : [entry.id],
|
||||||
|
external: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return entry;
|
return entry;
|
||||||
@@ -181,7 +184,10 @@ export async function batchEditEntries(ids: EntryId[], patch: Partial<OntimeEntr
|
|||||||
updateRuntimeOnChange(rundownMetadata);
|
updateRuntimeOnChange(rundownMetadata);
|
||||||
|
|
||||||
// notify timer and external services of change
|
// notify timer and external services of change
|
||||||
notifyChanges(rundownMetadata, revision, { timer: batchDidInvalidate ? true : changedIds, external: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, {
|
||||||
|
timer: batchDidInvalidate ? true : changedIds,
|
||||||
|
external: true,
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return rundownResult;
|
return rundownResult;
|
||||||
@@ -209,7 +215,7 @@ export async function deleteEntries(entryIds: EntryId[]): Promise<Rundown> {
|
|||||||
updateRuntimeOnChange(rundownMetadata);
|
updateRuntimeOnChange(rundownMetadata);
|
||||||
|
|
||||||
// notify timer and external services of change
|
// notify timer and external services of change
|
||||||
notifyChanges(rundownMetadata, revision, { timer: entryIds, external: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, { timer: entryIds, external: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
return rundownResult;
|
return rundownResult;
|
||||||
@@ -231,7 +237,7 @@ export async function deleteAllEntries(): Promise<Rundown> {
|
|||||||
updateRuntimeOnChange(rundownMetadata);
|
updateRuntimeOnChange(rundownMetadata);
|
||||||
|
|
||||||
// notify timer and external services of change
|
// notify timer and external services of change
|
||||||
notifyChanges(rundownMetadata, revision, { timer: true, external: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, { timer: true, external: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
return rundownResult;
|
return rundownResult;
|
||||||
@@ -263,7 +269,7 @@ export async function reorderEntry(entryId: EntryId, destinationId: EntryId, ord
|
|||||||
updateRuntimeOnChange(rundownMetadata);
|
updateRuntimeOnChange(rundownMetadata);
|
||||||
|
|
||||||
// notify timer and external services of change
|
// notify timer and external services of change
|
||||||
notifyChanges(rundownMetadata, revision, { timer: true, external: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, { timer: true, external: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
return rundownResult;
|
return rundownResult;
|
||||||
@@ -287,7 +293,7 @@ export function renumberEntries(ids: EntryId[], prefix: string, start: string, i
|
|||||||
|
|
||||||
setImmediate(() => {
|
setImmediate(() => {
|
||||||
updateRuntimeOnChange(rundownMetadata);
|
updateRuntimeOnChange(rundownMetadata);
|
||||||
notifyChanges(rundownMetadata, revision, { timer: ids, external: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, { timer: ids, external: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
return rundownResult;
|
return rundownResult;
|
||||||
@@ -318,7 +324,7 @@ export async function applyDelay(delayId: EntryId): Promise<Rundown> {
|
|||||||
updateRuntimeOnChange(rundownMetadata);
|
updateRuntimeOnChange(rundownMetadata);
|
||||||
|
|
||||||
// notify timer and external services of change
|
// notify timer and external services of change
|
||||||
notifyChanges(rundownMetadata, revision, { timer: true, external: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, { timer: true, external: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
return rundownResult;
|
return rundownResult;
|
||||||
@@ -351,7 +357,7 @@ export async function swapEvents(fromId: EntryId, toId: EntryId): Promise<Rundow
|
|||||||
updateRuntimeOnChange(rundownMetadata);
|
updateRuntimeOnChange(rundownMetadata);
|
||||||
|
|
||||||
// notify timer and external services of change
|
// notify timer and external services of change
|
||||||
notifyChanges(rundownMetadata, revision, { timer: true, external: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, { timer: true, external: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
return rundownResult;
|
return rundownResult;
|
||||||
@@ -380,11 +386,11 @@ export async function cloneEntry(entryId: EntryId, options: InsertOptions): Prom
|
|||||||
|
|
||||||
// notify timer and external services of change
|
// notify timer and external services of change
|
||||||
if (isOntimeGroup(newEntry)) {
|
if (isOntimeGroup(newEntry)) {
|
||||||
notifyChanges(rundownMetadata, revision, { timer: newEntry.entries, external: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, { timer: newEntry.entries, external: true });
|
||||||
} else if (isOntimeEvent(newEntry)) {
|
} else if (isOntimeEvent(newEntry)) {
|
||||||
notifyChanges(rundownMetadata, revision, { timer: [newEntry.id], external: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, { timer: [newEntry.id], external: true });
|
||||||
} else if (isOntimeDelay(newEntry)) {
|
} else if (isOntimeDelay(newEntry)) {
|
||||||
notifyChanges(rundownMetadata, revision, { external: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, { external: true });
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -406,7 +412,7 @@ export async function groupEntries(entryIds: EntryId[]): Promise<Rundown> {
|
|||||||
updateRuntimeOnChange(rundownMetadata);
|
updateRuntimeOnChange(rundownMetadata);
|
||||||
|
|
||||||
// we need to notify the timer since we might be grouping a running event
|
// we need to notify the timer since we might be grouping a running event
|
||||||
notifyChanges(rundownMetadata, revision, { external: true, timer: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, { external: true, timer: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
return rundownResult;
|
return rundownResult;
|
||||||
@@ -432,7 +438,7 @@ export async function ungroupEntries(groupId: EntryId): Promise<Rundown> {
|
|||||||
updateRuntimeOnChange(rundownMetadata);
|
updateRuntimeOnChange(rundownMetadata);
|
||||||
|
|
||||||
// we dont need to notify the timer since the grouping does not affect the runtime
|
// we dont need to notify the timer since the grouping does not affect the runtime
|
||||||
notifyChanges(rundownMetadata, revision, { external: true });
|
notifyChanges(rundown.id, rundownMetadata, revision, { external: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
return rundownResult;
|
return rundownResult;
|
||||||
@@ -522,7 +528,7 @@ export async function editCustomField(
|
|||||||
// schedule the side effects
|
// schedule the side effects
|
||||||
setImmediate(() => {
|
setImmediate(() => {
|
||||||
sendRefetch(RefetchKey.CustomFields);
|
sendRefetch(RefetchKey.CustomFields);
|
||||||
notifyChanges(rundownMetadata, revision, { timer: true, external: true });
|
notifyChanges(undefined, rundownMetadata, revision, { timer: true, external: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
return resultCustomFields;
|
return resultCustomFields;
|
||||||
@@ -561,7 +567,7 @@ export async function deleteCustomField(key: CustomFieldKey, projectRundowns: Pr
|
|||||||
// schedule the side effects
|
// schedule the side effects
|
||||||
setImmediate(() => {
|
setImmediate(() => {
|
||||||
sendRefetch(RefetchKey.CustomFields);
|
sendRefetch(RefetchKey.CustomFields);
|
||||||
notifyChanges(rundownMetadata, revision, { timer: true, external: true });
|
notifyChanges(undefined, rundownMetadata, revision, { timer: true, external: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
return resultCustomFields;
|
return resultCustomFields;
|
||||||
@@ -592,10 +598,17 @@ type NotifyChangesOptions = {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify services of changes in the rundown
|
* Notify services of changes in the rundown
|
||||||
|
* TODO: we could receive a runtime flag to call updateRuntimeOnChange
|
||||||
|
* instead of having it in every consumer
|
||||||
*/
|
*/
|
||||||
function notifyChanges(rundownMetadata: RundownMetadata, revision: number, options: NotifyChangesOptions) {
|
function notifyChanges(
|
||||||
// notify timer service of changed events
|
rundownId: string | undefined,
|
||||||
if (options.timer) {
|
rundownMetadata: RundownMetadata,
|
||||||
|
revision: number,
|
||||||
|
options: NotifyChangesOptions,
|
||||||
|
) {
|
||||||
|
// notify timer service of changed event
|
||||||
|
if (options.timer && rundownId && isCurrentRundown(rundownId)) {
|
||||||
runtimeService.notifyOfChangedEvents(rundownMetadata);
|
runtimeService.notifyOfChangedEvents(rundownMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -603,18 +616,23 @@ function notifyChanges(rundownMetadata: RundownMetadata, revision: number, optio
|
|||||||
if (options.reload) {
|
if (options.reload) {
|
||||||
sendRefetch(RefetchKey.All);
|
sendRefetch(RefetchKey.All);
|
||||||
} else if (options.external) {
|
} else if (options.external) {
|
||||||
sendRefetch(RefetchKey.Rundown, revision);
|
sendRefetch(RefetchKey.Rundown, revision, rundownId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isCurrentRundown(id: string) {
|
||||||
|
return id === getCurrentRundownId();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws if the provided id does not exist
|
* @throws if the provided id does not exist
|
||||||
*/
|
*/
|
||||||
export async function loadRundown(id: string) {
|
export async function loadRundown(id: string) {
|
||||||
const dataProvider = getDataProvider();
|
const dataProvider = getDataProvider();
|
||||||
if (id === getCurrentRundown().id) {
|
if (isCurrentRundown(id)) {
|
||||||
return dataProvider.getProjectRundowns();
|
return dataProvider.getProjectRundowns();
|
||||||
}
|
}
|
||||||
|
|
||||||
const rundown = dataProvider.getRundown(id);
|
const rundown = dataProvider.getRundown(id);
|
||||||
const customField = dataProvider.getCustomFields();
|
const customField = dataProvider.getCustomFields();
|
||||||
await initRundown(rundown, customField);
|
await initRundown(rundown, customField);
|
||||||
@@ -637,7 +655,7 @@ export async function initRundown(
|
|||||||
updateRuntimeOnChange(rundownMetadata);
|
updateRuntimeOnChange(rundownMetadata);
|
||||||
|
|
||||||
setImmediate(() => {
|
setImmediate(() => {
|
||||||
notifyChanges(rundownMetadata, revision, { timer: true, external: true, reload });
|
notifyChanges(rundown.id, rundownMetadata, revision, { timer: true, external: true, reload });
|
||||||
setLastLoadedRundown(rundown.id).catch((error) => {
|
setLastLoadedRundown(rundown.id).catch((error) => {
|
||||||
logger.error(LogOrigin.Server, `Failed to persist last loaded rundown: ${error}`);
|
logger.error(LogOrigin.Server, `Failed to persist last loaded rundown: ${error}`);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ type RefetchPacket = {
|
|||||||
payload: {
|
payload: {
|
||||||
target: RefetchKey;
|
target: RefetchKey;
|
||||||
revision: MaybeNumber;
|
revision: MaybeNumber;
|
||||||
|
rundownId?: string; // undefined means refetch all rundowns
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user