Files
ontime/client/src/common/hooks/useEventAction.js
T
Carlos Valente 6720626bd3 v2 event editor (#265)
* style: add cursor indicator in all blocks

* style: prevent reflows on width

* style: harmonise white range

* ux: focus elements on cursor nav

* ux: prevent focus on indicators

* refactor: convert to typescript

* fix: missing dependency

* chore: add linter rules for dependency arrays

* style: style editable component

* style: tweak input styles

* style: tweak switch styles

* style: style tweaks
2022-12-08 22:22:08 +01:00

289 lines
8.5 KiB
JavaScript

import { useCallback, useContext } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { RUNDOWN_TABLE, RUNDOWN_TABLE_KEY } from '../api/apiConstants';
import {
requestApplyDelay,
requestDelete,
requestDeleteAll,
requestPostEvent,
requestPutEvent,
requestReorderEvent,
} from '../api/eventsApi';
import { LoggingContext } from '../context/LoggingContext';
/**
* @description Set of utilities for events
*/
export const useEventAction = () => {
const queryClient = useQueryClient();
const { emitError } = useContext(LoggingContext);
/**
* @description 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);
},
});
/**
* @description Adds new event to list
* @param {object} event - Event to be added
* @param {object} [options] - Event options
*/
const addEvent = useCallback(
async (event, options) => {
const newEvent = { ...event };
// ************* CHECK OPTIONS
// there is an option to pass an index of an array to use as start time
if (typeof options?.startIsLastEnd !== 'undefined') {
const events = queryClient.getQueryData(RUNDOWN_TABLE);
const previousEvent = events.find((event) => event.id === options.startIsLastEnd);
newEvent.timeStart = previousEvent.timeEnd || 0;
}
// hard coding duration value to be as expected for now
// this until timeOptions gets implemented
if (newEvent.type === 'event') {
newEvent.duration = Math.max(0, newEvent.timeEnd - newEvent.timeStart) || 0;
}
try {
await _addEventMutation.mutateAsync(newEvent);
} catch (error) {
emitError(`Error fetching data: ${error.message}`);
}
},
[_addEventMutation, emitError, queryClient],
);
/**
* @description 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 (newEvent) => {
await queryClient.invalidateQueries([RUNDOWN_TABLE_KEY, newEvent.id]);
},
});
/**
* @description Updates existing event
* @param {object} event - Event to be added
*/
const updateEvent = useCallback(
async (event) => {
try {
await _updateEventMutation.mutateAsync(event);
} catch (error) {
emitError(`Error updating event: ${error.message}`);
}
},
[_updateEventMutation, emitError],
);
/**
* @description 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].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);
},
});
/**
* @description Deletes an event form the list
* @param {object} eventId - Event to be deleted
*/
const deleteEvent = useCallback(
async (eventId) => {
try {
await _deleteEventMutation.mutateAsync(eventId);
} catch (error) {
emitError(`Error deleting event: ${error.message}`);
}
},
[_deleteEventMutation, emitError],
);
/**
* @description 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);
const clear = [];
// optimistically update object
queryClient.setQueryData(RUNDOWN_TABLE, clear);
// 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);
},
});
/**
* @description Deletes all events from list
*/
const deleteAllEvents = useCallback(async () => {
try {
await _deleteAllEventsMutation.mutateAsync();
} catch (error) {
emitError(`Error deleting events: ${error.message}`);
}
}, [_deleteAllEventsMutation, emitError]);
/**
* @description Calls mutation to apply a delay
* @private
*/
const _applyDelayMutation = useMutation(requestApplyDelay, {
// Mutation finished, failed or successful
onSettled: () => {
queryClient.invalidateQueries(RUNDOWN_TABLE);
},
});
/**
* @description Applies a given delay
* @param {object} delayEventId - Id of delay to be applied
*/
const applyDelay = useCallback(
async (delayEventId) => {
try {
await _applyDelayMutation.mutateAsync(delayEventId);
} catch (error) {
emitError(`Error applying delay: ${error.message}`);
}
},
[_applyDelayMutation, emitError],
);
/**
* @description 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];
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);
},
});
/**
* @description Reorders a given event
* @param {string} eventID - ID of event to reorder
* @param {number} from - Current index
* @param {number} to - New Index
*/
const reorderEvent = useCallback(
async (eventId, from, to) => {
try {
const reorderObject = {
eventId: eventId,
from: from,
to: to,
};
await _reorderEventMutation.mutateAsync(reorderObject);
} catch (error) {
emitError(`Error re-ordering event: ${error.message}`);
}
},
[_reorderEventMutation, emitError],
);
return { addEvent, updateEvent, deleteEvent, deleteAllEvents, applyDelay, reorderEvent };
};