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