From 5501f57b45cb9b360d547233399c2b2d82691d19 Mon Sep 17 00:00:00 2001 From: Carlos Valente Date: Mon, 21 Oct 2024 19:54:03 +0200 Subject: [PATCH] feat: add link to context menu --- .../client/src/common/hooks/useEventAction.ts | 51 +------------------ .../rundown/event-block/EventBlock.tsx | 31 +++++++++++ .../rundown/time-input-flow/TimeInputFlow.tsx | 11 ++-- .../rundown-service/rundownCacheUtils.ts | 5 ++ 4 files changed, 42 insertions(+), 56 deletions(-) diff --git a/apps/client/src/common/hooks/useEventAction.ts b/apps/client/src/common/hooks/useEventAction.ts index eaa4ade97..54e0af182 100644 --- a/apps/client/src/common/hooks/useEventAction.ts +++ b/apps/client/src/common/hooks/useEventAction.ts @@ -1,15 +1,7 @@ import { useCallback } from 'react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; -import { isOntimeEvent, MaybeString, OntimeEvent, OntimeRundownEntry, RundownCached } from 'ontime-types'; -import { - dayInMs, - getLinkedTimes, - getPreviousEventNormal, - MILLIS_PER_SECOND, - parseUserTime, - reorderArray, - swapEventData, -} from 'ontime-utils'; +import { isOntimeEvent, OntimeEvent, OntimeRundownEntry, RundownCached } from 'ontime-types'; +import { dayInMs, MILLIS_PER_SECOND, parseUserTime, reorderArray, swapEventData } from 'ontime-utils'; import { RUNDOWN } from '../api/constants'; import { @@ -270,44 +262,6 @@ export const useEventAction = () => { [_updateEventMutation, queryClient], ); - /** - * Toggles link of an event to the previous - */ - const linkTimer = useCallback( - async (eventId: string, linkStart: MaybeString) => { - let newEvent: Partial = { id: eventId }; - - if (!linkStart) { - newEvent.linkStart = null; - } else { - const cachedRundown = queryClient.getQueryData(RUNDOWN); - if (!cachedRundown) { - return; - } - const currentEvent = cachedRundown.rundown[eventId] as OntimeEvent; - if (!isOntimeEvent(currentEvent)) { - return; - } - const { previousEvent } = getPreviousEventNormal(cachedRundown.rundown, cachedRundown.order, eventId); - - if (!previousEvent) { - newEvent.linkStart = null; - } else { - newEvent.linkStart = previousEvent.id; - const timePatch = getLinkedTimes(currentEvent, previousEvent); - newEvent = { ...newEvent, ...timePatch }; - } - } - - try { - await _updateEventMutation.mutateAsync(newEvent); - } catch (error) { - logAxiosError('Error updating event', error); - } - }, - [_updateEventMutation, queryClient], - ); - /** * Calls mutation to edit multiple events * @private @@ -614,7 +568,6 @@ export const useEventAction = () => { batchUpdateEvents, deleteEvent, deleteAllEvents, - linkTimer, reorderEvent, swapEvents, updateEvent, diff --git a/apps/client/src/features/rundown/event-block/EventBlock.tsx b/apps/client/src/features/rundown/event-block/EventBlock.tsx index 96cf546c5..cf8f21fb8 100644 --- a/apps/client/src/features/rundown/event-block/EventBlock.tsx +++ b/apps/client/src/features/rundown/event-block/EventBlock.tsx @@ -3,11 +3,13 @@ import { useSortable } from '@dnd-kit/sortable'; import { CSS } from '@dnd-kit/utilities'; import { IoAdd } from '@react-icons/all-files/io5/IoAdd'; import { IoDuplicateOutline } from '@react-icons/all-files/io5/IoDuplicateOutline'; +import { IoLink } from '@react-icons/all-files/io5/IoLink'; import { IoPeople } from '@react-icons/all-files/io5/IoPeople'; import { IoPeopleOutline } from '@react-icons/all-files/io5/IoPeopleOutline'; import { IoReorderTwo } from '@react-icons/all-files/io5/IoReorderTwo'; import { IoSwapVertical } from '@react-icons/all-files/io5/IoSwapVertical'; import { IoTrash } from '@react-icons/all-files/io5/IoTrash'; +import { IoUnlink } from '@react-icons/all-files/io5/IoUnlink'; import { EndAction, MaybeString, OntimeEvent, Playback, TimerType, TimeStrategy } from 'ontime-types'; import { useContextMenu } from '../../../common/hooks/useContextMenu'; @@ -93,6 +95,24 @@ export default function EventBlock(props: EventBlockProps) { const [onContextMenu] = useContextMenu( selectedEvents.size > 1 ? [ + { + label: 'Link to previous', + icon: IoLink, + onClick: () => + actionHandler('update', { + field: 'linkStart', + value: 'true', + }), + }, + { + label: 'Unlink from previous', + icon: IoUnlink, + onClick: () => + actionHandler('update', { + field: 'linkStart', + value: null, + }), + }, { label: 'Make public', icon: IoPeople, @@ -101,6 +121,7 @@ export default function EventBlock(props: EventBlockProps) { field: 'isPublic', value: true, }), + withDivider: true, }, { label: 'Make private', @@ -114,6 +135,15 @@ export default function EventBlock(props: EventBlockProps) { { withDivider: true, label: 'Delete', icon: IoTrash, onClick: () => actionHandler('delete') }, ] : [ + { + label: 'Toggle link to previous', + icon: IoLink, + onClick: () => + actionHandler('update', { + field: 'linkStart', + value: linkStart ? null : 'true', + }), + }, { label: 'Toggle public', icon: IoPeopleOutline, @@ -122,6 +152,7 @@ export default function EventBlock(props: EventBlockProps) { field: 'isPublic', value: !isPublic, }), + withDivider: true, }, { label: 'Add to swap', diff --git a/apps/client/src/features/rundown/time-input-flow/TimeInputFlow.tsx b/apps/client/src/features/rundown/time-input-flow/TimeInputFlow.tsx index 34b4b907e..fb69582dd 100644 --- a/apps/client/src/features/rundown/time-input-flow/TimeInputFlow.tsx +++ b/apps/client/src/features/rundown/time-input-flow/TimeInputFlow.tsx @@ -5,7 +5,7 @@ import { IoLink } from '@react-icons/all-files/io5/IoLink'; import { IoLockClosed } from '@react-icons/all-files/io5/IoLockClosed'; import { IoLockOpenOutline } from '@react-icons/all-files/io5/IoLockOpenOutline'; import { IoUnlink } from '@react-icons/all-files/io5/IoUnlink'; -import { MaybeString, OntimeEvent, TimeStrategy } from 'ontime-types'; +import { MaybeString, TimeStrategy } from 'ontime-types'; import TimeInputWithButton from '../../../common/components/input/time-input/TimeInputWithButton'; import { useEventAction } from '../../../common/hooks/useEventAction'; @@ -28,7 +28,7 @@ type TimeActions = 'timeStart' | 'timeEnd' | 'duration'; const TimeInputFlow = (props: EventBlockTimerProps) => { const { eventId, timeStart, timeEnd, duration, timeStrategy, linkStart, delay } = props; - const { updateEvent, updateTimer, linkTimer } = useEventAction(); + const { updateEvent, updateTimer } = useEventAction(); // In sync with EventEditorTimes const handleSubmit = (field: TimeActions, value: string) => { @@ -36,14 +36,11 @@ const TimeInputFlow = (props: EventBlockTimerProps) => { }; const handleChangeStrategy = (timeStrategy: TimeStrategy) => { - const newEvent: Partial = { id: eventId, timeStrategy }; - updateEvent(newEvent); + updateEvent({ id: eventId, timeStrategy }); }; const handleLink = (doLink: boolean) => { - // the string doesnt mean much for now, not more than an intent to link - // we imagine that we can leverage this to create offsets p+10 - linkTimer(eventId, doLink ? 'p' : null); + updateEvent({ id: eventId, linkStart: doLink ? 'true' : null }); }; const overMidnight = timeStart > timeEnd; diff --git a/apps/server/src/services/rundown-service/rundownCacheUtils.ts b/apps/server/src/services/rundown-service/rundownCacheUtils.ts index d99ad4727..8e8a1ab66 100644 --- a/apps/server/src/services/rundown-service/rundownCacheUtils.ts +++ b/apps/server/src/services/rundown-service/rundownCacheUtils.ts @@ -44,6 +44,11 @@ export function handleLink( return; } + // sometimes the client cannot set the previous event + if (mutableEvent.linkStart === 'true') { + mutableEvent.linkStart = linkedEvent.id; + } + links[linkedEvent.id] = mutableEvent.id; const timePatch = getLinkedTimes(mutableEvent, linkedEvent);