mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-22 07:29:08 +00:00
Remove public event feature (#1645)
This commit is contained in:
committed by
Carlos Valente
parent
0649678dca
commit
08d9e24871
@@ -103,13 +103,6 @@ IP.ADDRESS:4001/studio > Studio Clock
|
|||||||
IP.ADDRESS:4001/timeline > Timeline
|
IP.ADDRESS:4001/timeline > Timeline
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
|
||||||
For the public views
|
|
||||||
-------------------------------------------------------------
|
|
||||||
IP.ADDRESS:4001/public > Public / Foyer view
|
|
||||||
IP.ADDRESS:4001/lower > Lower Thirds
|
|
||||||
```
|
|
||||||
|
|
||||||
```
|
```
|
||||||
For production views
|
For production views
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ const Countdown = React.lazy(() => import('./features/viewers/countdown/Countdow
|
|||||||
|
|
||||||
const Backstage = React.lazy(() => import('./views/backstage/Backstage'));
|
const Backstage = React.lazy(() => import('./views/backstage/Backstage'));
|
||||||
const Timeline = React.lazy(() => import('./views/timeline/TimelinePage'));
|
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 Lower = React.lazy(() => import('./features/viewers/lower-thirds/LowerThird'));
|
||||||
const StudioClock = React.lazy(() => import('./features/viewers/studio/StudioClock'));
|
const StudioClock = React.lazy(() => import('./features/viewers/studio/StudioClock'));
|
||||||
const ProjectInfo = React.lazy(() => import('./views/project-info/ProjectInfo'));
|
const ProjectInfo = React.lazy(() => import('./views/project-info/ProjectInfo'));
|
||||||
@@ -40,7 +39,6 @@ const SClock = withPreset(withData(ClockView));
|
|||||||
const SCountdown = withPreset(withData(Countdown));
|
const SCountdown = withPreset(withData(Countdown));
|
||||||
const SBackstage = withPreset(withData(Backstage));
|
const SBackstage = withPreset(withData(Backstage));
|
||||||
const SProjectInfo = withPreset(withData(ProjectInfo));
|
const SProjectInfo = withPreset(withData(ProjectInfo));
|
||||||
const SPublic = withPreset(withData(Public));
|
|
||||||
const SLowerThird = withPreset(withData(Lower));
|
const SLowerThird = withPreset(withData(Lower));
|
||||||
const SStudio = withPreset(withData(StudioClock));
|
const SStudio = withPreset(withData(StudioClock));
|
||||||
const STimeline = withPreset(withData(Timeline));
|
const STimeline = withPreset(withData(Timeline));
|
||||||
@@ -88,14 +86,6 @@ export default function AppRouter() {
|
|||||||
</ViewLoader>
|
</ViewLoader>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Route
|
|
||||||
path='/public'
|
|
||||||
element={
|
|
||||||
<ViewLoader>
|
|
||||||
<SPublic />
|
|
||||||
</ViewLoader>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<Route
|
<Route
|
||||||
path='/minimal'
|
path='/minimal'
|
||||||
element={
|
element={
|
||||||
|
|||||||
@@ -36,11 +36,10 @@ import { logAxiosError } from '../api/utils';
|
|||||||
import { useEditorSettings } from '../stores/editorSettings';
|
import { useEditorSettings } from '../stores/editorSettings';
|
||||||
|
|
||||||
export type EventOptions = Partial<{
|
export type EventOptions = Partial<{
|
||||||
// options to any new block (event / delay / block)
|
// options of any new entries (event / delay / block)
|
||||||
after: MaybeString;
|
after: MaybeString;
|
||||||
before: MaybeString;
|
before: MaybeString;
|
||||||
// options to blocks of type OntimeEvent
|
// options of entries of type OntimeEvent
|
||||||
defaultPublic: boolean;
|
|
||||||
linkPrevious: boolean;
|
linkPrevious: boolean;
|
||||||
lastEventId: MaybeString;
|
lastEventId: MaybeString;
|
||||||
}>;
|
}>;
|
||||||
@@ -51,7 +50,6 @@ export type EventOptions = Partial<{
|
|||||||
export const useEntryActions = () => {
|
export const useEntryActions = () => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const {
|
const {
|
||||||
defaultPublic,
|
|
||||||
linkPrevious,
|
linkPrevious,
|
||||||
defaultTimeStrategy,
|
defaultTimeStrategy,
|
||||||
defaultDuration,
|
defaultDuration,
|
||||||
@@ -95,7 +93,6 @@ export const useEntryActions = () => {
|
|||||||
const applicationOptions = {
|
const applicationOptions = {
|
||||||
after: options?.after,
|
after: options?.after,
|
||||||
before: options?.before,
|
before: options?.before,
|
||||||
defaultPublic: options?.defaultPublic ?? defaultPublic,
|
|
||||||
lastEventId: options?.lastEventId,
|
lastEventId: options?.lastEventId,
|
||||||
linkPrevious: options?.linkPrevious ?? linkPrevious,
|
linkPrevious: options?.linkPrevious ?? linkPrevious,
|
||||||
};
|
};
|
||||||
@@ -111,7 +108,6 @@ export const useEntryActions = () => {
|
|||||||
|
|
||||||
// Override event with options from editor settings
|
// Override event with options from editor settings
|
||||||
newEntry.linkStart = applicationOptions.linkPrevious;
|
newEntry.linkStart = applicationOptions.linkPrevious;
|
||||||
newEntry.isPublic = applicationOptions.defaultPublic;
|
|
||||||
|
|
||||||
if (newEntry.duration === undefined && newEntry.timeEnd === undefined) {
|
if (newEntry.duration === undefined && newEntry.timeEnd === undefined) {
|
||||||
newEntry.duration = parseUserTime(defaultDuration);
|
newEntry.duration = parseUserTime(defaultDuration);
|
||||||
@@ -157,7 +153,6 @@ export const useEntryActions = () => {
|
|||||||
defaultDangerTime,
|
defaultDangerTime,
|
||||||
defaultDuration,
|
defaultDuration,
|
||||||
defaultEndAction,
|
defaultEndAction,
|
||||||
defaultPublic,
|
|
||||||
defaultTimerType,
|
defaultTimerType,
|
||||||
defaultTimeStrategy,
|
defaultTimeStrategy,
|
||||||
defaultWarnTime,
|
defaultWarnTime,
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
|
|
||||||
const scriptTagId = 'ontime-override';
|
|
||||||
export const useRuntimeStylesheet = (pathToFile) => {
|
|
||||||
const [shouldRender, setShouldRender] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const fetchData = async () => {
|
|
||||||
const response = await fetch(pathToFile);
|
|
||||||
if (response.ok) {
|
|
||||||
return response.text();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!pathToFile) {
|
|
||||||
document.getElementById(scriptTagId)?.remove();
|
|
||||||
setShouldRender(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (document.getElementById(scriptTagId)) {
|
|
||||||
setShouldRender(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setShouldRender(false);
|
|
||||||
const styleSheet = document.createElement('style');
|
|
||||||
styleSheet.rel = 'stylesheet';
|
|
||||||
styleSheet.setAttribute('id', scriptTagId);
|
|
||||||
|
|
||||||
fetchData()
|
|
||||||
.then((data) => {
|
|
||||||
styleSheet.innerHTML = data;
|
|
||||||
document.head.append(styleSheet);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error(`Error loading stylesheet: ${error}`);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
// schedule render for next tick
|
|
||||||
setTimeout(() => setShouldRender(true), 0);
|
|
||||||
});
|
|
||||||
}, [pathToFile]);
|
|
||||||
|
|
||||||
return { shouldRender };
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
const scriptTagId = 'ontime-override';
|
||||||
|
|
||||||
|
export const useRuntimeStylesheet = (pathToFile?: string): { shouldRender: boolean } => {
|
||||||
|
const [shouldRender, setShouldRender] = useState(false);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When a view mounts or the stylesheet path changes we need to handle potentially loading a new stylesheet
|
||||||
|
* - if no path is given, ensure there is no stylesheet loaded
|
||||||
|
* - if a path is given, fetch the stylesheet and inject it into the document head
|
||||||
|
* @returns { shouldRender: boolean } - after the stylesheet is handled and the clients are ready to render
|
||||||
|
*/
|
||||||
|
useEffect(() => {
|
||||||
|
if (!pathToFile) {
|
||||||
|
handleNoStylesheet();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// there is already a stylesheet loaded, nothing further to do
|
||||||
|
if (document.getElementById(scriptTagId)) {
|
||||||
|
setShouldRender(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setShouldRender(false);
|
||||||
|
|
||||||
|
fetchStylesheetData(pathToFile)
|
||||||
|
.then((data: string | undefined) => {
|
||||||
|
if (!data) {
|
||||||
|
console.error('Error loading stylesheet: no data');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return injectStylesheet(data);
|
||||||
|
})
|
||||||
|
.catch((error: unknown) => {
|
||||||
|
console.error(`Error loading stylesheet: ${error}`);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
// schedule render for next tick
|
||||||
|
setTimeout(() => setShouldRender(true), 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No stylesheet was provided, remove any existing stylesheet
|
||||||
|
*/
|
||||||
|
function handleNoStylesheet() {
|
||||||
|
document.getElementById(scriptTagId)?.remove();
|
||||||
|
setShouldRender(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get data from backend
|
||||||
|
*/
|
||||||
|
async function fetchStylesheetData(path: string) {
|
||||||
|
const response = await fetch(path);
|
||||||
|
if (response.ok) {
|
||||||
|
return response.text();
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a stylesheet with given content to the document head
|
||||||
|
*/
|
||||||
|
async function injectStylesheet(styleContent: string) {
|
||||||
|
const styleSheet = document.createElement('style');
|
||||||
|
styleSheet.setAttribute('id', scriptTagId);
|
||||||
|
styleSheet.innerHTML = styleContent;
|
||||||
|
document.head.append(styleSheet);
|
||||||
|
}
|
||||||
|
}, [pathToFile]);
|
||||||
|
|
||||||
|
return { shouldRender };
|
||||||
|
};
|
||||||
@@ -3,8 +3,6 @@ import { ProjectData } from 'ontime-types';
|
|||||||
export const projectDataPlaceholder: ProjectData = {
|
export const projectDataPlaceholder: ProjectData = {
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
publicUrl: '',
|
|
||||||
publicInfo: '',
|
|
||||||
backstageUrl: '',
|
backstageUrl: '',
|
||||||
backstageInfo: '',
|
backstageInfo: '',
|
||||||
projectLogo: null,
|
projectLogo: null,
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ type EditorSettingsStore = {
|
|||||||
defaultTimeStrategy: TimeStrategy;
|
defaultTimeStrategy: TimeStrategy;
|
||||||
defaultWarnTime: string;
|
defaultWarnTime: string;
|
||||||
defaultDangerTime: string;
|
defaultDangerTime: string;
|
||||||
defaultPublic: boolean;
|
|
||||||
defaultTimerType: TimerType;
|
defaultTimerType: TimerType;
|
||||||
defaultEndAction: EndAction;
|
defaultEndAction: EndAction;
|
||||||
setDefaultDuration: (defaultDuration: string) => void;
|
setDefaultDuration: (defaultDuration: string) => void;
|
||||||
@@ -18,7 +17,6 @@ type EditorSettingsStore = {
|
|||||||
setTimeStrategy: (timeStrategy: TimeStrategy) => void;
|
setTimeStrategy: (timeStrategy: TimeStrategy) => void;
|
||||||
setWarnTime: (warnTime: string) => void;
|
setWarnTime: (warnTime: string) => void;
|
||||||
setDangerTime: (dangerTime: string) => void;
|
setDangerTime: (dangerTime: string) => void;
|
||||||
setDefaultPublic: (defaultPublic: boolean) => void;
|
|
||||||
setDefaultTimerType: (defaultTimerType: TimerType) => void;
|
setDefaultTimerType: (defaultTimerType: TimerType) => void;
|
||||||
setDefaultEndAction: (defaultEndAction: EndAction) => void;
|
setDefaultEndAction: (defaultEndAction: EndAction) => void;
|
||||||
};
|
};
|
||||||
@@ -29,7 +27,6 @@ export const editorSettingsDefaults = {
|
|||||||
timeStrategy: TimeStrategy.LockDuration,
|
timeStrategy: TimeStrategy.LockDuration,
|
||||||
warnTime: '00:02:00', // 120000 same as backend
|
warnTime: '00:02:00', // 120000 same as backend
|
||||||
dangerTime: '00:01:00', // 60000 same as backend
|
dangerTime: '00:01:00', // 60000 same as backend
|
||||||
isPublic: true,
|
|
||||||
timerType: TimerType.CountDown,
|
timerType: TimerType.CountDown,
|
||||||
endAction: EndAction.None,
|
endAction: EndAction.None,
|
||||||
};
|
};
|
||||||
@@ -40,7 +37,6 @@ enum EditorSettingsKeys {
|
|||||||
DefaultTimeStrategy = 'ontime-time-strategy',
|
DefaultTimeStrategy = 'ontime-time-strategy',
|
||||||
DefaultWarnTime = 'ontime-default-warn-time',
|
DefaultWarnTime = 'ontime-default-warn-time',
|
||||||
DefaultDangerTime = 'ontime-default-danger-time',
|
DefaultDangerTime = 'ontime-default-danger-time',
|
||||||
DefaultPublic = 'ontime-default-public',
|
|
||||||
DefaultTimerType = 'ontime-default-timer-type',
|
DefaultTimerType = 'ontime-default-timer-type',
|
||||||
DefaultEndAction = 'ontime-default-end-action',
|
DefaultEndAction = 'ontime-default-end-action',
|
||||||
}
|
}
|
||||||
@@ -55,7 +51,6 @@ export const useEditorSettings = create<EditorSettingsStore>((set) => {
|
|||||||
),
|
),
|
||||||
defaultWarnTime: localStorage.getItem(EditorSettingsKeys.DefaultWarnTime) ?? editorSettingsDefaults.warnTime,
|
defaultWarnTime: localStorage.getItem(EditorSettingsKeys.DefaultWarnTime) ?? editorSettingsDefaults.warnTime,
|
||||||
defaultDangerTime: localStorage.getItem(EditorSettingsKeys.DefaultDangerTime) ?? editorSettingsDefaults.dangerTime,
|
defaultDangerTime: localStorage.getItem(EditorSettingsKeys.DefaultDangerTime) ?? editorSettingsDefaults.dangerTime,
|
||||||
defaultPublic: booleanFromLocalStorage(EditorSettingsKeys.DefaultPublic, editorSettingsDefaults.isPublic),
|
|
||||||
defaultTimerType: validateTimerType(
|
defaultTimerType: validateTimerType(
|
||||||
localStorage.getItem(EditorSettingsKeys.DefaultTimerType),
|
localStorage.getItem(EditorSettingsKeys.DefaultTimerType),
|
||||||
editorSettingsDefaults.timerType,
|
editorSettingsDefaults.timerType,
|
||||||
@@ -92,11 +87,6 @@ export const useEditorSettings = create<EditorSettingsStore>((set) => {
|
|||||||
localStorage.setItem(EditorSettingsKeys.DefaultDangerTime, String(defaultDangerTime));
|
localStorage.setItem(EditorSettingsKeys.DefaultDangerTime, String(defaultDangerTime));
|
||||||
return { defaultDangerTime };
|
return { defaultDangerTime };
|
||||||
}),
|
}),
|
||||||
setDefaultPublic: (defaultPublic) =>
|
|
||||||
set(() => {
|
|
||||||
localStorage.setItem(EditorSettingsKeys.DefaultPublic, String(defaultPublic));
|
|
||||||
return { defaultPublic };
|
|
||||||
}),
|
|
||||||
setDefaultTimerType: (defaultTimerType) =>
|
setDefaultTimerType: (defaultTimerType) =>
|
||||||
set(() => {
|
set(() => {
|
||||||
localStorage.setItem(EditorSettingsKeys.DefaultTimerType, String(defaultTimerType));
|
localStorage.setItem(EditorSettingsKeys.DefaultTimerType, String(defaultTimerType));
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ describe('cloneEvent()', () => {
|
|||||||
linkStart: false,
|
linkStart: false,
|
||||||
countToEnd: false,
|
countToEnd: false,
|
||||||
endAction: EndAction.None,
|
endAction: EndAction.None,
|
||||||
isPublic: false,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: 'F00',
|
colour: 'F00',
|
||||||
revision: 10,
|
revision: 10,
|
||||||
@@ -52,7 +51,6 @@ describe('cloneEvent()', () => {
|
|||||||
countToEnd: original.countToEnd,
|
countToEnd: original.countToEnd,
|
||||||
linkStart: original.linkStart,
|
linkStart: original.linkStart,
|
||||||
endAction: original.endAction,
|
endAction: original.endAction,
|
||||||
isPublic: original.isPublic,
|
|
||||||
skip: original.skip,
|
skip: original.skip,
|
||||||
colour: original.colour,
|
colour: original.colour,
|
||||||
revision: 0,
|
revision: 0,
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ export const cloneEvent = (event: OntimeEvent): ClonedEvent => {
|
|||||||
countToEnd: event.countToEnd,
|
countToEnd: event.countToEnd,
|
||||||
linkStart: event.linkStart,
|
linkStart: event.linkStart,
|
||||||
endAction: event.endAction,
|
endAction: event.endAction,
|
||||||
isPublic: event.isPublic,
|
|
||||||
skip: event.skip,
|
skip: event.skip,
|
||||||
colour: event.colour,
|
colour: event.colour,
|
||||||
parent: event.parent,
|
parent: event.parent,
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ const staticSelectProperties = [
|
|||||||
{ value: 'eventNow.title', label: 'Title' },
|
{ value: 'eventNow.title', label: 'Title' },
|
||||||
{ value: 'eventNow.cue', label: 'Cue' },
|
{ value: 'eventNow.cue', label: 'Cue' },
|
||||||
{ value: 'eventNow.countToEnd', label: 'Count to end' },
|
{ value: 'eventNow.countToEnd', label: 'Count to end' },
|
||||||
{ value: 'eventNow.isPublic', label: 'Is public' },
|
|
||||||
{ value: 'eventNow.note', label: 'Note' },
|
{ value: 'eventNow.note', label: 'Note' },
|
||||||
{ value: 'eventNow.colour', label: 'Colour' },
|
{ value: 'eventNow.colour', label: 'Colour' },
|
||||||
];
|
];
|
||||||
|
|||||||
-2
@@ -38,7 +38,6 @@ const eventStaticPropertiesNow = [
|
|||||||
'{{eventNow.timeStart}}',
|
'{{eventNow.timeStart}}',
|
||||||
'{{eventNow.timeEnd}}',
|
'{{eventNow.timeEnd}}',
|
||||||
'{{eventNow.duration}}',
|
'{{eventNow.duration}}',
|
||||||
'{{eventNow.isPublic}}',
|
|
||||||
'{{eventNow.colour}}',
|
'{{eventNow.colour}}',
|
||||||
'{{eventNow.delay}}',
|
'{{eventNow.delay}}',
|
||||||
];
|
];
|
||||||
@@ -51,7 +50,6 @@ const eventStaticPropertiesNext = [
|
|||||||
'{{eventNext.timeStart}}',
|
'{{eventNext.timeStart}}',
|
||||||
'{{eventNext.timeEnd}}',
|
'{{eventNext.timeEnd}}',
|
||||||
'{{eventNext.duration}}',
|
'{{eventNext.duration}}',
|
||||||
'{{eventNext.isPublic}}',
|
|
||||||
'{{eventNext.colour}}',
|
'{{eventNext.colour}}',
|
||||||
'{{eventNext.delay}}',
|
'{{eventNext.delay}}',
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ export default function EditorSettingsForm() {
|
|||||||
defaultTimeStrategy,
|
defaultTimeStrategy,
|
||||||
defaultWarnTime,
|
defaultWarnTime,
|
||||||
defaultDangerTime,
|
defaultDangerTime,
|
||||||
defaultPublic,
|
|
||||||
defaultTimerType,
|
defaultTimerType,
|
||||||
defaultEndAction,
|
defaultEndAction,
|
||||||
setDefaultDuration,
|
setDefaultDuration,
|
||||||
@@ -21,7 +20,6 @@ export default function EditorSettingsForm() {
|
|||||||
setTimeStrategy,
|
setTimeStrategy,
|
||||||
setWarnTime,
|
setWarnTime,
|
||||||
setDangerTime,
|
setDangerTime,
|
||||||
setDefaultPublic,
|
|
||||||
setDefaultTimerType,
|
setDefaultTimerType,
|
||||||
setDefaultEndAction,
|
setDefaultEndAction,
|
||||||
} = useEditorSettings((state) => state);
|
} = useEditorSettings((state) => state);
|
||||||
@@ -127,17 +125,6 @@ export default function EditorSettingsForm() {
|
|||||||
/>
|
/>
|
||||||
</Panel.ListItem>
|
</Panel.ListItem>
|
||||||
</Panel.ListGroup>
|
</Panel.ListGroup>
|
||||||
<Panel.ListGroup>
|
|
||||||
<Panel.ListItem>
|
|
||||||
<Panel.Field title='Default public' description='New events will be public' />
|
|
||||||
<Switch
|
|
||||||
variant='ontime'
|
|
||||||
size='lg'
|
|
||||||
defaultChecked={defaultPublic}
|
|
||||||
onChange={(event) => setDefaultPublic(event.target.checked)}
|
|
||||||
/>
|
|
||||||
</Panel.ListItem>
|
|
||||||
</Panel.ListGroup>
|
|
||||||
</Panel.Section>
|
</Panel.Section>
|
||||||
<Panel.Section>
|
<Panel.Section>
|
||||||
<Panel.Title>Run mode</Panel.Title>
|
<Panel.Title>Run mode</Panel.Title>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { PROJECT_LIST } from '../../../../common/api/constants';
|
|||||||
import { createProject } from '../../../../common/api/db';
|
import { createProject } from '../../../../common/api/db';
|
||||||
import { maybeAxiosError } from '../../../../common/api/utils';
|
import { maybeAxiosError } from '../../../../common/api/utils';
|
||||||
import { preventEscape } from '../../../../common/utils/keyEvent';
|
import { preventEscape } from '../../../../common/utils/keyEvent';
|
||||||
import { documentationUrl, websiteUrl } from '../../../../externals';
|
import { documentationUrl } from '../../../../externals';
|
||||||
import * as Panel from '../../panel-utils/PanelUtils';
|
import * as Panel from '../../panel-utils/PanelUtils';
|
||||||
|
|
||||||
import style from './ProjectPanel.module.scss';
|
import style from './ProjectPanel.module.scss';
|
||||||
@@ -20,8 +20,6 @@ interface ProjectCreateFromProps {
|
|||||||
type ProjectCreateFormValues = {
|
type ProjectCreateFormValues = {
|
||||||
title?: string;
|
title?: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
publicInfo?: string;
|
|
||||||
publicUrl?: string;
|
|
||||||
backstageInfo?: string;
|
backstageInfo?: string;
|
||||||
backstageUrl?: string;
|
backstageUrl?: string;
|
||||||
custom?: { title: string; value: string }[];
|
custom?: { title: string; value: string }[];
|
||||||
@@ -120,28 +118,6 @@ export default function ProjectCreateForm(props: ProjectCreateFromProps) {
|
|||||||
{...register('description')}
|
{...register('description')}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
|
||||||
Public info
|
|
||||||
<Textarea
|
|
||||||
variant='ontime-filled'
|
|
||||||
size='sm'
|
|
||||||
maxLength={150}
|
|
||||||
placeholder='Shows always start ontime'
|
|
||||||
autoComplete='off'
|
|
||||||
resize='none'
|
|
||||||
{...register('publicInfo')}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
Public QR code Url
|
|
||||||
<Input
|
|
||||||
variant='ontime-filled'
|
|
||||||
size='sm'
|
|
||||||
placeholder={websiteUrl}
|
|
||||||
autoComplete='off'
|
|
||||||
{...register('publicUrl')}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label>
|
<label>
|
||||||
Backstage info
|
Backstage info
|
||||||
<Textarea
|
<Textarea
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { maybeAxiosError } from '../../../../common/api/utils';
|
|||||||
import useProjectData from '../../../../common/hooks-query/useProjectData';
|
import useProjectData from '../../../../common/hooks-query/useProjectData';
|
||||||
import { preventEscape } from '../../../../common/utils/keyEvent';
|
import { preventEscape } from '../../../../common/utils/keyEvent';
|
||||||
import { validateLogo } from '../../../../common/utils/uploadUtils';
|
import { validateLogo } from '../../../../common/utils/uploadUtils';
|
||||||
import { documentationUrl, websiteUrl } from '../../../../externals';
|
import { documentationUrl } from '../../../../externals';
|
||||||
import * as Panel from '../../panel-utils/PanelUtils';
|
import * as Panel from '../../panel-utils/PanelUtils';
|
||||||
|
|
||||||
import style from './ProjectPanel.module.scss';
|
import style from './ProjectPanel.module.scss';
|
||||||
@@ -198,28 +198,6 @@ export default function ProjectData() {
|
|||||||
{...register('description')}
|
{...register('description')}
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label>
|
|
||||||
Public info
|
|
||||||
<Textarea
|
|
||||||
variant='ontime-filled'
|
|
||||||
size='sm'
|
|
||||||
maxLength={150}
|
|
||||||
placeholder='Shows always start ontime'
|
|
||||||
autoComplete='off'
|
|
||||||
resize='none'
|
|
||||||
{...register('publicInfo')}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label>
|
|
||||||
Public QR code URL
|
|
||||||
<Input
|
|
||||||
variant='ontime-filled'
|
|
||||||
size='sm'
|
|
||||||
placeholder={websiteUrl}
|
|
||||||
autoComplete='off'
|
|
||||||
{...register('publicUrl')}
|
|
||||||
/>
|
|
||||||
</label>
|
|
||||||
<label>
|
<label>
|
||||||
Backstage info
|
Backstage info
|
||||||
<Textarea
|
<Textarea
|
||||||
|
|||||||
-1
@@ -12,7 +12,6 @@ describe('convertToImportMap', () => {
|
|||||||
Duration: 'duration',
|
Duration: 'duration',
|
||||||
Cue: 'cue',
|
Cue: 'cue',
|
||||||
Title: 'title',
|
Title: 'title',
|
||||||
'Is Public': 'public',
|
|
||||||
Skip: 'skip',
|
Skip: 'skip',
|
||||||
Note: 'notes',
|
Note: 'notes',
|
||||||
Colour: 'colour',
|
Colour: 'colour',
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ const namedImportMap = {
|
|||||||
Cue: 'cue',
|
Cue: 'cue',
|
||||||
Title: 'title',
|
Title: 'title',
|
||||||
'Count to end': 'count to end',
|
'Count to end': 'count to end',
|
||||||
'Is Public': 'public',
|
|
||||||
Skip: 'skip',
|
Skip: 'skip',
|
||||||
Note: 'notes',
|
Note: 'notes',
|
||||||
Colour: 'colour',
|
Colour: 'colour',
|
||||||
@@ -50,7 +49,6 @@ export function convertToImportMap(namedImportMap: NamedImportMap): ImportMap {
|
|||||||
cue: namedImportMap.Cue,
|
cue: namedImportMap.Cue,
|
||||||
title: namedImportMap.Title,
|
title: namedImportMap.Title,
|
||||||
countToEnd: namedImportMap['Count to end'],
|
countToEnd: namedImportMap['Count to end'],
|
||||||
isPublic: namedImportMap['Is Public'],
|
|
||||||
skip: namedImportMap.Skip,
|
skip: namedImportMap.Skip,
|
||||||
note: namedImportMap.Note,
|
note: namedImportMap.Note,
|
||||||
colour: namedImportMap.Colour,
|
colour: namedImportMap.Colour,
|
||||||
|
|||||||
@@ -41,7 +41,6 @@ export default function PreviewRundown(props: PreviewRundownProps) {
|
|||||||
<th>Warning Time</th>
|
<th>Warning Time</th>
|
||||||
<th>Danger Time</th>
|
<th>Danger Time</th>
|
||||||
<th>Count to end</th>
|
<th>Count to end</th>
|
||||||
<th>Is Public</th>
|
|
||||||
<th>Skip</th>
|
<th>Skip</th>
|
||||||
<th>Colour</th>
|
<th>Colour</th>
|
||||||
<th>Timer Type</th>
|
<th>Timer Type</th>
|
||||||
@@ -75,7 +74,6 @@ export default function PreviewRundown(props: PreviewRundownProps) {
|
|||||||
eventIndex += 1;
|
eventIndex += 1;
|
||||||
const colour = entry.colour ? getAccessibleColour(entry.colour) : {};
|
const colour = entry.colour ? getAccessibleColour(entry.colour) : {};
|
||||||
const countToEnd = booleanToText(entry.countToEnd);
|
const countToEnd = booleanToText(entry.countToEnd);
|
||||||
const isPublic = booleanToText(entry.isPublic);
|
|
||||||
const skip = booleanToText(entry.skip);
|
const skip = booleanToText(entry.skip);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -98,7 +96,6 @@ export default function PreviewRundown(props: PreviewRundownProps) {
|
|||||||
<td>{millisToString(entry.timeWarning)}</td>
|
<td>{millisToString(entry.timeWarning)}</td>
|
||||||
<td>{millisToString(entry.timeDanger)}</td>
|
<td>{millisToString(entry.timeDanger)}</td>
|
||||||
<td className={style.center}>{countToEnd && <Tag>{countToEnd}</Tag>}</td>
|
<td className={style.center}>{countToEnd && <Tag>{countToEnd}</Tag>}</td>
|
||||||
<td className={style.center}>{isPublic && <Tag>{isPublic}</Tag>}</td>
|
|
||||||
<td>{skip && <Tag>{skip}</Tag>}</td>
|
<td>{skip && <Tag>{skip}</Tag>}</td>
|
||||||
<td style={{ ...colour }}>{entry.colour}</td>
|
<td style={{ ...colour }}>{entry.colour}</td>
|
||||||
<td className={style.center}>
|
<td className={style.center}>
|
||||||
|
|||||||
@@ -175,7 +175,6 @@ export default function RundownEntry(props: RundownEntryProps) {
|
|||||||
timeStrategy={data.timeStrategy}
|
timeStrategy={data.timeStrategy}
|
||||||
linkStart={data.linkStart}
|
linkStart={data.linkStart}
|
||||||
countToEnd={data.countToEnd}
|
countToEnd={data.countToEnd}
|
||||||
isPublic={data.isPublic}
|
|
||||||
endAction={data.endAction}
|
endAction={data.endAction}
|
||||||
timerType={data.timerType}
|
timerType={data.timerType}
|
||||||
title={data.title}
|
title={data.title}
|
||||||
@@ -196,6 +195,7 @@ export default function RundownEntry(props: RundownEntryProps) {
|
|||||||
totalGap={totalGap}
|
totalGap={totalGap}
|
||||||
isLinkedToLoaded={isLinkedToLoaded}
|
isLinkedToLoaded={isLinkedToLoaded}
|
||||||
actionHandler={actionHandler}
|
actionHandler={actionHandler}
|
||||||
|
hasTriggers={data.triggers.length > 0}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
} else if (isOntimeDelay(data)) {
|
} else if (isOntimeDelay(data)) {
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ import {
|
|||||||
IoDuplicateOutline,
|
IoDuplicateOutline,
|
||||||
IoFolder,
|
IoFolder,
|
||||||
IoLink,
|
IoLink,
|
||||||
IoPeople,
|
|
||||||
IoPeopleOutline,
|
|
||||||
IoReorderTwo,
|
IoReorderTwo,
|
||||||
IoSwapVertical,
|
IoSwapVertical,
|
||||||
IoTrash,
|
IoTrash,
|
||||||
@@ -36,7 +34,6 @@ interface EventBlockProps {
|
|||||||
linkStart: boolean;
|
linkStart: boolean;
|
||||||
countToEnd: boolean;
|
countToEnd: boolean;
|
||||||
eventIndex: number;
|
eventIndex: number;
|
||||||
isPublic: boolean;
|
|
||||||
endAction: EndAction;
|
endAction: EndAction;
|
||||||
timerType: TimerType;
|
timerType: TimerType;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -65,6 +62,7 @@ interface EventBlockProps {
|
|||||||
value: unknown;
|
value: unknown;
|
||||||
},
|
},
|
||||||
) => void;
|
) => void;
|
||||||
|
hasTriggers: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function EventBlock(props: EventBlockProps) {
|
export default function EventBlock(props: EventBlockProps) {
|
||||||
@@ -77,7 +75,6 @@ export default function EventBlock(props: EventBlockProps) {
|
|||||||
timeStrategy,
|
timeStrategy,
|
||||||
linkStart,
|
linkStart,
|
||||||
countToEnd,
|
countToEnd,
|
||||||
isPublic = true,
|
|
||||||
eventIndex,
|
eventIndex,
|
||||||
endAction,
|
endAction,
|
||||||
timerType,
|
timerType,
|
||||||
@@ -99,6 +96,7 @@ export default function EventBlock(props: EventBlockProps) {
|
|||||||
totalGap,
|
totalGap,
|
||||||
isLinkedToLoaded,
|
isLinkedToLoaded,
|
||||||
actionHandler,
|
actionHandler,
|
||||||
|
hasTriggers,
|
||||||
} = props;
|
} = props;
|
||||||
const { selectedEventId, setSelectedEventId, clearSelectedEventId } = useEventIdSwapping();
|
const { selectedEventId, setSelectedEventId, clearSelectedEventId } = useEventIdSwapping();
|
||||||
const { selectedEvents, setSelectedEvents } = useEventSelection();
|
const { selectedEvents, setSelectedEvents } = useEventSelection();
|
||||||
@@ -126,25 +124,6 @@ export default function EventBlock(props: EventBlockProps) {
|
|||||||
value: null,
|
value: null,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'Make public',
|
|
||||||
icon: IoPeople,
|
|
||||||
onClick: () =>
|
|
||||||
actionHandler('update', {
|
|
||||||
field: 'isPublic',
|
|
||||||
value: true,
|
|
||||||
}),
|
|
||||||
withDivider: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Make private',
|
|
||||||
icon: IoPeopleOutline,
|
|
||||||
onClick: () =>
|
|
||||||
actionHandler('update', {
|
|
||||||
field: 'isPublic',
|
|
||||||
value: false,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
{ withDivider: true, label: 'Group', icon: IoFolder, onClick: () => actionHandler('group') },
|
{ withDivider: true, label: 'Group', icon: IoFolder, onClick: () => actionHandler('group') },
|
||||||
{ withDivider: true, label: 'Delete', icon: IoTrash, onClick: () => actionHandler('delete') },
|
{ withDivider: true, label: 'Delete', icon: IoTrash, onClick: () => actionHandler('delete') },
|
||||||
]
|
]
|
||||||
@@ -158,16 +137,6 @@ export default function EventBlock(props: EventBlockProps) {
|
|||||||
value: linkStart,
|
value: linkStart,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: 'Toggle public',
|
|
||||||
icon: IoPeopleOutline,
|
|
||||||
onClick: () =>
|
|
||||||
actionHandler('update', {
|
|
||||||
field: 'isPublic',
|
|
||||||
value: !isPublic,
|
|
||||||
}),
|
|
||||||
withDivider: true,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: 'Add to swap',
|
label: 'Add to swap',
|
||||||
icon: IoAdd,
|
icon: IoAdd,
|
||||||
@@ -308,7 +277,6 @@ export default function EventBlock(props: EventBlockProps) {
|
|||||||
timeStrategy={timeStrategy}
|
timeStrategy={timeStrategy}
|
||||||
eventId={eventId}
|
eventId={eventId}
|
||||||
eventIndex={eventIndex}
|
eventIndex={eventIndex}
|
||||||
isPublic={isPublic}
|
|
||||||
endAction={endAction}
|
endAction={endAction}
|
||||||
timerType={timerType}
|
timerType={timerType}
|
||||||
title={title}
|
title={title}
|
||||||
@@ -323,6 +291,7 @@ export default function EventBlock(props: EventBlockProps) {
|
|||||||
isPast={isPast}
|
isPast={isPast}
|
||||||
totalGap={totalGap}
|
totalGap={totalGap}
|
||||||
isLinkedToLoaded={isLinkedToLoaded}
|
isLinkedToLoaded={isLinkedToLoaded}
|
||||||
|
hasTriggers={hasTriggers}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
IoArrowUp,
|
IoArrowUp,
|
||||||
IoBan,
|
IoBan,
|
||||||
IoFlag,
|
IoFlag,
|
||||||
IoPeople,
|
IoFlash,
|
||||||
IoPlay,
|
IoPlay,
|
||||||
IoPlayForward,
|
IoPlayForward,
|
||||||
IoPlaySkipForward,
|
IoPlaySkipForward,
|
||||||
@@ -33,7 +33,6 @@ interface EventBlockInnerProps {
|
|||||||
linkStart: boolean;
|
linkStart: boolean;
|
||||||
countToEnd: boolean;
|
countToEnd: boolean;
|
||||||
eventIndex: number;
|
eventIndex: number;
|
||||||
isPublic: boolean;
|
|
||||||
endAction: EndAction;
|
endAction: EndAction;
|
||||||
timerType: TimerType;
|
timerType: TimerType;
|
||||||
title: string;
|
title: string;
|
||||||
@@ -48,6 +47,7 @@ interface EventBlockInnerProps {
|
|||||||
isPast: boolean;
|
isPast: boolean;
|
||||||
totalGap: number;
|
totalGap: number;
|
||||||
isLinkedToLoaded: boolean;
|
isLinkedToLoaded: boolean;
|
||||||
|
hasTriggers: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
function EventBlockInner(props: EventBlockInnerProps) {
|
function EventBlockInner(props: EventBlockInnerProps) {
|
||||||
@@ -59,7 +59,6 @@ function EventBlockInner(props: EventBlockInnerProps) {
|
|||||||
timeStrategy,
|
timeStrategy,
|
||||||
linkStart,
|
linkStart,
|
||||||
countToEnd,
|
countToEnd,
|
||||||
isPublic = true,
|
|
||||||
endAction,
|
endAction,
|
||||||
timerType,
|
timerType,
|
||||||
title,
|
title,
|
||||||
@@ -74,6 +73,7 @@ function EventBlockInner(props: EventBlockInnerProps) {
|
|||||||
isPast,
|
isPast,
|
||||||
totalGap,
|
totalGap,
|
||||||
isLinkedToLoaded,
|
isLinkedToLoaded,
|
||||||
|
hasTriggers,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [renderInner, setRenderInner] = useState(false);
|
const [renderInner, setRenderInner] = useState(false);
|
||||||
@@ -132,7 +132,7 @@ function EventBlockInner(props: EventBlockInnerProps) {
|
|||||||
duration={duration}
|
duration={duration}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<div className={style.statusElements} id='block-status' data-ispublic={isPublic}>
|
<div className={style.statusElements} id='block-status' data-timerType={timerType}>
|
||||||
<span className={style.eventNote}>{note}</span>
|
<span className={style.eventNote}>{note}</span>
|
||||||
<div className={loaded ? style.progressBg : `${style.progressBg} ${style.hidden}`}>
|
<div className={loaded ? style.progressBg : `${style.progressBg} ${style.hidden}`}>
|
||||||
{loaded && <EventBlockProgressBar />}
|
{loaded && <EventBlockProgressBar />}
|
||||||
@@ -153,9 +153,9 @@ function EventBlockInner(props: EventBlockInnerProps) {
|
|||||||
<IoFlag className={`${style.statusIcon} ${countToEnd ? style.active : style.disabled}`} />
|
<IoFlag className={`${style.statusIcon} ${countToEnd ? style.active : style.disabled}`} />
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip label={`${isPublic ? 'Event is public' : 'Event is private'}`} openDelay={tooltipDelayMid}>
|
<Tooltip label='Event has Triggers' openDelay={tooltipDelayMid}>
|
||||||
<span>
|
<span>
|
||||||
<IoPeople className={`${style.statusIcon} ${isPublic ? style.active : style.disabled}`} />
|
<IoFlash className={`${style.statusIcon} ${hasTriggers ? style.active : style.disabled}`} />
|
||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ export default function EventEditor(props: EventEditorProps) {
|
|||||||
linkStart={event.linkStart}
|
linkStart={event.linkStart}
|
||||||
countToEnd={event.countToEnd}
|
countToEnd={event.countToEnd}
|
||||||
delay={event.delay ?? 0}
|
delay={event.delay ?? 0}
|
||||||
isPublic={event.isPublic}
|
|
||||||
endAction={event.endAction}
|
endAction={event.endAction}
|
||||||
timerType={event.timerType}
|
timerType={event.timerType}
|
||||||
timeWarning={event.timeWarning}
|
timeWarning={event.timeWarning}
|
||||||
|
|||||||
@@ -21,14 +21,13 @@ interface EventEditorTimesProps {
|
|||||||
linkStart: boolean;
|
linkStart: boolean;
|
||||||
countToEnd: boolean;
|
countToEnd: boolean;
|
||||||
delay: number;
|
delay: number;
|
||||||
isPublic: boolean;
|
|
||||||
endAction: EndAction;
|
endAction: EndAction;
|
||||||
timerType: TimerType;
|
timerType: TimerType;
|
||||||
timeWarning: number;
|
timeWarning: number;
|
||||||
timeDanger: number;
|
timeDanger: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
type HandledActions = 'countToEnd' | 'timerType' | 'endAction' | 'isPublic' | 'timeWarning' | 'timeDanger';
|
type HandledActions = 'countToEnd' | 'timerType' | 'endAction' | 'timeWarning' | 'timeDanger';
|
||||||
|
|
||||||
function EventEditorTimes(props: EventEditorTimesProps) {
|
function EventEditorTimes(props: EventEditorTimesProps) {
|
||||||
const {
|
const {
|
||||||
@@ -40,7 +39,6 @@ function EventEditorTimes(props: EventEditorTimesProps) {
|
|||||||
linkStart,
|
linkStart,
|
||||||
countToEnd,
|
countToEnd,
|
||||||
delay,
|
delay,
|
||||||
isPublic,
|
|
||||||
endAction,
|
endAction,
|
||||||
timerType,
|
timerType,
|
||||||
timeWarning,
|
timeWarning,
|
||||||
@@ -49,11 +47,6 @@ function EventEditorTimes(props: EventEditorTimesProps) {
|
|||||||
const { updateEntry } = useEntryActions();
|
const { updateEntry } = useEntryActions();
|
||||||
|
|
||||||
const handleSubmit = (field: HandledActions, value: string | boolean) => {
|
const handleSubmit = (field: HandledActions, value: string | boolean) => {
|
||||||
if (field === 'isPublic') {
|
|
||||||
updateEntry({ id: eventId, isPublic: !(value as boolean) });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field === 'countToEnd') {
|
if (field === 'countToEnd') {
|
||||||
updateEntry({ id: eventId, countToEnd: !(value as boolean) });
|
updateEntry({ id: eventId, countToEnd: !(value as boolean) });
|
||||||
return;
|
return;
|
||||||
@@ -160,6 +153,9 @@ function EventEditorTimes(props: EventEditorTimesProps) {
|
|||||||
<option value={TimerType.None}>None</option>
|
<option value={TimerType.None}>None</option>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
{/* TODO: rearrange this grid */}
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Editor.Label htmlFor='timeWarning'>Warning Time</Editor.Label>
|
<Editor.Label htmlFor='timeWarning'>Warning Time</Editor.Label>
|
||||||
<TimeInput
|
<TimeInput
|
||||||
@@ -171,19 +167,6 @@ function EventEditorTimes(props: EventEditorTimesProps) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
|
||||||
<Editor.Label htmlFor='isPublic'>Event Visibility</Editor.Label>
|
|
||||||
<Editor.Label className={style.switchLabel}>
|
|
||||||
<Switch
|
|
||||||
id='isPublic'
|
|
||||||
size='md'
|
|
||||||
isChecked={isPublic}
|
|
||||||
onChange={() => handleSubmit('isPublic', isPublic)}
|
|
||||||
variant='ontime'
|
|
||||||
/>
|
|
||||||
{isPublic ? 'Public' : 'Private'}
|
|
||||||
</Editor.Label>
|
|
||||||
</div>
|
|
||||||
<div>
|
<div>
|
||||||
<Editor.Label htmlFor='timeDanger'>Danger Time</Editor.Label>
|
<Editor.Label htmlFor='timeDanger'>Danger Time</Editor.Label>
|
||||||
<TimeInput
|
<TimeInput
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ function QuickAddBlock(props: QuickAddBlockProps) {
|
|||||||
const { addEntry } = useEntryActions();
|
const { addEntry } = useEntryActions();
|
||||||
|
|
||||||
const doLinkPrevious = useRef<HTMLInputElement | null>(null);
|
const doLinkPrevious = useRef<HTMLInputElement | null>(null);
|
||||||
const doPublic = useRef<HTMLInputElement | null>(null);
|
|
||||||
|
|
||||||
const addEvent = () => {
|
const addEvent = () => {
|
||||||
addEntry(
|
addEntry(
|
||||||
@@ -30,7 +29,6 @@ function QuickAddBlock(props: QuickAddBlockProps) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
after: previousEventId,
|
after: previousEventId,
|
||||||
defaultPublic: doPublic?.current?.checked,
|
|
||||||
lastEventId: previousEventId,
|
lastEventId: previousEventId,
|
||||||
linkPrevious: doLinkPrevious?.current?.checked,
|
linkPrevious: doLinkPrevious?.current?.checked,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { ComponentType, useMemo } from 'react';
|
import { ComponentType } from 'react';
|
||||||
import { ViewExtendedTimer } from 'common/models/TimeManager.type';
|
import { ViewExtendedTimer } from 'common/models/TimeManager.type';
|
||||||
import {
|
import {
|
||||||
CustomFields,
|
CustomFields,
|
||||||
@@ -8,7 +8,6 @@ import {
|
|||||||
Runtime,
|
Runtime,
|
||||||
Settings,
|
Settings,
|
||||||
SimpleTimerState,
|
SimpleTimerState,
|
||||||
SupportedEntry,
|
|
||||||
TimerType,
|
TimerType,
|
||||||
ViewSettings,
|
ViewSettings,
|
||||||
} from 'ontime-types';
|
} from 'ontime-types';
|
||||||
@@ -25,19 +24,15 @@ import { useViewOptionsStore } from '../../common/stores/viewOptions';
|
|||||||
|
|
||||||
type WithDataProps = {
|
type WithDataProps = {
|
||||||
auxTimer: SimpleTimerState;
|
auxTimer: SimpleTimerState;
|
||||||
backstageEvents: OntimeEvent[];
|
events: OntimeEvent[];
|
||||||
customFields: CustomFields;
|
customFields: CustomFields;
|
||||||
eventNext: OntimeEvent | null;
|
eventNext: OntimeEvent | null;
|
||||||
eventNow: OntimeEvent | null;
|
eventNow: OntimeEvent | null;
|
||||||
events: OntimeEvent[];
|
|
||||||
general: ProjectData;
|
general: ProjectData;
|
||||||
isMirrored: boolean;
|
isMirrored: boolean;
|
||||||
message: MessageState;
|
message: MessageState;
|
||||||
nextId: string | null;
|
nextId: string | null;
|
||||||
onAir: boolean;
|
onAir: boolean;
|
||||||
publicEventNext: OntimeEvent | null;
|
|
||||||
publicEventNow: OntimeEvent | null;
|
|
||||||
publicSelectedId: string | null;
|
|
||||||
runtime: Runtime;
|
runtime: Runtime;
|
||||||
selectedId: string | null;
|
selectedId: string | null;
|
||||||
settings: Settings | undefined; // TODO: what is the case for this being undefined?
|
settings: Settings | undefined; // TODO: what is the case for this being undefined?
|
||||||
@@ -61,17 +56,9 @@ const withData = <P extends WithDataProps>(Component: ComponentType<P>) => {
|
|||||||
const { data: settings } = useSettings();
|
const { data: settings } = useSettings();
|
||||||
const { data: customFields } = useCustomFields();
|
const { data: customFields } = useCustomFields();
|
||||||
|
|
||||||
const publicEvents = useMemo(() => {
|
|
||||||
if (Array.isArray(rundownData)) {
|
|
||||||
return rundownData.filter((e) => e.type === SupportedEntry.Event && e.title && e.isPublic);
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}, [rundownData]);
|
|
||||||
|
|
||||||
// websocket data
|
// websocket data
|
||||||
const { clock, timer, message, onAir, eventNext, publicEventNext, publicEventNow, eventNow, runtime, auxtimer1 } =
|
const { clock, timer, message, onAir, eventNext, eventNow, runtime, auxtimer1 } =
|
||||||
useStore(runtimeStore);
|
useStore(runtimeStore);
|
||||||
const publicSelectedId = publicEventNow?.id ?? null;
|
|
||||||
const selectedId = eventNow?.id ?? null;
|
const selectedId = eventNow?.id ?? null;
|
||||||
const nextId = eventNext?.id ?? null;
|
const nextId = eventNext?.id ?? null;
|
||||||
|
|
||||||
@@ -91,19 +78,15 @@ const withData = <P extends WithDataProps>(Component: ComponentType<P>) => {
|
|||||||
<Component
|
<Component
|
||||||
{...props}
|
{...props}
|
||||||
auxTimer={auxtimer1}
|
auxTimer={auxtimer1}
|
||||||
backstageEvents={rundownData}
|
events={rundownData}
|
||||||
customFields={customFields}
|
customFields={customFields}
|
||||||
eventNext={eventNext}
|
eventNext={eventNext}
|
||||||
eventNow={eventNow}
|
eventNow={eventNow}
|
||||||
events={publicEvents}
|
|
||||||
general={project}
|
general={project}
|
||||||
isMirrored={isMirrored}
|
isMirrored={isMirrored}
|
||||||
message={message}
|
message={message}
|
||||||
nextId={nextId}
|
nextId={nextId}
|
||||||
onAir={onAir}
|
onAir={onAir}
|
||||||
publicEventNext={publicEventNext}
|
|
||||||
publicEventNow={publicEventNow}
|
|
||||||
publicSelectedId={publicSelectedId}
|
|
||||||
runtime={runtime}
|
runtime={runtime}
|
||||||
selectedId={selectedId}
|
selectedId={selectedId}
|
||||||
settings={settings}
|
settings={settings}
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
ProjectData,
|
ProjectData,
|
||||||
Runtime,
|
Runtime,
|
||||||
Settings,
|
Settings,
|
||||||
SupportedEntry,
|
|
||||||
TimerPhase,
|
TimerPhase,
|
||||||
} from 'ontime-types';
|
} from 'ontime-types';
|
||||||
|
|
||||||
@@ -27,7 +26,7 @@ import CountdownSelect from './CountdownSelect';
|
|||||||
import './Countdown.scss';
|
import './Countdown.scss';
|
||||||
|
|
||||||
interface CountdownProps {
|
interface CountdownProps {
|
||||||
backstageEvents: OntimeEvent[];
|
events: OntimeEvent[];
|
||||||
general: ProjectData;
|
general: ProjectData;
|
||||||
isMirrored: boolean;
|
isMirrored: boolean;
|
||||||
runtime: Runtime;
|
runtime: Runtime;
|
||||||
@@ -37,7 +36,7 @@ interface CountdownProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function Countdown(props: CountdownProps) {
|
export default function Countdown(props: CountdownProps) {
|
||||||
const { backstageEvents, general, isMirrored, runtime, selectedId, settings, time } = props;
|
const { events, general, isMirrored, runtime, selectedId, settings, time } = props;
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
const { getLocalizedString } = useTranslation();
|
const { getLocalizedString } = useTranslation();
|
||||||
|
|
||||||
@@ -49,7 +48,7 @@ export default function Countdown(props: CountdownProps) {
|
|||||||
// eg. http://localhost:4001/countdown?eventId=ei0us
|
// eg. http://localhost:4001/countdown?eventId=ei0us
|
||||||
// update data to the event we are following
|
// update data to the event we are following
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!backstageEvents) {
|
if (!events) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,7 +62,6 @@ export default function Countdown(props: CountdownProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let followThis: OntimeEvent | null = null;
|
let followThis: OntimeEvent | null = null;
|
||||||
const events: OntimeEvent[] = [...backstageEvents].filter((event) => event.type === SupportedEntry.Event);
|
|
||||||
|
|
||||||
if (eventId !== null) {
|
if (eventId !== null) {
|
||||||
followThis = events.find((event) => event.id === eventId) || null;
|
followThis = events.find((event) => event.id === eventId) || null;
|
||||||
@@ -72,11 +70,11 @@ export default function Countdown(props: CountdownProps) {
|
|||||||
}
|
}
|
||||||
if (followThis !== null) {
|
if (followThis !== null) {
|
||||||
setFollow(followThis);
|
setFollow(followThis);
|
||||||
const idx: number = backstageEvents.findIndex((event: OntimeEntry) => event.id === followThis?.id);
|
const idx: number = events.findIndex((event: OntimeEntry) => event.id === followThis?.id);
|
||||||
const delayToEvent = backstageEvents[idx]?.delay ?? 0;
|
const delayToEvent = events[idx]?.delay ?? 0;
|
||||||
setDelay(delayToEvent);
|
setDelay(delayToEvent);
|
||||||
}
|
}
|
||||||
}, [backstageEvents, searchParams]);
|
}, [events, searchParams]);
|
||||||
|
|
||||||
const { message: runningMessage, timer: runningTimer } = fetchTimerData(time, follow, selectedId, runtime.offset);
|
const { message: runningMessage, timer: runningTimer } = fetchTimerData(time, follow, selectedId, runtime.offset);
|
||||||
|
|
||||||
@@ -119,7 +117,7 @@ export default function Countdown(props: CountdownProps) {
|
|||||||
{general?.projectLogo && <ViewLogo name={general.projectLogo} className='logo' />}
|
{general?.projectLogo && <ViewLogo name={general.projectLogo} className='logo' />}
|
||||||
<ViewParamsEditor viewOptions={viewOptions} />
|
<ViewParamsEditor viewOptions={viewOptions} />
|
||||||
{follow === null ? (
|
{follow === null ? (
|
||||||
<CountdownSelect events={backstageEvents} />
|
<CountdownSelect events={events} />
|
||||||
) : (
|
) : (
|
||||||
<div className='countdown-container' data-testid='countdown-event'>
|
<div className='countdown-container' data-testid='countdown-event'>
|
||||||
<div className='clock-container'>
|
<div className='clock-container'>
|
||||||
|
|||||||
+24
-22
@@ -1,3 +1,5 @@
|
|||||||
|
import { ViewExtendedTimer } from 'common/models/TimeManager.type';
|
||||||
|
import { OntimeEvent } from 'ontime-types';
|
||||||
import { dayInMs } from 'ontime-utils';
|
import { dayInMs } from 'ontime-utils';
|
||||||
|
|
||||||
import { fetchTimerData, sanitiseTitle, TimerMessage } from '../countdown.helpers';
|
import { fetchTimerData, sanitiseTitle, TimerMessage } from '../countdown.helpers';
|
||||||
@@ -14,7 +16,7 @@ describe('sanitiseTitle() function', () => {
|
|||||||
it('should return {no title} when invalid', () => {
|
it('should return {no title} when invalid', () => {
|
||||||
const invalidTitles = ['', undefined, null];
|
const invalidTitles = ['', undefined, null];
|
||||||
for (const title of invalidTitles) {
|
for (const title of invalidTitles) {
|
||||||
expect(sanitiseTitle(title)).toBe('{no title}');
|
expect(sanitiseTitle(title as unknown as string)).toBe('{no title}');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -23,10 +25,10 @@ describe('fetchTimerData() function', () => {
|
|||||||
it('shows current timer if current is the one we follow', () => {
|
it('shows current timer if current is the one we follow', () => {
|
||||||
const followId = 'testId';
|
const followId = 'testId';
|
||||||
const currentMockValue = 13;
|
const currentMockValue = 13;
|
||||||
const follow = { id: followId };
|
const follow = { id: followId } as OntimeEvent;
|
||||||
const time = { current: currentMockValue };
|
const time = { current: currentMockValue } as ViewExtendedTimer;
|
||||||
|
|
||||||
const { message, timer } = fetchTimerData(time, follow, followId);
|
const { message, timer } = fetchTimerData(time, follow, followId, 0);
|
||||||
expect(message).toBe(TimerMessage.running);
|
expect(message).toBe(TimerMessage.running);
|
||||||
expect(timer).toBe(currentMockValue);
|
expect(timer).toBe(currentMockValue);
|
||||||
});
|
});
|
||||||
@@ -34,10 +36,10 @@ describe('fetchTimerData() function', () => {
|
|||||||
it('shows the countdown to an upcoming event', () => {
|
it('shows the countdown to an upcoming event', () => {
|
||||||
const startMockValue = 10000;
|
const startMockValue = 10000;
|
||||||
const timeNow = 1000;
|
const timeNow = 1000;
|
||||||
const follow = { id: 'anotherevent', timeStart: startMockValue };
|
const follow = { id: 'anotherevent', timeStart: startMockValue } as OntimeEvent;
|
||||||
const time = { clock: timeNow };
|
const time = { clock: timeNow } as ViewExtendedTimer;
|
||||||
|
|
||||||
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
|
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent', 0);
|
||||||
expect(message).toBe(TimerMessage.toStart);
|
expect(message).toBe(TimerMessage.toStart);
|
||||||
expect(timer).toBe(startMockValue - timeNow);
|
expect(timer).toBe(startMockValue - timeNow);
|
||||||
});
|
});
|
||||||
@@ -47,10 +49,10 @@ describe('fetchTimerData() function', () => {
|
|||||||
const endMockValue = 20000;
|
const endMockValue = 20000;
|
||||||
const timeNow = 15000;
|
const timeNow = 15000;
|
||||||
const followId = 'testId';
|
const followId = 'testId';
|
||||||
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
|
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue } as OntimeEvent;
|
||||||
const time = { clock: timeNow, current: endMockValue - startMockValue };
|
const time = { clock: timeNow, current: endMockValue - startMockValue } as ViewExtendedTimer;
|
||||||
|
|
||||||
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
|
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent', 0);
|
||||||
expect(message).toBe(TimerMessage.waiting);
|
expect(message).toBe(TimerMessage.waiting);
|
||||||
expect(timer).toBe(endMockValue - startMockValue);
|
expect(timer).toBe(endMockValue - startMockValue);
|
||||||
});
|
});
|
||||||
@@ -60,10 +62,10 @@ describe('fetchTimerData() function', () => {
|
|||||||
const endMockValue = 20000;
|
const endMockValue = 20000;
|
||||||
const timeNow = 30000;
|
const timeNow = 30000;
|
||||||
const followId = 'testId';
|
const followId = 'testId';
|
||||||
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
|
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue } as OntimeEvent;
|
||||||
const time = { clock: timeNow, current: endMockValue - startMockValue };
|
const time = { clock: timeNow, current: endMockValue - startMockValue } as ViewExtendedTimer;
|
||||||
|
|
||||||
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
|
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent', 0);
|
||||||
expect(message).toBe(TimerMessage.ended);
|
expect(message).toBe(TimerMessage.ended);
|
||||||
expect(timer).toBe(endMockValue);
|
expect(timer).toBe(endMockValue);
|
||||||
});
|
});
|
||||||
@@ -73,10 +75,10 @@ describe('fetchTimerData() function', () => {
|
|||||||
const endMockValue = 1000;
|
const endMockValue = 1000;
|
||||||
const timeNow = 15000;
|
const timeNow = 15000;
|
||||||
const followId = 'testId';
|
const followId = 'testId';
|
||||||
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
|
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue } as OntimeEvent;
|
||||||
const time = { clock: timeNow, current: dayInMs + endMockValue - startMockValue };
|
const time = { clock: timeNow, current: dayInMs + endMockValue - startMockValue } as ViewExtendedTimer;
|
||||||
|
|
||||||
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
|
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent', 0);
|
||||||
expect(message).toBe(TimerMessage.waiting);
|
expect(message).toBe(TimerMessage.waiting);
|
||||||
expect(timer).toBe(dayInMs + endMockValue - startMockValue);
|
expect(timer).toBe(dayInMs + endMockValue - startMockValue);
|
||||||
});
|
});
|
||||||
@@ -86,10 +88,10 @@ describe('fetchTimerData() function', () => {
|
|||||||
const endMockValue = 1000;
|
const endMockValue = 1000;
|
||||||
const timeNow = 15000;
|
const timeNow = 15000;
|
||||||
const followId = 'testId';
|
const followId = 'testId';
|
||||||
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
|
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue } as OntimeEvent;
|
||||||
const time = { clock: timeNow, current: dayInMs + endMockValue - startMockValue };
|
const time = { clock: timeNow, current: dayInMs + endMockValue - startMockValue } as ViewExtendedTimer;
|
||||||
|
|
||||||
const { message, timer } = fetchTimerData(time, follow, followId);
|
const { message, timer } = fetchTimerData(time, follow, followId, 0);
|
||||||
expect(message).toBe(TimerMessage.running);
|
expect(message).toBe(TimerMessage.running);
|
||||||
expect(timer).toBe(dayInMs + endMockValue - startMockValue);
|
expect(timer).toBe(dayInMs + endMockValue - startMockValue);
|
||||||
});
|
});
|
||||||
@@ -99,10 +101,10 @@ describe('fetchTimerData() function', () => {
|
|||||||
const endMockValue = 1000;
|
const endMockValue = 1000;
|
||||||
const timeNow = 2000;
|
const timeNow = 2000;
|
||||||
const followId = 'testId';
|
const followId = 'testId';
|
||||||
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue };
|
const follow = { id: followId, timeStart: startMockValue, timeEnd: endMockValue } as OntimeEvent;
|
||||||
const time = { clock: timeNow, current: dayInMs + endMockValue - startMockValue };
|
const time = { clock: timeNow, current: dayInMs + endMockValue - startMockValue } as ViewExtendedTimer;
|
||||||
|
|
||||||
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent');
|
const { message, timer } = fetchTimerData(time, follow, 'notthesameevent', 0);
|
||||||
expect(message).toBe(TimerMessage.toStart);
|
expect(message).toBe(TimerMessage.toStart);
|
||||||
expect(timer).toBe(startMockValue - timeNow);
|
expect(timer).toBe(startMockValue - timeNow);
|
||||||
});
|
});
|
||||||
@@ -26,7 +26,7 @@ export default function LowerThird(props: LowerProps) {
|
|||||||
const animationTimeout = useRef<NodeJS.Timeout>();
|
const animationTimeout = useRef<NodeJS.Timeout>();
|
||||||
const [playState, setPlayState] = useState<boolean>(false);
|
const [playState, setPlayState] = useState<boolean>(false);
|
||||||
const [textValue, setTextValue] = useState<{ top: string; bottom: string }>({ top: '', bottom: '' });
|
const [textValue, setTextValue] = useState<{ top: string; bottom: string }>({ top: '', bottom: '' });
|
||||||
useRuntimeStylesheet(viewSettings?.overrideStyles && overrideStylesURL);
|
useRuntimeStylesheet(viewSettings?.overrideStyles ? overrideStylesURL : undefined);
|
||||||
const options = useLowerOptions();
|
const options = useLowerOptions();
|
||||||
const { playback } = time;
|
const { playback } = time;
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import StudioClockSchedule from './StudioClockSchedule';
|
|||||||
import './StudioClock.scss';
|
import './StudioClock.scss';
|
||||||
|
|
||||||
interface StudioClockProps {
|
interface StudioClockProps {
|
||||||
backstageEvents: OntimeEntry[];
|
events: OntimeEntry[];
|
||||||
eventNext: OntimeEvent | null;
|
eventNext: OntimeEvent | null;
|
||||||
general: ProjectData;
|
general: ProjectData;
|
||||||
isMirrored: boolean;
|
isMirrored: boolean;
|
||||||
@@ -29,7 +29,7 @@ interface StudioClockProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export default function StudioClock(props: StudioClockProps) {
|
export default function StudioClock(props: StudioClockProps) {
|
||||||
const { backstageEvents, eventNext, general, isMirrored, time, selectedId, nextId, onAir, settings } = props;
|
const { events, eventNext, general, isMirrored, time, selectedId, nextId, onAir, settings } = props;
|
||||||
|
|
||||||
const [searchParams] = useSearchParams();
|
const [searchParams] = useSearchParams();
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ export default function StudioClock(props: StudioClockProps) {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!hideRight && (
|
{!hideRight && (
|
||||||
<StudioClockSchedule rundown={backstageEvents} selectedId={selectedId} nextId={nextId} onAir={onAir} />
|
<StudioClockSchedule rundown={events} selectedId={selectedId} nextId={nextId} onAir={onAir} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export const langDe: TranslationObject = {
|
|||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Jetzt',
|
'common.now': 'Jetzt',
|
||||||
'common.next': 'Nächste',
|
'common.next': 'Nächste',
|
||||||
'common.public_message': 'Öffentliche Nachricht',
|
|
||||||
'common.scheduled_start': 'Geplanter beginn',
|
'common.scheduled_start': 'Geplanter beginn',
|
||||||
'common.scheduled_end': 'Geplantes ende',
|
'common.scheduled_end': 'Geplantes ende',
|
||||||
'common.projected_start': 'Erwartetes beginn',
|
'common.projected_start': 'Erwartetes beginn',
|
||||||
@@ -28,6 +27,4 @@ export const langDe: TranslationObject = {
|
|||||||
'project.description': 'Beschreibung',
|
'project.description': 'Beschreibung',
|
||||||
'project.backstage_info': 'Backstage-Informationen',
|
'project.backstage_info': 'Backstage-Informationen',
|
||||||
'project.backstage_url': 'Backstage-URL',
|
'project.backstage_url': 'Backstage-URL',
|
||||||
'project.public_info': 'Öffentliche Informationen',
|
|
||||||
'project.public_url': 'Öffentliche URL',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ export const langEn = {
|
|||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Now',
|
'common.now': 'Now',
|
||||||
'common.next': 'Next',
|
'common.next': 'Next',
|
||||||
'common.public_message': 'Public message',
|
|
||||||
'common.scheduled_start': 'Scheduled start',
|
'common.scheduled_start': 'Scheduled start',
|
||||||
'common.scheduled_end': 'Scheduled end',
|
'common.scheduled_end': 'Scheduled end',
|
||||||
'common.projected_start': 'Projected start',
|
'common.projected_start': 'Projected start',
|
||||||
@@ -26,8 +25,6 @@ export const langEn = {
|
|||||||
'project.description': 'Description',
|
'project.description': 'Description',
|
||||||
'project.backstage_info': 'Backstage Info',
|
'project.backstage_info': 'Backstage Info',
|
||||||
'project.backstage_url': 'Backstage URL',
|
'project.backstage_url': 'Backstage URL',
|
||||||
'project.public_info': 'Public Info',
|
|
||||||
'project.public_url': 'Public URL',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TranslationObject = Record<keyof typeof langEn, string>;
|
export type TranslationObject = Record<keyof typeof langEn, string>;
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export const langEs: TranslationObject = {
|
|||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Ahora',
|
'common.now': 'Ahora',
|
||||||
'common.next': 'Siguiente',
|
'common.next': 'Siguiente',
|
||||||
'common.public_message': 'Mensaje público',
|
|
||||||
'common.scheduled_start': 'Inicio programado',
|
'common.scheduled_start': 'Inicio programado',
|
||||||
'common.scheduled_end': 'Fin programado',
|
'common.scheduled_end': 'Fin programado',
|
||||||
'common.projected_start': 'Inicio previsto',
|
'common.projected_start': 'Inicio previsto',
|
||||||
@@ -28,6 +27,4 @@ export const langEs: TranslationObject = {
|
|||||||
'project.description': 'Descripción',
|
'project.description': 'Descripción',
|
||||||
'project.backstage_info': 'Información de backstage',
|
'project.backstage_info': 'Información de backstage',
|
||||||
'project.backstage_url': 'URL de backstage',
|
'project.backstage_url': 'URL de backstage',
|
||||||
'project.public_info': 'Información pública',
|
|
||||||
'project.public_url': 'URL pública',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export const langFr: TranslationObject = {
|
|||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Maintenant',
|
'common.now': 'Maintenant',
|
||||||
'common.next': 'A suivre',
|
'common.next': 'A suivre',
|
||||||
'common.public_message': 'Message public',
|
|
||||||
'common.scheduled_start': 'Début prévu',
|
'common.scheduled_start': 'Début prévu',
|
||||||
'common.scheduled_end': 'Fin prévue',
|
'common.scheduled_end': 'Fin prévue',
|
||||||
'common.projected_start': 'Début projeté',
|
'common.projected_start': 'Début projeté',
|
||||||
@@ -28,6 +27,5 @@ export const langFr: TranslationObject = {
|
|||||||
'project.description': 'Description',
|
'project.description': 'Description',
|
||||||
'project.backstage_info': 'Informations des coulisses',
|
'project.backstage_info': 'Informations des coulisses',
|
||||||
'project.backstage_url': 'URL des coulisses',
|
'project.backstage_url': 'URL des coulisses',
|
||||||
'project.public_info': 'Informations publiques',
|
|
||||||
'project.public_url': 'URL publique',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export const langHu: TranslationObject = {
|
|||||||
'common.minutes': 'perc',
|
'common.minutes': 'perc',
|
||||||
'common.now': 'Most',
|
'common.now': 'Most',
|
||||||
'common.next': 'Következő',
|
'common.next': 'Következő',
|
||||||
'common.public_message': 'Nyilvános közlemény',
|
|
||||||
'common.scheduled_start': 'Ütemezett kezdés',
|
'common.scheduled_start': 'Ütemezett kezdés',
|
||||||
'common.scheduled_end': 'Ütemezett befejezés',
|
'common.scheduled_end': 'Ütemezett befejezés',
|
||||||
'common.projected_start': 'Várható kezdés',
|
'common.projected_start': 'Várható kezdés',
|
||||||
@@ -28,6 +27,4 @@ export const langHu: TranslationObject = {
|
|||||||
'project.description': 'Leírás',
|
'project.description': 'Leírás',
|
||||||
'project.backstage_info': 'Kulisszák mögötti információ',
|
'project.backstage_info': 'Kulisszák mögötti információ',
|
||||||
'project.backstage_url': 'Kulisszák mögötti URL',
|
'project.backstage_url': 'Kulisszák mögötti URL',
|
||||||
'project.public_info': 'Nyilvános információ',
|
|
||||||
'project.public_url': 'Nyilvános URL',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export const langIt: TranslationObject = {
|
|||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Adesso',
|
'common.now': 'Adesso',
|
||||||
'common.next': 'Prossimo',
|
'common.next': 'Prossimo',
|
||||||
'common.public_message': 'Messaggio pubblico',
|
|
||||||
'common.scheduled_start': 'Inizio programmato',
|
'common.scheduled_start': 'Inizio programmato',
|
||||||
'common.scheduled_end': 'Fine programmata',
|
'common.scheduled_end': 'Fine programmata',
|
||||||
'common.projected_start': 'Inizio previsto',
|
'common.projected_start': 'Inizio previsto',
|
||||||
@@ -28,6 +27,4 @@ export const langIt: TranslationObject = {
|
|||||||
'project.description': 'Descrizione',
|
'project.description': 'Descrizione',
|
||||||
'project.backstage_info': 'Informazioni di backstage',
|
'project.backstage_info': 'Informazioni di backstage',
|
||||||
'project.backstage_url': 'URL di backstage',
|
'project.backstage_url': 'URL di backstage',
|
||||||
'project.public_info': 'Informazioni pubbliche',
|
|
||||||
'project.public_url': 'URL pubblico',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export const langNo: TranslationObject = {
|
|||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Nå',
|
'common.now': 'Nå',
|
||||||
'common.next': 'Neste',
|
'common.next': 'Neste',
|
||||||
'common.public_message': 'Offentlig beskjed',
|
|
||||||
'common.scheduled_start': 'Planlagt start',
|
'common.scheduled_start': 'Planlagt start',
|
||||||
'common.scheduled_end': 'Planlagt slutt',
|
'common.scheduled_end': 'Planlagt slutt',
|
||||||
'common.projected_start': 'Forventet start',
|
'common.projected_start': 'Forventet start',
|
||||||
@@ -28,6 +27,4 @@ export const langNo: TranslationObject = {
|
|||||||
'project.description': 'Beskrivelse',
|
'project.description': 'Beskrivelse',
|
||||||
'project.backstage_info': 'Backstage-informasjon',
|
'project.backstage_info': 'Backstage-informasjon',
|
||||||
'project.backstage_url': 'Backstage-URL',
|
'project.backstage_url': 'Backstage-URL',
|
||||||
'project.public_info': 'Offentlig informasjon',
|
|
||||||
'project.public_url': 'Offentlig URL',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export const langPl: TranslationObject = {
|
|||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Teraz',
|
'common.now': 'Teraz',
|
||||||
'common.next': 'Następnie',
|
'common.next': 'Następnie',
|
||||||
'common.public_message': 'Wiadomość publiczna',
|
|
||||||
'common.scheduled_start': 'Planowany początek',
|
'common.scheduled_start': 'Planowany początek',
|
||||||
'common.scheduled_end': 'Planowany koniec',
|
'common.scheduled_end': 'Planowany koniec',
|
||||||
'common.projected_start': 'Przewidywany początek',
|
'common.projected_start': 'Przewidywany początek',
|
||||||
@@ -28,6 +27,4 @@ export const langPl: TranslationObject = {
|
|||||||
'project.description': 'Opis',
|
'project.description': 'Opis',
|
||||||
'project.backstage_info': 'Informacje zaplecza',
|
'project.backstage_info': 'Informacje zaplecza',
|
||||||
'project.backstage_url': 'URL zaplecza',
|
'project.backstage_url': 'URL zaplecza',
|
||||||
'project.public_info': 'Informacje publiczne',
|
|
||||||
'project.public_url': 'URL publiczny',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export const langPt: TranslationObject = {
|
|||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Agora',
|
'common.now': 'Agora',
|
||||||
'common.next': 'Próximo',
|
'common.next': 'Próximo',
|
||||||
'common.public_message': 'Mensagem pública',
|
|
||||||
'common.scheduled_start': 'Início programado',
|
'common.scheduled_start': 'Início programado',
|
||||||
'common.scheduled_end': 'Fim programado',
|
'common.scheduled_end': 'Fim programado',
|
||||||
'common.projected_start': 'Início previsto',
|
'common.projected_start': 'Início previsto',
|
||||||
@@ -28,6 +27,4 @@ export const langPt: TranslationObject = {
|
|||||||
'project.description': 'Descrição',
|
'project.description': 'Descrição',
|
||||||
'project.backstage_info': 'Informações de bastidores',
|
'project.backstage_info': 'Informações de bastidores',
|
||||||
'project.backstage_url': 'URL de bastidores',
|
'project.backstage_url': 'URL de bastidores',
|
||||||
'project.public_info': 'Informações públicas',
|
|
||||||
'project.public_url': 'URL pública',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export const langSv: TranslationObject = {
|
|||||||
'common.minutes': 'min',
|
'common.minutes': 'min',
|
||||||
'common.now': 'Nu',
|
'common.now': 'Nu',
|
||||||
'common.next': 'Nästa',
|
'common.next': 'Nästa',
|
||||||
'common.public_message': 'Offentligt meddelande',
|
|
||||||
'common.scheduled_start': 'Planerad start',
|
'common.scheduled_start': 'Planerad start',
|
||||||
'common.scheduled_end': 'Planerad slut',
|
'common.scheduled_end': 'Planerad slut',
|
||||||
'common.projected_start': 'Beräknad start',
|
'common.projected_start': 'Beräknad start',
|
||||||
@@ -28,6 +27,4 @@ export const langSv: TranslationObject = {
|
|||||||
'project.description': 'Beskrivning',
|
'project.description': 'Beskrivning',
|
||||||
'project.backstage_info': 'Backstageinformation',
|
'project.backstage_info': 'Backstageinformation',
|
||||||
'project.backstage_url': 'Backstage-URL',
|
'project.backstage_url': 'Backstage-URL',
|
||||||
'project.public_info': 'Offentlig information',
|
|
||||||
'project.public_url': 'Offentlig URL',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ export const langZhCn: TranslationObject = {
|
|||||||
'common.minutes': '分钟',
|
'common.minutes': '分钟',
|
||||||
'common.now': '现在',
|
'common.now': '现在',
|
||||||
'common.next': '接下来',
|
'common.next': '接下来',
|
||||||
'common.public_message': '公众消息',
|
|
||||||
'common.scheduled_start': '计划开始',
|
'common.scheduled_start': '计划开始',
|
||||||
'common.scheduled_end': '计划结束',
|
'common.scheduled_end': '计划结束',
|
||||||
'common.projected_start': '预计开始',
|
'common.projected_start': '预计开始',
|
||||||
@@ -28,6 +27,4 @@ export const langZhCn: TranslationObject = {
|
|||||||
'project.description': '描述',
|
'project.description': '描述',
|
||||||
'project.backstage_info': '后台信息',
|
'project.backstage_info': '后台信息',
|
||||||
'project.backstage_url': '后台网址',
|
'project.backstage_url': '后台网址',
|
||||||
'project.public_info': '公开信息',
|
|
||||||
'project.public_url': '公开网址',
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ export const navigatorConstants = [
|
|||||||
{ url: 'clock', label: 'Wall Clock' },
|
{ url: 'clock', label: 'Wall Clock' },
|
||||||
{ url: 'backstage', label: 'Backstage' },
|
{ url: 'backstage', label: 'Backstage' },
|
||||||
{ url: 'timeline', label: 'Timeline (beta)' },
|
{ url: 'timeline', label: 'Timeline (beta)' },
|
||||||
{ url: 'public', label: 'Public' },
|
|
||||||
{ url: 'lower', label: 'Lower Thirds' },
|
{ url: 'lower', label: 'Lower Thirds' },
|
||||||
{ url: 'studio', label: 'Studio Clock' },
|
{ url: 'studio', label: 'Studio Clock' },
|
||||||
{ url: 'countdown', label: 'Countdown' },
|
{ url: 'countdown', label: 'Countdown' },
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import style from './ViewLoader.module.scss';
|
|||||||
|
|
||||||
export default function ViewLoader({ children }: PropsWithChildren) {
|
export default function ViewLoader({ children }: PropsWithChildren) {
|
||||||
const { data } = useViewSettings();
|
const { data } = useViewSettings();
|
||||||
const { shouldRender } = useRuntimeStylesheet(data.overrideStyles && overrideStylesURL);
|
const { shouldRender } = useRuntimeStylesheet(data.overrideStyles ? overrideStylesURL : undefined);
|
||||||
|
|
||||||
// eventually we would want to leverage suspense here
|
// eventually we would want to leverage suspense here
|
||||||
// while the feature is not ready, we simply trigger a loader
|
// while the feature is not ready, we simply trigger a loader
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import { getCardData, getIsPendingStart, getShowProgressBar, isOvertime } from '
|
|||||||
import './Backstage.scss';
|
import './Backstage.scss';
|
||||||
|
|
||||||
interface BackstageProps {
|
interface BackstageProps {
|
||||||
backstageEvents: OntimeEvent[];
|
events: OntimeEvent[];
|
||||||
customFields: CustomFields;
|
customFields: CustomFields;
|
||||||
eventNext: OntimeEvent | null;
|
eventNext: OntimeEvent | null;
|
||||||
eventNow: OntimeEvent | null;
|
eventNow: OntimeEvent | null;
|
||||||
@@ -37,7 +37,7 @@ interface BackstageProps {
|
|||||||
|
|
||||||
export default function Backstage(props: BackstageProps) {
|
export default function Backstage(props: BackstageProps) {
|
||||||
const {
|
const {
|
||||||
backstageEvents,
|
events,
|
||||||
customFields,
|
customFields,
|
||||||
eventNext,
|
eventNext,
|
||||||
eventNow,
|
eventNow,
|
||||||
@@ -68,7 +68,7 @@ export default function Backstage(props: BackstageProps) {
|
|||||||
}, [selectedId]);
|
}, [selectedId]);
|
||||||
|
|
||||||
// gather card data
|
// gather card data
|
||||||
const hasEvents = backstageEvents.length > 0;
|
const hasEvents = events.length > 0;
|
||||||
const { showNow, nowMain, nowSecondary, showNext, nextMain, nextSecondary } = getCardData(
|
const { showNow, nowMain, nowSecondary, showNext, nextMain, nextSecondary } = getCardData(
|
||||||
eventNow,
|
eventNow,
|
||||||
eventNext,
|
eventNext,
|
||||||
@@ -176,7 +176,7 @@ export default function Backstage(props: BackstageProps) {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showSchedule && <ScheduleExport selectedId={selectedId} isBackstage />}
|
{showSchedule && <ScheduleExport selectedId={selectedId} />}
|
||||||
|
|
||||||
<div className={cx(['info', !showSchedule && 'info--stretch'])}>
|
<div className={cx(['info', !showSchedule && 'info--stretch'])}>
|
||||||
{general.backstageUrl && <QRCode value={general.backstageUrl} size={qrSize} level='L' className='qr' />}
|
{general.backstageUrl && <QRCode value={general.backstageUrl} size={qrSize} level='L' className='qr' />}
|
||||||
|
|||||||
@@ -7,12 +7,11 @@ import ScheduleItem from './ScheduleItem';
|
|||||||
import './Schedule.scss';
|
import './Schedule.scss';
|
||||||
|
|
||||||
interface ScheduleProps {
|
interface ScheduleProps {
|
||||||
isProduction?: boolean;
|
|
||||||
className?: string;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Schedule({ isProduction, className }: ScheduleProps) {
|
export default function Schedule({ className }: ScheduleProps) {
|
||||||
const { events, isBackstage, containerRef } = useSchedule();
|
const { events, containerRef } = useSchedule();
|
||||||
|
|
||||||
if (events?.length < 1) {
|
if (events?.length < 1) {
|
||||||
return null;
|
return null;
|
||||||
@@ -21,7 +20,7 @@ export default function Schedule({ isProduction, className }: ScheduleProps) {
|
|||||||
return (
|
return (
|
||||||
<ul className={cx(['schedule', className])} ref={containerRef}>
|
<ul className={cx(['schedule', className])} ref={containerRef}>
|
||||||
{events.map((event) => {
|
{events.map((event) => {
|
||||||
const { timeStart, timeEnd, delay } = getScheduledTimes(event, isProduction);
|
const { timeStart, timeEnd, delay } = getScheduledTimes(event);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScheduleItem
|
<ScheduleItem
|
||||||
@@ -29,8 +28,7 @@ export default function Schedule({ isProduction, className }: ScheduleProps) {
|
|||||||
timeStart={timeStart}
|
timeStart={timeStart}
|
||||||
timeEnd={timeEnd}
|
timeEnd={timeEnd}
|
||||||
title={event.title}
|
title={event.title}
|
||||||
colour={isBackstage ? event.colour : undefined}
|
colour={event.colour}
|
||||||
backstageEvent={!event.isPublic}
|
|
||||||
skip={event.skip}
|
skip={event.skip}
|
||||||
delay={delay}
|
delay={delay}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ interface ScheduleContextState {
|
|||||||
selectedEventId: string | null;
|
selectedEventId: string | null;
|
||||||
numPages: number;
|
numPages: number;
|
||||||
visiblePage: number;
|
visiblePage: number;
|
||||||
isBackstage: boolean;
|
|
||||||
containerRef: RefObject<HTMLUListElement>;
|
containerRef: RefObject<HTMLUListElement>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,20 +26,12 @@ const ScheduleContext = createContext<ScheduleContextState | undefined>(undefine
|
|||||||
|
|
||||||
interface ScheduleProviderProps {
|
interface ScheduleProviderProps {
|
||||||
selectedEventId: string | null;
|
selectedEventId: string | null;
|
||||||
isBackstage?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ScheduleProvider = ({
|
export const ScheduleProvider = ({ children, selectedEventId }: PropsWithChildren<ScheduleProviderProps>) => {
|
||||||
children,
|
|
||||||
selectedEventId,
|
|
||||||
isBackstage = false,
|
|
||||||
}: PropsWithChildren<ScheduleProviderProps>) => {
|
|
||||||
const { cycleInterval, stopCycle } = useScheduleOptions();
|
const { cycleInterval, stopCycle } = useScheduleOptions();
|
||||||
const { data: events } = usePartialRundown((event: OntimeEntry) => {
|
const { data: events } = usePartialRundown((event: OntimeEntry) => {
|
||||||
if (isBackstage) {
|
return isOntimeEvent(event);
|
||||||
return isOntimeEvent(event);
|
|
||||||
}
|
|
||||||
return isOntimeEvent(event) && event.isPublic && !event.skip;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const [firstIndex, setFirstIndex] = useState(-1);
|
const [firstIndex, setFirstIndex] = useState(-1);
|
||||||
@@ -150,7 +141,6 @@ export const ScheduleProvider = ({
|
|||||||
selectedEventId,
|
selectedEventId,
|
||||||
numPages,
|
numPages,
|
||||||
visiblePage,
|
visiblePage,
|
||||||
isBackstage,
|
|
||||||
containerRef,
|
containerRef,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -7,16 +7,15 @@ import ScheduleNav from './ScheduleNav';
|
|||||||
|
|
||||||
interface ScheduleExportProps {
|
interface ScheduleExportProps {
|
||||||
selectedId: MaybeString;
|
selectedId: MaybeString;
|
||||||
isBackstage?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default memo(ScheduleExport);
|
export default memo(ScheduleExport);
|
||||||
function ScheduleExport(props: ScheduleExportProps) {
|
function ScheduleExport(props: ScheduleExportProps) {
|
||||||
const { selectedId, isBackstage } = props;
|
const { selectedId } = props;
|
||||||
return (
|
return (
|
||||||
<ScheduleProvider selectedEventId={selectedId} isBackstage={isBackstage}>
|
<ScheduleProvider selectedEventId={selectedId}>
|
||||||
<ScheduleNav className='schedule-nav-container' />
|
<ScheduleNav className='schedule-nav-container' />
|
||||||
<Schedule isProduction={isBackstage} className='schedule-container' />
|
<Schedule className='schedule-container' />
|
||||||
</ScheduleProvider>
|
</ScheduleProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,13 @@ interface ScheduleItemProps {
|
|||||||
timeStart: number;
|
timeStart: number;
|
||||||
timeEnd: number;
|
timeEnd: number;
|
||||||
title: string;
|
title: string;
|
||||||
backstageEvent: boolean;
|
|
||||||
colour?: string;
|
colour?: string;
|
||||||
skip?: boolean;
|
skip?: boolean;
|
||||||
delay: number;
|
delay: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function ScheduleItem(props: ScheduleItemProps) {
|
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();
|
const { showProjected } = useScheduleOptions();
|
||||||
|
|
||||||
if (showProjected) {
|
if (showProjected) {
|
||||||
@@ -33,7 +32,6 @@ export default function ScheduleItem(props: ScheduleItemProps) {
|
|||||||
timeEnd={timeEnd}
|
timeEnd={timeEnd}
|
||||||
title={title}
|
title={title}
|
||||||
colour={colour}
|
colour={colour}
|
||||||
backstageEvent={backstageEvent}
|
|
||||||
skip={skip}
|
skip={skip}
|
||||||
delay={delay}
|
delay={delay}
|
||||||
/>
|
/>
|
||||||
@@ -47,7 +45,6 @@ export default function ScheduleItem(props: ScheduleItemProps) {
|
|||||||
timeEnd={timeEnd}
|
timeEnd={timeEnd}
|
||||||
title={title}
|
title={title}
|
||||||
colour={colour}
|
colour={colour}
|
||||||
backstageEvent={backstageEvent}
|
|
||||||
skip={skip}
|
skip={skip}
|
||||||
delay={delay}
|
delay={delay}
|
||||||
/>
|
/>
|
||||||
@@ -63,7 +60,6 @@ export default function ScheduleItem(props: ScheduleItemProps) {
|
|||||||
<SuperscriptTime time={start} />
|
<SuperscriptTime time={start} />
|
||||||
→
|
→
|
||||||
<SuperscriptTime time={end} />
|
<SuperscriptTime time={end} />
|
||||||
{backstageEvent && '*'}
|
|
||||||
</div>
|
</div>
|
||||||
<div className='entry-title'>{title}</div>
|
<div className='entry-title'>{title}</div>
|
||||||
</li>
|
</li>
|
||||||
@@ -71,7 +67,7 @@ export default function ScheduleItem(props: ScheduleItemProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function DelayedScheduleItem(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 start = formatTime(timeStart, formatOptions);
|
||||||
const end = formatTime(timeEnd, formatOptions);
|
const end = formatTime(timeEnd, formatOptions);
|
||||||
@@ -86,13 +82,11 @@ function DelayedScheduleItem(props: ScheduleItemProps) {
|
|||||||
<SuperscriptTime time={start} />
|
<SuperscriptTime time={start} />
|
||||||
→
|
→
|
||||||
<SuperscriptTime time={end} />
|
<SuperscriptTime time={end} />
|
||||||
{backstageEvent && '*'}
|
|
||||||
</span>
|
</span>
|
||||||
<span className='entry-times--delay'>
|
<span className='entry-times--delay'>
|
||||||
<SuperscriptTime time={delayedStart} />
|
<SuperscriptTime time={delayedStart} />
|
||||||
→
|
→
|
||||||
<SuperscriptTime time={delayedEnd} />
|
<SuperscriptTime time={delayedEnd} />
|
||||||
{backstageEvent && '*'}
|
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className='entry-title'>{title}</div>
|
<div className='entry-title'>{title}</div>
|
||||||
@@ -101,7 +95,7 @@ function DelayedScheduleItem(props: ScheduleItemProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function ProjectedScheduleItem(props: ScheduleItemProps) {
|
function ProjectedScheduleItem(props: ScheduleItemProps) {
|
||||||
const { timeStart, timeEnd, title, backstageEvent, colour, skip, delay } = props;
|
const { timeStart, timeEnd, title, colour, skip, delay } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li className={cx(['entry', skip && 'entry--skip'])}>
|
<li className={cx(['entry', skip && 'entry--skip'])}>
|
||||||
@@ -110,7 +104,6 @@ function ProjectedScheduleItem(props: ScheduleItemProps) {
|
|||||||
<ProjectedTime time={timeStart} delay={delay} />
|
<ProjectedTime time={timeStart} delay={delay} />
|
||||||
→
|
→
|
||||||
<ProjectedTime time={timeEnd} delay={delay} />
|
<ProjectedTime time={timeEnd} delay={delay} />
|
||||||
{backstageEvent && '*'}
|
|
||||||
</div>
|
</div>
|
||||||
<div className='entry-title'>{title}</div>
|
<div className='entry-title'>{title}</div>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -3,17 +3,10 @@ import { OntimeEvent } from 'ontime-types';
|
|||||||
/**
|
/**
|
||||||
* Gather rules for how to present scheduled times
|
* Gather rules for how to present scheduled times
|
||||||
*/
|
*/
|
||||||
export function getScheduledTimes(event: OntimeEvent, isProduction?: boolean) {
|
export function getScheduledTimes(event: OntimeEvent) {
|
||||||
if (isProduction) {
|
|
||||||
return {
|
|
||||||
timeStart: event.timeStart,
|
|
||||||
timeEnd: event.timeEnd,
|
|
||||||
delay: event.skip ? 0 : event.delay,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
timeStart: event.timeStart,
|
timeStart: event.timeStart,
|
||||||
timeEnd: event.timeEnd,
|
timeEnd: event.timeEnd,
|
||||||
delay: 0,
|
delay: event.skip ? 0 : event.delay,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,22 +12,6 @@ describe('parseField()', () => {
|
|||||||
expect(parseField('duration', testData3)).toBe('00:10:00');
|
expect(parseField('duration', testData3)).toBe('00:10:00');
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('returns an x when isPublic is truthy, empty string otherwise', () => {
|
|
||||||
const testTruthy = [1, true, 'x', 'test'];
|
|
||||||
const testFalsy = ['', null, undefined, false, 0];
|
|
||||||
|
|
||||||
testTruthy.forEach((value) => {
|
|
||||||
test(`${value}`, () => {
|
|
||||||
expect(parseField('isPublic', value)).toBe('x');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
testFalsy.forEach((value) => {
|
|
||||||
test(`${value}`, () => {
|
|
||||||
expect(parseField('isPublic', value)).toBe('');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('returns an empty string on undefined fields', () => {
|
it('returns an empty string on undefined fields', () => {
|
||||||
// @ts-expect-error -- testing user data with missing fields
|
// @ts-expect-error -- testing user data with missing fields
|
||||||
expect(parseField('title')).toBe('');
|
expect(parseField('title')).toBe('');
|
||||||
@@ -61,7 +45,7 @@ describe('makeTable()', () => {
|
|||||||
title: 'test title 1',
|
title: 'test title 1',
|
||||||
timeStart: 0,
|
timeStart: 0,
|
||||||
timeEnd: 0,
|
timeEnd: 0,
|
||||||
isPublic: 'x',
|
skip: true,
|
||||||
lighting: { value: 'test lighting' },
|
lighting: { value: 'test lighting' },
|
||||||
sound: { value: 'test sound' },
|
sound: { value: 'test sound' },
|
||||||
},
|
},
|
||||||
@@ -93,7 +77,6 @@ describe('makeTable()', () => {
|
|||||||
"Cue",
|
"Cue",
|
||||||
"Title",
|
"Title",
|
||||||
"Note",
|
"Note",
|
||||||
"Is Public? (x)",
|
|
||||||
"Skip?",
|
"Skip?",
|
||||||
"lighting",
|
"lighting",
|
||||||
"Type",
|
"Type",
|
||||||
@@ -110,7 +93,6 @@ describe('makeTable()', () => {
|
|||||||
"x",
|
"x",
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
"",
|
|
||||||
],
|
],
|
||||||
]
|
]
|
||||||
`);
|
`);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export const parseField = (field: CsvHeaderKey, data: unknown): string => {
|
|||||||
return millisToString(data as MaybeNumber, { fallback: '' });
|
return millisToString(data as MaybeNumber, { fallback: '' });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field === 'isPublic' || field === 'skip') {
|
if (field === 'skip') {
|
||||||
return data ? 'x' : '';
|
return data ? 'x' : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,7 +52,6 @@ export const makeTable = (headerData: ProjectData, rundown: OntimeEntry[], custo
|
|||||||
'cue',
|
'cue',
|
||||||
'title',
|
'title',
|
||||||
'note',
|
'note',
|
||||||
'isPublic',
|
|
||||||
'skip',
|
'skip',
|
||||||
...customFieldKeys,
|
...customFieldKeys,
|
||||||
'type',
|
'type',
|
||||||
@@ -67,7 +66,6 @@ export const makeTable = (headerData: ProjectData, rundown: OntimeEntry[], custo
|
|||||||
'Cue',
|
'Cue',
|
||||||
'Title',
|
'Title',
|
||||||
'Note',
|
'Note',
|
||||||
'Is Public? (x)',
|
|
||||||
'Skip?',
|
'Skip?',
|
||||||
...customFieldLabels,
|
...customFieldLabels,
|
||||||
'Type',
|
'Type',
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { useTranslation } from '../../translation/TranslationProvider';
|
|||||||
|
|
||||||
import BackstageInfo from './backstage-info/BackstageInfo';
|
import BackstageInfo from './backstage-info/BackstageInfo';
|
||||||
import CustomInfo from './custom-info/CustomInfo';
|
import CustomInfo from './custom-info/CustomInfo';
|
||||||
import PublicInfo from './public-info/PublicInfo';
|
|
||||||
import { projectInfoOptions } from './projectInfo.options';
|
import { projectInfoOptions } from './projectInfo.options';
|
||||||
|
|
||||||
import './ProjectInfo.scss';
|
import './ProjectInfo.scss';
|
||||||
@@ -66,7 +65,6 @@ export default function ProjectInfo(props: ProjectInfoProps) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<BackstageInfo general={general} />
|
<BackstageInfo general={general} />
|
||||||
<PublicInfo general={general} />
|
|
||||||
<CustomInfo general={general} />
|
<CustomInfo general={general} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,13 +13,6 @@ export const projectInfoOptions: ViewOption[] = [
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'showPublic',
|
|
||||||
title: 'Show Public Data',
|
|
||||||
description: 'Whether to show fields related to the public views',
|
|
||||||
type: 'boolean',
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'showCustom',
|
id: 'showCustom',
|
||||||
title: 'Show Custom Data',
|
title: 'Show Custom Data',
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
import { useSearchParams } from 'react-router-dom';
|
|
||||||
import { ProjectData } from 'ontime-types';
|
|
||||||
|
|
||||||
import { isStringBoolean } from '../../../features/viewers/common/viewUtils';
|
|
||||||
import { useTranslation } from '../../../translation/TranslationProvider';
|
|
||||||
|
|
||||||
interface PublicInfoProps {
|
|
||||||
general: ProjectData;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function PublicInfo(props: PublicInfoProps) {
|
|
||||||
const { general } = props;
|
|
||||||
const [searchParams] = useSearchParams();
|
|
||||||
const { getLocalizedString } = useTranslation();
|
|
||||||
|
|
||||||
const showPublic = isStringBoolean(searchParams.get('showPublic'));
|
|
||||||
|
|
||||||
if (!showPublic) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{general.publicInfo && (
|
|
||||||
<>
|
|
||||||
<div className='info__label'>{getLocalizedString('project.public_info')}</div>
|
|
||||||
<div className='info__value'>{general.publicInfo}</div>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{general.publicUrl && (
|
|
||||||
<>
|
|
||||||
<div className='info__label'>{getLocalizedString('project.public_url')}</div>
|
|
||||||
<a href={general.publicUrl} target='_blank' rel='noreferrer' className='info__value'>
|
|
||||||
{general.publicUrl}
|
|
||||||
</a>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 (
|
|
||||||
<div className={`public-screen ${isMirrored ? 'mirror' : ''}`} data-testid='public-view'>
|
|
||||||
<ViewParamsEditor viewOptions={publicOptions} />
|
|
||||||
<div className='project-header'>
|
|
||||||
{general?.projectLogo ? <ViewLogo name={general.projectLogo} className='logo' /> : <div className='logo' />}
|
|
||||||
<div className='title'>{general.title}</div>
|
|
||||||
<div className='clock-container'>
|
|
||||||
<div className='label'>{getLocalizedString('common.time_now')}</div>
|
|
||||||
<SuperscriptTime time={clock} className='time' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{!hasEvents && <Empty text={getLocalizedString('countdown.waiting')} className='empty-container' />}
|
|
||||||
|
|
||||||
<div className='card-container'>
|
|
||||||
{showNow && hasEvents && (
|
|
||||||
<TitleCard className='event now' label='now' title={nowMain} secondary={nowSecondary} />
|
|
||||||
)}
|
|
||||||
{showPending && (
|
|
||||||
<div className='event'>
|
|
||||||
<div className='title-card__placeholder'>{getLocalizedString('countdown.waiting')}</div>
|
|
||||||
<div className='timer-group'>
|
|
||||||
<div className='time-entry'>
|
|
||||||
<div className={cx(['time-entry__label', isPendingStart && 'time-entry--pending'])}>
|
|
||||||
{getLocalizedString('common.scheduled_start')}
|
|
||||||
</div>
|
|
||||||
<SuperscriptTime time={formatTime(scheduledStart)} className='time-entry__value' />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
{showNext && hasEvents && (
|
|
||||||
<TitleCard className='event next' label='next' title={nextMain} secondary={nextSecondary} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{showSchedule && <ScheduleExport selectedId={publicSelectedId} />}
|
|
||||||
|
|
||||||
<div className={cx(['info', !showSchedule && 'info--stretch'])}>
|
|
||||||
{general.publicUrl && <QRCode value={general.publicUrl} size={qrSize} level='L' className='qr' />}
|
|
||||||
{general.publicInfo && <div className='info__message'>{general.publicInfo}</div>}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -17,7 +17,7 @@ import { getTimeToStart, getUpcomingEvents, useScopedRundown } from './timeline.
|
|||||||
import './TimelinePage.scss';
|
import './TimelinePage.scss';
|
||||||
|
|
||||||
interface TimelinePageProps {
|
interface TimelinePageProps {
|
||||||
backstageEvents: OntimeEvent[];
|
events: OntimeEvent[];
|
||||||
general: ProjectData;
|
general: ProjectData;
|
||||||
runtime: Runtime;
|
runtime: Runtime;
|
||||||
selectedId: MaybeString;
|
selectedId: MaybeString;
|
||||||
@@ -31,9 +31,9 @@ interface TimelinePageProps {
|
|||||||
* There is little point splitting or memoising top level elements
|
* There is little point splitting or memoising top level elements
|
||||||
*/
|
*/
|
||||||
export default function TimelinePage(props: TimelinePageProps) {
|
export default function TimelinePage(props: TimelinePageProps) {
|
||||||
const { backstageEvents, general, runtime, selectedId, settings, time } = props;
|
const { events, general, runtime, selectedId, settings, time } = props;
|
||||||
// holds copy of the rundown with only relevant events
|
// holds copy of the rundown with only relevant events
|
||||||
const { scopedRundown, firstStart, totalDuration } = useScopedRundown(backstageEvents, selectedId);
|
const { scopedRundown, firstStart, totalDuration } = useScopedRundown(events, selectedId);
|
||||||
const { getLocalizedString } = useTranslation();
|
const { getLocalizedString } = useTranslation();
|
||||||
const clock = formatTime(time.clock);
|
const clock = formatTime(time.clock);
|
||||||
|
|
||||||
|
|||||||
@@ -15,13 +15,6 @@ export const getTimelineOptions = (timeFormat: string): ViewOption[] => {
|
|||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'hideBackstage',
|
|
||||||
title: 'Hide Private Events',
|
|
||||||
description: 'Whether to hide non-public events',
|
|
||||||
type: 'boolean',
|
|
||||||
defaultValue: false,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -95,7 +95,6 @@ export function useScopedRundown(rundown: OntimeEntry[], selectedEventId: MaybeS
|
|||||||
return { scopedRundown: [], firstStart: 0, totalDuration: 0 };
|
return { scopedRundown: [], firstStart: 0, totalDuration: 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
const hideBackstage = isStringBoolean(searchParams.get('hideBackstage'));
|
|
||||||
const hidePast = isStringBoolean(searchParams.get('hidePast'));
|
const hidePast = isStringBoolean(searchParams.get('hidePast'));
|
||||||
|
|
||||||
const scopedRundown: PlayableEvent[] = [];
|
const scopedRundown: PlayableEvent[] = [];
|
||||||
@@ -117,11 +116,6 @@ export function useScopedRundown(rundown: OntimeEntry[], selectedEventId: MaybeS
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// maybe filter backstage
|
|
||||||
if (!currentEntry.isPublic && hideBackstage) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// add to scopedRundown
|
// add to scopedRundown
|
||||||
scopedRundown.push(currentEntry);
|
scopedRundown.push(currentEntry);
|
||||||
|
|
||||||
|
|||||||
@@ -42,6 +42,6 @@
|
|||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules",
|
"node_modules",
|
||||||
"build"
|
"build",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,12 @@ export default defineConfig({
|
|||||||
configure: logProxyRequests,
|
configure: logProxyRequests,
|
||||||
ws: true,
|
ws: true,
|
||||||
},
|
},
|
||||||
|
'^/user*': {
|
||||||
|
target: 'http://localhost:4001/',
|
||||||
|
changeOrigin: true,
|
||||||
|
configure: logProxyRequests,
|
||||||
|
ws: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
test: {
|
test: {
|
||||||
|
|||||||
@@ -148,7 +148,6 @@ function makeViewMenu(clientUrl) {
|
|||||||
return {
|
return {
|
||||||
label: 'Views',
|
label: 'Views',
|
||||||
submenu: [
|
submenu: [
|
||||||
makeItemOpenInBrowser('Public', `${clientUrl}/public`),
|
|
||||||
makeItemOpenInBrowser('Lower Thirds', `${clientUrl}/lower`),
|
makeItemOpenInBrowser('Lower Thirds', `${clientUrl}/lower`),
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
makeItemOpenInBrowser('Timer', `${clientUrl}/timer`),
|
makeItemOpenInBrowser('Timer', `${clientUrl}/timer`),
|
||||||
|
|||||||
@@ -53,8 +53,6 @@ export async function createProjectFile(req: Request, res: Response<{ filename:
|
|||||||
project: {
|
project: {
|
||||||
title: req.body?.title ?? '',
|
title: req.body?.title ?? '',
|
||||||
description: req.body?.description ?? '',
|
description: req.body?.description ?? '',
|
||||||
publicUrl: req.body?.publicUrl ?? '',
|
|
||||||
publicInfo: req.body?.publicInfo ?? '',
|
|
||||||
backstageUrl: req.body?.backstageUrl ?? '',
|
backstageUrl: req.body?.backstageUrl ?? '',
|
||||||
backstageInfo: req.body?.backstageInfo ?? '',
|
backstageInfo: req.body?.backstageInfo ?? '',
|
||||||
projectLogo: req.body?.projectLogo ?? null,
|
projectLogo: req.body?.projectLogo ?? null,
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ export const validateNewProject = [
|
|||||||
body('filename').optional().isString().trim(),
|
body('filename').optional().isString().trim(),
|
||||||
body('title').optional().isString().trim(),
|
body('title').optional().isString().trim(),
|
||||||
body('description').optional().isString().trim(),
|
body('description').optional().isString().trim(),
|
||||||
body('publicUrl').optional().isString().trim(),
|
|
||||||
body('publicInfo').optional().isString().trim(),
|
|
||||||
body('backstageUrl').optional().isString().trim(),
|
body('backstageUrl').optional().isString().trim(),
|
||||||
body('backstageInfo').optional().isString().trim(),
|
body('backstageInfo').optional().isString().trim(),
|
||||||
body('projectLogo').optional().isString().trim(),
|
body('projectLogo').optional().isString().trim(),
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ describe('parseExcel()', () => {
|
|||||||
title: 'Guest Welcome',
|
title: 'Guest Welcome',
|
||||||
timerType: 'count-down',
|
timerType: 'count-down',
|
||||||
endAction: 'none',
|
endAction: 'none',
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
note: 'Ballyhoo',
|
note: 'Ballyhoo',
|
||||||
custom: {
|
custom: {
|
||||||
@@ -76,7 +75,6 @@ describe('parseExcel()', () => {
|
|||||||
title: 'A song from the hearth',
|
title: 'A song from the hearth',
|
||||||
timerType: 'clock',
|
timerType: 'clock',
|
||||||
endAction: 'load-next',
|
endAction: 'load-next',
|
||||||
isPublic: false,
|
|
||||||
skip: true,
|
skip: true,
|
||||||
note: 'Rainbow chase',
|
note: 'Rainbow chase',
|
||||||
custom: {},
|
custom: {},
|
||||||
@@ -117,7 +115,6 @@ describe('parseExcel()', () => {
|
|||||||
title: 'Guest Welcome',
|
title: 'Guest Welcome',
|
||||||
timerType: 'count-down',
|
timerType: 'count-down',
|
||||||
endAction: 'none',
|
endAction: 'none',
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
note: 'Ballyhoo',
|
note: 'Ballyhoo',
|
||||||
custom: {},
|
custom: {},
|
||||||
@@ -132,7 +129,6 @@ describe('parseExcel()', () => {
|
|||||||
title: 'A song from the hearth',
|
title: 'A song from the hearth',
|
||||||
timerType: 'clock',
|
timerType: 'clock',
|
||||||
endAction: 'load-next',
|
endAction: 'load-next',
|
||||||
isPublic: false,
|
|
||||||
skip: true,
|
skip: true,
|
||||||
note: 'Rainbow chase',
|
note: 'Rainbow chase',
|
||||||
custom: {},
|
custom: {},
|
||||||
@@ -434,7 +430,6 @@ describe('getCustomFieldData()', () => {
|
|||||||
cue: 'cue',
|
cue: 'cue',
|
||||||
title: 'title',
|
title: 'title',
|
||||||
countToEnd: 'count to end',
|
countToEnd: 'count to end',
|
||||||
isPublic: 'public',
|
|
||||||
skip: 'skip',
|
skip: 'skip',
|
||||||
note: 'notes',
|
note: 'notes',
|
||||||
colour: 'colour',
|
colour: 'colour',
|
||||||
@@ -487,7 +482,6 @@ describe('getCustomFieldData()', () => {
|
|||||||
cue: 'cue',
|
cue: 'cue',
|
||||||
title: 'title',
|
title: 'title',
|
||||||
countToEnd: 'count to end',
|
countToEnd: 'count to end',
|
||||||
isPublic: 'public',
|
|
||||||
skip: 'skip',
|
skip: 'skip',
|
||||||
note: 'notes',
|
note: 'notes',
|
||||||
colour: 'colour',
|
colour: 'colour',
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ export const dataFromExcelTemplate = [
|
|||||||
'End Action',
|
'End Action',
|
||||||
'Timer type',
|
'Timer type',
|
||||||
'Count to end',
|
'Count to end',
|
||||||
'Public',
|
|
||||||
'Skip',
|
'Skip',
|
||||||
'Notes',
|
'Notes',
|
||||||
't0',
|
't0',
|
||||||
@@ -27,7 +26,6 @@ export const dataFromExcelTemplate = [
|
|||||||
'', // <-- endAction
|
'', // <-- endAction
|
||||||
'', // <-- timerType
|
'', // <-- timerType
|
||||||
'x', // <-- count to end
|
'x', // <-- count to end
|
||||||
'x', // <-- public
|
|
||||||
'', // <-- skip
|
'', // <-- skip
|
||||||
'Ballyhoo', // <-- notes
|
'Ballyhoo', // <-- notes
|
||||||
'a0', // <-- t0
|
'a0', // <-- t0
|
||||||
@@ -45,7 +43,6 @@ export const dataFromExcelTemplate = [
|
|||||||
'load-next', // <-- endAction
|
'load-next', // <-- endAction
|
||||||
'clock', // timerType
|
'clock', // timerType
|
||||||
'x', // <-- count to end
|
'x', // <-- count to end
|
||||||
'', // <-- public
|
|
||||||
'x', // <-- skip
|
'x', // <-- skip
|
||||||
'Rainbow chase', // <-- notes
|
'Rainbow chase', // <-- notes
|
||||||
'b0', // <-- t0
|
'b0', // <-- t0
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ export const parseExcel = (
|
|||||||
let colourIndex: number | null = null;
|
let colourIndex: number | null = null;
|
||||||
|
|
||||||
// options: booleans
|
// options: booleans
|
||||||
let isPublicIndex: number | null = null;
|
|
||||||
let skipIndex: number | null = null;
|
let skipIndex: number | null = null;
|
||||||
let countToEndIndex: number | null = null;
|
let countToEndIndex: number | null = null;
|
||||||
|
|
||||||
@@ -128,10 +127,6 @@ export const parseExcel = (
|
|||||||
countToEndIndex = col;
|
countToEndIndex = col;
|
||||||
rundownMetadata['countToEnd'] = { row, col };
|
rundownMetadata['countToEnd'] = { row, col };
|
||||||
},
|
},
|
||||||
[importMap.isPublic]: (row: number, col: number) => {
|
|
||||||
isPublicIndex = col;
|
|
||||||
rundownMetadata['isPublic'] = { row, col };
|
|
||||||
},
|
|
||||||
[importMap.skip]: (row: number, col: number) => {
|
[importMap.skip]: (row: number, col: number) => {
|
||||||
skipIndex = col;
|
skipIndex = col;
|
||||||
rundownMetadata['skip'] = { row, col };
|
rundownMetadata['skip'] = { row, col };
|
||||||
@@ -203,8 +198,6 @@ export const parseExcel = (
|
|||||||
entry.cue = makeString(column, '');
|
entry.cue = makeString(column, '');
|
||||||
} else if (j === countToEndIndex) {
|
} else if (j === countToEndIndex) {
|
||||||
entry.countToEnd = parseBooleanString(column);
|
entry.countToEnd = parseBooleanString(column);
|
||||||
} else if (j === isPublicIndex) {
|
|
||||||
entry.isPublic = parseBooleanString(column);
|
|
||||||
} else if (j === skipIndex) {
|
} else if (j === skipIndex) {
|
||||||
entry.skip = parseBooleanString(column);
|
entry.skip = parseBooleanString(column);
|
||||||
} else if (j === notesIndex) {
|
} else if (j === notesIndex) {
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ export function parseProjectData(data: Partial<DatabaseModel>, emitError?: Error
|
|||||||
return {
|
return {
|
||||||
title: data.project.title ?? dbModel.project.title,
|
title: data.project.title ?? dbModel.project.title,
|
||||||
description: data.project.description ?? dbModel.project.description,
|
description: data.project.description ?? dbModel.project.description,
|
||||||
publicUrl: data.project.publicUrl ?? dbModel.project.publicUrl,
|
|
||||||
publicInfo: data.project.publicInfo ?? dbModel.project.publicInfo,
|
|
||||||
backstageUrl: data.project.backstageUrl ?? dbModel.project.backstageUrl,
|
backstageUrl: data.project.backstageUrl ?? dbModel.project.backstageUrl,
|
||||||
backstageInfo: data.project.backstageInfo ?? dbModel.project.backstageInfo,
|
backstageInfo: data.project.backstageInfo ?? dbModel.project.backstageInfo,
|
||||||
projectLogo: data.project.projectLogo ?? dbModel.project.projectLogo,
|
projectLogo: data.project.projectLogo ?? dbModel.project.projectLogo,
|
||||||
|
|||||||
@@ -21,8 +21,6 @@ router.post('/', projectSanitiser, async (req: Request, res: Response<ProjectDat
|
|||||||
const newData: Partial<ProjectData> = removeUndefined({
|
const newData: Partial<ProjectData> = removeUndefined({
|
||||||
title: req.body?.title,
|
title: req.body?.title,
|
||||||
description: req.body?.description,
|
description: req.body?.description,
|
||||||
publicUrl: req.body?.publicUrl,
|
|
||||||
publicInfo: req.body?.publicInfo,
|
|
||||||
backstageUrl: req.body?.backstageUrl,
|
backstageUrl: req.body?.backstageUrl,
|
||||||
backstageInfo: req.body?.backstageInfo,
|
backstageInfo: req.body?.backstageInfo,
|
||||||
endMessage: req.body?.endMessage,
|
endMessage: req.body?.endMessage,
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ export const projectSanitiser = [
|
|||||||
body().notEmpty().withMessage('No object found in request'),
|
body().notEmpty().withMessage('No object found in request'),
|
||||||
body('title').optional().isString().trim(),
|
body('title').optional().isString().trim(),
|
||||||
body('description').optional().isString().trim(),
|
body('description').optional().isString().trim(),
|
||||||
body('publicUrl').optional().isString().trim(),
|
|
||||||
body('publicInfo').optional().isString().trim(),
|
|
||||||
body('backstageUrl').optional().isString().trim(),
|
body('backstageUrl').optional().isString().trim(),
|
||||||
body('backstageInfo').optional().isString().trim(),
|
body('backstageInfo').optional().isString().trim(),
|
||||||
body('endMessage').optional().isString().trim(),
|
body('endMessage').optional().isString().trim(),
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ describe('test event validator', () => {
|
|||||||
timeStart: expect.any(Number),
|
timeStart: expect.any(Number),
|
||||||
timeEnd: expect.any(Number),
|
timeEnd: expect.any(Number),
|
||||||
countToEnd: expect.any(Boolean),
|
countToEnd: expect.any(Boolean),
|
||||||
isPublic: expect.any(Boolean),
|
|
||||||
skip: expect.any(Boolean),
|
skip: expect.any(Boolean),
|
||||||
revision: expect.any(Number),
|
revision: expect.any(Number),
|
||||||
type: expect.any(String),
|
type: expect.any(String),
|
||||||
@@ -107,7 +106,6 @@ describe('doesInvalidateMetadata()', () => {
|
|||||||
note: 'note',
|
note: 'note',
|
||||||
endAction: EndAction.LoadNext,
|
endAction: EndAction.LoadNext,
|
||||||
timerType: TimerType.Clock,
|
timerType: TimerType.Clock,
|
||||||
isPublic: false,
|
|
||||||
colour: 'colour',
|
colour: 'colour',
|
||||||
timeWarning: 1,
|
timeWarning: 1,
|
||||||
timeDanger: 2,
|
timeDanger: 2,
|
||||||
|
|||||||
@@ -83,7 +83,6 @@ export function createPatch(originalEvent: OntimeEvent, patchEvent: Partial<Onti
|
|||||||
endAction: validateEndAction(patchEvent.endAction, originalEvent.endAction),
|
endAction: validateEndAction(patchEvent.endAction, originalEvent.endAction),
|
||||||
timerType: validateTimerType(patchEvent.timerType, originalEvent.timerType),
|
timerType: validateTimerType(patchEvent.timerType, originalEvent.timerType),
|
||||||
countToEnd: typeof patchEvent.countToEnd === 'boolean' ? patchEvent.countToEnd : originalEvent.countToEnd,
|
countToEnd: typeof patchEvent.countToEnd === 'boolean' ? patchEvent.countToEnd : originalEvent.countToEnd,
|
||||||
isPublic: typeof patchEvent.isPublic === 'boolean' ? patchEvent.isPublic : originalEvent.isPublic,
|
|
||||||
skip: typeof patchEvent.skip === 'boolean' ? patchEvent.skip : originalEvent.skip,
|
skip: typeof patchEvent.skip === 'boolean' ? patchEvent.skip : originalEvent.skip,
|
||||||
note: makeString(patchEvent.note, originalEvent.note),
|
note: makeString(patchEvent.note, originalEvent.note),
|
||||||
colour: makeString(patchEvent.colour, originalEvent.colour),
|
colour: makeString(patchEvent.colour, originalEvent.colour),
|
||||||
@@ -214,7 +213,6 @@ enum RegenerateWhitelist {
|
|||||||
'endAction',
|
'endAction',
|
||||||
'timerType',
|
'timerType',
|
||||||
'countToEnd',
|
'countToEnd',
|
||||||
'isPublic',
|
|
||||||
'colour',
|
'colour',
|
||||||
'timeWarning',
|
'timeWarning',
|
||||||
'timeDanger',
|
'timeDanger',
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ const propertyConversion = {
|
|||||||
note: coerceString,
|
note: coerceString,
|
||||||
cue: coerceString,
|
cue: coerceString,
|
||||||
|
|
||||||
isPublic: coerceBoolean,
|
|
||||||
skip: coerceBoolean,
|
skip: coerceBoolean,
|
||||||
|
|
||||||
colour: coerceColour,
|
colour: coerceColour,
|
||||||
|
|||||||
@@ -185,9 +185,7 @@ export const startServer = async (): Promise<{ message: string; serverPort: numb
|
|||||||
block: null,
|
block: null,
|
||||||
startedAt: null,
|
startedAt: null,
|
||||||
},
|
},
|
||||||
publicEventNow: state.publicEventNow,
|
|
||||||
eventNext: state.eventNext,
|
eventNext: state.eventNext,
|
||||||
publicEventNext: state.publicEventNext,
|
|
||||||
auxtimer1: {
|
auxtimer1: {
|
||||||
duration: timerConfig.auxTimerDefault,
|
duration: timerConfig.auxTimerDefault,
|
||||||
current: timerConfig.auxTimerDefault,
|
current: timerConfig.auxTimerDefault,
|
||||||
|
|||||||
@@ -42,7 +42,6 @@ describe('safeMerge', () => {
|
|||||||
const mergedData = safeMerge(demoDb, {
|
const mergedData = safeMerge(demoDb, {
|
||||||
project: {
|
project: {
|
||||||
title: 'new title',
|
title: 'new title',
|
||||||
publicInfo: 'new public info',
|
|
||||||
backstageInfo: 'new backstage info',
|
backstageInfo: 'new backstage info',
|
||||||
custom: [
|
custom: [
|
||||||
{
|
{
|
||||||
@@ -56,8 +55,6 @@ describe('safeMerge', () => {
|
|||||||
expect(mergedData.project).toStrictEqual({
|
expect(mergedData.project).toStrictEqual({
|
||||||
title: 'new title',
|
title: 'new title',
|
||||||
description: 'Turin 2022',
|
description: 'Turin 2022',
|
||||||
publicUrl: 'www.getontime.no',
|
|
||||||
publicInfo: 'new public info',
|
|
||||||
backstageUrl: 'www.github.com/cpvalente/ontime',
|
backstageUrl: 'www.github.com/cpvalente/ontime',
|
||||||
backstageInfo: 'new backstage info',
|
backstageInfo: 'new backstage info',
|
||||||
projectLogo: null,
|
projectLogo: null,
|
||||||
|
|||||||
@@ -17,8 +17,6 @@ export const dbModel: DatabaseModel = {
|
|||||||
project: {
|
project: {
|
||||||
title: '',
|
title: '',
|
||||||
description: '',
|
description: '',
|
||||||
publicUrl: '',
|
|
||||||
publicInfo: '',
|
|
||||||
backstageUrl: '',
|
backstageUrl: '',
|
||||||
backstageInfo: '',
|
backstageInfo: '',
|
||||||
projectLogo: null,
|
projectLogo: null,
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 36000000,
|
timeStart: 36000000,
|
||||||
timeEnd: 37200000,
|
timeEnd: 37200000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -101,7 +100,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 37500000,
|
timeStart: 37500000,
|
||||||
timeEnd: 38700000,
|
timeEnd: 38700000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -131,7 +129,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 39000000,
|
timeStart: 39000000,
|
||||||
timeEnd: 40200000,
|
timeEnd: 40200000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -161,7 +158,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 40500000,
|
timeStart: 40500000,
|
||||||
timeEnd: 41700000,
|
timeEnd: 41700000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -191,7 +187,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 42000000,
|
timeStart: 42000000,
|
||||||
timeEnd: 43200000,
|
timeEnd: 43200000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -238,7 +233,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 47100000,
|
timeStart: 47100000,
|
||||||
timeEnd: 48300000,
|
timeEnd: 48300000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -268,7 +262,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 48600000,
|
timeStart: 48600000,
|
||||||
timeEnd: 49800000,
|
timeEnd: 49800000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -298,7 +291,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 50100000,
|
timeStart: 50100000,
|
||||||
timeEnd: 51300000,
|
timeEnd: 51300000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -328,7 +320,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 51600000,
|
timeStart: 51600000,
|
||||||
timeEnd: 52800000,
|
timeEnd: 52800000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -358,7 +349,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 53100000,
|
timeStart: 53100000,
|
||||||
timeEnd: 54300000,
|
timeEnd: 54300000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -405,7 +395,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 56100000,
|
timeStart: 56100000,
|
||||||
timeEnd: 57300000,
|
timeEnd: 57300000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -435,7 +424,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 57600000,
|
timeStart: 57600000,
|
||||||
timeEnd: 58800000,
|
timeEnd: 58800000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -465,7 +453,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 59100000,
|
timeStart: 59100000,
|
||||||
timeEnd: 60300000,
|
timeEnd: 60300000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -495,7 +482,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
timeStart: 60600000,
|
timeStart: 60600000,
|
||||||
timeEnd: 61800000,
|
timeEnd: 61800000,
|
||||||
duration: 1200000,
|
duration: 1200000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -518,8 +504,6 @@ export const demoDb: DatabaseModel = {
|
|||||||
project: {
|
project: {
|
||||||
title: 'Eurovision Song Contest',
|
title: 'Eurovision Song Contest',
|
||||||
description: 'Turin 2022',
|
description: 'Turin 2022',
|
||||||
publicUrl: 'www.getontime.no',
|
|
||||||
publicInfo: 'Rehearsal Schedule - Turin 2022',
|
|
||||||
backstageUrl: 'www.github.com/cpvalente/ontime',
|
backstageUrl: 'www.github.com/cpvalente/ontime',
|
||||||
backstageInfo: 'Rehearsal Schedule - Turin 2022\nAll performers to wear full costumes for 1st rehearsal',
|
backstageInfo: 'Rehearsal Schedule - Turin 2022\nAll performers to wear full costumes for 1st rehearsal',
|
||||||
projectLogo: null,
|
projectLogo: null,
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ export const event: Omit<OntimeEvent, 'id' | 'cue'> = {
|
|||||||
timeStart: 0,
|
timeStart: 0,
|
||||||
timeEnd: 0,
|
timeEnd: 0,
|
||||||
duration: 0,
|
duration: 0,
|
||||||
isPublic: false,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: '',
|
colour: '',
|
||||||
timeWarning: 120000,
|
timeWarning: 120000,
|
||||||
|
|||||||
@@ -9,49 +9,41 @@ describe('loadRoll()', () => {
|
|||||||
id: '1',
|
id: '1',
|
||||||
timeStart: 5,
|
timeStart: 5,
|
||||||
timeEnd: 10,
|
timeEnd: 10,
|
||||||
isPublic: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
timeStart: 10,
|
timeStart: 10,
|
||||||
timeEnd: 20,
|
timeEnd: 20,
|
||||||
isPublic: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
timeStart: 20,
|
timeStart: 20,
|
||||||
timeEnd: 30,
|
timeEnd: 30,
|
||||||
isPublic: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4',
|
id: '4',
|
||||||
timeStart: 30,
|
timeStart: 30,
|
||||||
timeEnd: 40,
|
timeEnd: 40,
|
||||||
isPublic: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '5',
|
id: '5',
|
||||||
timeStart: 40,
|
timeStart: 40,
|
||||||
timeEnd: 50,
|
timeEnd: 50,
|
||||||
isPublic: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '6',
|
id: '6',
|
||||||
timeStart: 50,
|
timeStart: 50,
|
||||||
timeEnd: 60,
|
timeEnd: 60,
|
||||||
isPublic: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '7',
|
id: '7',
|
||||||
timeStart: 60,
|
timeStart: 60,
|
||||||
timeEnd: 70,
|
timeEnd: 70,
|
||||||
isPublic: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '8',
|
id: '8',
|
||||||
timeStart: 70,
|
timeStart: 70,
|
||||||
timeEnd: 80,
|
timeEnd: 80,
|
||||||
isPublic: false,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const timedEvents = prepareTimedEvents(eventlist);
|
const timedEvents = prepareTimedEvents(eventlist);
|
||||||
@@ -155,31 +147,26 @@ describe('loadRoll() handle edge cases with midnight', () => {
|
|||||||
id: '0',
|
id: '0',
|
||||||
timeStart: 9 * MILLIS_PER_HOUR,
|
timeStart: 9 * MILLIS_PER_HOUR,
|
||||||
timeEnd: 10 * MILLIS_PER_HOUR,
|
timeEnd: 10 * MILLIS_PER_HOUR,
|
||||||
isPublic: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '1',
|
id: '1',
|
||||||
timeStart: 20 * MILLIS_PER_HOUR,
|
timeStart: 20 * MILLIS_PER_HOUR,
|
||||||
timeEnd: 22 * MILLIS_PER_HOUR,
|
timeEnd: 22 * MILLIS_PER_HOUR,
|
||||||
isPublic: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
timeStart: 22 * MILLIS_PER_HOUR,
|
timeStart: 22 * MILLIS_PER_HOUR,
|
||||||
timeEnd: 1 * MILLIS_PER_HOUR,
|
timeEnd: 1 * MILLIS_PER_HOUR,
|
||||||
isPublic: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
timeStart: 1 * MILLIS_PER_HOUR,
|
timeStart: 1 * MILLIS_PER_HOUR,
|
||||||
timeEnd: 1 * MILLIS_PER_HOUR + 10 * MILLIS_PER_MINUTE,
|
timeEnd: 1 * MILLIS_PER_HOUR + 10 * MILLIS_PER_MINUTE,
|
||||||
isPublic: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '4',
|
id: '4',
|
||||||
timeStart: 1 * MILLIS_PER_HOUR,
|
timeStart: 1 * MILLIS_PER_HOUR,
|
||||||
timeEnd: 2 * MILLIS_PER_HOUR,
|
timeEnd: 2 * MILLIS_PER_HOUR,
|
||||||
isPublic: true,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const timedEvents = prepareTimedEvents(eventlist);
|
const timedEvents = prepareTimedEvents(eventlist);
|
||||||
@@ -311,7 +298,6 @@ describe('loadRoll() handle edge cases with before and after start', () => {
|
|||||||
id: '1',
|
id: '1',
|
||||||
timeStart: 10 * MILLIS_PER_HOUR,
|
timeStart: 10 * MILLIS_PER_HOUR,
|
||||||
timeEnd: 11 * MILLIS_PER_HOUR,
|
timeEnd: 11 * MILLIS_PER_HOUR,
|
||||||
isPublic: true,
|
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -331,7 +317,6 @@ describe('loadRoll() handle edge cases with before and after start', () => {
|
|||||||
id: '1',
|
id: '1',
|
||||||
timeStart: 10 * MILLIS_PER_HOUR,
|
timeStart: 10 * MILLIS_PER_HOUR,
|
||||||
timeEnd: 11 * MILLIS_PER_HOUR,
|
timeEnd: 11 * MILLIS_PER_HOUR,
|
||||||
isPublic: true,
|
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -351,7 +336,6 @@ describe('loadRoll() handle edge cases with before and after start', () => {
|
|||||||
id: '1',
|
id: '1',
|
||||||
timeStart: 10 * MILLIS_PER_HOUR,
|
timeStart: 10 * MILLIS_PER_HOUR,
|
||||||
timeEnd: 2 * MILLIS_PER_HOUR,
|
timeEnd: 2 * MILLIS_PER_HOUR,
|
||||||
isPublic: true,
|
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
const expected = {
|
const expected = {
|
||||||
@@ -370,7 +354,6 @@ describe('loadRoll() handle edge cases with before and after start', () => {
|
|||||||
id: '1',
|
id: '1',
|
||||||
timeStart: 72000000, // 20:00
|
timeStart: 72000000, // 20:00
|
||||||
timeEnd: 72010000, // 20:10
|
timeEnd: 72010000, // 20:10
|
||||||
isPublic: true,
|
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
const expected = {
|
const expected = {
|
||||||
@@ -389,19 +372,16 @@ describe('loadRoll() test that roll behaviour with overlapping times', () => {
|
|||||||
id: '1',
|
id: '1',
|
||||||
timeStart: 10,
|
timeStart: 10,
|
||||||
timeEnd: 10,
|
timeEnd: 10,
|
||||||
isPublic: false,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
timeStart: 10,
|
timeStart: 10,
|
||||||
timeEnd: 20,
|
timeEnd: 20,
|
||||||
isPublic: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: '3',
|
id: '3',
|
||||||
timeStart: 10,
|
timeStart: 10,
|
||||||
timeEnd: 30,
|
timeEnd: 30,
|
||||||
isPublic: false,
|
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const timedEvents = prepareTimedEvents(eventlist);
|
const timedEvents = prepareTimedEvents(eventlist);
|
||||||
@@ -472,7 +452,6 @@ describe('loadRoll() test that roll behaviour multi day event edge cases', () =>
|
|||||||
id: '1',
|
id: '1',
|
||||||
timeStart: 66000000, // 19:20
|
timeStart: 66000000, // 19:20
|
||||||
timeEnd: 54600000, // 16:10
|
timeEnd: 54600000, // 16:10
|
||||||
isPublic: false,
|
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
const expected = {
|
const expected = {
|
||||||
@@ -491,7 +470,6 @@ describe('loadRoll() test that roll behaviour multi day event edge cases', () =>
|
|||||||
id: '1',
|
id: '1',
|
||||||
timeStart: 67200000, // 19:40
|
timeStart: 67200000, // 19:40
|
||||||
timeEnd: 66900000, // 19:35
|
timeEnd: 66900000, // 19:35
|
||||||
isPublic: false,
|
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
const expected = {
|
const expected = {
|
||||||
|
|||||||
@@ -914,7 +914,6 @@ describe('getRuntimeOffset()', () => {
|
|||||||
endAction: EndAction.None,
|
endAction: EndAction.None,
|
||||||
timerType: TimerType.CountDown,
|
timerType: TimerType.CountDown,
|
||||||
countToEnd: true,
|
countToEnd: true,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
note: '',
|
note: '',
|
||||||
colour: '',
|
colour: '',
|
||||||
@@ -967,7 +966,6 @@ describe('getRuntimeOffset()', () => {
|
|||||||
endAction: EndAction.None,
|
endAction: EndAction.None,
|
||||||
timerType: TimerType.CountDown,
|
timerType: TimerType.CountDown,
|
||||||
countToEnd: true,
|
countToEnd: true,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
note: '',
|
note: '',
|
||||||
colour: '',
|
colour: '',
|
||||||
@@ -1222,9 +1220,7 @@ describe('getTimerPhase()', () => {
|
|||||||
const state = {
|
const state = {
|
||||||
clock: 55691050,
|
clock: 55691050,
|
||||||
eventNow: null,
|
eventNow: null,
|
||||||
publicEventNow: null,
|
|
||||||
eventNext: null,
|
eventNext: null,
|
||||||
publicEventNext: null,
|
|
||||||
runtime: {
|
runtime: {
|
||||||
selectedEventIndex: null,
|
selectedEventIndex: null,
|
||||||
numEvents: 1,
|
numEvents: 1,
|
||||||
@@ -1263,9 +1259,7 @@ describe('getTimerPhase()', () => {
|
|||||||
const state = {
|
const state = {
|
||||||
clock: 55691050,
|
clock: 55691050,
|
||||||
eventNow: null,
|
eventNow: null,
|
||||||
publicEventNow: null,
|
|
||||||
eventNext: null,
|
eventNext: null,
|
||||||
publicEventNext: null,
|
|
||||||
runtime: {
|
runtime: {
|
||||||
selectedEventIndex: null,
|
selectedEventIndex: null,
|
||||||
numEvents: 1,
|
numEvents: 1,
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ import {
|
|||||||
getShouldTimerUpdate,
|
getShouldTimerUpdate,
|
||||||
} from './rundownService.utils.js';
|
} from './rundownService.utils.js';
|
||||||
|
|
||||||
type RuntimeStateEventKeys = keyof Pick<RuntimeState, 'eventNext' | 'eventNow' | 'publicEventNow' | 'publicEventNext'>;
|
type RuntimeStateEventKeys = keyof Pick<RuntimeState, 'eventNext' | 'eventNow'>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service manages runtime status of app
|
* Service manages runtime status of app
|
||||||
@@ -185,15 +185,8 @@ class RuntimeService {
|
|||||||
private affectsLoaded(affectedIds: string[]): boolean {
|
private affectsLoaded(affectedIds: string[]): boolean {
|
||||||
const state = runtimeState.getState();
|
const state = runtimeState.getState();
|
||||||
const now = state.eventNow?.id;
|
const now = state.eventNow?.id;
|
||||||
const nowPublic = state.publicEventNow?.id;
|
|
||||||
const next = state.eventNext?.id;
|
const next = state.eventNext?.id;
|
||||||
const nextPublic = state.publicEventNext?.id;
|
return (now !== undefined && affectedIds.includes(now)) || (next !== undefined && affectedIds.includes(next));
|
||||||
return (
|
|
||||||
(now !== undefined && affectedIds.includes(now)) ||
|
|
||||||
(nowPublic !== undefined && affectedIds.includes(nowPublic)) ||
|
|
||||||
(next !== undefined && affectedIds.includes(next)) ||
|
|
||||||
(nextPublic !== undefined && affectedIds.includes(nextPublic))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private isNewNext() {
|
private isNewNext() {
|
||||||
@@ -209,32 +202,7 @@ class RuntimeService {
|
|||||||
const indexNow = timedEvents.findIndex((event) => event.id === now);
|
const indexNow = timedEvents.findIndex((event) => event.id === now);
|
||||||
const indexNext = timedEvents.findIndex((event) => event.id === next);
|
const indexNext = timedEvents.findIndex((event) => event.id === next);
|
||||||
|
|
||||||
if (indexNext - indexNow !== 1) {
|
return indexNext - indexNow !== 1;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// iterate through timed events and see if there are public events between nowPublic and nextPublic
|
|
||||||
const nowPublic = state.publicEventNow?.id;
|
|
||||||
const nextPublic = state.publicEventNext?.id;
|
|
||||||
|
|
||||||
let foundNew = false;
|
|
||||||
let isAfter = false;
|
|
||||||
for (const event of timedEvents) {
|
|
||||||
if (!isAfter) {
|
|
||||||
if (event.id === nowPublic) {
|
|
||||||
isAfter = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (event.id === nextPublic) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (event.isPublic) {
|
|
||||||
foundNew = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return foundNew;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -852,9 +820,7 @@ function broadcastResult(_target: any, _propertyKey: string, descriptor: Propert
|
|||||||
|
|
||||||
// Update the events if they have changed
|
// Update the events if they have changed
|
||||||
updateEventIfChanged('eventNow', state);
|
updateEventIfChanged('eventNow', state);
|
||||||
updateEventIfChanged('publicEventNow', state);
|
|
||||||
updateEventIfChanged('eventNext', state);
|
updateEventIfChanged('eventNext', state);
|
||||||
updateEventIfChanged('publicEventNext', state);
|
|
||||||
|
|
||||||
// Helper function to update an event if it has changed
|
// Helper function to update an event if it has changed
|
||||||
function updateEventIfChanged(eventKey: RuntimeStateEventKeys, state: runtimeState.RuntimeState) {
|
function updateEventIfChanged(eventKey: RuntimeStateEventKeys, state: runtimeState.RuntimeState) {
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ describe('cellRequestFromEvent()', () => {
|
|||||||
timerType: TimerType.CountDown,
|
timerType: TimerType.CountDown,
|
||||||
countToEnd: false,
|
countToEnd: false,
|
||||||
duration: 10800000,
|
duration: 10800000,
|
||||||
isPublic: false,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: 'red',
|
colour: 'red',
|
||||||
delay: 0,
|
delay: 0,
|
||||||
@@ -57,7 +56,6 @@ describe('cellRequestFromEvent()', () => {
|
|||||||
endAction: { row: 1, col: 22 },
|
endAction: { row: 1, col: 22 },
|
||||||
timerType: { row: 1, col: 23 },
|
timerType: { row: 1, col: 23 },
|
||||||
duration: { row: 1, col: 24 },
|
duration: { row: 1, col: 24 },
|
||||||
isPublic: { row: 1, col: 25 },
|
|
||||||
skip: { row: 1, col: 26 },
|
skip: { row: 1, col: 26 },
|
||||||
colour: { row: 1, col: 27 },
|
colour: { row: 1, col: 27 },
|
||||||
revision: { row: 1, col: 38 },
|
revision: { row: 1, col: 38 },
|
||||||
@@ -83,7 +81,6 @@ describe('cellRequestFromEvent()', () => {
|
|||||||
duration: 10800000,
|
duration: 10800000,
|
||||||
timeStrategy: TimeStrategy.LockEnd,
|
timeStrategy: TimeStrategy.LockEnd,
|
||||||
linkStart: false,
|
linkStart: false,
|
||||||
isPublic: false,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: 'red',
|
colour: 'red',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -107,7 +104,6 @@ describe('cellRequestFromEvent()', () => {
|
|||||||
endAction: { row: 1, col: 22 },
|
endAction: { row: 1, col: 22 },
|
||||||
timerType: { row: 1, col: 23 },
|
timerType: { row: 1, col: 23 },
|
||||||
duration: { row: 1, col: 24 },
|
duration: { row: 1, col: 24 },
|
||||||
isPublic: { row: 1, col: 25 },
|
|
||||||
skip: { row: 1, col: 26 },
|
skip: { row: 1, col: 26 },
|
||||||
colour: { row: 1, col: 27 },
|
colour: { row: 1, col: 27 },
|
||||||
revision: { row: 1, col: 38 },
|
revision: { row: 1, col: 38 },
|
||||||
@@ -135,7 +131,6 @@ describe('cellRequestFromEvent()', () => {
|
|||||||
duration: 10800000,
|
duration: 10800000,
|
||||||
timeStrategy: TimeStrategy.LockEnd,
|
timeStrategy: TimeStrategy.LockEnd,
|
||||||
linkStart: false,
|
linkStart: false,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: 'red',
|
colour: 'red',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -159,7 +154,6 @@ describe('cellRequestFromEvent()', () => {
|
|||||||
endAction: { row: 1, col: 22 },
|
endAction: { row: 1, col: 22 },
|
||||||
timerType: { row: 1, col: 23 },
|
timerType: { row: 1, col: 23 },
|
||||||
duration: { row: 1, col: 24 },
|
duration: { row: 1, col: 24 },
|
||||||
isPublic: { row: 1, col: 25 },
|
|
||||||
skip: { row: 1, col: 26 },
|
skip: { row: 1, col: 26 },
|
||||||
colour: { row: 1, col: 27 },
|
colour: { row: 1, col: 27 },
|
||||||
revision: { row: 1, col: 38 },
|
revision: { row: 1, col: 38 },
|
||||||
@@ -168,7 +162,6 @@ describe('cellRequestFromEvent()', () => {
|
|||||||
timeDanger: { row: 1, col: 41 },
|
timeDanger: { row: 1, col: 41 },
|
||||||
};
|
};
|
||||||
const result = cellRequestFromEvent(event, 1, 1234, metadata);
|
const result = cellRequestFromEvent(event, 1, 1234, metadata);
|
||||||
expect(result.updateCells?.rows?.at(0)?.values?.at(11)?.userEnteredValue?.boolValue).toStrictEqual(true);
|
|
||||||
expect(result.updateCells?.rows?.at(0)?.values?.at(12)?.userEnteredValue?.boolValue).toStrictEqual(false);
|
expect(result.updateCells?.rows?.at(0)?.values?.at(12)?.userEnteredValue?.boolValue).toStrictEqual(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -186,7 +179,6 @@ describe('cellRequestFromEvent()', () => {
|
|||||||
timeStrategy: TimeStrategy.LockEnd,
|
timeStrategy: TimeStrategy.LockEnd,
|
||||||
linkStart: false,
|
linkStart: false,
|
||||||
duration: 10800000,
|
duration: 10800000,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: 'red',
|
colour: 'red',
|
||||||
delay: 0,
|
delay: 0,
|
||||||
@@ -223,7 +215,6 @@ describe('cellRequestFromEvent()', () => {
|
|||||||
duration: 10800000,
|
duration: 10800000,
|
||||||
timeStrategy: TimeStrategy.LockEnd,
|
timeStrategy: TimeStrategy.LockEnd,
|
||||||
linkStart: false,
|
linkStart: false,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: 'red',
|
colour: 'red',
|
||||||
parent: null,
|
parent: null,
|
||||||
@@ -261,7 +252,6 @@ describe('cellRequestFromEvent()', () => {
|
|||||||
duration: 10800000,
|
duration: 10800000,
|
||||||
timeStrategy: TimeStrategy.LockEnd,
|
timeStrategy: TimeStrategy.LockEnd,
|
||||||
linkStart: false,
|
linkStart: false,
|
||||||
isPublic: true,
|
|
||||||
skip: false,
|
skip: false,
|
||||||
colour: 'red',
|
colour: 'red',
|
||||||
parent: null,
|
parent: null,
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ const baseState: RuntimeState = {
|
|||||||
startedAt: null,
|
startedAt: null,
|
||||||
},
|
},
|
||||||
eventNow: null,
|
eventNow: null,
|
||||||
publicEventNow: null,
|
|
||||||
eventNext: null,
|
eventNext: null,
|
||||||
publicEventNext: null,
|
|
||||||
runtime: {
|
runtime: {
|
||||||
selectedEventIndex: null,
|
selectedEventIndex: null,
|
||||||
numEvents: 0,
|
numEvents: 0,
|
||||||
|
|||||||
@@ -30,9 +30,7 @@ const mockEvent = {
|
|||||||
const mockState = {
|
const mockState = {
|
||||||
clock: 666,
|
clock: 666,
|
||||||
eventNow: null,
|
eventNow: null,
|
||||||
publicEventNow: null,
|
|
||||||
eventNext: null,
|
eventNext: null,
|
||||||
publicEventNext: null,
|
|
||||||
runtime: {
|
runtime: {
|
||||||
selectedEventIndex: null,
|
selectedEventIndex: null,
|
||||||
numEvents: 0,
|
numEvents: 0,
|
||||||
|
|||||||
@@ -34,9 +34,7 @@ export type RuntimeState = {
|
|||||||
clock: number; // realtime clock
|
clock: number; // realtime clock
|
||||||
eventNow: PlayableEvent | null;
|
eventNow: PlayableEvent | null;
|
||||||
currentBlock: CurrentBlockState;
|
currentBlock: CurrentBlockState;
|
||||||
publicEventNow: PlayableEvent | null;
|
|
||||||
eventNext: PlayableEvent | null;
|
eventNext: PlayableEvent | null;
|
||||||
publicEventNext: PlayableEvent | null;
|
|
||||||
runtime: Runtime;
|
runtime: Runtime;
|
||||||
timer: TimerState;
|
timer: TimerState;
|
||||||
// private properties of the timer calculations
|
// private properties of the timer calculations
|
||||||
@@ -54,9 +52,7 @@ const runtimeState: RuntimeState = {
|
|||||||
clock: timeNow(),
|
clock: timeNow(),
|
||||||
currentBlock: { ...runtimeStorePlaceholder.currentBlock },
|
currentBlock: { ...runtimeStorePlaceholder.currentBlock },
|
||||||
eventNow: null,
|
eventNow: null,
|
||||||
publicEventNow: null,
|
|
||||||
eventNext: null,
|
eventNext: null,
|
||||||
publicEventNext: null,
|
|
||||||
runtime: { ...runtimeStorePlaceholder.runtime },
|
runtime: { ...runtimeStorePlaceholder.runtime },
|
||||||
timer: { ...runtimeStorePlaceholder.timer },
|
timer: { ...runtimeStorePlaceholder.timer },
|
||||||
_timer: {
|
_timer: {
|
||||||
@@ -75,8 +71,6 @@ export function getState(): Readonly<RuntimeState> {
|
|||||||
...runtimeState,
|
...runtimeState,
|
||||||
eventNow: runtimeState.eventNow ? { ...runtimeState.eventNow } : null,
|
eventNow: runtimeState.eventNow ? { ...runtimeState.eventNow } : null,
|
||||||
eventNext: runtimeState.eventNext ? { ...runtimeState.eventNext } : null,
|
eventNext: runtimeState.eventNext ? { ...runtimeState.eventNext } : null,
|
||||||
publicEventNow: runtimeState.publicEventNow ? { ...runtimeState.publicEventNow } : null,
|
|
||||||
publicEventNext: runtimeState.publicEventNext ? { ...runtimeState.publicEventNext } : null,
|
|
||||||
runtime: { ...runtimeState.runtime },
|
runtime: { ...runtimeState.runtime },
|
||||||
timer: { ...runtimeState.timer },
|
timer: { ...runtimeState.timer },
|
||||||
_timer: { ...runtimeState._timer },
|
_timer: { ...runtimeState._timer },
|
||||||
@@ -89,9 +83,7 @@ export function getState(): Readonly<RuntimeState> {
|
|||||||
*/
|
*/
|
||||||
export function clearEventData() {
|
export function clearEventData() {
|
||||||
runtimeState.eventNow = null;
|
runtimeState.eventNow = null;
|
||||||
runtimeState.publicEventNow = null;
|
|
||||||
runtimeState.eventNext = null;
|
runtimeState.eventNext = null;
|
||||||
runtimeState.publicEventNext = null;
|
|
||||||
|
|
||||||
runtimeState.runtime.offset = 0;
|
runtimeState.runtime.offset = 0;
|
||||||
runtimeState.runtime.relativeOffset = 0;
|
runtimeState.runtime.relativeOffset = 0;
|
||||||
@@ -111,14 +103,11 @@ export function clearEventData() {
|
|||||||
// clear all necessary data when doing a full stop and the event is unloaded
|
// clear all necessary data when doing a full stop and the event is unloaded
|
||||||
export function clearState() {
|
export function clearState() {
|
||||||
runtimeState.eventNow = null;
|
runtimeState.eventNow = null;
|
||||||
runtimeState.publicEventNow = null;
|
|
||||||
runtimeState.eventNext = null;
|
runtimeState.eventNext = null;
|
||||||
|
|
||||||
runtimeState.currentBlock.block = null;
|
runtimeState.currentBlock.block = null;
|
||||||
runtimeState.currentBlock.startedAt = null;
|
runtimeState.currentBlock.startedAt = null;
|
||||||
|
|
||||||
runtimeState.publicEventNext = null;
|
|
||||||
|
|
||||||
runtimeState.runtime.offset = 0;
|
runtimeState.runtime.offset = 0;
|
||||||
runtimeState.runtime.relativeOffset = 0;
|
runtimeState.runtime.relativeOffset = 0;
|
||||||
runtimeState.runtime.actualStart = null;
|
runtimeState.runtime.actualStart = null;
|
||||||
@@ -242,33 +231,6 @@ export function loadNow(timedEvents: OntimeEvent[], eventIndex: MaybeNumber = ru
|
|||||||
const event = timedEvents[eventIndex] as PlayableEvent;
|
const event = timedEvents[eventIndex] as PlayableEvent;
|
||||||
runtimeState.runtime.selectedEventIndex = eventIndex;
|
runtimeState.runtime.selectedEventIndex = eventIndex;
|
||||||
runtimeState.eventNow = event;
|
runtimeState.eventNow = event;
|
||||||
|
|
||||||
// check if current is also public
|
|
||||||
if (event.isPublic) {
|
|
||||||
runtimeState.publicEventNow = event;
|
|
||||||
} else {
|
|
||||||
// assume there is no public event
|
|
||||||
runtimeState.publicEventNow = null;
|
|
||||||
|
|
||||||
// if there is nothing before, return
|
|
||||||
if (!eventIndex) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// iterate backwards to find it
|
|
||||||
for (let i = eventIndex; i >= 0; i--) {
|
|
||||||
const event = timedEvents[i];
|
|
||||||
// we dont deal with events that are not playable
|
|
||||||
if (!isPlayableEvent(event)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.isPublic) {
|
|
||||||
runtimeState.publicEventNow = event;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -281,34 +243,21 @@ export function loadNext(
|
|||||||
if (eventIndex === null) {
|
if (eventIndex === null) {
|
||||||
// reset the state to indicate there is no future event
|
// reset the state to indicate there is no future event
|
||||||
runtimeState.eventNext = null;
|
runtimeState.eventNext = null;
|
||||||
runtimeState.publicEventNext = null;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// temporarily reset this value to simplify loop logic
|
// temporarily reset this value to simplify loop logic
|
||||||
runtimeState.eventNext = null;
|
runtimeState.eventNext = null;
|
||||||
|
|
||||||
|
//TODO: do we already have a it as a list of not skipped events
|
||||||
for (let i = eventIndex + 1; i < timedEvents.length; i++) {
|
for (let i = eventIndex + 1; i < timedEvents.length; i++) {
|
||||||
const event = timedEvents[i];
|
const event = timedEvents[i];
|
||||||
// we dont deal with events that are not playable
|
// we dont deal with events that are not playable
|
||||||
if (!isPlayableEvent(event)) {
|
if (!isPlayableEvent(event)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
runtimeState.eventNext = event;
|
||||||
// the private event is the one immediately after the current event
|
return;
|
||||||
if (runtimeState.eventNext === null) {
|
|
||||||
runtimeState.eventNext = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if event is public
|
|
||||||
if (event.isPublic) {
|
|
||||||
runtimeState.publicEventNext = event;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop if both are set
|
|
||||||
if (runtimeState.eventNext !== null && runtimeState.publicEventNext !== null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,6 @@
|
|||||||
"timeStart": 36000000,
|
"timeStart": 36000000,
|
||||||
"timeEnd": 37200000,
|
"timeEnd": 37200000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -65,7 +64,6 @@
|
|||||||
"timeStart": 37500000,
|
"timeStart": 37500000,
|
||||||
"timeEnd": 38700000,
|
"timeEnd": 38700000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -94,7 +92,6 @@
|
|||||||
"timeStart": 39000000,
|
"timeStart": 39000000,
|
||||||
"timeEnd": 40200000,
|
"timeEnd": 40200000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -123,7 +120,6 @@
|
|||||||
"timeStart": 40500000,
|
"timeStart": 40500000,
|
||||||
"timeEnd": 41700000,
|
"timeEnd": 41700000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -152,7 +148,6 @@
|
|||||||
"timeStart": 42000000,
|
"timeStart": 42000000,
|
||||||
"timeEnd": 43200000,
|
"timeEnd": 43200000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -196,7 +191,6 @@
|
|||||||
"timeStart": 47100000,
|
"timeStart": 47100000,
|
||||||
"timeEnd": 48300000,
|
"timeEnd": 48300000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -225,7 +219,6 @@
|
|||||||
"timeStart": 48600000,
|
"timeStart": 48600000,
|
||||||
"timeEnd": 49800000,
|
"timeEnd": 49800000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -254,7 +247,6 @@
|
|||||||
"timeStart": 50100000,
|
"timeStart": 50100000,
|
||||||
"timeEnd": 51300000,
|
"timeEnd": 51300000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -283,7 +275,6 @@
|
|||||||
"timeStart": 51600000,
|
"timeStart": 51600000,
|
||||||
"timeEnd": 52800000,
|
"timeEnd": 52800000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -312,7 +303,6 @@
|
|||||||
"timeStart": 53100000,
|
"timeStart": 53100000,
|
||||||
"timeEnd": 54300000,
|
"timeEnd": 54300000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -357,7 +347,6 @@
|
|||||||
"timeStart": 56100000,
|
"timeStart": 56100000,
|
||||||
"timeEnd": 57300000,
|
"timeEnd": 57300000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -386,7 +375,6 @@
|
|||||||
"timeStart": 57600000,
|
"timeStart": 57600000,
|
||||||
"timeEnd": 58800000,
|
"timeEnd": 58800000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -415,7 +403,6 @@
|
|||||||
"timeStart": 59100000,
|
"timeStart": 59100000,
|
||||||
"timeEnd": 60300000,
|
"timeEnd": 60300000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -444,7 +431,6 @@
|
|||||||
"timeStart": 60600000,
|
"timeStart": 60600000,
|
||||||
"timeEnd": 61800000,
|
"timeEnd": 61800000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -466,8 +452,6 @@
|
|||||||
"project": {
|
"project": {
|
||||||
"title": "Eurovision Song Contest",
|
"title": "Eurovision Song Contest",
|
||||||
"description": "Turin 2022",
|
"description": "Turin 2022",
|
||||||
"publicUrl": "www.getontime.no",
|
|
||||||
"publicInfo": "Rehearsal Schedule - Turin 2022",
|
|
||||||
"backstageUrl": "www.github.com/cpvalente/ontime",
|
"backstageUrl": "www.github.com/cpvalente/ontime",
|
||||||
"backstageInfo": "Rehearsal Schedule - Turin 2022\nAll performers to wear full costumes for 1st rehearsal",
|
"backstageInfo": "Rehearsal Schedule - Turin 2022\nAll performers to wear full costumes for 1st rehearsal",
|
||||||
"projectLogo": null,
|
"projectLogo": null,
|
||||||
|
|||||||
@@ -58,13 +58,6 @@ test.describe('pages routes are available', () => {
|
|||||||
await page.screenshot({ path: 'automated-screenshots/backstage.png' });
|
await page.screenshot({ path: 'automated-screenshots/backstage.png' });
|
||||||
});
|
});
|
||||||
|
|
||||||
test('public', async ({ page }) => {
|
|
||||||
await page.goto('http://localhost:4001/public');
|
|
||||||
|
|
||||||
await expect(page).toHaveTitle(/ontime/);
|
|
||||||
await page.screenshot({ path: 'automated-screenshots/public.png' });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('studio', async ({ page }) => {
|
test('studio', async ({ page }) => {
|
||||||
await page.goto('http://localhost:4001/studio');
|
await page.goto('http://localhost:4001/studio');
|
||||||
|
|
||||||
|
|||||||
@@ -30,12 +30,6 @@ test.describe('test view navigation feature', () => {
|
|||||||
page.locator('data-test-id=backstage-view');
|
page.locator('data-test-id=backstage-view');
|
||||||
await expect(page).toHaveURL('http://localhost:4001/backstage');
|
await expect(page).toHaveURL('http://localhost:4001/backstage');
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'toggle menu' }).click();
|
|
||||||
page.locator('data-test-id=navigation__menu');
|
|
||||||
await page.getByRole('link', { name: 'Public' }).click();
|
|
||||||
page.locator('data-test-id=public-view');
|
|
||||||
await expect(page).toHaveURL('http://localhost:4001/public');
|
|
||||||
|
|
||||||
await page.getByRole('button', { name: 'toggle menu' }).click();
|
await page.getByRole('button', { name: 'toggle menu' }).click();
|
||||||
page.locator('data-test-id=navigation__menu');
|
page.locator('data-test-id=navigation__menu');
|
||||||
await page.getByRole('link', { name: 'Lower Thirds' }).click();
|
await page.getByRole('link', { name: 'Lower Thirds' }).click();
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ test('delays are show correctly', async ({ page }) => {
|
|||||||
await page.getByTestId('block__title').click();
|
await page.getByTestId('block__title').click();
|
||||||
await page.getByTestId('block__title').fill('test');
|
await page.getByTestId('block__title').fill('test');
|
||||||
await page.getByTestId('block__title').press('Enter');
|
await page.getByTestId('block__title').press('Enter');
|
||||||
await expect(page.getByTestId('entry-1').locator('#block-status')).toHaveAttribute('data-ispublic', 'true');
|
await expect(page.getByTestId('entry-1').locator('#block-status')).toHaveAttribute('data-timerType', 'count-down');
|
||||||
|
|
||||||
// add a delay
|
// add a delay
|
||||||
await page.getByRole('button', { name: 'Delay' }).nth(0).click();
|
await page.getByRole('button', { name: 'Delay' }).nth(0).click();
|
||||||
@@ -82,10 +82,6 @@ test('delays are show correctly', async ({ page }) => {
|
|||||||
await page.goto('http://localhost:4001/cuesheet');
|
await page.goto('http://localhost:4001/cuesheet');
|
||||||
await page.getByRole('cell', { name: 'Delayed by 1 min' }).click();
|
await page.getByRole('cell', { name: 'Delayed by 1 min' }).click();
|
||||||
|
|
||||||
// delay is NOT shown in the public view
|
|
||||||
await page.goto('http://localhost:4001/public');
|
|
||||||
await page.getByText('00:10→00:20').click();
|
|
||||||
|
|
||||||
// delay is shown in the backstage view
|
// delay is shown in the backstage view
|
||||||
await page.goto('http://localhost:4001/backstage');
|
await page.goto('http://localhost:4001/backstage');
|
||||||
await page.getByText('00:11→00:21').click();
|
await page.getByText('00:11→00:21').click();
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ test('CRUD operations on the rundown', async ({ page }) => {
|
|||||||
await page.getByRole('button', { name: 'Event', exact: true }).nth(1).click();
|
await page.getByRole('button', { name: 'Event', exact: true }).nth(1).click();
|
||||||
expect(await page.getByTestId('entry-3').getByTestId('time-input-timeStart').inputValue()).toContain('00:30:00');
|
expect(await page.getByTestId('entry-3').getByTestId('time-input-timeStart').inputValue()).toContain('00:30:00');
|
||||||
|
|
||||||
// test quick add options - event is public
|
// test quick add options
|
||||||
await page.getByRole('button', { name: 'Event', exact: true }).nth(1).click();
|
await page.getByRole('button', { name: 'Event', exact: true }).nth(1).click();
|
||||||
|
|
||||||
await expect(page.getByTestId('entry-4').locator('#block-status')).toHaveAttribute('data-ispublic', 'true');
|
await expect(page.getByTestId('entry-4').locator('#block-status')).toHaveAttribute('data-timerType', 'count-down');
|
||||||
});
|
});
|
||||||
|
|||||||
Vendored
-16
@@ -54,7 +54,6 @@
|
|||||||
"timeStart": 36000000,
|
"timeStart": 36000000,
|
||||||
"timeEnd": 37200000,
|
"timeEnd": 37200000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -83,7 +82,6 @@
|
|||||||
"timeStart": 37500000,
|
"timeStart": 37500000,
|
||||||
"timeEnd": 38700000,
|
"timeEnd": 38700000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -112,7 +110,6 @@
|
|||||||
"timeStart": 39000000,
|
"timeStart": 39000000,
|
||||||
"timeEnd": 40200000,
|
"timeEnd": 40200000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -141,7 +138,6 @@
|
|||||||
"timeStart": 40500000,
|
"timeStart": 40500000,
|
||||||
"timeEnd": 41700000,
|
"timeEnd": 41700000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -170,7 +166,6 @@
|
|||||||
"timeStart": 42000000,
|
"timeStart": 42000000,
|
||||||
"timeEnd": 43200000,
|
"timeEnd": 43200000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -214,7 +209,6 @@
|
|||||||
"timeStart": 47100000,
|
"timeStart": 47100000,
|
||||||
"timeEnd": 48300000,
|
"timeEnd": 48300000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -243,7 +237,6 @@
|
|||||||
"timeStart": 48600000,
|
"timeStart": 48600000,
|
||||||
"timeEnd": 49800000,
|
"timeEnd": 49800000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -272,7 +265,6 @@
|
|||||||
"timeStart": 50100000,
|
"timeStart": 50100000,
|
||||||
"timeEnd": 51300000,
|
"timeEnd": 51300000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -301,7 +293,6 @@
|
|||||||
"timeStart": 51600000,
|
"timeStart": 51600000,
|
||||||
"timeEnd": 52800000,
|
"timeEnd": 52800000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -330,7 +321,6 @@
|
|||||||
"timeStart": 53100000,
|
"timeStart": 53100000,
|
||||||
"timeEnd": 54300000,
|
"timeEnd": 54300000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -374,7 +364,6 @@
|
|||||||
"timeStart": 56100000,
|
"timeStart": 56100000,
|
||||||
"timeEnd": 57300000,
|
"timeEnd": 57300000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -403,7 +392,6 @@
|
|||||||
"timeStart": 57600000,
|
"timeStart": 57600000,
|
||||||
"timeEnd": 58800000,
|
"timeEnd": 58800000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -432,7 +420,6 @@
|
|||||||
"timeStart": 59100000,
|
"timeStart": 59100000,
|
||||||
"timeEnd": 60300000,
|
"timeEnd": 60300000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -461,7 +448,6 @@
|
|||||||
"timeStart": 60600000,
|
"timeStart": 60600000,
|
||||||
"timeEnd": 61800000,
|
"timeEnd": 61800000,
|
||||||
"duration": 1200000,
|
"duration": 1200000,
|
||||||
"isPublic": true,
|
|
||||||
"skip": false,
|
"skip": false,
|
||||||
"colour": "",
|
"colour": "",
|
||||||
"parent": null,
|
"parent": null,
|
||||||
@@ -483,8 +469,6 @@
|
|||||||
"project": {
|
"project": {
|
||||||
"title": "Eurovision Song Contest",
|
"title": "Eurovision Song Contest",
|
||||||
"description": "Turin 2022",
|
"description": "Turin 2022",
|
||||||
"publicUrl": "www.getontime.no",
|
|
||||||
"publicInfo": "Rehearsal Schedule - Turin 2022",
|
|
||||||
"backstageUrl": "www.github.com/cpvalente/ontime",
|
"backstageUrl": "www.github.com/cpvalente/ontime",
|
||||||
"backstageInfo": "Rehearsal Schedule - Turin 2022\nAll performers to wear full costumes for 1st rehearsal",
|
"backstageInfo": "Rehearsal Schedule - Turin 2022\nAll performers to wear full costumes for 1st rehearsal",
|
||||||
"projectLogo": null,
|
"projectLogo": null,
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ export type OntimeEvent = OntimeBaseEvent & {
|
|||||||
timeStart: number;
|
timeStart: number;
|
||||||
timeEnd: number;
|
timeEnd: number;
|
||||||
duration: number;
|
duration: number;
|
||||||
isPublic: boolean;
|
|
||||||
skip: boolean;
|
skip: boolean;
|
||||||
colour: string;
|
colour: string;
|
||||||
timeWarning: number;
|
timeWarning: number;
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
export type ProjectData = {
|
export type ProjectData = {
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
publicUrl: string;
|
|
||||||
publicInfo: string;
|
|
||||||
backstageUrl: string;
|
backstageUrl: string;
|
||||||
backstageInfo: string;
|
backstageInfo: string;
|
||||||
projectLogo: string | null;
|
projectLogo: string | null;
|
||||||
|
|||||||
@@ -46,8 +46,6 @@ export const runtimeStorePlaceholder: Readonly<RuntimeStore> = {
|
|||||||
},
|
},
|
||||||
eventNow: null,
|
eventNow: null,
|
||||||
eventNext: null,
|
eventNext: null,
|
||||||
publicEventNow: null,
|
|
||||||
publicEventNext: null,
|
|
||||||
auxtimer1: {
|
auxtimer1: {
|
||||||
current: 0,
|
current: 0,
|
||||||
direction: SimpleDirection.CountUp,
|
direction: SimpleDirection.CountUp,
|
||||||
|
|||||||
@@ -18,9 +18,7 @@ export type RuntimeStore = {
|
|||||||
runtime: Runtime;
|
runtime: Runtime;
|
||||||
currentBlock: CurrentBlockState;
|
currentBlock: CurrentBlockState;
|
||||||
eventNow: OntimeEvent | null;
|
eventNow: OntimeEvent | null;
|
||||||
publicEventNow: OntimeEvent | null;
|
|
||||||
eventNext: OntimeEvent | null;
|
eventNext: OntimeEvent | null;
|
||||||
publicEventNext: OntimeEvent | null;
|
|
||||||
|
|
||||||
// extra timers
|
// extra timers
|
||||||
auxtimer1: SimpleTimerState;
|
auxtimer1: SimpleTimerState;
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ describe('isImportMap()', () => {
|
|||||||
cue: 'cue',
|
cue: 'cue',
|
||||||
title: 'title',
|
title: 'title',
|
||||||
countToEnd: 'count to end',
|
countToEnd: 'count to end',
|
||||||
isPublic: 'public',
|
|
||||||
skip: 'skip',
|
skip: 'skip',
|
||||||
note: 'notes',
|
note: 'notes',
|
||||||
colour: 'colour',
|
colour: 'colour',
|
||||||
@@ -37,7 +36,6 @@ describe('isImportMap()', () => {
|
|||||||
cue: 'cue',
|
cue: 'cue',
|
||||||
title: 'title',
|
title: 'title',
|
||||||
countToEnd: 'count to end',
|
countToEnd: 'count to end',
|
||||||
isPublic: 'public',
|
|
||||||
skip: 'skip',
|
skip: 'skip',
|
||||||
note: 'notes',
|
note: 'notes',
|
||||||
colour: 'colour',
|
colour: 'colour',
|
||||||
@@ -61,7 +59,6 @@ describe('isImportMap()', () => {
|
|||||||
cue: 'cue',
|
cue: 'cue',
|
||||||
title: 'title',
|
title: 'title',
|
||||||
countToEnd: 'count to end',
|
countToEnd: 'count to end',
|
||||||
isPublic: 'public',
|
|
||||||
skip: 'skip',
|
skip: 'skip',
|
||||||
note: 'notes',
|
note: 'notes',
|
||||||
colour: 'colour',
|
colour: 'colour',
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ export const defaultImportMap = {
|
|||||||
cue: 'cue',
|
cue: 'cue',
|
||||||
title: 'title',
|
title: 'title',
|
||||||
countToEnd: 'count to end',
|
countToEnd: 'count to end',
|
||||||
isPublic: 'public',
|
|
||||||
skip: 'skip',
|
skip: 'skip',
|
||||||
note: 'notes',
|
note: 'notes',
|
||||||
colour: 'colour',
|
colour: 'colour',
|
||||||
|
|||||||
Reference in New Issue
Block a user