Skip to content

Commit

Permalink
refactor: scope rundown to selected
Browse files Browse the repository at this point in the history
  • Loading branch information
cpvalente committed Sep 7, 2024
1 parent a8182ca commit 4706ed3
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 35 deletions.
9 changes: 0 additions & 9 deletions apps/client/src/common/hooks/useSocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,15 +187,6 @@ export const useRuntimePlaybackOverview = () => {
return useRuntimeStore(featureSelector);
};

export const useTimelineOverview = () => {
const featureSelector = (state: RuntimeStore) => ({
plannedStart: state.runtime.plannedStart,
plannedEnd: state.runtime.plannedEnd,
});

return useRuntimeStore(featureSelector);
};

export const useTimelineStatus = () => {
const featureSelector = (state: RuntimeStore) => ({
clock: state.clock,
Expand Down
20 changes: 10 additions & 10 deletions apps/client/src/features/viewers/timeline/Timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,33 @@ import { useViewportSize } from '@mantine/hooks';
import { isOntimeEvent, isPlayableEvent, MaybeNumber, OntimeRundown } from 'ontime-types';
import { checkIsNextDay, dayInMs, getLastEvent, MILLIS_PER_HOUR } from 'ontime-utils';

import { useTimelineOverview } from '../../../common/hooks/useSocket';

import TimelineMarkers from './timeline-markers/TimelineMarkers';
import ProgressBar from './timeline-progress-bar/TimelineProgressBar';
import TimelineProgressBar from './timeline-progress-bar/TimelineProgressBar';
import { getElementPosition, getEndHour, getStartHour } from './timeline.utils';
import { ProgressStatus, TimelineEntry } from './TimelineEntry';

import style from './Timeline.module.scss';

interface TimelineProps {
selectedEventId: string | null;
firstStart: number;
rundown: OntimeRundown;
selectedEventId: string | null;
totalDuration: number;
}

export default memo(Timeline);

function Timeline(props: TimelineProps) {
const { selectedEventId, rundown } = props;
const { firstStart, rundown, selectedEventId, totalDuration } = props;
const { width: screenWidth } = useViewportSize();
const { plannedStart, plannedEnd } = useTimelineOverview();

if (plannedStart === null || plannedEnd === null) {
if (totalDuration === 0) {
return null;
}

const { lastEvent } = getLastEvent(rundown);
const startHour = getStartHour(plannedStart);
const endHour = getEndHour(plannedEnd + (lastEvent?.delay ?? 0));
const startHour = getStartHour(firstStart);
const endHour = getEndHour(firstStart + totalDuration + (lastEvent?.delay ?? 0));

let previousEventStartTime: MaybeNumber = null;
// we use selectedEventId as a signifier on whether the timeline is live
Expand All @@ -39,7 +39,7 @@ function Timeline(props: TimelineProps) {
return (
<div className={style.timeline}>
<TimelineMarkers startHour={startHour} endHour={endHour} />
<ProgressBar startHour={startHour} endHour={endHour} />
<TimelineProgressBar startHour={startHour} endHour={endHour} />
<div className={style.timelineEvents}>
{rundown.map((event) => {
// for now we dont render delays and blocks
Expand Down
9 changes: 7 additions & 2 deletions apps/client/src/features/viewers/timeline/TimelinePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export default function TimelinePage(props: TimelinePageProps) {
const { backstageEvents, general, selectedId, settings, time, viewSettings } = props;
const { shouldRender } = useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
// holds copy of the rundown with only relevant events
const scopedRundown = useScopedRundown(backstageEvents, selectedId);
const { scopedRundown, firstStart, totalDuration } = useScopedRundown(backstageEvents, selectedId);
const { getLocalizedString } = useTranslation();
const clock = formatTime(time.clock);

Expand Down Expand Up @@ -82,7 +82,12 @@ export default function TimelinePage(props: TimelinePageProps) {
category='next'
/>
</div>
<Timeline selectedEventId={selectedId} rundown={scopedRundown} />
<Timeline
firstStart={firstStart}
rundown={scopedRundown}
selectedEventId={selectedId}
totalDuration={totalDuration}
/>
</div>
);
}
79 changes: 65 additions & 14 deletions apps/client/src/features/viewers/timeline/timeline.utils.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { useMemo } from 'react';
import { useSearchParams } from 'react-router-dom';
import { isOntimeEvent, MaybeString, OntimeEvent, OntimeRundown } from 'ontime-types';
import { isOntimeEvent, isPlayableEvent, MaybeString, OntimeEvent, OntimeRundown, PlayableEvent } from 'ontime-types';
import {
dayInMs,
getEventWithId,
getFirstEvent,
getNextEvent,
getTimeFromPrevious,
isNewLatest,
MILLIS_PER_HOUR,
millisToString,
removeSeconds,
Expand Down Expand Up @@ -89,31 +91,80 @@ export function getStatusLabel(timeToStart: number, status: ProgressStatus): str
return formatDuration(timeToStart);
}

export function useScopedRundown(rundown: OntimeRundown, selectedEventId: MaybeString): OntimeRundown {
interface ScopedRundownData {
scopedRundown: PlayableEvent[];
firstStart: number;
totalDuration: number;
}

export function useScopedRundown(rundown: OntimeRundown, selectedEventId: MaybeString): ScopedRundownData {
const [searchParams] = useSearchParams();

const data = useMemo(() => {
if (rundown.length === 0) {
return [];
return { scopedRundown: [], firstStart: 0, totalDuration: 0 };
}

const hideBackstage = isStringBoolean(searchParams.get('hideBackstage'));
const hidePast = isStringBoolean(searchParams.get('hidePast'));

let scopedRundown = [...rundown];

if (hidePast && selectedEventId) {
const currentIndex = rundown.findIndex((event) => event.id === selectedEventId);
if (currentIndex >= 0) {
scopedRundown = scopedRundown.slice(currentIndex);
const scopedRundown: PlayableEvent[] = [];
let selectedIndex = selectedEventId ? Infinity : -1;
let firstStart = null;
let totalDuration = 0;
let lastEntry: PlayableEvent | null = null;

for (let i = 0; i < rundown.length; i++) {
const currentEntry = rundown[i];
// we only deal with playableEvents
if (isOntimeEvent(currentEntry) && isPlayableEvent(currentEntry)) {
if (currentEntry.id === selectedEventId) {
selectedIndex = i;
}

// maybe filter past
if (hidePast && i < selectedIndex) {
continue;
}

// maybe filter backstage
if (!currentEntry.isPublic && hideBackstage) {
continue;
}

// add to scopedRundown
scopedRundown.push(currentEntry);

/**
* Derive timers
* This logic is partially from rundownCache.generate
* With the addition of deriving the current day offset
*/
if (firstStart === null) {
firstStart = currentEntry.timeStart;
}

const timeFromPrevious: number = getTimeFromPrevious(
currentEntry.timeStart,
lastEntry?.timeStart,
lastEntry?.timeEnd,
lastEntry?.duration,
);

if (timeFromPrevious === 0) {
totalDuration += currentEntry.duration;
} else if (timeFromPrevious > 0) {
totalDuration += timeFromPrevious + currentEntry.duration;
} else if (timeFromPrevious < 0) {
totalDuration += Math.max(currentEntry.duration + timeFromPrevious, 0);
}
if (isNewLatest(currentEntry.timeStart, currentEntry.timeEnd, lastEntry?.timeStart, lastEntry?.timeEnd)) {
lastEntry = currentEntry;
}
}
}

if (hideBackstage) {
scopedRundown = scopedRundown.filter((event) => !isOntimeEvent(event) || event.isPublic);
}

return scopedRundown;
return { scopedRundown, firstStart: firstStart ?? 0, totalDuration };
}, [rundown, searchParams, selectedEventId]);

return data;
Expand Down

0 comments on commit 4706ed3

Please sign in to comment.