mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-10 01:43:43 +00:00
feat: expand user options for views with schedule (#565)
* feat: expand user options for views with schedule * fix: selected public id on load * fix: load events on roll * style: remove empty container
This commit is contained in:
@@ -1,5 +1,3 @@
|
||||
import Empty from '../state/Empty';
|
||||
|
||||
import { useSchedule } from './ScheduleContext';
|
||||
import ScheduleItem from './ScheduleItem';
|
||||
|
||||
@@ -13,8 +11,9 @@ interface ScheduleProps {
|
||||
export default function Schedule({ isProduction, className }: ScheduleProps) {
|
||||
const { paginatedEvents, selectedEventId, isBackstage, scheduleType } = useSchedule();
|
||||
|
||||
// TODO: design a nice placeholder for empty schedules
|
||||
if (paginatedEvents?.length < 1) {
|
||||
return <Empty text='No events to show' />;
|
||||
return null;
|
||||
}
|
||||
|
||||
let selectedState: 'past' | 'now' | 'future' = 'past';
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { createContext, PropsWithChildren, useContext, useState } from 'react';
|
||||
import { useSearchParams } from 'react-router-dom';
|
||||
import { OntimeEvent } from 'ontime-types';
|
||||
|
||||
import { useInterval } from '../../hooks/useInterval';
|
||||
import { isStringBoolean } from '../../utils/viewUtils';
|
||||
|
||||
interface ScheduleContextState {
|
||||
events: OntimeEvent[];
|
||||
@@ -32,12 +34,25 @@ export const ScheduleProvider = ({
|
||||
time = 10,
|
||||
}: PropsWithChildren<ScheduleProviderProps>) => {
|
||||
const [visiblePage, setVisiblePage] = useState(0);
|
||||
const [searchParams] = useSearchParams();
|
||||
|
||||
const numPages = Math.ceil(events.length / eventsPerPage);
|
||||
// look for overrides from views
|
||||
const hidePast = isStringBoolean(searchParams.get('hidePast'));
|
||||
const stopCycle = isStringBoolean(searchParams.get('stopCycle'));
|
||||
|
||||
let selectedEventIndex = events.findIndex((event) => event.id === selectedEventId);
|
||||
|
||||
const viewEvents = [...events];
|
||||
if (hidePast) {
|
||||
// we want to show the event after the next
|
||||
viewEvents.splice(0, selectedEventIndex + 2);
|
||||
selectedEventIndex = 0;
|
||||
}
|
||||
|
||||
const numPages = Math.ceil(viewEvents.length / eventsPerPage);
|
||||
const eventStart = eventsPerPage * visiblePage;
|
||||
const eventEnd = eventsPerPage * (visiblePage + 1);
|
||||
const paginatedEvents = events.slice(eventStart, eventEnd);
|
||||
const selectedEventIndex = events.findIndex((event) => event.id === selectedEventId);
|
||||
const paginatedEvents = viewEvents.slice(eventStart, eventEnd);
|
||||
|
||||
const resolveScheduleType = () => {
|
||||
if (selectedEventIndex >= eventStart && selectedEventIndex < eventEnd) {
|
||||
@@ -52,7 +67,9 @@ export const ScheduleProvider = ({
|
||||
|
||||
// every SCROLL_TIME go to the next array
|
||||
useInterval(() => {
|
||||
if (events.length > eventsPerPage) {
|
||||
if (stopCycle) {
|
||||
setVisiblePage(0);
|
||||
} else if (events.length > eventsPerPage) {
|
||||
const next = (visiblePage + 1) % numPages;
|
||||
setVisiblePage(next);
|
||||
}
|
||||
|
||||
@@ -194,6 +194,37 @@ export const LOWER_THIRDS_OPTIONS: ParamField[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const BACKSTAGE_OPTIONS: ParamField[] = [
|
||||
TIME_FORMAT_OPTION,
|
||||
{
|
||||
id: 'hidePast',
|
||||
title: 'Hide past events',
|
||||
description: 'Scheduler will only show upcoming events',
|
||||
type: 'boolean',
|
||||
},
|
||||
{
|
||||
id: 'stopCycle',
|
||||
title: 'Stop cycling through event pages',
|
||||
description: 'Schedule will not auto-cycle through events',
|
||||
type: 'boolean',
|
||||
},
|
||||
];
|
||||
|
||||
export const PUBLIC_OPTIONS: ParamField[] = [
|
||||
TIME_FORMAT_OPTION,
|
||||
{
|
||||
id: 'hidePast',
|
||||
title: 'Hide past events',
|
||||
description: 'Scheduler will only show upcoming events',
|
||||
type: 'boolean',
|
||||
},
|
||||
{
|
||||
id: 'stopCycle',
|
||||
title: 'Stop cycling through event pages',
|
||||
description: 'Schedule will not auto-cycle through events',
|
||||
type: 'boolean',
|
||||
},
|
||||
];
|
||||
export const STUDIO_CLOCK_OPTIONS: ParamField[] = [
|
||||
TIME_FORMAT_OPTION,
|
||||
{
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/* eslint-disable react/display-name */
|
||||
import { ComponentType, useMemo } from 'react';
|
||||
import { SupportedEvent } from 'ontime-types';
|
||||
import { useStore } from 'zustand';
|
||||
|
||||
import useProjectData from '../../common/hooks-query/useProjectData';
|
||||
@@ -20,7 +21,7 @@ const withData = <P extends object>(Component: ComponentType<P>) => {
|
||||
|
||||
const publicEvents = useMemo(() => {
|
||||
if (Array.isArray(rundownData)) {
|
||||
return rundownData.filter((e) => e.type === 'event' && e.title && e.isPublic);
|
||||
return rundownData.filter((e) => e.type === SupportedEvent.Event && e.title && e.isPublic);
|
||||
}
|
||||
return [];
|
||||
}, [rundownData]);
|
||||
|
||||
@@ -99,7 +99,11 @@
|
||||
margin-top: 2em;
|
||||
padding-top: 1em;
|
||||
display: flex;
|
||||
gap: 7.5em;
|
||||
}
|
||||
|
||||
.timer-gap {
|
||||
flex: 1;
|
||||
max-width: 7.5em;
|
||||
}
|
||||
|
||||
.aux-timers {
|
||||
|
||||
@@ -11,7 +11,7 @@ import Schedule from '../../../common/components/schedule/Schedule';
|
||||
import { ScheduleProvider } from '../../../common/components/schedule/ScheduleContext';
|
||||
import ScheduleNav from '../../../common/components/schedule/ScheduleNav';
|
||||
import TitleCard from '../../../common/components/title-card/TitleCard';
|
||||
import { TIME_FORMAT_OPTION } from '../../../common/components/view-params-editor/constants';
|
||||
import { BACKSTAGE_OPTIONS } from '../../../common/components/view-params-editor/constants';
|
||||
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
|
||||
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||
import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
@@ -92,7 +92,7 @@ export default function Backstage(props: BackstageProps) {
|
||||
return (
|
||||
<div className={`backstage ${isMirrored ? 'mirror' : ''}`} data-testid='backstage-view'>
|
||||
<NavigationMenu />
|
||||
<ViewParamsEditor paramFields={[TIME_FORMAT_OPTION]} />
|
||||
<ViewParamsEditor paramFields={BACKSTAGE_OPTIONS} />
|
||||
<div className='project-header'>
|
||||
{general.title}
|
||||
<div className='clock-container'>
|
||||
@@ -130,10 +130,12 @@ export default function Backstage(props: BackstageProps) {
|
||||
<div className='aux-timers__label'>{getLocalizedString('common.started_at')}</div>
|
||||
<div className='aux-timers__value'>{startedAt}</div>
|
||||
</div>
|
||||
<div className='timer-gap' />
|
||||
<div className='aux-timers'>
|
||||
<div className='aux-timers__label'>{getLocalizedString('common.expected_finish')}</div>
|
||||
<div className='aux-timers__value'>{expectedFinish}</div>
|
||||
</div>
|
||||
<div className='timer-gap' />
|
||||
<div className='aux-timers'>
|
||||
<div className='aux-timers__label'>{getLocalizedString('common.stage_timer')}</div>
|
||||
<div className='aux-timers__value'>{stageTimer}</div>
|
||||
|
||||
@@ -9,7 +9,7 @@ import Schedule from '../../../common/components/schedule/Schedule';
|
||||
import { ScheduleProvider } from '../../../common/components/schedule/ScheduleContext';
|
||||
import ScheduleNav from '../../../common/components/schedule/ScheduleNav';
|
||||
import TitleCard from '../../../common/components/title-card/TitleCard';
|
||||
import { TIME_FORMAT_OPTION } from '../../../common/components/view-params-editor/constants';
|
||||
import { PUBLIC_OPTIONS } from '../../../common/components/view-params-editor/constants';
|
||||
import ViewParamsEditor from '../../../common/components/view-params-editor/ViewParamsEditor';
|
||||
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
|
||||
import { TimeManagerType } from '../../../common/models/TimeManager.type';
|
||||
@@ -58,7 +58,7 @@ export default function Public(props: BackstageProps) {
|
||||
return (
|
||||
<div className={`public-screen ${isMirrored ? 'mirror' : ''}`} data-testid='public-view'>
|
||||
<NavigationMenu />
|
||||
<ViewParamsEditor paramFields={[TIME_FORMAT_OPTION]} />
|
||||
<ViewParamsEditor paramFields={PUBLIC_OPTIONS} />
|
||||
<div className='project-header'>
|
||||
{general.title}
|
||||
<div className='clock-container'>
|
||||
|
||||
@@ -183,6 +183,8 @@ export class EventLoader {
|
||||
this.loaded.nextEventId = nextEvent?.id || null;
|
||||
this.loaded.nextPublicEventId = nextPublicEvent?.id || null;
|
||||
|
||||
this._loadEvent();
|
||||
|
||||
return { currentEvent, nextEvent, timeToNext };
|
||||
}
|
||||
|
||||
@@ -281,6 +283,7 @@ export class EventLoader {
|
||||
// check if current is also public
|
||||
if (event.isPublic) {
|
||||
this.publicEventNow = event;
|
||||
this.loaded.selectedPublicEventId = event.id;
|
||||
} else {
|
||||
// assume there is no public event
|
||||
this.publicEventNow = null;
|
||||
|
||||
Reference in New Issue
Block a user