mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-27 01:49:10 +00:00
feat: keep clock ticking on server drops
This commit is contained in:
committed by
Carlos Valente
parent
9e771a2f24
commit
37dc9a620d
@@ -0,0 +1,33 @@
|
|||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
|
import { useRuntimeStore } from '../stores/runtime';
|
||||||
|
|
||||||
|
const takeOverTimeMs = 1500;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keeps the clock value ticking if the server drops updates
|
||||||
|
*/
|
||||||
|
export function useAutoTickingClock(): number {
|
||||||
|
const clock = useRuntimeStore((state) => state.clock);
|
||||||
|
|
||||||
|
const baselineRef = useRef({ clock, time: performance.now() });
|
||||||
|
const [, setTick] = useState(0);
|
||||||
|
|
||||||
|
// Update baseline when server clock changes
|
||||||
|
useEffect(() => {
|
||||||
|
baselineRef.current = { clock, time: performance.now() };
|
||||||
|
}, [clock]);
|
||||||
|
|
||||||
|
// Force periodic re-renders
|
||||||
|
useEffect(() => {
|
||||||
|
const id = setInterval(() => setTick((t) => t + 1), 1000);
|
||||||
|
return () => clearInterval(id);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Always derive from baseline + elapsed at render time
|
||||||
|
const elapsed = performance.now() - baselineRef.current.time;
|
||||||
|
if (elapsed < takeOverTimeMs) {
|
||||||
|
return clock;
|
||||||
|
}
|
||||||
|
return baselineRef.current.clock + Math.round(elapsed);
|
||||||
|
}
|
||||||
@@ -143,8 +143,6 @@ export const useTimer = createSelector((state: RuntimeStore) => ({
|
|||||||
...state.timer,
|
...state.timer,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
export const useClock = createSelector((state: RuntimeStore) => state.clock);
|
|
||||||
|
|
||||||
export const useNextFlag = createSelector((state: RuntimeStore) => ({
|
export const useNextFlag = createSelector((state: RuntimeStore) => ({
|
||||||
id: state.eventFlag?.id ?? null,
|
id: state.eventFlag?.id ?? null,
|
||||||
expectedStart: state.offset.expectedFlagStart,
|
expectedStart: state.offset.expectedFlagStart,
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import {
|
|||||||
|
|
||||||
import Tooltip from '../../../common/components/tooltip/Tooltip';
|
import Tooltip from '../../../common/components/tooltip/Tooltip';
|
||||||
import { useEntry } from '../../../common/hooks-query/useRundown';
|
import { useEntry } from '../../../common/hooks-query/useRundown';
|
||||||
|
import { useAutoTickingClock } from '../../../common/hooks/useAutoTickingClock';
|
||||||
import {
|
import {
|
||||||
useClock,
|
|
||||||
useCurrentGroupId,
|
useCurrentGroupId,
|
||||||
useFlagTimerOverView,
|
useFlagTimerOverView,
|
||||||
useGroupTimerOverView,
|
useGroupTimerOverView,
|
||||||
@@ -342,7 +342,7 @@ export function OffsetOverview() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function ClockOverview({ shouldFormat, className }: OverviewTimeElementsProps & { className?: string }) {
|
export function ClockOverview({ shouldFormat, className }: OverviewTimeElementsProps & { className?: string }) {
|
||||||
const clock = useClock();
|
const clock = useAutoTickingClock();
|
||||||
const formattedClock = shouldFormat ? formatTime(clock) : millisToString(clock);
|
const formattedClock = shouldFormat ? formatTime(clock) : millisToString(clock);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import EmptyPage from '../../common/components/state/EmptyPage';
|
|||||||
import TitleCard from '../../common/components/title-card/TitleCard';
|
import TitleCard from '../../common/components/title-card/TitleCard';
|
||||||
import ViewLogo from '../../common/components/view-logo/ViewLogo';
|
import ViewLogo from '../../common/components/view-logo/ViewLogo';
|
||||||
import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
|
import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
|
||||||
import { useBackstageSocket, useClock } from '../../common/hooks/useSocket';
|
import { useAutoTickingClock } from '../../common/hooks/useAutoTickingClock';
|
||||||
|
import { useBackstageSocket } from '../../common/hooks/useSocket';
|
||||||
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
||||||
import { cx, timerPlaceholderMin } from '../../common/utils/styleUtils';
|
import { cx, timerPlaceholderMin } from '../../common/utils/styleUtils';
|
||||||
import { formatTime, getDefaultFormat } from '../../common/utils/time';
|
import { formatTime, getDefaultFormat } from '../../common/utils/time';
|
||||||
@@ -214,7 +215,7 @@ function ExtraInfo({ projectData, size, source }: ExtraInfoProps) {
|
|||||||
|
|
||||||
function BackstageClock() {
|
function BackstageClock() {
|
||||||
const { getLocalizedString } = useTranslation();
|
const { getLocalizedString } = useTranslation();
|
||||||
const clock = useClock();
|
const clock = useAutoTickingClock();
|
||||||
|
|
||||||
// gather timer data
|
// gather timer data
|
||||||
const formattedClock = formatTime(clock);
|
const formattedClock = formatTime(clock);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import Empty from '../../common/components/state/Empty';
|
|||||||
import EmptyPage from '../../common/components/state/EmptyPage';
|
import EmptyPage from '../../common/components/state/EmptyPage';
|
||||||
import ViewLogo from '../../common/components/view-logo/ViewLogo';
|
import ViewLogo from '../../common/components/view-logo/ViewLogo';
|
||||||
import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
|
import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
|
||||||
import { useClock } from '../../common/hooks/useSocket';
|
import { useAutoTickingClock } from '../../common/hooks/useAutoTickingClock';
|
||||||
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
||||||
import { ExtendedEntry } from '../../common/utils/rundownMetadata';
|
import { ExtendedEntry } from '../../common/utils/rundownMetadata';
|
||||||
import { formatTime, getDefaultFormat } from '../../common/utils/time';
|
import { formatTime, getDefaultFormat } from '../../common/utils/time';
|
||||||
@@ -140,7 +140,7 @@ function CountdownContents({ playableEvents, subscriptions, goToEditMode }: Coun
|
|||||||
|
|
||||||
function CountdownClock() {
|
function CountdownClock() {
|
||||||
const { getLocalizedString } = useTranslation();
|
const { getLocalizedString } = useTranslation();
|
||||||
const clock = useClock();
|
const clock = useAutoTickingClock();
|
||||||
|
|
||||||
// gather timer data
|
// gather timer data
|
||||||
const formattedClock = formatTime(clock);
|
const formattedClock = formatTime(clock);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Playback } from 'ontime-types';
|
import { Playback } from 'ontime-types';
|
||||||
|
|
||||||
|
import { useAutoTickingClock } from '../../common/hooks/useAutoTickingClock';
|
||||||
import { useIsSmallScreen } from '../../common/hooks/useIsSmallScreen';
|
import { useIsSmallScreen } from '../../common/hooks/useIsSmallScreen';
|
||||||
import { useStudioClockSocket } from '../../common/hooks/useSocket';
|
import { useStudioClockSocket } from '../../common/hooks/useSocket';
|
||||||
import { cx } from '../../common/utils/styleUtils';
|
import { cx } from '../../common/utils/styleUtils';
|
||||||
@@ -18,7 +19,8 @@ interface StudioClockProps {
|
|||||||
|
|
||||||
export default function StudioClock({ hideCards }: StudioClockProps) {
|
export default function StudioClock({ hideCards }: StudioClockProps) {
|
||||||
const isSmallScreen = useIsSmallScreen();
|
const isSmallScreen = useIsSmallScreen();
|
||||||
const { clock, playback } = useStudioClockSocket();
|
const clock = useAutoTickingClock();
|
||||||
|
const { playback } = useStudioClockSocket();
|
||||||
const onAir = playback !== Playback.Stop;
|
const onAir = playback !== Playback.Stop;
|
||||||
|
|
||||||
// if we are on mobile and have to show the cards
|
// if we are on mobile and have to show the cards
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ import { useMemo } from 'react';
|
|||||||
import EmptyPage from '../../common/components/state/EmptyPage';
|
import EmptyPage from '../../common/components/state/EmptyPage';
|
||||||
import ViewLogo from '../../common/components/view-logo/ViewLogo';
|
import ViewLogo from '../../common/components/view-logo/ViewLogo';
|
||||||
import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
|
import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
|
||||||
import { useClock, useSelectedEventId } from '../../common/hooks/useSocket';
|
import { useAutoTickingClock } from '../../common/hooks/useAutoTickingClock';
|
||||||
|
import { useSelectedEventId } from '../../common/hooks/useSocket';
|
||||||
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
||||||
import { formatTime, getDefaultFormat } from '../../common/utils/time';
|
import { formatTime, getDefaultFormat } from '../../common/utils/time';
|
||||||
import { useTranslation } from '../../translation/TranslationProvider';
|
import { useTranslation } from '../../translation/TranslationProvider';
|
||||||
@@ -72,7 +73,7 @@ function TimelinePage({ events, customFields, projectData, settings }: TimelineD
|
|||||||
|
|
||||||
function TimelineClock() {
|
function TimelineClock() {
|
||||||
const { getLocalizedString } = useTranslation();
|
const { getLocalizedString } = useTranslation();
|
||||||
const clock = useClock();
|
const clock = useAutoTickingClock();
|
||||||
|
|
||||||
// gather timer data
|
// gather timer data
|
||||||
const formattedClock = formatTime(clock);
|
const formattedClock = formatTime(clock);
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import EmptyPage from '../../common/components/state/EmptyPage';
|
|||||||
import TitleCard from '../../common/components/title-card/TitleCard';
|
import TitleCard from '../../common/components/title-card/TitleCard';
|
||||||
import ViewLogo from '../../common/components/view-logo/ViewLogo';
|
import ViewLogo from '../../common/components/view-logo/ViewLogo';
|
||||||
import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
|
import ViewParamsEditor from '../../common/components/view-params-editor/ViewParamsEditor';
|
||||||
|
import { useAutoTickingClock } from '../../common/hooks/useAutoTickingClock';
|
||||||
import { useTimerSocket } from '../../common/hooks/useSocket';
|
import { useTimerSocket } from '../../common/hooks/useSocket';
|
||||||
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
import { useWindowTitle } from '../../common/hooks/useWindowTitle';
|
||||||
import { cx } from '../../common/utils/styleUtils';
|
import { cx } from '../../common/utils/styleUtils';
|
||||||
@@ -101,7 +102,6 @@ function Timer({ customFields, projectData, isMirrored, settings, viewSettings,
|
|||||||
|
|
||||||
// gather timer data
|
// gather timer data
|
||||||
const totalTime = getTotalTime(time.duration, time.addedTime);
|
const totalTime = getTotalTime(time.duration, time.addedTime);
|
||||||
const formattedClock = formatTime(clock);
|
|
||||||
const stageTimer = getTimerByType(freezeOvertime, timerTypeNow, clock, time, timerType);
|
const stageTimer = getTimerByType(freezeOvertime, timerTypeNow, clock, time, timerType);
|
||||||
const display = getFormattedTimer(stageTimer, viewTimerType, localisedMinutes, {
|
const display = getFormattedTimer(stageTimer, viewTimerType, localisedMinutes, {
|
||||||
removeSeconds: hideTimerSeconds,
|
removeSeconds: hideTimerSeconds,
|
||||||
@@ -164,12 +164,7 @@ function Timer({ customFields, projectData, isMirrored, settings, viewSettings,
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{showClock && (
|
{showClock && <TimerAutoTickingClock />}
|
||||||
<div className='clock-container'>
|
|
||||||
<div className='label'>{getLocalizedString('common.time_now')}</div>
|
|
||||||
<SuperscriptTime time={formattedClock} className='clock' />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className={cx(['timer-container', message.timer.blink && !showOverlay && 'blink'])}>
|
<div className={cx(['timer-container', message.timer.blink && !showOverlay && 'blink'])}>
|
||||||
{showEndMessage ? (
|
{showEndMessage ? (
|
||||||
@@ -216,3 +211,16 @@ function Timer({ customFields, projectData, isMirrored, settings, viewSettings,
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TimerAutoTickingClock() {
|
||||||
|
const autoTickingClock = useAutoTickingClock();
|
||||||
|
const formattedClock = formatTime(autoTickingClock);
|
||||||
|
const { getLocalizedString } = useTranslation();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='clock-container'>
|
||||||
|
<div className='label'>{getLocalizedString('common.time_now')}</div>
|
||||||
|
<SuperscriptTime time={formattedClock} className='clock' />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user