mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-12 02:43:50 +00:00
de9a7a87fd
* refactor(project structure): UI * refactor(project structure): extract utilities * refactor(project structure): remove unused * refactor(project structure): electron * refactor(project structure): server refactor: migrate to vitest refactor: monorepo config * refactor: extract application menu * refactor: exit process * refactor: extract tray menu * chore: electron build * Added Seconds in studio clock #282 --------- Co-authored-by: Fabian Posenau <fabian@fphome.de> --------- Co-authored-by: Fabian Posenau <fabian.p99@gmx.de> Co-authored-by: Fabian Posenau <fabian@fphome.de>
340 lines
10 KiB
TypeScript
340 lines
10 KiB
TypeScript
import { useCallback, useContext } from 'react';
|
|
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
|
import axios, { AxiosError } from 'axios';
|
|
import { useAtomValue } from 'jotai';
|
|
|
|
import { RUNDOWN_TABLE, RUNDOWN_TABLE_KEY } from '../api/apiConstants';
|
|
import {
|
|
ReorderEntry,
|
|
requestApplyDelay,
|
|
requestDelete,
|
|
requestDeleteAll,
|
|
requestPostEvent,
|
|
requestPutEvent,
|
|
requestReorderEvent,
|
|
} from '../api/eventsApi';
|
|
import { defaultPublicAtom, startTimeIsLastEndAtom } from '../atoms/LocalEventSettings';
|
|
import { LoggingContext } from '../context/LoggingContext';
|
|
import { OntimeRundown, OntimeRundownEntry, SupportedEvent } from '../models/EventTypes';
|
|
|
|
/**
|
|
* @description Set of utilities for events
|
|
*/
|
|
export const useEventAction = () => {
|
|
const queryClient = useQueryClient();
|
|
const { emitError } = useContext(LoggingContext);
|
|
const defaultPublic = useAtomValue(defaultPublicAtom);
|
|
const startTimeIsLastEnd = useAtomValue(startTimeIsLastEndAtom);
|
|
|
|
/**
|
|
* 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);
|
|
},
|
|
});
|
|
|
|
type AddOptions = {
|
|
defaultPublic?: boolean;
|
|
startTimeIsLastEnd?: boolean;
|
|
lastEventId?: string;
|
|
after?: string;
|
|
}
|
|
|
|
/**
|
|
* Adds an event to rundown
|
|
*/
|
|
const addEvent = useCallback(
|
|
async (event: Partial<OntimeRundownEntry>, options?: AddOptions) => {
|
|
const newEvent: Partial<OntimeRundownEntry> = { ...event };
|
|
|
|
|
|
// ************* CHECK OPTIONS
|
|
// there is an option to pass an index of an array to use as start time
|
|
// only events have options
|
|
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;
|
|
}
|
|
|
|
if (applicationOptions?.after) {
|
|
newEvent.after = applicationOptions.after;
|
|
}
|
|
}
|
|
|
|
try {
|
|
// @ts-expect-error we know that the event here is one of the defined types
|
|
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]);
|
|
},
|
|
});
|
|
|
|
/**
|
|
* 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);
|
|
},
|
|
});
|
|
|
|
/**
|
|
* 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);
|
|
},
|
|
});
|
|
|
|
/**
|
|
* 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);
|
|
},
|
|
});
|
|
|
|
/**
|
|
* 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);
|
|
},
|
|
});
|
|
|
|
/**
|
|
* 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 };
|
|
};
|