From 71dace669b2a2fde0eee0f5be03e87398ca42549 Mon Sep 17 00:00:00 2001 From: Carlos Valente <34649812+cpvalente@users.noreply.github.com> Date: Tue, 13 Feb 2024 18:43:41 +0100 Subject: [PATCH] fix: recover cursor logic (#773) --- apps/client/src/common/stores/appModeStore.ts | 4 +++- apps/client/src/features/rundown/Rundown.tsx | 24 +++++++++++-------- .../src/features/rundown/RundownEntry.tsx | 6 ++--- .../src/features/rundown/_blockMixins.scss | 3 ++- .../event-block/EventBlock.module.scss | 6 ++++- .../rundown/event-block/EventBlock.tsx | 19 ++++++++------- .../rundown/event-block/EventBlockInner.tsx | 12 +++++----- .../event-block/composite/BlockActionMenu.tsx | 2 +- .../composite/EventBlockPlayback.tsx | 6 ++--- .../quick-add-block/QuickAddBlock.module.scss | 4 +--- .../rundown/quick-add-block/QuickAddBlock.tsx | 4 ++++ apps/client/src/theme/ontimeButton.ts | 7 +++++- 12 files changed, 59 insertions(+), 38 deletions(-) diff --git a/apps/client/src/common/stores/appModeStore.ts b/apps/client/src/common/stores/appModeStore.ts index ffbd0731d..699b75456 100644 --- a/apps/client/src/common/stores/appModeStore.ts +++ b/apps/client/src/common/stores/appModeStore.ts @@ -17,8 +17,9 @@ function persistModeToSession(mode: AppMode) { type AppModeStore = { mode: AppMode; - cursor: string | null; setMode: (mode: AppMode) => void; + cursor: string | null; + setCursor: (cursor: string | null) => void; }; export const useAppMode = create()((set) => ({ @@ -31,4 +32,5 @@ export const useAppMode = create()((set) => ({ return { mode }; }); }, + setCursor: (cursor: string | null) => set(() => ({ cursor })), })); diff --git a/apps/client/src/features/rundown/Rundown.tsx b/apps/client/src/features/rundown/Rundown.tsx index ef95b6c1d..b4e376c9e 100644 --- a/apps/client/src/features/rundown/Rundown.tsx +++ b/apps/client/src/features/rundown/Rundown.tsx @@ -34,7 +34,7 @@ export default function Rundown({ data }: RundownProps) { const showQuickEntry = eventSettings.showQuickEntry; // cursor - const { cursor, mode: appMode } = useAppMode(); + const { cursor, mode: appMode, setCursor } = useAppMode(); const viewFollowsCursor = appMode === AppMode.Run; const cursorRef = useRef(null); const scrollRef = useRef(null); @@ -95,9 +95,9 @@ export default function Rundown({ data }: RundownProps) { return; } const nextEvent = - cursor == null ? getFirstNormal(rundown, order) : getNextNormal(rundown, order, cursor)?.nextEvent; + cursor === null ? getFirstNormal(rundown, order) : getNextNormal(rundown, order, cursor)?.nextEvent; if (nextEvent) { - // moveCursorTo(nextEvent.id, nextEvent.type === SupportedEvent.Event); + setCursor(nextEvent.id); } break; } @@ -107,9 +107,11 @@ export default function Rundown({ data }: RundownProps) { } // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- we check for this before const previousEvent = - cursor == null ? getFirstNormal(rundown, order) : getPreviousNormal(rundown, order, cursor).previousEvent; + cursor === null + ? getFirstNormal(rundown, order) + : getPreviousNormal(rundown, order, cursor).previousEvent; if (previousEvent) { - // moveCursorTo(previousEvent.id, previousEvent.type === SupportedEvent.Event); + setCursor(previousEvent.id); } break; } @@ -138,11 +140,13 @@ export default function Rundown({ data }: RundownProps) { if (order.length < 2 || cursor == null) { return; } + // Alt + Ctrl + Arrow Down if (event.code == 'ArrowDown') { const { nextEvent, nextIndex } = getNextNormal(rundown, order, cursor); if (nextEvent && nextIndex !== null) { reorderEvent(cursor, nextIndex - 1, nextIndex); } + // Alt + Ctrl + Arrow Up } else if (event.code == 'ArrowUp') { const { previousEvent, previousIndex } = getPreviousNormal(rundown, order, cursor); if (previousEvent && previousIndex !== null) { @@ -151,7 +155,7 @@ export default function Rundown({ data }: RundownProps) { } } }, - [cursor, insertAtCursor, order, rundown, reorderEvent], + [order, cursor, rundown, setCursor, insertAtCursor, reorderEvent], ); // we copy the state from the store here @@ -231,10 +235,10 @@ export default function Rundown({ data }: RundownProps) { previousEventId = event.id; } const isLast = index === order.length - 1; - const isSelected = featureData?.selectedEventId === event.id; + const isLoaded = featureData?.selectedEventId === event.id; const isNext = featureData?.nextEventId === event.id; const hasCursor = event.id === cursor; - if (isSelected) { + if (isLoaded) { isPast = false; } @@ -248,12 +252,12 @@ export default function Rundown({ data }: RundownProps) { isPast={isPast} eventIndex={eventIndex} data={event} - selected={isSelected} + loaded={isLoaded} hasCursor={hasCursor} next={isNext} previousEnd={previousEnd} previousEventId={previousEventId} - playback={isSelected ? featureData.playback : undefined} + playback={isLoaded ? featureData.playback : undefined} isRolling={featureData.playback === Playback.Roll} /> diff --git a/apps/client/src/features/rundown/RundownEntry.tsx b/apps/client/src/features/rundown/RundownEntry.tsx index f2cd03b8b..d9e802da8 100644 --- a/apps/client/src/features/rundown/RundownEntry.tsx +++ b/apps/client/src/features/rundown/RundownEntry.tsx @@ -19,7 +19,7 @@ interface RundownEntryProps { type: SupportedEvent; isPast: boolean; data: OntimeRundownEntry; - selected: boolean; + loaded: boolean; eventIndex: number; hasCursor: boolean; next: boolean; @@ -30,7 +30,7 @@ interface RundownEntryProps { } export default function RundownEntry(props: RundownEntryProps) { - const { isPast, data, selected, hasCursor, next, previousEnd, previousEventId, playback, isRolling, eventIndex } = + const { isPast, data, loaded, hasCursor, next, previousEnd, previousEventId, playback, isRolling, eventIndex } = props; const { emitError } = useEmitLog(); const { addEvent, updateEvent, batchUpdateEvents, deleteEvent, swapEvents } = useEventAction(); @@ -140,7 +140,7 @@ export default function RundownEntry(props: RundownEntryProps) { isPast={isPast} next={next} skip={data.skip} - selected={selected} + loaded={loaded} hasCursor={hasCursor} playback={playback} isRolling={isRolling} diff --git a/apps/client/src/features/rundown/_blockMixins.scss b/apps/client/src/features/rundown/_blockMixins.scss index 6787fc285..aa92b3b56 100644 --- a/apps/client/src/features/rundown/_blockMixins.scss +++ b/apps/client/src/features/rundown/_blockMixins.scss @@ -13,7 +13,8 @@ $block-bg: $gray-1250; $block-bg2: $gray-1050; // for delay and blocks $block-box-shadow: rgba(0, 0, 0, 0.5) 0 0 3px 2px; $secondary-block-height: 2.5rem; -$block-cursor-color: $blue-400; +$block-selected-color: $blue-400; +$block-cursor-color: $orange-400; @mixin block-styling() { box-sizing: content-box; diff --git a/apps/client/src/features/rundown/event-block/EventBlock.module.scss b/apps/client/src/features/rundown/event-block/EventBlock.module.scss index 65890902d..eebc35431 100644 --- a/apps/client/src/features/rundown/event-block/EventBlock.module.scss +++ b/apps/client/src/features/rundown/event-block/EventBlock.module.scss @@ -28,7 +28,7 @@ $skip-opacity: 0.1; --status-color-active-override: #{$green-400}; } - &.selected { + &.loaded { background-color: $gray-1325; } @@ -47,6 +47,10 @@ $skip-opacity: 0.1; @include declare-overrides; } + &.selected { + outline: 1px solid $block-selected-color; + } + &.hasCursor { outline: 1px solid $block-cursor-color; } diff --git a/apps/client/src/features/rundown/event-block/EventBlock.tsx b/apps/client/src/features/rundown/event-block/EventBlock.tsx index a23e6455c..2d7189e7b 100644 --- a/apps/client/src/features/rundown/event-block/EventBlock.tsx +++ b/apps/client/src/features/rundown/event-block/EventBlock.tsx @@ -10,6 +10,7 @@ import { IoSwapVertical } from '@react-icons/all-files/io5/IoSwapVertical'; import { EndAction, MaybeNumber, MaybeString, OntimeEvent, Playback, TimerType, TimeStrategy } from 'ontime-types'; import { useContextMenu } from '../../../common/hooks/useContextMenu'; +import { useAppMode } from '../../../common/stores/appModeStore'; import copyToClipboard from '../../../common/utils/copyToClipboard'; import { cx, getAccessibleColour } from '../../../common/utils/styleUtils'; import type { EventItemActions } from '../RundownEntry'; @@ -41,7 +42,7 @@ interface EventBlockProps { isPast: boolean; next: boolean; skip: boolean; - selected: boolean; + loaded: boolean; hasCursor: boolean; playback?: Playback; isRolling: boolean; @@ -77,7 +78,7 @@ export default function EventBlock(props: EventBlockProps) { isPast, next, skip = false, - selected, + loaded, hasCursor, playback, isRolling, @@ -85,6 +86,7 @@ export default function EventBlock(props: EventBlockProps) { } = props; const { selectedEventId, setSelectedEventId, clearSelectedEventId } = useEventIdSwapping(); const { selectedEvents, setSelectedEvents } = useEventSelection(); + const setCursor = useAppMode((state) => state.setCursor); const handleRef = useRef(null); const [isVisible, setIsVisible] = useState(false); @@ -205,13 +207,15 @@ export default function EventBlock(props: EventBlockProps) { }; }, [handleRef]); + const isSelected = selectedEvents.has(eventId); const blockClasses = cx([ style.eventBlock, skip ? style.skip : null, isPast ? style.past : null, - selected ? style.selected : null, + loaded ? style.loaded : null, playback ? style[playback] : null, - selectedEvents.has(eventId) ? style.hasCursor : null, + isSelected ? style.selected : null, + hasCursor ? style.hasCursor : null, ]); const handleFocusClick = (event: MouseEvent) => { @@ -227,9 +231,8 @@ export default function EventBlock(props: EventBlockProps) { // UI indexes are 1 based const index = eventIndex - 1; const editMode = getSelectionMode(event); - return setSelectedEvents({ id: eventId, index, selectMode: editMode }); - - // moveCursorTo(eventId, true); + setSelectedEvents({ id: eventId, index, selectMode: editMode }); + setCursor(eventId); }; return ( @@ -267,7 +270,7 @@ export default function EventBlock(props: EventBlockProps) { delay={delay} next={next} skip={skip} - selected={selected} + loaded={loaded} playback={playback} isRolling={isRolling} actionHandler={actionHandler} diff --git a/apps/client/src/features/rundown/event-block/EventBlockInner.tsx b/apps/client/src/features/rundown/event-block/EventBlockInner.tsx index 27da5edf9..69fb7446c 100644 --- a/apps/client/src/features/rundown/event-block/EventBlockInner.tsx +++ b/apps/client/src/features/rundown/event-block/EventBlockInner.tsx @@ -42,7 +42,7 @@ interface EventBlockInnerProps { delay: number; next: boolean; skip: boolean; - selected: boolean; + loaded: boolean; playback?: Playback; isRolling: boolean; actionHandler: (action: EventItemActions, payload?: any) => void; @@ -64,7 +64,7 @@ const EventBlockInner = (props: EventBlockInnerProps) => { delay, next, skip = false, - selected, + loaded, playback, isRolling, actionHandler, @@ -110,13 +110,13 @@ const EventBlockInner = (props: EventBlockInnerProps) => { skip={skip} isPlaying={eventIsPlaying} isPaused={eventIsPaused} - selected={selected} + loaded={loaded} disablePlayback={skip || isRolling} />
{note} -
- {selected && } +
+ {loaded && }
@@ -137,7 +137,7 @@ const EventBlockInner = (props: EventBlockInnerProps) => {
- +
); diff --git a/apps/client/src/features/rundown/event-block/composite/BlockActionMenu.tsx b/apps/client/src/features/rundown/event-block/composite/BlockActionMenu.tsx index 4f0b2470e..04452cfa3 100644 --- a/apps/client/src/features/rundown/event-block/composite/BlockActionMenu.tsx +++ b/apps/client/src/features/rundown/event-block/composite/BlockActionMenu.tsx @@ -34,7 +34,7 @@ export default function BlockActionMenu(props: BlockActionMenuProps) { aria-label='Event options' icon={} tabIndex={-1} - variant='ontime-subtle-white' + variant='ontime-ghosted-white' size='sm' className={className} /> diff --git a/apps/client/src/features/rundown/event-block/composite/EventBlockPlayback.tsx b/apps/client/src/features/rundown/event-block/composite/EventBlockPlayback.tsx index 4e0f43409..e713a604e 100644 --- a/apps/client/src/features/rundown/event-block/composite/EventBlockPlayback.tsx +++ b/apps/client/src/features/rundown/event-block/composite/EventBlockPlayback.tsx @@ -32,12 +32,12 @@ interface EventBlockPlaybackProps { skip: boolean; isPlaying: boolean; isPaused: boolean; - selected: boolean; + loaded: boolean; disablePlayback: boolean; } const EventBlockPlayback = (props: EventBlockPlaybackProps) => { - const { eventId, skip, isPlaying, isPaused, selected, disablePlayback } = props; + const { eventId, skip, isPlaying, isPaused, loaded, disablePlayback } = props; const { updateEvent } = useEventAction(); const toggleSkip = () => { @@ -93,7 +93,7 @@ const EventBlockPlayback = (props: EventBlockPlaybackProps) => { {...blockBtnStyle} clickHandler={toggleSkip} tabIndex={-1} - isDisabled={selected} + isDisabled={loaded} /> { variant='ontime-subtle-white' className={style.quickBtn} data-testid='quick-add-event' + leftIcon={} > Event {showKbd && {`${deviceAlt} + E`}} @@ -94,6 +96,7 @@ const QuickAddBlock = (props: QuickAddBlockProps) => { disabled={disableAddDelay} className={style.quickBtn} data-testid='quick-add-delay' + leftIcon={} > Delay {showKbd && {`${deviceAlt} + D`}} @@ -106,6 +109,7 @@ const QuickAddBlock = (props: QuickAddBlockProps) => { disabled={disableAddBlock} className={style.quickBtn} data-testid='quick-add-block' + leftIcon={} > Block {showKbd && {`${deviceAlt} + B`}} diff --git a/apps/client/src/theme/ontimeButton.ts b/apps/client/src/theme/ontimeButton.ts index 59be791e7..bf7ce55c1 100644 --- a/apps/client/src/theme/ontimeButton.ts +++ b/apps/client/src/theme/ontimeButton.ts @@ -52,7 +52,12 @@ export const ontimeButtonGhostedWhite = { ...ontimeButtonSubtle, backgroundColor: 'transparent', color: 'white', - _hover: { background: '#ebedf0', color: '#333' }, + _hover: { + background: '#404040', // $gray-1000 + }, + _active: { + background: '#2d2d2d', // $gray-1100 + }, }; export const ontimeButtonGhosted = {