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:
asharonbaltazar
2023-08-15 16:04:53 -04:00
committed by GitHub
parent a46a0e1631
commit 51c31adaf1
17 changed files with 363 additions and 123 deletions
@@ -1,4 +1,3 @@
import { OntimeEvent } from 'ontime-types';
import { failEmptyObjects } from '../utils/routerUtils.js';
import {
addEvent,
@@ -7,19 +6,21 @@ import {
deleteEvent,
editEvent,
reorderEvent,
swapEvents,
} from '../services/rundown-service/RundownService.js';
import { getDelayedRundown } from '../services/rundown-service/delayedRundown.utils.js';
import { RequestHandler } from 'express';
// Create controller for GET request to '/events'
// Returns -
export const rundownGetAll = async (req, res) => {
export const rundownGetAll: RequestHandler = async (_req, res) => {
const delayedRundown = getDelayedRundown();
res.json(delayedRundown);
};
// Create controller for POST request to '/events/'
// Returns -
export const rundownPost = async (req, res) => {
export const rundownPost: RequestHandler = async (req, res) => {
if (failEmptyObjects(req.body, res)) {
return;
}
@@ -34,7 +35,7 @@ export const rundownPost = async (req, res) => {
// Create controller for PUT request to '/events/'
// Returns -
export const rundownPut = async (req, res) => {
export const rundownPut: RequestHandler = async (req, res) => {
if (failEmptyObjects(req.body, res)) {
return;
}
@@ -47,7 +48,7 @@ export const rundownPut = async (req, res) => {
}
};
export const rundownReorder = async (req, res) => {
export const rundownReorder: RequestHandler = async (req, res) => {
if (failEmptyObjects(req.body, res)) {
return;
}
@@ -61,9 +62,23 @@ export const rundownReorder = async (req, res) => {
}
};
export const rundownSwap: RequestHandler = async (req, res) => {
if (failEmptyObjects(req.body, res)) {
return;
}
try {
const { from, to } = req.body;
await swapEvents(from, to);
res.sendStatus(200);
} catch (error) {
res.status(400).send(error);
}
};
// Create controller for PATCH request to '/events/applydelay/:eventId'
// Returns -
export const rundownApplyDelay = async (req, res) => {
export const rundownApplyDelay: RequestHandler = async (req, res) => {
try {
await applyDelay(req.params.eventId);
res.sendStatus(200);
@@ -74,7 +89,7 @@ export const rundownApplyDelay = async (req, res) => {
// Create controller for DELETE request to '/events/:eventId'
// Returns -
export const deleteEventById = async (req, res) => {
export const deleteEventById: RequestHandler = async (req, res) => {
try {
await deleteEvent(req.params.eventId);
res.sendStatus(204);
@@ -85,7 +100,7 @@ export const deleteEventById = async (req, res) => {
// Create controller for DELETE request to '/events/'
// Returns -
export const rundownDelete = async (req, res) => {
export const rundownDelete: RequestHandler = async (req, res) => {
try {
await deleteAllEvents();
res.sendStatus(204);
@@ -29,6 +29,16 @@ export const rundownReorderValidator = [
},
];
export const rundownSwapValidator = [
body('from').isString().exists(),
body('to').isString().exists(),
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) return res.status(422).json({ errors: errors.array() });
next();
},
];
export const paramsMustHaveEventId = [
param('eventId').exists(),
(req, res, next) => {
+4
View File
@@ -7,12 +7,14 @@ import {
rundownPost,
rundownPut,
rundownReorder,
rundownSwap,
} from '../controllers/rundownController.js';
import {
paramsMustHaveEventId,
rundownPostValidator,
rundownPutValidator,
rundownReorderValidator,
rundownSwapValidator,
} from '../controllers/rundownController.validate.js';
export const router = express.Router();
@@ -29,6 +31,8 @@ router.put('/', rundownPutValidator, rundownPut);
// create route between controller and '/events/reorder' endpoint
router.patch('/reorder/', rundownReorderValidator, rundownReorder);
router.patch('/swap', rundownSwapValidator, rundownSwap);
// create route between controller and '/events/applydelay/:eventId' endpoint
router.patch('/applydelay/:eventId', paramsMustHaveEventId, rundownApplyDelay);
@@ -22,6 +22,7 @@ import {
cachedDelete,
cachedEdit,
cachedReorder,
cachedSwap,
delayedRundownCacheKey,
} from './delayedRundown.utils.js';
import { logger } from '../../classes/Logger.js';
@@ -304,6 +305,22 @@ export function _applyDelay(
return { delayIndex, updatedRundown };
}
/**
* swaps two events
* @param {string} from - id of event from
* @param {string} to - id of event to
* @returns {Promise<void>}
*/
export async function swapEvents(from: string, to: string) {
await cachedSwap(from, to);
// notify timer service of changed events
updateTimer();
// advice socket subscribers of change
sendRefetch();
}
/**
* applies delay value for given event
* @param eventId
@@ -1,8 +1,10 @@
import { OntimeBlock, OntimeDelay, OntimeEvent, OntimeRundown, SupportedEvent } from 'ontime-types';
import { DataProvider } from '../../classes/data-provider/DataProvider.js';
import { getCached, runtimeCacheStore } from '../../stores/cachingStore.js';
import { isProduction } from '../../setup.js';
import { deleteAtIndex, insertAtIndex, reorderArray } from '../../utils/arrayUtils.js';
import { swapOntimeEvents } from 'ontime-utils';
/**
* Key of rundown in cache
@@ -174,7 +176,33 @@ export async function cachedReorder(eventId: string, from: number, to: number) {
export async function cachedClear() {
await DataProvider.clearRundown();
runtimeCacheStore.setCached(delayedRundownCacheKey, []);
console.log(DataProvider.getRundown(), getDelayedRundown());
}
/**
* Swaps two events
* @param {string} fromEventId
* @param {string} toEventId
*/
export async function cachedSwap(fromEventId: string, toEventId: string) {
const fromEventIndex = DataProvider.getIndexOf(fromEventId);
const toEventIndex = DataProvider.getIndexOf(toEventId);
const rundown = DataProvider.getRundown();
const rundownToUpdate = swapOntimeEvents(rundown, fromEventIndex, toEventIndex);
const delayedRundown = getDelayedRundown();
const fromCachedEvent = delayedRundown.at(fromEventIndex);
const toCachedEvent = delayedRundown.at(toEventIndex);
if (fromCachedEvent.id !== fromEventId || toCachedEvent.id !== toEventId) {
// something went wrong, we invalidate the cache
runtimeCacheStore.invalidate(delayedRundownCacheKey);
} else {
const delayedRundownToUpdate = swapOntimeEvents(delayedRundown, fromEventIndex, toEventIndex);
runtimeCacheStore.setCached(delayedRundownCacheKey, delayedRundownToUpdate);
}
await DataProvider.setRundown(rundownToUpdate);
}
/**