mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-08 00:43:54 +00:00
bca7ad30b1
* fix: fetch in offline environments (#295) * add arm platforms to docker build --------- Co-authored-by: Fabian Posenau <fabian@fphome.de> * fix: docker build (#298) Co-authored-by: Fabian Posenau <fabian@fphome.de> * Timer: fix too many renders error when using ?progress (#305) * ux: remove duplicate showing of selected event * several small fixes and UX improvements --------- Co-authored-by: Fabian Posenau <fabian.p99@gmx.de> Co-authored-by: Fabian Posenau <fabian@fphome.de> Co-authored-by: Marks Polakovs <github@markspolakovs.me>
346 lines
10 KiB
TypeScript
346 lines
10 KiB
TypeScript
import { useCallback } from 'react';
|
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import axios, { AxiosError } from 'axios';
|
|
import { OntimeRundown, OntimeRundownEntry, SupportedEvent } from 'ontime-types';
|
|
|
|
import { RUNDOWN_TABLE, RUNDOWN_TABLE_KEY } from '../api/apiConstants';
|
|
import {
|
|
ReorderEntry,
|
|
requestApplyDelay,
|
|
requestDelete,
|
|
requestDeleteAll,
|
|
requestPostEvent,
|
|
requestPutEvent,
|
|
requestReorderEvent,
|
|
} from '../api/eventsApi';
|
|
import { useLocalEvent } from '../stores/localEvent';
|
|
import { useEmitLog } from '../stores/logger';
|
|
|
|
/**
|
|
* @description Set of utilities for events
|
|
*/
|
|
export const useEventAction = () => {
|
|
const queryClient = useQueryClient();
|
|
const { emitError } = useEmitLog();
|
|
const { eventSettings } = useLocalEvent();
|
|
const defaultPublic = eventSettings.defaultPublic;
|
|
const startTimeIsLastEnd = eventSettings.startTimeIsLastEnd;
|
|
|
|
/**
|
|
* Calls mutation to add new event
|
|
* @private
|
|
*/
|
|
const _addEventMutation = useMutation(requestPostEvent, {
|
|
// Mutation finished, failed or successful
|
|
// Fetch anyway, just to be sure
|
|
onSettled: () => {
|
|
queryClient.invalidateQueries(RUNDOWN_TABLE);
|
|
},
|
|
networkMode: 'always',
|
|
});
|
|
|
|
type BaseOptions = {
|
|
after?: string;
|
|
};
|
|
|
|
type EventOptions = BaseOptions & {
|
|
defaultPublic?: boolean;
|
|
lastEventId?: string;
|
|
startTimeIsLastEnd?: boolean;
|
|
};
|
|
|
|
/**
|
|
* Adds an event to rundown
|
|
*/
|
|
const addEvent = useCallback(
|
|
async (event: Partial<OntimeRundownEntry>, options?: EventOptions) => {
|
|
const newEvent: Partial<OntimeRundownEntry> = { ...event };
|
|
|
|
// ************* CHECK OPTIONS specific to events
|
|
if (newEvent.type === SupportedEvent.Event) {
|
|
const applicationOptions = {
|
|
defaultPublic: options?.defaultPublic ?? defaultPublic,
|
|
startTimeIsLastEnd: options?.startTimeIsLastEnd ?? startTimeIsLastEnd,
|
|
lastEventId: options?.lastEventId,
|
|
after: options?.after,
|
|
};
|
|
|
|
// hard coding duration value to be as expected for now
|
|
// this until timeOptions gets implemented
|
|
if (typeof newEvent?.timeStart !== 'undefined' && typeof newEvent.timeEnd !== 'undefined') {
|
|
newEvent.duration = Math.max(0, newEvent?.timeEnd - newEvent?.timeStart) || 0;
|
|
}
|
|
|
|
if (applicationOptions.startTimeIsLastEnd && applicationOptions?.lastEventId) {
|
|
const rundown = queryClient.getQueryData(RUNDOWN_TABLE) as OntimeRundown;
|
|
const previousEvent = rundown.find((event) => event.id === applicationOptions.lastEventId);
|
|
if (typeof previousEvent !== 'undefined' && previousEvent.type === 'event') {
|
|
newEvent.timeStart = previousEvent.timeEnd;
|
|
}
|
|
}
|
|
|
|
if (applicationOptions.defaultPublic) {
|
|
newEvent.isPublic = true;
|
|
}
|
|
}
|
|
|
|
// handle adding options that concern all event type
|
|
if (options?.after) {
|
|
newEvent.after = options.after;
|
|
}
|
|
|
|
try {
|
|
// @ts-expect-error -- we know that the object is well formed now
|
|
await _addEventMutation.mutateAsync(newEvent);
|
|
} catch (error) {
|
|
if (!axios.isAxiosError(error)) {
|
|
emitError(`Error fetching data: ${(error as AxiosError).message}`);
|
|
} else {
|
|
emitError(`Error fetching data: ${error}`);
|
|
}
|
|
}
|
|
},
|
|
[_addEventMutation, defaultPublic, emitError, queryClient, startTimeIsLastEnd],
|
|
);
|
|
|
|
/**
|
|
* Calls mutation to update existing event
|
|
* @private
|
|
*/
|
|
const _updateEventMutation = useMutation(requestPutEvent, {
|
|
// we optimistically update here
|
|
onMutate: async (newEvent) => {
|
|
// cancel ongoing queries
|
|
await queryClient.cancelQueries([RUNDOWN_TABLE_KEY, newEvent.id]);
|
|
|
|
// Snapshot the previous value
|
|
const previousEvent = queryClient.getQueryData([RUNDOWN_TABLE_KEY, newEvent.id]);
|
|
|
|
// optimistically update object
|
|
queryClient.setQueryData([RUNDOWN_TABLE_KEY, newEvent.id], newEvent);
|
|
|
|
// Return a context with the previous and new events
|
|
return { previousEvent, newEvent };
|
|
},
|
|
|
|
// Mutation fails, rollback undoes optimist update
|
|
onError: (_error, _newEvent, context) => {
|
|
queryClient.setQueryData([RUNDOWN_TABLE_KEY, context?.newEvent.id], context?.previousEvent);
|
|
},
|
|
// Mutation finished, failed or successful
|
|
// Fetch anyway, just to be sure
|
|
onSettled: async () => {
|
|
await queryClient.invalidateQueries([RUNDOWN_TABLE_KEY]);
|
|
},
|
|
networkMode: 'always',
|
|
});
|
|
|
|
/**
|
|
* Updates existing event
|
|
*/
|
|
const updateEvent = useCallback(
|
|
async (event: Partial<OntimeRundownEntry>) => {
|
|
try {
|
|
await _updateEventMutation.mutateAsync(event);
|
|
} catch (error) {
|
|
if (!axios.isAxiosError(error)) {
|
|
emitError(`Error updating event: ${(error as AxiosError).message}`);
|
|
} else {
|
|
emitError(`Error updating event: ${error}`);
|
|
}
|
|
}
|
|
},
|
|
[_updateEventMutation, emitError],
|
|
);
|
|
|
|
/**
|
|
* Calls mutation to delete an event
|
|
* @private
|
|
*/
|
|
const _deleteEventMutation = useMutation(requestDelete, {
|
|
// we optimistically update here
|
|
onMutate: async (eventId) => {
|
|
// cancel ongoing queries
|
|
await queryClient.cancelQueries([RUNDOWN_TABLE_KEY, eventId]);
|
|
|
|
// Snapshot the previous value
|
|
const previousEvents = queryClient.getQueryData(RUNDOWN_TABLE);
|
|
|
|
const filtered = [...(previousEvents as OntimeRundown)].filter((e) => e.id !== eventId);
|
|
|
|
// optimistically update object
|
|
queryClient.setQueryData(RUNDOWN_TABLE, filtered);
|
|
|
|
// Return a context with the previous and new events
|
|
return { previousEvents };
|
|
},
|
|
|
|
// Mutation fails, rollback undoes optimist update
|
|
onError: (_error, _eventId, context) => {
|
|
queryClient.setQueryData(RUNDOWN_TABLE, context?.previousEvents);
|
|
},
|
|
// Mutation finished, failed or successful
|
|
// Fetch anyway, just to be sure
|
|
onSettled: () => {
|
|
queryClient.invalidateQueries(RUNDOWN_TABLE);
|
|
},
|
|
networkMode: 'always',
|
|
});
|
|
|
|
/**
|
|
* Deletes an event form the list
|
|
*/
|
|
const deleteEvent = useCallback(
|
|
async (eventId: string) => {
|
|
try {
|
|
await _deleteEventMutation.mutateAsync(eventId);
|
|
} catch (error) {
|
|
if (!axios.isAxiosError(error)) {
|
|
emitError(`Error deleting event: ${(error as AxiosError).message}`);
|
|
} else {
|
|
emitError(`Error deleting event: ${error}`);
|
|
}
|
|
}
|
|
},
|
|
[_deleteEventMutation, emitError],
|
|
);
|
|
|
|
/**
|
|
* Calls mutation to delete all events
|
|
* @private
|
|
*/
|
|
const _deleteAllEventsMutation = useMutation(requestDeleteAll, {
|
|
// we optimistically update here
|
|
onMutate: async () => {
|
|
// cancel ongoing queries
|
|
await queryClient.cancelQueries(RUNDOWN_TABLE, { exact: true });
|
|
|
|
// Snapshot the previous value
|
|
const previousEvents = queryClient.getQueryData(RUNDOWN_TABLE);
|
|
|
|
// optimistically update object
|
|
queryClient.setQueryData(RUNDOWN_TABLE, []);
|
|
|
|
// Return a context with the previous and new events
|
|
return { previousEvents };
|
|
},
|
|
|
|
// Mutation fails, rollback undos optimist update
|
|
onError: (_error, _eventId, context) => {
|
|
queryClient.setQueryData(RUNDOWN_TABLE, context?.previousEvents);
|
|
},
|
|
// Mutation finished, failed or successful
|
|
// Fetch anyway, just to be sure
|
|
onSettled: () => {
|
|
queryClient.invalidateQueries(RUNDOWN_TABLE);
|
|
},
|
|
networkMode: 'always',
|
|
});
|
|
|
|
/**
|
|
* Deletes all events from list
|
|
*/
|
|
const deleteAllEvents = useCallback(async () => {
|
|
try {
|
|
await _deleteAllEventsMutation.mutateAsync();
|
|
} catch (error) {
|
|
if (!axios.isAxiosError(error)) {
|
|
emitError(`Error deleting events: ${(error as AxiosError).message}`);
|
|
} else {
|
|
emitError(`Error deleting events: ${error}`);
|
|
}
|
|
}
|
|
}, [_deleteAllEventsMutation, emitError]);
|
|
|
|
/**
|
|
* Calls mutation to apply a delay
|
|
* @private
|
|
*/
|
|
const _applyDelayMutation = useMutation(requestApplyDelay, {
|
|
// Mutation finished, failed or successful
|
|
onSettled: () => {
|
|
queryClient.invalidateQueries(RUNDOWN_TABLE);
|
|
},
|
|
networkMode: 'always',
|
|
});
|
|
|
|
/**
|
|
* Applies a given delay block
|
|
*/
|
|
const applyDelay = useCallback(
|
|
async (delayEventId: string) => {
|
|
try {
|
|
await _applyDelayMutation.mutateAsync(delayEventId);
|
|
} catch (error) {
|
|
if (!axios.isAxiosError(error)) {
|
|
emitError(`Error applying delay: ${(error as AxiosError).message}`);
|
|
} else {
|
|
emitError(`Error applying delay: ${error}`);
|
|
}
|
|
}
|
|
},
|
|
[_applyDelayMutation, emitError],
|
|
);
|
|
|
|
/**
|
|
* Calls mutation to reorder an event
|
|
* @private
|
|
*/
|
|
const _reorderEventMutation = useMutation(requestReorderEvent, {
|
|
// we optimistically update here
|
|
onMutate: async (data) => {
|
|
// cancel ongoing queries
|
|
await queryClient.cancelQueries(RUNDOWN_TABLE, { exact: true });
|
|
|
|
// Snapshot the previous value
|
|
const previousEvents = queryClient.getQueryData(RUNDOWN_TABLE);
|
|
|
|
const e = [...(previousEvents as OntimeRundown)];
|
|
const [reorderedItem] = e.splice(data.from, 1);
|
|
e.splice(data.to, 0, reorderedItem);
|
|
|
|
// optimistically update object
|
|
queryClient.setQueryData(RUNDOWN_TABLE, e);
|
|
|
|
// Return a context with the previous and new events
|
|
return { previousEvents };
|
|
},
|
|
|
|
// Mutation fails, rollback undoes optimist update
|
|
onError: (_error, _eventId, context) => {
|
|
queryClient.setQueryData(RUNDOWN_TABLE, context?.previousEvents);
|
|
},
|
|
// Mutation finished, failed or successful
|
|
// Fetch anyway, just to be sure
|
|
onSettled: () => {
|
|
queryClient.invalidateQueries(RUNDOWN_TABLE);
|
|
},
|
|
networkMode: 'always',
|
|
});
|
|
|
|
/**
|
|
* Reorders a given event
|
|
*/
|
|
const reorderEvent = useCallback(
|
|
async (eventId: string, from: number, to: number) => {
|
|
try {
|
|
const reorderObject: ReorderEntry = {
|
|
eventId: eventId,
|
|
from: from,
|
|
to: to,
|
|
};
|
|
await _reorderEventMutation.mutateAsync(reorderObject);
|
|
} catch (error) {
|
|
if (!axios.isAxiosError(error)) {
|
|
emitError(`Error re-ordering event: ${(error as AxiosError).message}`);
|
|
} else {
|
|
emitError(`Error re-ordering event: ${error}`);
|
|
}
|
|
}
|
|
},
|
|
[_reorderEventMutation, emitError],
|
|
);
|
|
|
|
return { addEvent, updateEvent, deleteEvent, deleteAllEvents, applyDelay, reorderEvent };
|
|
};
|