mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-18 05:34:09 +00:00
Swap Event Data (#446)
* use zustand store in `ContextMenuContext` * add `withDivider` prop to context menu `Option` * add `isDisabled` prop to `Option` * create `eventIdSwapping` store * create swapping context menu options * fix `key` in ContextMenu * address `tanstack-eslint` errors * create initial `requestEventSwap` event * create initial `swapEvents` action * use `swapEvents` in `EventBlock` * move from `emitError` to `logAxiosError` * remove extra curly brace from Copy ID * add `clearEventId` func * finalize context menu swapping logic * write optimistic swapping logic * remove unused import and type out handler in `rundownController` * create `rundownSwapValidator` * move index increase to `EventBlock` * move swapping logic from `id` to `index` * create `swapEvents` endpoint * move `useEventIdSwapping` hook into its own file * revert to using event `id` * logic now uses indexes to swap events * add `todo` to `swapEvents` * revert index increment * create `swapOntimeEvents` and export in `index.ts` * use `swapOntimeEvents` in frontend & server * update import path * remove extra `setCached`
This commit is contained in:
@@ -1,22 +1,16 @@
|
||||
import { MouseEvent, useContext } from 'react';
|
||||
import { MouseEvent } from 'react';
|
||||
|
||||
import { ContextMenuContext, Option } from '../context/ContextMenuContext';
|
||||
import { Option, useContextMenuStore } from '../components/context-menu/ContextMenu';
|
||||
|
||||
export const useContextMenu = <T extends HTMLElement>(options: Option[]) => {
|
||||
const contextMenuContext = useContext(ContextMenuContext);
|
||||
|
||||
if (contextMenuContext === null) {
|
||||
throw new Error('useContextMenu should be wrapped by ContextMenuProvider');
|
||||
}
|
||||
|
||||
const { createContextMenu } = contextMenuContext;
|
||||
const { setContextMenu } = useContextMenuStore();
|
||||
|
||||
const localCreateContextMenu = (contextMenuEvent: MouseEvent<T, globalThis.MouseEvent>) => {
|
||||
// prevent browser default context menu from showing up
|
||||
contextMenuEvent.preventDefault();
|
||||
|
||||
const { pageX, pageY } = contextMenuEvent;
|
||||
return createContextMenu(options, { x: pageX, y: pageY });
|
||||
return setContextMenu({ x: pageX, y: pageY }, options);
|
||||
};
|
||||
|
||||
return [localCreateContextMenu];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback } from 'react';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { OntimeRundown, OntimeRundownEntry, SupportedEvent } from 'ontime-types';
|
||||
import { swapOntimeEvents } from 'ontime-utils';
|
||||
|
||||
import { RUNDOWN_TABLE, RUNDOWN_TABLE_KEY } from '../api/apiConstants';
|
||||
import { logAxiosError } from '../api/apiUtils';
|
||||
@@ -9,9 +10,11 @@ import {
|
||||
requestApplyDelay,
|
||||
requestDelete,
|
||||
requestDeleteAll,
|
||||
requestEventSwap,
|
||||
requestPostEvent,
|
||||
requestPutEvent,
|
||||
requestReorderEvent,
|
||||
SwapEntry,
|
||||
} from '../api/eventsApi';
|
||||
import { useEditorSettings } from '../stores/editorSettings';
|
||||
|
||||
@@ -28,9 +31,10 @@ export const useEventAction = () => {
|
||||
* Calls mutation to add new event
|
||||
* @private
|
||||
*/
|
||||
const _addEventMutation = useMutation(requestPostEvent, {
|
||||
const _addEventMutation = useMutation({
|
||||
// Mutation finished, failed or successful
|
||||
// Fetch anyway, just to be sure
|
||||
mutationFn: requestPostEvent,
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(RUNDOWN_TABLE);
|
||||
},
|
||||
@@ -102,7 +106,8 @@ export const useEventAction = () => {
|
||||
* Calls mutation to update existing event
|
||||
* @private
|
||||
*/
|
||||
const _updateEventMutation = useMutation(requestPutEvent, {
|
||||
const _updateEventMutation = useMutation({
|
||||
mutationFn: requestPutEvent,
|
||||
// we optimistically update here
|
||||
onMutate: async (newEvent) => {
|
||||
// cancel ongoing queries
|
||||
@@ -117,11 +122,11 @@ export const useEventAction = () => {
|
||||
// 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 () => {
|
||||
@@ -148,7 +153,8 @@ export const useEventAction = () => {
|
||||
* Calls mutation to delete an event
|
||||
* @private
|
||||
*/
|
||||
const _deleteEventMutation = useMutation(requestDelete, {
|
||||
const _deleteEventMutation = useMutation({
|
||||
mutationFn: requestDelete,
|
||||
// we optimistically update here
|
||||
onMutate: async (eventId) => {
|
||||
// cancel ongoing queries
|
||||
@@ -196,7 +202,8 @@ export const useEventAction = () => {
|
||||
* Calls mutation to delete all events
|
||||
* @private
|
||||
*/
|
||||
const _deleteAllEventsMutation = useMutation(requestDeleteAll, {
|
||||
const _deleteAllEventsMutation = useMutation({
|
||||
mutationFn: requestDeleteAll,
|
||||
// we optimistically update here
|
||||
onMutate: async () => {
|
||||
// cancel ongoing queries
|
||||
@@ -239,7 +246,8 @@ export const useEventAction = () => {
|
||||
* Calls mutation to apply a delay
|
||||
* @private
|
||||
*/
|
||||
const _applyDelayMutation = useMutation(requestApplyDelay, {
|
||||
const _applyDelayMutation = useMutation({
|
||||
mutationFn: requestApplyDelay,
|
||||
// Mutation finished, failed or successful
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(RUNDOWN_TABLE);
|
||||
@@ -265,7 +273,8 @@ export const useEventAction = () => {
|
||||
* Calls mutation to reorder an event
|
||||
* @private
|
||||
*/
|
||||
const _reorderEventMutation = useMutation(requestReorderEvent, {
|
||||
const _reorderEventMutation = useMutation({
|
||||
mutationFn: requestReorderEvent,
|
||||
// we optimistically update here
|
||||
onMutate: async (data) => {
|
||||
// cancel ongoing queries
|
||||
@@ -316,5 +325,67 @@ export const useEventAction = () => {
|
||||
[_reorderEventMutation],
|
||||
);
|
||||
|
||||
return { addEvent, updateEvent, deleteEvent, deleteAllEvents, applyDelay, reorderEvent };
|
||||
/**
|
||||
* Calls mutation to swap events
|
||||
* @private
|
||||
*/
|
||||
const _swapEvents = useMutation({
|
||||
mutationFn: requestEventSwap,
|
||||
// we optimistically update here
|
||||
onMutate: async ({ from, to }) => {
|
||||
// cancel ongoing queries
|
||||
await queryClient.cancelQueries(RUNDOWN_TABLE, { exact: true });
|
||||
|
||||
// Snapshot the previous value
|
||||
const rundown = queryClient.getQueryData(RUNDOWN_TABLE) as OntimeRundown;
|
||||
|
||||
const fromEventIndex = rundown.findIndex((event) => event.id === from);
|
||||
const toEventIndex = rundown.findIndex((event) => event.id === to);
|
||||
|
||||
const previousEvents = swapOntimeEvents(rundown, fromEventIndex, toEventIndex);
|
||||
|
||||
// optimistically update object
|
||||
queryClient.setQueryData(RUNDOWN_TABLE, previousEvents);
|
||||
|
||||
// Return a context with the previous 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',
|
||||
});
|
||||
|
||||
/**
|
||||
* Swaps the schedule of two events
|
||||
*/
|
||||
const swapEvents = useCallback(
|
||||
async ({ from, to }: SwapEntry) => {
|
||||
// TODO: before calling `/swapEvents`,
|
||||
// we should determine the events are of type `OntimeEvent`
|
||||
try {
|
||||
await _swapEvents.mutateAsync({ from, to });
|
||||
} catch (error) {
|
||||
logAxiosError('Error re-ordering event', error);
|
||||
}
|
||||
},
|
||||
[_swapEvents],
|
||||
);
|
||||
|
||||
return {
|
||||
addEvent,
|
||||
updateEvent,
|
||||
deleteEvent,
|
||||
deleteAllEvents,
|
||||
applyDelay,
|
||||
reorderEvent,
|
||||
swapEvents,
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user