diff --git a/apps/client/src/AppRouter.tsx b/apps/client/src/AppRouter.tsx index 63cb9099d..7b747d639 100644 --- a/apps/client/src/AppRouter.tsx +++ b/apps/client/src/AppRouter.tsx @@ -29,7 +29,6 @@ const Countdown = React.lazy(() => import('./features/viewers/countdown/Countdow const Backstage = React.lazy(() => import('./views/backstage/Backstage')); const Timeline = React.lazy(() => import('./views/timeline/TimelinePage')); -const Public = React.lazy(() => import('./views/public/Public')); const Lower = React.lazy(() => import('./features/viewers/lower-thirds/LowerThird')); const StudioClock = React.lazy(() => import('./features/viewers/studio/StudioClock')); const ProjectInfo = React.lazy(() => import('./views/project-info/ProjectInfo')); @@ -40,7 +39,6 @@ const SClock = withPreset(withData(ClockView)); const SCountdown = withPreset(withData(Countdown)); const SBackstage = withPreset(withData(Backstage)); const SProjectInfo = withPreset(withData(ProjectInfo)); -const SPublic = withPreset(withData(Public)); const SLowerThird = withPreset(withData(Lower)); const SStudio = withPreset(withData(StudioClock)); const STimeline = withPreset(withData(Timeline)); @@ -88,14 +86,6 @@ export default function AppRouter() { } /> - - - - } - /> - {showSchedule && } + {showSchedule && }
{general.backstageUrl && } diff --git a/apps/client/src/views/common/schedule/Schedule.tsx b/apps/client/src/views/common/schedule/Schedule.tsx index acb6d54d8..a99c268a8 100644 --- a/apps/client/src/views/common/schedule/Schedule.tsx +++ b/apps/client/src/views/common/schedule/Schedule.tsx @@ -1,18 +1,16 @@ import { cx } from '../../../common/utils/styleUtils'; -import { getScheduledTimes } from './schedule.utils'; import { useSchedule } from './ScheduleContext'; import ScheduleItem from './ScheduleItem'; import './Schedule.scss'; interface ScheduleProps { - isProduction?: boolean; className?: string; } -export default function Schedule({ isProduction, className }: ScheduleProps) { - const { events, isBackstage, containerRef } = useSchedule(); +export default function Schedule({ className }: ScheduleProps) { + const { events, containerRef } = useSchedule(); if (events?.length < 1) { return null; @@ -21,18 +19,15 @@ export default function Schedule({ isProduction, className }: ScheduleProps) { return (
    {events.map((event) => { - const { timeStart, timeEnd, delay } = getScheduledTimes(event, isProduction); - return ( ); })} diff --git a/apps/client/src/views/common/schedule/ScheduleExport.tsx b/apps/client/src/views/common/schedule/ScheduleExport.tsx index 43f8118d0..898cb2d81 100644 --- a/apps/client/src/views/common/schedule/ScheduleExport.tsx +++ b/apps/client/src/views/common/schedule/ScheduleExport.tsx @@ -7,16 +7,15 @@ import ScheduleNav from './ScheduleNav'; interface ScheduleExportProps { selectedId: MaybeString; - isBackstage?: boolean; } export default memo(ScheduleExport); function ScheduleExport(props: ScheduleExportProps) { - const { selectedId, isBackstage } = props; + const { selectedId } = props; return ( - + - + ); } diff --git a/apps/client/src/views/common/schedule/ScheduleItem.tsx b/apps/client/src/views/common/schedule/ScheduleItem.tsx index b9bc3278b..7674a9bfb 100644 --- a/apps/client/src/views/common/schedule/ScheduleItem.tsx +++ b/apps/client/src/views/common/schedule/ScheduleItem.tsx @@ -16,14 +16,13 @@ interface ScheduleItemProps { timeStart: number; timeEnd: number; title: string; - backstageEvent: boolean; colour?: string; skip?: boolean; delay: number; } export default function ScheduleItem(props: ScheduleItemProps) { - const { timeStart, timeEnd, title, backstageEvent, colour, skip, delay } = props; + const { timeStart, timeEnd, title, colour, skip, delay } = props; const { showProjected } = useScheduleOptions(); if (showProjected) { @@ -33,7 +32,6 @@ export default function ScheduleItem(props: ScheduleItemProps) { timeEnd={timeEnd} title={title} colour={colour} - backstageEvent={backstageEvent} skip={skip} delay={delay} /> @@ -47,7 +45,6 @@ export default function ScheduleItem(props: ScheduleItemProps) { timeEnd={timeEnd} title={title} colour={colour} - backstageEvent={backstageEvent} skip={skip} delay={delay} /> @@ -63,7 +60,6 @@ export default function ScheduleItem(props: ScheduleItemProps) { - {backstageEvent && '*'}
{title}
@@ -71,7 +67,7 @@ export default function ScheduleItem(props: ScheduleItemProps) { } function DelayedScheduleItem(props: ScheduleItemProps) { - const { timeStart, timeEnd, title, backstageEvent, colour, skip, delay } = props; + const { timeStart, timeEnd, title, colour, skip, delay } = props; const start = formatTime(timeStart, formatOptions); const end = formatTime(timeEnd, formatOptions); @@ -86,13 +82,11 @@ function DelayedScheduleItem(props: ScheduleItemProps) { - {backstageEvent && '*'} - {backstageEvent && '*'}
{title}
@@ -101,7 +95,7 @@ function DelayedScheduleItem(props: ScheduleItemProps) { } function ProjectedScheduleItem(props: ScheduleItemProps) { - const { timeStart, timeEnd, title, backstageEvent, colour, skip, delay } = props; + const { timeStart, timeEnd, title, colour, skip, delay } = props; return (
  • @@ -110,7 +104,6 @@ function ProjectedScheduleItem(props: ScheduleItemProps) { - {backstageEvent && '*'}
    {title}
  • diff --git a/apps/client/src/views/common/schedule/schedule.options.ts b/apps/client/src/views/common/schedule/schedule.options.ts index 58b3b531d..0fbf13b14 100644 --- a/apps/client/src/views/common/schedule/schedule.options.ts +++ b/apps/client/src/views/common/schedule/schedule.options.ts @@ -19,14 +19,14 @@ export const scheduleOptions: ViewOption = { { id: 'cycleInterval', title: 'Cycle interval', - description: 'How long (in seconds) should each schedule page be shown.', + description: 'How long (in seconds) should each schedule page be shown', type: 'number', defaultValue: 10, }, { id: 'showProjected', title: 'Show projected time', - description: 'Whether scheduled times should account for runtime offset.', + description: 'Whether scheduled times should account for runtime offset', type: 'boolean', defaultValue: false, }, diff --git a/apps/client/src/views/common/schedule/schedule.utils.ts b/apps/client/src/views/common/schedule/schedule.utils.ts deleted file mode 100644 index 934cb156c..000000000 --- a/apps/client/src/views/common/schedule/schedule.utils.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { OntimeEvent } from 'ontime-types'; - -/** - * Gather rules for how to present scheduled times - */ -export function getScheduledTimes(event: OntimeEvent, isProduction?: boolean) { - if (isProduction) { - return { - timeStart: event.timeStart, - timeEnd: event.timeEnd, - delay: event.skip ? 0 : event.delay, - }; - } - return { - timeStart: event.timeStart, - timeEnd: event.timeEnd, - delay: 0, - }; -} diff --git a/apps/client/src/views/public/Public.scss b/apps/client/src/views/public/Public.scss deleted file mode 100644 index 490bdc9b6..000000000 --- a/apps/client/src/views/public/Public.scss +++ /dev/null @@ -1,204 +0,0 @@ -@use '../../theme/viewerDefs' as *; - -.public-screen { - margin: 0; - box-sizing: border-box; /* reset */ - overflow: hidden; - width: 100%; /* restrict the page width to viewport */ - height: 100vh; - - font-family: var(--font-family-override, $viewer-font-family); - background: var(--background-color-override, $viewer-background-color); - color: var(--color-override, $viewer-color); - gap: $view-element-gap; - padding: $view-outer-padding; - font-size: $base-font-size; - - display: grid; - grid-template-columns: 1fr 40vw; - grid-template-rows: auto 12px 1fr auto; - grid-template-areas: - 'header header' - 'now schedule-nav' - 'info schedule'; - - .empty-container { - position: absolute; - top: 0; - left: 0; - right: 0; - margin-top: 25vh; - color: var(--label-color-override, $viewer-label-color); - } - - /* =================== HEADER + EXTRAS ===================*/ - - .project-header { - grid-area: header; - font-size: $header-font-size; - font-weight: 600; - display: flex; - gap: 1rem; - } - - .logo { - max-width: min(200px, 20vw); - } - - .title { - line-height: 1.1em; - } - - .clock-container { - margin-left: auto; - font-weight: 600; - - .label { - font-size: $timer-label-size; - color: var(--label-color-override, $viewer-label-color); - text-transform: uppercase; - } - - .time { - font-size: $timer-value-size; - color: var(--secondary-color-override, $viewer-secondary-color); - letter-spacing: 0.05em; - line-height: 0.95em; - } - } - - /* =================== MAIN - NOW ===================*/ - - .card-container { - grid-area: now; - display: flex; - flex-direction: column; - gap: $view-element-gap; - } - - .event { - background-color: var(--card-background-color-override, $viewer-card-bg-color); - padding: $view-card-padding; - border-radius: $element-border-radius; - } - - .timer-group { - border-top: 2px solid var(--background-color-override, $viewer-background-color); - margin-top: max(1vh, 16px); - padding-top: max(1vh, 16px); - display: flex; - row-gap: 0.5em; - } - - .time-entry { - &__label { - font-size: $timer-label-size; - color: var(--label-color-override, $viewer-label-color); - font-weight: 600; - text-transform: uppercase; - } - - &__value { - font-size: $base-font-size; - color: var(--secondary-color-override, $viewer-secondary-color); - letter-spacing: 0.05em; - line-height: 0.95em; - } - - &--pending { - color: var(--timer-pending-color-override, $ontime-roll); - } - } - - /* =================== MAIN - SCHEDULE ===================*/ - - $schedule-left-spacing: clamp(16px, 4vw, 64px); - .schedule-container { - grid-area: schedule; - overflow: hidden; - height: 100%; - padding-left: $schedule-left-spacing; - } - - .schedule-nav-container { - grid-area: schedule-nav; - padding-left: $schedule-left-spacing; - } - - /* =================== MAIN - INFO ===================*/ - - .info { - grid-area: info; - display: flex; - gap: max(1vw, 16px); - align-self: flex-end; - overflow: hidden; - align-items: end; - - &__message { - font-size: $base-font-size; - line-height: 1.2em; - white-space: pre-line; - overflow: hidden; - flex: 1; - } - - .qr { - padding: 0.5rem; - background-color: $ui-white; - border-radius: 2px; - } - } -} - -/* =================== MOBILE ===================*/ -@media screen and (max-width: 768px) { - .public-screen { - display: flex; - flex-direction: column; - overflow-y: auto; - - .project-header { - flex-direction: column; - position: relative; - gap: 0.5rem; - } - - .logo img{ - height: min(50px, 10vh); - } - - .timer-group { - flex-wrap: wrap; - } - - .clock-container { - position: absolute; - top: 0; - right: 0; - } - - .schedule-nav-container { - padding-left: 0; - justify-content: center; - } - - .schedule-container { - padding-left: 0; - flex: 1; - } - - .info { - width: 100%; - text-align: right; - } - - .qr { - display: none; - } - - .info--stretch { - flex: 1; - } - } -} diff --git a/apps/client/src/views/public/Public.tsx b/apps/client/src/views/public/Public.tsx deleted file mode 100644 index 19aaa2cf6..000000000 --- a/apps/client/src/views/public/Public.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import QRCode from 'react-qr-code'; -import { useViewportSize } from '@mantine/hooks'; -import { CustomFields, OntimeEvent, ProjectData, Settings } from 'ontime-types'; - -import Empty from '../../common/components/state/Empty'; -import TitleCard from '../../common/components/title-card/TitleCard'; -import ViewLogo from '../../common/components/view-logo/ViewLogo'; -import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor'; -import { useWindowTitle } from '../../common/hooks/useWindowTitle'; -import { ViewExtendedTimer } from '../../common/models/TimeManager.type'; -import { cx } from '../../common/utils/styleUtils'; -import { formatTime, getDefaultFormat } from '../../common/utils/time'; -import SuperscriptTime from '../../features/viewers/common/superscript-time/SuperscriptTime'; -import { useTranslation } from '../../translation/TranslationProvider'; -import { getIsPendingStart } from '../backstage/backstage.utils'; -import ScheduleExport from '../common/schedule/ScheduleExport'; - -import { getPublicOptions, usePublicOptions } from './public.options'; -import { getCardData, getFirstStartTime } from './public.utils'; - -import './Public.scss'; - -interface BackstageProps { - customFields: CustomFields; - events: OntimeEvent[]; - general: ProjectData; - isMirrored: boolean; - publicEventNow: OntimeEvent | null; - publicEventNext: OntimeEvent | null; - time: ViewExtendedTimer; - publicSelectedId: string | null; - settings: Settings | undefined; -} - -export default function Public(props: BackstageProps) { - const { - customFields, - events, - general, - isMirrored, - publicEventNow, - publicEventNext, - time, - publicSelectedId, - settings, - } = props; - - const { getLocalizedString } = useTranslation(); - const { secondarySource } = usePublicOptions(); - const { height: screenHeight } = useViewportSize(); - - useWindowTitle('Public Schedule'); - - // gather card data - const hasEvents = events.length > 0; - const { showNow, nowMain, nowSecondary, showNext, nextMain, nextSecondary } = getCardData( - publicEventNow, - publicEventNext, - 'title', - secondarySource, - time.playback, - ); - - // gather timer data - const clock = formatTime(time.clock); - const isPendingStart = getIsPendingStart(time.playback, time.phase); - const scheduledStart = (() => { - if (showNow) return undefined; - if (!hasEvents) return undefined; - return getFirstStartTime(events[0]); - })(); - - // gather presentation styles - const qrSize = Math.max(window.innerWidth / 15, 72); - const showSchedule = hasEvents && screenHeight > 700; // in vertical screens we may not have space - const showPending = scheduledStart !== undefined; - - // gather option data - const defaultFormat = getDefaultFormat(settings?.timeFormat); - const publicOptions = getPublicOptions(defaultFormat, customFields); - - return ( -
    - -
    - {general?.projectLogo ? :
    } -
    {general.title}
    -
    -
    {getLocalizedString('common.time_now')}
    - -
    -
    - - {!hasEvents && } - -
    - {showNow && hasEvents && ( - - )} - {showPending && ( -
    -
    {getLocalizedString('countdown.waiting')}
    -
    -
    -
    - {getLocalizedString('common.scheduled_start')} -
    - -
    -
    -
    - )} - {showNext && hasEvents && ( - - )} -
    - - {showSchedule && } - -
    - {general.publicUrl && } - {general.publicInfo &&
    {general.publicInfo}
    } -
    -
    - ); -} diff --git a/apps/client/src/views/public/public.options.ts b/apps/client/src/views/public/public.options.ts deleted file mode 100644 index 821de86c1..000000000 --- a/apps/client/src/views/public/public.options.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { useMemo } from 'react'; -import { useSearchParams } from 'react-router-dom'; -import { CustomFields, OntimeEvent } from 'ontime-types'; - -import { - getTimeOption, - makeOptionsFromCustomFields, - OptionTitle, -} from '../../common/components/view-params-editor/constants'; -import { ViewOption } from '../../common/components/view-params-editor/types'; -import { scheduleOptions } from '../common/schedule/schedule.options'; - -export const getPublicOptions = (timeFormat: string, customFields: CustomFields): ViewOption[] => { - const secondaryOptions = makeOptionsFromCustomFields(customFields, { note: 'Note' }); - - return [ - { title: OptionTitle.ClockOptions, collapsible: true, options: [getTimeOption(timeFormat)] }, - { - title: OptionTitle.DataSources, - collapsible: true, - options: [ - { - id: 'secondary-src', - title: 'Event secondary text', - description: 'Select the data source for auxiliary text shown in now and next cards', - type: 'option', - values: secondaryOptions, - defaultValue: '', - }, - ], - }, - scheduleOptions, - ]; -}; - -type PublicOptions = { - secondarySource: keyof OntimeEvent | null; -}; - -/** - * Utility extract the view options from URL Params - * the names and fallback are manually matched with timerOptions - */ -function getOptionsFromParams(searchParams: URLSearchParams): PublicOptions { - // we manually make an object that matches the key above - return { - secondarySource: searchParams.get('secondary-src') as keyof OntimeEvent | null, - }; -} - -/** - * Hook exposes the backstage view options - */ -export function usePublicOptions(): PublicOptions { - const [searchParams] = useSearchParams(); - const options = useMemo(() => getOptionsFromParams(searchParams), [searchParams]); - return options; -} diff --git a/apps/client/src/views/public/public.utils.ts b/apps/client/src/views/public/public.utils.ts deleted file mode 100644 index a0b463b8b..000000000 --- a/apps/client/src/views/public/public.utils.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { OntimeEvent, Playback } from 'ontime-types'; - -import { enDash } from '../../common/utils/styleUtils'; -import { getPropertyValue } from '../../features/viewers/common/viewUtils'; - -/** - * What should we be showing in the cards? - */ -export function getCardData( - eventNow: OntimeEvent | null, - eventNext: OntimeEvent | null, - mainSource: keyof OntimeEvent | null, - secondarySource: keyof OntimeEvent | null, - playback: Playback, -) { - if (playback === Playback.Stop) { - return { - showNow: false, - nowMain: undefined, - nowSecondary: undefined, - showNext: false, - nextMain: undefined, - nextSecondary: undefined, - }; - } - - // if we are loaded, we show the upcoming event as next - const nowMain = getPropertyValue(eventNow, mainSource ?? 'title') || enDash; - const nowSecondary = getPropertyValue(eventNow, secondarySource); - const nextMain = getPropertyValue(eventNext, mainSource ?? 'title') || enDash; - const nextSecondary = getPropertyValue(eventNext, secondarySource); - - return { - showNow: eventNow !== null, - nowMain, - nowSecondary, - showNext: eventNext !== null, - nextMain, - nextSecondary, - }; -} - -export function getFirstStartTime(firstPublicEvent: OntimeEvent | null): number | undefined { - return firstPublicEvent?.timeStart; -}