V2 fix socket (#296)

* refactor: rename timer endpoint

* refactor: typescript

* refactor: rename eventData and viewSettings

* refactor: message manager uses event store
This commit is contained in:
Carlos Valente
2023-02-24 22:47:07 +01:00
committed by GitHub
parent 03552056cb
commit edac1d7f75
102 changed files with 836 additions and 715 deletions
+4 -5
View File
@@ -1,7 +1,7 @@
export const STATIC_PORT = 4001; export const STATIC_PORT = 4001;
// REST stuff // REST stuff
export const EVENT_TABLE = ['event']; export const EVENTDATA_TABLE = ['eventdata'];
export const ALIASES = ['aliases']; export const ALIASES = ['aliases'];
export const USERFIELDS = ['userFields']; export const USERFIELDS = ['userFields'];
export const RUNDOWN_TABLE_KEY = 'rundown'; export const RUNDOWN_TABLE_KEY = 'rundown';
@@ -17,17 +17,16 @@ export const FEAT_INFO = 'feat-info';
export const FEAT_MESSAGECONTROL = 'feat-messagecontrol'; export const FEAT_MESSAGECONTROL = 'feat-messagecontrol';
export const FEAT_PLAYBACKCONTROL = 'feat-playbackcontrol'; export const FEAT_PLAYBACKCONTROL = 'feat-playbackcontrol';
export const FEAT_RUNDOWN = 'feat-rundown'; export const FEAT_RUNDOWN = 'feat-rundown';
export const TIMER = 'ontime-timer'; export const TIMER = 'timer';
/** /**
* @description finds server path given the current location, it * @description finds server path given the current location, it
* @return {*} * @return {*}
*/ */
export const calculateServer = () => export const calculateServer = () => (import.meta.env.DEV ? `http://localhost:${STATIC_PORT}` : window.location.origin);
import.meta.env.DEV ? `http://localhost:${STATIC_PORT}` : window.location.origin;
export const serverURL = calculateServer(); export const serverURL = calculateServer();
export const eventURL = `${serverURL}/event`; export const eventURL = `${serverURL}/eventdata`;
export const rundownURL = `${serverURL}/eventlist`; export const rundownURL = `${serverURL}/eventlist`;
export const ontimeURL = `${serverURL}/ontime`; export const ontimeURL = `${serverURL}/ontime`;
@@ -1,6 +1,5 @@
import axios from 'axios'; import axios from 'axios';
import { EventData } from 'ontime-types';
import { EventDataType } from '../models/EventData.type';
import { eventURL } from './apiConstants'; import { eventURL } from './apiConstants';
@@ -8,7 +7,7 @@ import { eventURL } from './apiConstants';
* @description HTTP request to fetch event data * @description HTTP request to fetch event data
* @return {Promise} * @return {Promise}
*/ */
export async function fetchEvent(): Promise<EventDataType> { export async function fetchEventData(): Promise<EventData> {
const res = await axios.get(eventURL); const res = await axios.get(eventURL);
return res.data; return res.data;
} }
@@ -17,6 +16,6 @@ export async function fetchEvent(): Promise<EventDataType> {
* @description HTTP request to mutate event data * @description HTTP request to mutate event data
* @return {Promise} * @return {Promise}
*/ */
export async function postEvent(data: EventDataType) { export async function postEventData(data: EventData) {
return axios.post(eventURL, data); return axios.post(eventURL, data);
} }
+6 -7
View File
@@ -1,6 +1,5 @@
import axios from 'axios'; import axios from 'axios';
import { OntimeRundown, OntimeRundownEntry } from 'ontime-types';
import { OntimeRundown, OntimeRundownEntry } from '../models/EventTypes';
import { rundownURL } from './apiConstants'; import { rundownURL } from './apiConstants';
@@ -37,12 +36,12 @@ export async function requestPatchEvent(data: OntimeRundownEntry) {
return axios.patch(rundownURL, data); return axios.patch(rundownURL, data);
} }
export type ReorderEntry = { export type ReorderEntry = {
eventId: string, eventId: string;
from: number, from: number;
to: number, to: number;
} };
/** /**
* @description HTTP request to reorder events * @description HTTP request to reorder events
* @return {Promise} * @return {Promise}
+10 -14
View File
@@ -1,11 +1,7 @@
import axios from 'axios'; import axios from 'axios';
import { Alias, OSCSettings, Settings, UserFields, ViewSettings } from 'ontime-types';
import { URLAliasType } from '../models/Alias.type'; import { InfoType } from '../models/Info';
import { InfoType } from '../models/Info.types';
import { OntimeSettingsType } from '../models/OntimeSettings.type';
import { OSCSettings } from '../models/OscSettings.type';
import { UserFieldsType } from '../models/UserFields.type';
import { ViewSettingsType } from '../models/ViewSettings.type';
import { ontimeURL } from './apiConstants'; import { ontimeURL } from './apiConstants';
@@ -13,7 +9,7 @@ import { ontimeURL } from './apiConstants';
* @description HTTP request to retrieve application settings * @description HTTP request to retrieve application settings
* @return {Promise} * @return {Promise}
*/ */
export async function getSettings(): Promise<OntimeSettingsType> { export async function getSettings(): Promise<Settings> {
const res = await axios.get(`${ontimeURL}/settings`); const res = await axios.get(`${ontimeURL}/settings`);
return res.data; return res.data;
} }
@@ -22,7 +18,7 @@ export async function getSettings(): Promise<OntimeSettingsType> {
* @description HTTP request to mutate application settings * @description HTTP request to mutate application settings
* @return {Promise} * @return {Promise}
*/ */
export async function postSettings(data: OntimeSettingsType) { export async function postSettings(data: Settings) {
return axios.post(`${ontimeURL}/settings`, data); return axios.post(`${ontimeURL}/settings`, data);
} }
@@ -39,7 +35,7 @@ export async function getInfo(): Promise<InfoType> {
* @description HTTP request to retrieve view settings * @description HTTP request to retrieve view settings
* @return {Promise} * @return {Promise}
*/ */
export async function getView(): Promise<ViewSettingsType> { export async function getView(): Promise<ViewSettings> {
const res = await axios.get(`${ontimeURL}/views`); const res = await axios.get(`${ontimeURL}/views`);
return res.data; return res.data;
} }
@@ -48,7 +44,7 @@ export async function getView(): Promise<ViewSettingsType> {
* @description HTTP request to mutate view settings * @description HTTP request to mutate view settings
* @return {Promise} * @return {Promise}
*/ */
export async function postView(data: ViewSettingsType) { export async function postView(data: ViewSettings) {
return axios.post(`${ontimeURL}/views`, data); return axios.post(`${ontimeURL}/views`, data);
} }
@@ -56,7 +52,7 @@ export async function postView(data: ViewSettingsType) {
* @description HTTP request to retrieve aliases * @description HTTP request to retrieve aliases
* @return {Promise} * @return {Promise}
*/ */
export async function getAliases(): Promise<URLAliasType[]> { export async function getAliases(): Promise<Alias[]> {
const res = await axios.get(`${ontimeURL}/aliases`); const res = await axios.get(`${ontimeURL}/aliases`);
return res.data; return res.data;
} }
@@ -65,7 +61,7 @@ export async function getAliases(): Promise<URLAliasType[]> {
* @description HTTP request to mutate aliases * @description HTTP request to mutate aliases
* @return {Promise} * @return {Promise}
*/ */
export async function postAliases(data: URLAliasType[]) { export async function postAliases(data: Alias[]) {
return axios.post(`${ontimeURL}/aliases`, data); return axios.post(`${ontimeURL}/aliases`, data);
} }
@@ -73,7 +69,7 @@ export async function postAliases(data: URLAliasType[]) {
* @description HTTP request to retrieve user fields * @description HTTP request to retrieve user fields
* @return {Promise} * @return {Promise}
*/ */
export async function getUserFields(): Promise<UserFieldsType> { export async function getUserFields(): Promise<UserFields> {
const res = await axios.get(`${ontimeURL}/userfields`); const res = await axios.get(`${ontimeURL}/userfields`);
return res.data; return res.data;
} }
@@ -82,7 +78,7 @@ export async function getUserFields(): Promise<UserFieldsType> {
* @description HTTP request to mutate user fields * @description HTTP request to mutate user fields
* @return {Promise} * @return {Promise}
*/ */
export async function postUserFields(data: UserFieldsType) { export async function postUserFields(data: UserFields) {
return axios.post(`${ontimeURL}/userfields`, data); return axios.post(`${ontimeURL}/userfields`, data);
} }
@@ -13,7 +13,7 @@ import {
import { FiPower } from '@react-icons/all-files/fi/FiPower'; import { FiPower } from '@react-icons/all-files/fi/FiPower';
import { LoggingContext } from '../../context/LoggingContext'; import { LoggingContext } from '../../context/LoggingContext';
import { Size } from '../../models/UtilTypes'; import { Size } from '../../models/Util.type';
interface QuitIconBtnProps { interface QuitIconBtnProps {
clickHandler: () => void; clickHandler: () => void;
@@ -3,7 +3,7 @@ import { Button, ButtonGroup, IconButton, Tooltip } from '@chakra-ui/react';
import { IoCopy } from '@react-icons/all-files/io5/IoCopy'; import { IoCopy } from '@react-icons/all-files/io5/IoCopy';
import { tooltipDelayFast } from '../../../ontimeConfig'; import { tooltipDelayFast } from '../../../ontimeConfig';
import { Size } from '../../models/UtilTypes'; import { Size } from '../../models/Util.type';
interface CopyTagProps { interface CopyTagProps {
label: string; label: string;
@@ -2,7 +2,7 @@ import { useCallback, useRef } from 'react';
import { Input, Textarea } from '@chakra-ui/react'; import { Input, Textarea } from '@chakra-ui/react';
import { EventEditorSubmitActions } from '../../../../features/event-editor/EventEditor'; import { EventEditorSubmitActions } from '../../../../features/event-editor/EventEditor';
import { Size } from '../../../models/UtilTypes'; import { Size } from '../../../models/Util.type';
import useReactiveTextInput from './useReactiveTextInput'; import useReactiveTextInput from './useReactiveTextInput';
@@ -1,7 +1,7 @@
import { createContext, PropsWithChildren, useContext, useState } from 'react'; import { createContext, PropsWithChildren, useContext, useState } from 'react';
import { OntimeEvent } from 'ontime-types';
import { useInterval } from '../../hooks/useInterval'; import { useInterval } from '../../hooks/useInterval';
import { OntimeEvent } from '../../models/EventTypes';
interface ScheduleContextState { interface ScheduleContextState {
events: OntimeEvent[]; events: OntimeEvent[];
@@ -1,14 +1,14 @@
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { queryRefetchIntervalSlow } from '../../ontimeConfig'; import { queryRefetchIntervalSlow } from '../../ontimeConfig';
import { EVENT_TABLE } from '../api/apiConstants'; import { EVENTDATA_TABLE } from '../api/apiConstants';
import { fetchEvent } from '../api/eventApi'; import { fetchEventData } from '../api/eventDataApi';
import { eventDataPlaceholder } from '../models/EventData.type'; import { eventDataPlaceholder } from '../models/EventData';
export default function useEvent() { export default function useEventData() {
const { data, status, isError, refetch } = useQuery({ const { data, status, isError, refetch } = useQuery({
queryKey: EVENT_TABLE, queryKey: EVENTDATA_TABLE,
queryFn: fetchEvent, queryFn: fetchEventData,
placeholderData: eventDataPlaceholder, placeholderData: eventDataPlaceholder,
retry: 5, retry: 5,
retryDelay: (attempt) => attempt * 2500, retryDelay: (attempt) => attempt * 2500,
@@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query';
import { queryRefetchIntervalSlow } from '../../ontimeConfig'; import { queryRefetchIntervalSlow } from '../../ontimeConfig';
import { APP_INFO } from '../api/apiConstants'; import { APP_INFO } from '../api/apiConstants';
import { getInfo } from '../api/ontimeApi'; import { getInfo } from '../api/ontimeApi';
import { ontimePlaceholderInfo } from '../models/Info.types'; import { ontimePlaceholderInfo } from '../models/Info';
export default function useInfo() { export default function useInfo() {
const { data, status, isError, refetch } = useQuery({ const { data, status, isError, refetch } = useQuery({
@@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query';
import { queryRefetchIntervalSlow } from '../../ontimeConfig'; import { queryRefetchIntervalSlow } from '../../ontimeConfig';
import { OSC_SETTINGS } from '../api/apiConstants'; import { OSC_SETTINGS } from '../api/apiConstants';
import { getOSC } from '../api/ontimeApi'; import { getOSC } from '../api/ontimeApi';
import { oscPlaceholderSettings } from '../models/OscSettings.type'; import { oscPlaceholderSettings } from '../models/OscSettings';
export default function useOscSettings() { export default function useOscSettings() {
const { data, status, isError, refetch } = useQuery({ const { data, status, isError, refetch } = useQuery({
@@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query';
import { queryRefetchIntervalSlow } from '../../ontimeConfig'; import { queryRefetchIntervalSlow } from '../../ontimeConfig';
import { APP_SETTINGS } from '../api/apiConstants'; import { APP_SETTINGS } from '../api/apiConstants';
import { getSettings } from '../api/ontimeApi'; import { getSettings } from '../api/ontimeApi';
import { ontimePlaceholderSettings } from '../models/OntimeSettings.type'; import { ontimePlaceholderSettings } from '../models/OntimeSettings';
export default function useSettings() { export default function useSettings() {
const { data, status, isError, refetch } = useQuery({ const { data, status, isError, refetch } = useQuery({
@@ -3,7 +3,7 @@ import { useQuery } from '@tanstack/react-query';
import { queryRefetchInterval } from '../../ontimeConfig'; import { queryRefetchInterval } from '../../ontimeConfig';
import { USERFIELDS } from '../api/apiConstants'; import { USERFIELDS } from '../api/apiConstants';
import { getUserFields } from '../api/ontimeApi'; import { getUserFields } from '../api/ontimeApi';
import { userFieldsPlaceholder } from '../models/UserFields.type'; import { userFieldsPlaceholder } from '../models/UserFields';
export default function useUserFields() { export default function useUserFields() {
const { data, status, isError, refetch } = useQuery({ const { data, status, isError, refetch } = useQuery({
@@ -2,6 +2,7 @@ import { useCallback, useContext } from 'react';
import { useMutation, useQueryClient } from '@tanstack/react-query'; import { useMutation, useQueryClient } from '@tanstack/react-query';
import axios, { AxiosError } from 'axios'; import axios, { AxiosError } from 'axios';
import { useAtomValue } from 'jotai'; import { useAtomValue } from 'jotai';
import { OntimeRundown, OntimeRundownEntry, SupportedEvent } from 'ontime-types';
import { RUNDOWN_TABLE, RUNDOWN_TABLE_KEY } from '../api/apiConstants'; import { RUNDOWN_TABLE, RUNDOWN_TABLE_KEY } from '../api/apiConstants';
import { import {
@@ -15,7 +16,6 @@ import {
} from '../api/eventsApi'; } from '../api/eventsApi';
import { defaultPublicAtom, startTimeIsLastEndAtom } from '../atoms/LocalEventSettings'; import { defaultPublicAtom, startTimeIsLastEndAtom } from '../atoms/LocalEventSettings';
import { LoggingContext } from '../context/LoggingContext'; import { LoggingContext } from '../context/LoggingContext';
import { OntimeRundown, OntimeRundownEntry, SupportedEvent } from '../models/EventTypes';
/** /**
* @description Set of utilities for events * @description Set of utilities for events
@@ -88,7 +88,6 @@ export const useEventAction = () => {
} }
try { try {
// @ts-expect-error we know that the event here is one of the defined types
await _addEventMutation.mutateAsync(newEvent); await _addEventMutation.mutateAsync(newEvent);
} catch (error) { } catch (error) {
if (!axios.isAxiosError(error)) { if (!axios.isAxiosError(error)) {
+16 -15
View File
@@ -1,6 +1,5 @@
import { useQuery } from '@tanstack/react-query'; import { useQuery } from '@tanstack/react-query';
import { ontimeQueryClient as queryClient } from '../queryClient'; import { Playback } from 'ontime-types';
import socket, { subscribeOnce } from '../utils/socket';
import { import {
FEAT_CUESHEET, FEAT_CUESHEET,
@@ -10,7 +9,8 @@ import {
FEAT_RUNDOWN, FEAT_RUNDOWN,
TIMER, TIMER,
} from '../api/apiConstants'; } from '../api/apiConstants';
import { Playback } from '../models/OntimeTypes'; import { ontimeQueryClient as queryClient } from '../queryClient';
import socket, { subscribeOnce } from '../utils/socket';
function createSocketHook<T>(key: string, defaultValue: T | null = null) { function createSocketHook<T>(key: string, defaultValue: T | null = null) {
subscribeOnce<T>(key, (data) => queryClient.setQueryData([key], data)); subscribeOnce<T>(key, (data) => queryClient.setQueryData([key], data));
@@ -37,17 +37,19 @@ const emptyRundown: IRundown = {
export const useRundownEditor = createSocketHook(FEAT_RUNDOWN, emptyRundown); export const useRundownEditor = createSocketHook(FEAT_RUNDOWN, emptyRundown);
const emptyMessageControl = { const emptyMessageControl = {
presenter: { messages: {
text: '', presenter: {
visible: false, text: '',
}, visible: false,
public: { },
text: '', public: {
visible: false, text: '',
}, visible: false,
lower: { },
text: '', lower: {
visible: false, text: '',
visible: false,
},
}, },
onAir: false, onAir: false,
}; };
@@ -124,7 +126,6 @@ export const emptyCuesheet = {
export const useCuesheet = createSocketHook(FEAT_CUESHEET, emptyCuesheet); export const useCuesheet = createSocketHook(FEAT_CUESHEET, emptyCuesheet);
export const setEventPlayback = { export const setEventPlayback = {
loadEvent: (eventId: string) => socket.emit('set-loadid', eventId), loadEvent: (eventId: string) => socket.emit('set-loadid', eventId),
startEvent: (eventId: string) => socket.emit('set-startid', eventId), startEvent: (eventId: string) => socket.emit('set-startid', eventId),
+7
View File
@@ -0,0 +1,7 @@
import { Alias } from 'ontime-types';
export const aliasPlaceholder: Alias = {
enabled: false,
alias: '',
pathAndParams: '',
};
@@ -1,11 +0,0 @@
export type URLAliasType = {
enabled: boolean;
alias: string;
pathAndParams: string;
}
export const aliasPlaceholder: URLAliasType = {
enabled: false,
alias: '',
pathAndParams: '',
};
@@ -0,0 +1,10 @@
import { EventData } from 'ontime-types';
export const eventDataPlaceholder: EventData = {
title: '',
publicUrl: '',
publicInfo: '',
backstageUrl: '',
backstageInfo: '',
endMessage: '',
};
@@ -1,17 +0,0 @@
export type EventDataType = {
title: string;
publicUrl: string;
publicInfo: string;
backstageUrl: string;
backstageInfo: string;
endMessage: string;
};
export const eventDataPlaceholder: EventDataType = {
title: '',
publicUrl: '',
publicInfo: '',
backstageUrl: '',
backstageInfo: '',
endMessage: '',
};
@@ -1,50 +0,0 @@
export enum SupportedEvent {
Event = 'event',
Delay = 'delay',
Block = 'block'
}
export interface OntimeBaseEvent {
type: SupportedEvent;
id: string;
after?: string; // used when creating an event to indicate its position in rundown
}
export type OntimeDelay = OntimeBaseEvent & {
type: SupportedEvent.Delay;
duration: number;
revision: number;
}
export type OntimeBlock = OntimeBaseEvent & {
type: SupportedEvent.Block;
}
export type OntimeEvent = OntimeBaseEvent & {
type: SupportedEvent.Event;
title: string,
subtitle: string,
presenter: string,
note: string,
timeType?: string,
timeStart: number,
timeEnd: number,
duration: number,
isPublic: boolean,
skip: boolean,
colour: string,
user0: string,
user1: string,
user2: string,
user3: string,
user4: string,
user5: string,
user6: string,
user7: string,
user8: string,
user9: string,
revision: number,
}
export type OntimeRundownEntry = OntimeDelay | OntimeBlock | OntimeEvent;
export type OntimeRundown = OntimeRundownEntry[]
@@ -1,19 +1,19 @@
import { OntimeSettingsType } from './OntimeSettings.type'; import { Settings } from 'ontime-types';
type NetworkInterfaceType = { type NetworkInterfaceType = {
name: string; name: string;
address: string; address: string;
} };
export type InfoType = { export type InfoType = {
networkInterfaces: NetworkInterfaceType[]; networkInterfaces: NetworkInterfaceType[];
settings: Pick<OntimeSettingsType, 'version' | 'serverPort'> settings: Pick<Settings, 'version' | 'serverPort'>;
} };
export const ontimePlaceholderInfo: InfoType = { export const ontimePlaceholderInfo: InfoType = {
networkInterfaces: [], networkInterfaces: [],
settings: { settings: {
version: 0, version: 2,
serverPort: 4001, serverPort: 4001,
}, },
}; };
@@ -0,0 +1,10 @@
import { Settings } from 'ontime-types';
export const ontimePlaceholderSettings: Settings = {
app: 'ontime',
version: 2,
serverPort: 4001,
lock: null,
pinCode: null,
timeFormat: '24',
};
@@ -1,19 +0,0 @@
import { TimeFormat } from './OntimeTypes';
export type OntimeSettingsType = {
app: string;
version: number;
serverPort: number;
lock: null | boolean;
pinCode: null | number | string;
timeFormat: TimeFormat;
}
export const ontimePlaceholderSettings: OntimeSettingsType = {
app: 'ontime',
version: 1,
serverPort: 4001,
lock: null,
pinCode: null,
timeFormat: '24',
};
@@ -1,4 +0,0 @@
export type PresenterMessageType = {
text: string;
visible: boolean;
}
@@ -1,4 +1,4 @@
import { Playback } from './OntimeTypes'; import { Playback } from 'ontime-types';
export type TimeManagerType = { export type TimeManagerType = {
clock: number; clock: number;
@@ -12,4 +12,4 @@ export type TimeManagerType = {
finished: boolean; finished: boolean;
playback: Playback; playback: Playback;
} };
@@ -0,0 +1,14 @@
import { UserFields } from 'ontime-types';
export const userFieldsPlaceholder: UserFields = {
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
};
@@ -1,25 +0,0 @@
export type UserFieldsType = {
user0: string;
user1: string;
user2: string;
user3: string;
user4: string;
user5: string;
user6: string;
user7: string;
user8: string;
user9: string;
}
export const userFieldsPlaceholder: UserFieldsType = {
user0: '',
user1: '',
user2: '',
user3: '',
user4: '',
user5: '',
user6: '',
user7: '',
user8: '',
user9: '',
};
@@ -1,7 +1,5 @@
export type ViewSettingsType = { import { ViewSettings } from 'ontime-types';
overrideStyles: boolean;
}
export const viewsSettingsPlaceholder: ViewSettingsType = { export const viewsSettingsPlaceholder: ViewSettings = {
overrideStyles: false, overrideStyles: false,
}; };
@@ -1,4 +1,4 @@
import { OntimeEvent, OntimeRundownEntry, SupportedEvent } from '../models/EventTypes'; import { OntimeEvent, OntimeRundownEntry, SupportedEvent } from 'ontime-types';
import { formatTime } from './time'; import { formatTime } from './time';
@@ -16,26 +16,26 @@ export default function MessageControl() {
<InputRow <InputRow
label='Timer screen message' label='Timer screen message'
placeholder='Shown in stage timer' placeholder='Shown in stage timer'
text={data?.presenter.text || ''} text={data?.messages.presenter.text || ''}
visible={data?.presenter.visible || false} visible={data?.messages.presenter.visible || false}
changeHandler={(newValue) => setMessage.presenterText(newValue)} changeHandler={(newValue) => setMessage.presenterText(newValue)}
actionHandler={() => setMessage.presenterVisible(!data?.presenter.visible)} actionHandler={() => setMessage.presenterVisible(!data?.messages.presenter.visible)}
/> />
<InputRow <InputRow
label='Public / Backstage screen message' label='Public / Backstage screen message'
placeholder='Shown in public and backstage screens' placeholder='Shown in public and backstage screens'
text={data?.public.text || ''} text={data?.messages.public.text || ''}
visible={data?.public.visible || false} visible={data?.messages.public.visible || false}
changeHandler={(newValue) => setMessage.publicText(newValue)} changeHandler={(newValue) => setMessage.publicText(newValue)}
actionHandler={() => setMessage.publicVisible(!data?.public.visible)} actionHandler={() => setMessage.publicVisible(!data?.messages.public.visible)}
/> />
<InputRow <InputRow
label='Lower third message' label='Lower third message'
placeholder='Shown in lower third' placeholder='Shown in lower third'
text={data?.lower.text || ''} text={data?.messages.lower.text || ''}
visible={data?.lower.visible || false} visible={data?.messages.lower.visible || false}
changeHandler={(newValue) => setMessage.lowerText(newValue)} changeHandler={(newValue) => setMessage.lowerText(newValue)}
actionHandler={() => setMessage.lowerVisible(!data?.lower.visible)} actionHandler={() => setMessage.lowerVisible(!data?.messages.lower.visible)}
/> />
<div className={style.onAirSection}> <div className={style.onAirSection}>
<label className={style.label}>Toggle On Air state</label> <label className={style.label}>Toggle On Air state</label>
@@ -1,4 +1,4 @@
import { Playback } from '../../../common/models/OntimeTypes'; import { Playback } from 'ontime-types';
import PlaybackDisplay from './PlaybackDisplay'; import PlaybackDisplay from './PlaybackDisplay';
import Transport from './Transport'; import Transport from './Transport';
@@ -1,5 +1,6 @@
import { Playback } from 'ontime-types';
import { usePlaybackControl } from '../../../common/hooks/useSocket'; import { usePlaybackControl } from '../../../common/hooks/useSocket';
import { Playback } from '../../../common/models/OntimeTypes';
import PlaybackButtons from './PlaybackButtons'; import PlaybackButtons from './PlaybackButtons';
import PlaybackTimer from './PlaybackTimer'; import PlaybackTimer from './PlaybackTimer';
@@ -1,9 +1,9 @@
import { IoPause } from '@react-icons/all-files/io5/IoPause'; import { IoPause } from '@react-icons/all-files/io5/IoPause';
import { IoPlay } from '@react-icons/all-files/io5/IoPlay'; import { IoPlay } from '@react-icons/all-files/io5/IoPlay';
import { IoTimeOutline } from '@react-icons/all-files/io5/IoTimeOutline'; import { IoTimeOutline } from '@react-icons/all-files/io5/IoTimeOutline';
import { Playback } from 'ontime-types';
import { setPlayback } from '../../../common/hooks/useSocket'; import { setPlayback } from '../../../common/hooks/useSocket';
import { Playback } from '../../../common/models/OntimeTypes';
import TapButton from './TapButton'; import TapButton from './TapButton';
@@ -1,8 +1,8 @@
import { Tooltip } from '@chakra-ui/react'; import { Tooltip } from '@chakra-ui/react';
import { Playback } from 'ontime-types';
import TimerDisplay from '../../../common/components/timer-display/TimerDisplay'; import TimerDisplay from '../../../common/components/timer-display/TimerDisplay';
import { setPlayback, useTimer } from '../../../common/hooks/useSocket'; import { setPlayback, useTimer } from '../../../common/hooks/useSocket';
import { Playback } from '../../../common/models/OntimeTypes';
import { stringFromMillis } from '../../../common/utils/time'; import { stringFromMillis } from '../../../common/utils/time';
import { tooltipDelayMid } from '../../../ontimeConfig'; import { tooltipDelayMid } from '../../../ontimeConfig';
@@ -1,6 +1,5 @@
import { ForwardedRef, forwardRef, PropsWithChildren } from 'react'; import { ForwardedRef, forwardRef, PropsWithChildren } from 'react';
import { Playback } from 'ontime-types';
import { Playback } from '../../../common/models/OntimeTypes';
import style from './TapButton.module.scss'; import style from './TapButton.module.scss';
@@ -3,9 +3,9 @@ import { IoPlaySkipBack } from '@react-icons/all-files/io5/IoPlaySkipBack';
import { IoPlaySkipForward } from '@react-icons/all-files/io5/IoPlaySkipForward'; import { IoPlaySkipForward } from '@react-icons/all-files/io5/IoPlaySkipForward';
import { IoReload } from '@react-icons/all-files/io5/IoReload'; import { IoReload } from '@react-icons/all-files/io5/IoReload';
import { IoStop } from '@react-icons/all-files/io5/IoStop'; import { IoStop } from '@react-icons/all-files/io5/IoStop';
import { Playback } from 'ontime-types';
import { setPlayback } from '../../../common/hooks/useSocket'; import { setPlayback } from '../../../common/hooks/useSocket';
import { Playback } from '../../../common/models/OntimeTypes';
import { tooltipDelayMid } from '../../../ontimeConfig'; import { tooltipDelayMid } from '../../../ontimeConfig';
import TapButton from './TapButton'; import TapButton from './TapButton';
@@ -2,6 +2,7 @@ import { useCallback, useContext, useEffect, useState } from 'react';
import { Button, Select, Switch } from '@chakra-ui/react'; import { Button, Select, Switch } from '@chakra-ui/react';
import { IoBan } from '@react-icons/all-files/io5/IoBan'; import { IoBan } from '@react-icons/all-files/io5/IoBan';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { OntimeEvent } from 'ontime-types';
import { editorEventId } from '../../common/atoms/LocalEventSettings'; import { editorEventId } from '../../common/atoms/LocalEventSettings';
import CopyTag from '../../common/components/copy-tag/CopyTag'; import CopyTag from '../../common/components/copy-tag/CopyTag';
@@ -11,7 +12,6 @@ import TimeInput from '../../common/components/input/time-input/TimeInput';
import { LoggingContext } from '../../common/context/LoggingContext'; import { LoggingContext } from '../../common/context/LoggingContext';
import { useEventAction } from '../../common/hooks/useEventAction'; import { useEventAction } from '../../common/hooks/useEventAction';
import useRundown from '../../common/hooks-query/useRundown'; import useRundown from '../../common/hooks-query/useRundown';
import { OntimeEvent } from '../../common/models/EventTypes';
import { millisToMinutes } from '../../common/utils/dateConfig'; import { millisToMinutes } from '../../common/utils/dateConfig';
import getDelayTo from '../../common/utils/getDelayTo'; import getDelayTo from '../../common/utils/getDelayTo';
import { stringFromMillis } from '../../common/utils/time'; import { stringFromMillis } from '../../common/utils/time';
@@ -4,10 +4,10 @@ import { FiMinusCircle } from '@react-icons/all-files/fi/FiMinusCircle';
import { FiTrash2 } from '@react-icons/all-files/fi/FiTrash2'; import { FiTrash2 } from '@react-icons/all-files/fi/FiTrash2';
import { IoAdd } from '@react-icons/all-files/io5/IoAdd'; import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
import { IoTimerOutline } from '@react-icons/all-files/io5/IoTimerOutline'; import { IoTimerOutline } from '@react-icons/all-files/io5/IoTimerOutline';
import { SupportedEvent } from 'ontime-types';
import { CursorContext } from '../../common/context/CursorContext'; import { CursorContext } from '../../common/context/CursorContext';
import { useEventAction } from '../../common/hooks/useEventAction'; import { useEventAction } from '../../common/hooks/useEventAction';
import { SupportedEvent } from '../../common/models/EventTypes';
import style from './RundownMenu.module.scss'; import style from './RundownMenu.module.scss';
@@ -21,7 +21,7 @@ import { eventSettingsAtom } from '../../common/atoms/LocalEventSettings';
import TooltipActionBtn from '../../common/components/buttons/TooltipActionBtn'; import TooltipActionBtn from '../../common/components/buttons/TooltipActionBtn';
import { LoggingContext } from '../../common/context/LoggingContext'; import { LoggingContext } from '../../common/context/LoggingContext';
import useSettings from '../../common/hooks-query/useSettings'; import useSettings from '../../common/hooks-query/useSettings';
import { ontimePlaceholderSettings } from '../../common/models/OntimeSettings.type'; import { ontimePlaceholderSettings } from '../../common/models/OntimeSettings';
import { inputProps } from './modalHelper'; import { inputProps } from './modalHelper';
import SubmitContainer from './SubmitContainer'; import SubmitContainer from './SubmitContainer';
@@ -1,10 +1,10 @@
import { useCallback, useContext, useEffect, useState } from 'react'; import { useCallback, useContext, useEffect, useState } from 'react';
import { FormLabel, Input, ModalBody, Textarea } from '@chakra-ui/react'; import { FormLabel, Input, ModalBody, Textarea } from '@chakra-ui/react';
import { postEvent } from '../../common/api/eventApi'; import { postEventData } from '../../common/api/eventDataApi';
import { LoggingContext } from '../../common/context/LoggingContext'; import { LoggingContext } from '../../common/context/LoggingContext';
import useEvent from '../../common/hooks-query/useEvent'; import useEventData from '../../common/hooks-query/useEventData';
import { eventDataPlaceholder } from '../../common/models/EventData.type'; import { eventDataPlaceholder } from '../../common/models/EventData';
import { inputProps } from './modalHelper'; import { inputProps } from './modalHelper';
import SubmitContainer from './SubmitContainer'; import SubmitContainer from './SubmitContainer';
@@ -12,7 +12,7 @@ import SubmitContainer from './SubmitContainer';
import style from './Modals.module.scss'; import style from './Modals.module.scss';
export default function SettingsModal() { export default function SettingsModal() {
const { data, status, refetch } = useEvent(); const { data, status, refetch } = useEventData();
const { emitError } = useContext(LoggingContext); const { emitError } = useContext(LoggingContext);
const [formData, setFormData] = useState(eventDataPlaceholder); const [formData, setFormData] = useState(eventDataPlaceholder);
const [changed, setChanged] = useState(false); const [changed, setChanged] = useState(false);
@@ -44,7 +44,7 @@ export default function SettingsModal() {
setSubmitting(true); setSubmitting(true);
try { try {
await postEvent(formData); await postEventData(formData);
} catch (error) { } catch (error) {
emitError(`Error saving event settings: ${error}`); emitError(`Error saving event settings: ${error}`);
} finally { } finally {
@@ -4,7 +4,7 @@ import { FiInfo } from '@react-icons/all-files/fi/FiInfo';
import { LoggingContext } from '../../common/context/LoggingContext'; import { LoggingContext } from '../../common/context/LoggingContext';
import useInfo from '../../common/hooks-query/useInfo'; import useInfo from '../../common/hooks-query/useInfo';
import { httpPlaceholder } from '../../common/models/Http.type'; import { httpPlaceholder } from '../../common/models/Http';
import { ontimeVars } from '../../common/models/OntimeVars'; import { ontimeVars } from '../../common/models/OntimeVars';
import { inputProps } from './modalHelper'; import { inputProps } from './modalHelper';
@@ -5,7 +5,7 @@ import { IoInformationCircleOutline } from '@react-icons/all-files/io5/IoInforma
import { postUserFields } from '../../common/api/ontimeApi'; import { postUserFields } from '../../common/api/ontimeApi';
import { LoggingContext } from '../../common/context/LoggingContext'; import { LoggingContext } from '../../common/context/LoggingContext';
import useUserFields from '../../common/hooks-query/useUserFields'; import useUserFields from '../../common/hooks-query/useUserFields';
import { userFieldsPlaceholder } from '../../common/models/UserFields.type'; import { userFieldsPlaceholder } from '../../common/models/UserFields';
import { handleLinks, host } from '../../common/utils/linkUtils'; import { handleLinks, host } from '../../common/utils/linkUtils';
import SubmitContainer from './SubmitContainer'; import SubmitContainer from './SubmitContainer';
@@ -5,7 +5,7 @@ import { Button, FormControl, Input, ModalBody, ModalFooter, Switch } from '@cha
import { postOSC } from '../../../common/api/ontimeApi'; import { postOSC } from '../../../common/api/ontimeApi';
import { LoggingContext } from '../../../common/context/LoggingContext'; import { LoggingContext } from '../../../common/context/LoggingContext';
import useOscSettings from '../../../common/hooks-query/useOscSettings'; import useOscSettings from '../../../common/hooks-query/useOscSettings';
import { PlaceholderSettings } from '../../../common/models/OscSettings.type'; import { PlaceholderSettings } from '../../../common/models/OscSettings';
import { isIPAddress, isOnlyNumbers } from '../../../common/utils/regex'; import { isIPAddress, isOnlyNumbers } from '../../../common/utils/regex';
import styles from '../Modal.module.scss'; import styles from '../Modal.module.scss';
@@ -6,7 +6,7 @@ import { postOSC } from '../../../common/api/ontimeApi';
import EnableBtn from '../../../common/components/buttons/EnableBtn'; import EnableBtn from '../../../common/components/buttons/EnableBtn';
import { LoggingContext } from '../../../common/context/LoggingContext'; import { LoggingContext } from '../../../common/context/LoggingContext';
import useOscSettings from '../../../common/hooks-query/useOscSettings'; import useOscSettings from '../../../common/hooks-query/useOscSettings';
import { oscPlaceholderSettings } from '../../../common/models/OscSettings.type'; import { oscPlaceholderSettings } from '../../../common/models/OscSettings';
import { inputProps, portInputProps } from '../modalHelper'; import { inputProps, portInputProps } from '../modalHelper';
import SubmitContainer from '../SubmitContainer'; import SubmitContainer from '../SubmitContainer';
+2 -8
View File
@@ -3,14 +3,13 @@ import { DragDropContext, Droppable, DropResult } from 'react-beautiful-dnd';
import { Button } from '@chakra-ui/react'; import { Button } from '@chakra-ui/react';
import { IoAdd } from '@react-icons/all-files/io5/IoAdd'; import { IoAdd } from '@react-icons/all-files/io5/IoAdd';
import { useAtomValue } from 'jotai'; import { useAtomValue } from 'jotai';
import PropTypes from 'prop-types'; import { OntimeRundown, SupportedEvent } from 'ontime-types';
import { defaultPublicAtom, showQuickEntryAtom, startTimeIsLastEndAtom } from '../../common/atoms/LocalEventSettings'; import { defaultPublicAtom, showQuickEntryAtom, startTimeIsLastEndAtom } from '../../common/atoms/LocalEventSettings';
import Empty from '../../common/components/state/Empty'; import Empty from '../../common/components/state/Empty';
import { CursorContext } from '../../common/context/CursorContext'; import { CursorContext } from '../../common/context/CursorContext';
import { useEventAction } from '../../common/hooks/useEventAction'; import { useEventAction } from '../../common/hooks/useEventAction';
import { useRundownEditor } from '../../common/hooks/useSocket'; import { useRundownEditor } from '../../common/hooks/useSocket';
import { OntimeRundown, SupportedEvent } from '../../common/models/EventTypes';
import { cloneEvent } from '../../common/utils/eventsManager'; import { cloneEvent } from '../../common/utils/eventsManager';
import QuickAddBlock from './quick-add-block/QuickAddBlock'; import QuickAddBlock from './quick-add-block/QuickAddBlock';
@@ -25,8 +24,7 @@ interface RundownProps {
export default function Rundown(props: RundownProps) { export default function Rundown(props: RundownProps) {
const { entries } = props; const { entries } = props;
const { data } = useRundownEditor(); const { data } = useRundownEditor();
const { cursor, moveCursorUp, moveCursorDown, moveCursorTo, isCursorLocked } = const { cursor, moveCursorUp, moveCursorDown, moveCursorTo, isCursorLocked } = useContext(CursorContext);
useContext(CursorContext);
const startTimeIsLastEnd = useAtomValue(startTimeIsLastEndAtom); const startTimeIsLastEnd = useAtomValue(startTimeIsLastEndAtom);
const defaultPublic = useAtomValue(defaultPublicAtom); const defaultPublic = useAtomValue(defaultPublicAtom);
const { addEvent, reorderEvent } = useEventAction(); const { addEvent, reorderEvent } = useEventAction();
@@ -260,7 +258,3 @@ export default function Rundown(props: RundownProps) {
</div> </div>
); );
} }
Rundown.propTypes = {
entries: PropTypes.array,
};
@@ -1,12 +1,11 @@
import { useCallback, useContext } from 'react'; import { useCallback, useContext } from 'react';
import { useAtom, useAtomValue } from 'jotai'; import { useAtom, useAtomValue } from 'jotai';
import { OntimeEvent, OntimeRundownEntry, Playback, SupportedEvent } from 'ontime-types';
import { defaultPublicAtom, editorEventId, startTimeIsLastEndAtom } from '../../common/atoms/LocalEventSettings'; import { defaultPublicAtom, editorEventId, startTimeIsLastEndAtom } from '../../common/atoms/LocalEventSettings';
import { CursorContext } from '../../common/context/CursorContext'; import { CursorContext } from '../../common/context/CursorContext';
import { LoggingContext } from '../../common/context/LoggingContext'; import { LoggingContext } from '../../common/context/LoggingContext';
import { useEventAction } from '../../common/hooks/useEventAction'; import { useEventAction } from '../../common/hooks/useEventAction';
import { OntimeEvent, OntimeRundownEntry, SupportedEvent } from '../../common/models/EventTypes';
import { Playback } from '../../common/models/OntimeTypes';
import { cloneEvent } from '../../common/utils/eventsManager'; import { cloneEvent } from '../../common/utils/eventsManager';
import { calculateDuration } from '../../common/utils/timesManager'; import { calculateDuration } from '../../common/utils/timesManager';
@@ -14,14 +13,7 @@ import BlockBlock from './block-block/BlockBlock';
import DelayBlock from './delay-block/DelayBlock'; import DelayBlock from './delay-block/DelayBlock';
import EventBlock from './event-block/EventBlock'; import EventBlock from './event-block/EventBlock';
export type EventItemActions = export type EventItemActions = 'set-cursor' | 'event' | 'delay' | 'block' | 'delete' | 'clone' | 'update';
'set-cursor'
| 'event'
| 'delay'
| 'block'
| 'delete'
| 'clone'
| 'update'
interface RundownEntryProps { interface RundownEntryProps {
type: SupportedEvent; type: SupportedEvent;
@@ -38,18 +30,7 @@ interface RundownEntryProps {
} }
export default function RundownEntry(props: RundownEntryProps) { export default function RundownEntry(props: RundownEntryProps) {
const { const { index, eventIndex, data, selected, hasCursor, next, delay, previousEnd, previousEventId, playback } = props;
index,
eventIndex,
data,
selected,
hasCursor,
next,
delay,
previousEnd,
previousEventId,
playback,
} = props;
const { emitError } = useContext(LoggingContext); const { emitError } = useContext(LoggingContext);
const startTimeIsLastEnd = useAtomValue(startTimeIsLastEndAtom); const startTimeIsLastEnd = useAtomValue(startTimeIsLastEndAtom);
const defaultPublic = useAtomValue(defaultPublicAtom); const defaultPublic = useAtomValue(defaultPublicAtom);
@@ -61,7 +42,7 @@ export default function RundownEntry(props: RundownEntryProps) {
type FieldValue = { type FieldValue = {
field: keyof Omit<OntimeEvent, 'duration'> | 'durationOverride'; field: keyof Omit<OntimeEvent, 'duration'> | 'durationOverride';
value: unknown; value: unknown;
} };
const actionHandler = useCallback( const actionHandler = useCallback(
(action: EventItemActions, payload?: number | FieldValue) => { (action: EventItemActions, payload?: number | FieldValue) => {
switch (action) { switch (action) {
@@ -171,19 +152,11 @@ export default function RundownEntry(props: RundownEntryProps) {
/> />
); );
} else if (data.type === SupportedEvent.Block) { } else if (data.type === SupportedEvent.Block) {
return <BlockBlock // @ts-expect-error -- revise types here
index={index} return <BlockBlock index={index} data={data} hasCursor={hasCursor} actionHandler={actionHandler} />;
data={data}
hasCursor={hasCursor}
actionHandler={actionHandler}
/>;
} else if (data.type === SupportedEvent.Delay) { } else if (data.type === SupportedEvent.Delay) {
return <DelayBlock // @ts-expect-error -- revise types here
index={index} return <DelayBlock index={index} data={data} hasCursor={hasCursor} actionHandler={actionHandler} />;
data={data}
hasCursor={hasCursor}
actionHandler={actionHandler}
/>;
} }
return null; return null;
} }
@@ -1,8 +1,8 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { Draggable } from 'react-beautiful-dnd'; import { Draggable } from 'react-beautiful-dnd';
import { IoReorderTwo } from '@react-icons/all-files/io5/IoReorderTwo'; import { IoReorderTwo } from '@react-icons/all-files/io5/IoReorderTwo';
import { OntimeBlock, OntimeEvent } from 'ontime-types';
import { OntimeBlock, OntimeEvent } from '../../../common/models/EventTypes';
import { cx } from '../../../common/utils/styleUtils'; import { cx } from '../../../common/utils/styleUtils';
import BlockActionMenu from '../event-block/composite/BlockActionMenu'; import BlockActionMenu from '../event-block/composite/BlockActionMenu';
import { EventItemActions } from '../RundownEntry'; import { EventItemActions } from '../RundownEntry';
@@ -3,11 +3,11 @@ import { Draggable } from 'react-beautiful-dnd';
import { Button, HStack } from '@chakra-ui/react'; import { Button, HStack } from '@chakra-ui/react';
import { IoCheckmark } from '@react-icons/all-files/io5/IoCheckmark'; import { IoCheckmark } from '@react-icons/all-files/io5/IoCheckmark';
import { IoReorderTwo } from '@react-icons/all-files/io5/IoReorderTwo'; import { IoReorderTwo } from '@react-icons/all-files/io5/IoReorderTwo';
import { OntimeDelay, OntimeEvent } from 'ontime-types';
import DelayInput from '../../../common/components/input/delay-input/DelayInput'; import DelayInput from '../../../common/components/input/delay-input/DelayInput';
import { useEventAction } from '../../../common/hooks/useEventAction'; import { useEventAction } from '../../../common/hooks/useEventAction';
import { millisToMinutes } from '../../../common/utils/dateConfig'; import { millisToMinutes } from '../../../common/utils/dateConfig';
import { OntimeDelay, OntimeEvent } from '../../../common/models/EventTypes';
import { cx } from '../../../common/utils/styleUtils'; import { cx } from '../../../common/utils/styleUtils';
import BlockActionMenu from '../event-block/composite/BlockActionMenu'; import BlockActionMenu from '../event-block/composite/BlockActionMenu';
import { EventItemActions } from '../RundownEntry'; import { EventItemActions } from '../RundownEntry';
@@ -15,10 +15,10 @@ import { EventItemActions } from '../RundownEntry';
import style from './DelayBlock.module.scss'; import style from './DelayBlock.module.scss';
interface DelayBlockProps { interface DelayBlockProps {
data: OntimeDelay, data: OntimeDelay;
index: number; index: number;
hasCursor: boolean; hasCursor: boolean;
actionHandler: (action: EventItemActions, payload?: number | { field: keyof OntimeEvent, value: unknown }) => void; actionHandler: (action: EventItemActions, payload?: number | { field: keyof OntimeEvent; value: unknown }) => void;
} }
export default function DelayBlock(props: DelayBlockProps) { export default function DelayBlock(props: DelayBlockProps) {
@@ -30,7 +30,7 @@ export default function DelayBlock(props: DelayBlockProps) {
if (hasCursor) { if (hasCursor) {
onFocusRef?.current?.focus(); onFocusRef?.current?.focus();
} }
}, [hasCursor]) }, [hasCursor]);
const applyDelayHandler = useCallback(() => { const applyDelayHandler = useCallback(() => {
applyDelay(data.id); applyDelay(data.id);
@@ -48,10 +48,7 @@ export default function DelayBlock(props: DelayBlockProps) {
[data.id, updateEvent], [data.id, updateEvent],
); );
const blockClasses = cx([ const blockClasses = cx([style.delay, hasCursor ? style.hasCursor : null]);
style.delay,
hasCursor ? style.hasCursor : null,
]);
const delayValue = data.duration != null ? millisToMinutes(data.duration) : undefined; const delayValue = data.duration != null ? millisToMinutes(data.duration) : undefined;
@@ -62,17 +59,9 @@ export default function DelayBlock(props: DelayBlockProps) {
<span className={style.drag} {...provided.dragHandleProps} ref={onFocusRef}> <span className={style.drag} {...provided.dragHandleProps} ref={onFocusRef}>
<IoReorderTwo /> <IoReorderTwo />
</span> </span>
<DelayInput <DelayInput value={delayValue} submitHandler={delaySubmitHandler} />
value={delayValue}
submitHandler={delaySubmitHandler}
/>
<HStack spacing='8px' className={style.actionOverlay}> <HStack spacing='8px' className={style.actionOverlay}>
<Button <Button onClick={applyDelayHandler} size='sm' leftIcon={<IoCheckmark />} variant='ontime-subtle-white'>
onClick={applyDelayHandler}
size='sm'
leftIcon={<IoCheckmark />}
variant='ontime-subtle-white'
>
Apply delay Apply delay
</Button> </Button>
<BlockActionMenu showAdd enableDelete actionHandler={actionHandler} /> <BlockActionMenu showAdd enableDelete actionHandler={actionHandler} />
@@ -10,14 +10,14 @@ import { IoReload } from '@react-icons/all-files/io5/IoReload';
import { IoRemoveCircle } from '@react-icons/all-files/io5/IoRemoveCircle'; import { IoRemoveCircle } from '@react-icons/all-files/io5/IoRemoveCircle';
import { IoRemoveCircleOutline } from '@react-icons/all-files/io5/IoRemoveCircleOutline'; import { IoRemoveCircleOutline } from '@react-icons/all-files/io5/IoRemoveCircleOutline';
import { IoReorderTwo } from '@react-icons/all-files/io5/IoReorderTwo'; import { IoReorderTwo } from '@react-icons/all-files/io5/IoReorderTwo';
import { useAtom } from 'jotai';
import { Playback } from 'ontime-types';
import { editorEventId } from '../../../common/atoms/LocalEventSettings'; import { editorEventId } from '../../../common/atoms/LocalEventSettings';
import TooltipActionBtn from '../../../common/components/buttons/TooltipActionBtn'; import TooltipActionBtn from '../../../common/components/buttons/TooltipActionBtn';
import { cx, getAccessibleColour } from '../../../common/utils/styleUtils';
import { useAtom } from 'jotai';
import { useEventAction } from '../../../common/hooks/useEventAction'; import { useEventAction } from '../../../common/hooks/useEventAction';
import { setEventPlayback } from '../../../common/hooks/useSocket'; import { setEventPlayback } from '../../../common/hooks/useSocket';
import { Playback } from '../../../common/models/OntimeTypes'; import { cx, getAccessibleColour } from '../../../common/utils/styleUtils';
import { tooltipDelayMid } from '../../../ontimeConfig'; import { tooltipDelayMid } from '../../../ontimeConfig';
import { EventItemActions } from '../RundownEntry'; import { EventItemActions } from '../RundownEntry';
@@ -129,11 +129,7 @@ export default function EventBlock(props: EventBlockProps) {
return ( return (
<Draggable key={eventId} draggableId={eventId} index={index}> <Draggable key={eventId} draggableId={eventId} index={index}>
{(provided) => ( {(provided) => (
<div <div className={blockClasses} {...provided.draggableProps} ref={provided.innerRef}>
className={blockClasses}
{...provided.draggableProps}
ref={provided.innerRef}
>
<div <div
className={style.binder} className={style.binder}
style={{ ...binderColours }} style={{ ...binderColours }}
@@ -206,22 +202,13 @@ export default function EventBlock(props: EventBlockProps) {
<div className={selected ? style.progressBg : `${style.progressBg} ${style.hidden}`}> <div className={selected ? style.progressBg : `${style.progressBg} ${style.hidden}`}>
<EventBlockProgressBar playback={playback} /> <EventBlockProgressBar playback={playback} />
</div> </div>
<div className={style.eventStatus} tabIndex={-1} <div className={style.eventStatus} tabIndex={-1}>
> <Tooltip label='Next event' isDisabled={!next} {...tooltipProps}>
<Tooltip
label='Next event'
isDisabled={!next}
{...tooltipProps}
>
<span> <span>
<IoPlaySkipForward <IoPlaySkipForward className={`${style.statusIcon} ${next ? style.active : ''}`} />
className={`${style.statusIcon} ${next ? style.active : ''}`} />
</span> </span>
</Tooltip> </Tooltip>
<Tooltip <Tooltip label={`${isPublic ? 'Event is public' : 'Event is private'}`} {...tooltipProps}>
label={`${isPublic ? 'Event is public' : 'Event is private'}`}
{...tooltipProps}
>
<span> <span>
<IoPeople className={`${style.statusIcon} ${isPublic ? style.active : ''}`} /> <IoPeople className={`${style.statusIcon} ${isPublic ? style.active : ''}`} />
</span> </span>
@@ -234,7 +221,7 @@ export default function EventBlock(props: EventBlockProps) {
variant='ontime-subtle-white' variant='ontime-subtle-white'
size='sm' size='sm'
icon={<IoOptions />} icon={<IoOptions />}
clickHandler={() => setOpenId((prev) => prev === eventId ? null : eventId)} clickHandler={() => setOpenId((prev) => (prev === eventId ? null : eventId))}
tooltip='Event options' tooltip='Event options'
aria-label='Event options' aria-label='Event options'
tabIndex={-1} tabIndex={-1}
@@ -1,5 +1,6 @@
import { Playback } from 'ontime-types';
import { useTimer } from '../../../../common/hooks/useSocket'; import { useTimer } from '../../../../common/hooks/useSocket';
import { Playback } from '../../../../common/models/OntimeTypes';
import { clamp } from '../../../../common/utils/math'; import { clamp } from '../../../../common/utils/math';
import style from './EventBlockProgressBar.module.scss'; import style from './EventBlockProgressBar.module.scss';
@@ -1,11 +1,11 @@
import { useCallback, useContext, useRef } from 'react'; import { useCallback, useContext, useRef } from 'react';
import { Button, Checkbox, Tooltip } from '@chakra-ui/react'; import { Button, Checkbox, Tooltip } from '@chakra-ui/react';
import { useAtomValue } from 'jotai';
import { SupportedEvent } from 'ontime-types';
import { defaultPublicAtom, startTimeIsLastEndAtom } from '../../../common/atoms/LocalEventSettings'; import { defaultPublicAtom, startTimeIsLastEndAtom } from '../../../common/atoms/LocalEventSettings';
import { LoggingContext } from '../../../common/context/LoggingContext'; import { LoggingContext } from '../../../common/context/LoggingContext';
import { useEventAction } from '../../../common/hooks/useEventAction'; import { useEventAction } from '../../../common/hooks/useEventAction';
import { SupportedEvent } from '../../../common/models/EventTypes';
import { useAtomValue } from 'jotai';
import { tooltipDelayMid } from '../../../ontimeConfig'; import { tooltipDelayMid } from '../../../ontimeConfig';
import style from './QuickAddBlock.module.scss'; import style from './QuickAddBlock.module.scss';
@@ -19,13 +19,7 @@ interface QuickAddBlockProps {
} }
export default function QuickAddBlock(props: QuickAddBlockProps) { export default function QuickAddBlock(props: QuickAddBlockProps) {
const { const { showKbd, eventId, previousEventId, disableAddDelay = true, disableAddBlock } = props;
showKbd,
eventId,
previousEventId,
disableAddDelay = true,
disableAddBlock,
} = props;
const { addEvent } = useEventAction(); const { addEvent } = useEventAction();
const { emitError } = useContext(LoggingContext); const { emitError } = useContext(LoggingContext);
const startTimeIsLastEnd = useAtomValue(startTimeIsLastEndAtom); const startTimeIsLastEnd = useAtomValue(startTimeIsLastEndAtom);
@@ -33,45 +27,47 @@ export default function QuickAddBlock(props: QuickAddBlockProps) {
const doStartTime = useRef<HTMLInputElement | null>(null); const doStartTime = useRef<HTMLInputElement | null>(null);
const doPublic = useRef<HTMLInputElement | null>(null); const doPublic = useRef<HTMLInputElement | null>(null);
const handleCreateEvent = useCallback((eventType: SupportedEvent) => { const handleCreateEvent = useCallback(
switch (eventType) { (eventType: SupportedEvent) => {
case 'event': { switch (eventType) {
const isPublicOption = doPublic?.current?.checked; case 'event': {
const startTimeIsLastEndOption = doStartTime?.current?.checked; const isPublicOption = doPublic?.current?.checked;
const startTimeIsLastEndOption = doStartTime?.current?.checked;
const newEvent = { type: SupportedEvent.Event }; const newEvent = { type: SupportedEvent.Event };
const options = { const options = {
defaultPublic: isPublicOption, defaultPublic: isPublicOption,
startTimeIsLastEnd: startTimeIsLastEndOption, startTimeIsLastEnd: startTimeIsLastEndOption,
lastEventId: previousEventId, lastEventId: previousEventId,
after: eventId, after: eventId,
}; };
addEvent(newEvent, options); addEvent(newEvent, options);
break; break;
}
case 'delay': {
const options = {
lastEventId: previousEventId,
after: eventId,
} }
addEvent({ type: SupportedEvent.Delay }, options); case 'delay': {
break; const options = {
} lastEventId: previousEventId,
case 'block': { after: eventId,
const options= { };
lastEventId: previousEventId, addEvent({ type: SupportedEvent.Delay }, options);
after: eventId, break;
}
case 'block': {
const options = {
lastEventId: previousEventId,
after: eventId,
};
addEvent({ type: SupportedEvent.Block }, options);
break;
}
default: {
emitError(`Cannot create unknown event type: ${eventType}`);
break;
} }
addEvent({ type: SupportedEvent.Block }, options);
break;
} }
default: { },
emitError(`Cannot create unknown event type: ${eventType}`); [previousEventId, eventId, addEvent, emitError],
break; );
}
}
}, [previousEventId, eventId, addEvent, emitError]);
return ( return (
<div className={style.quickAdd}> <div className={style.quickAdd}>
@@ -110,20 +106,10 @@ export default function QuickAddBlock(props: QuickAddBlockProps) {
</Tooltip> </Tooltip>
</div> </div>
<div className={style.options}> <div className={style.options}>
<Checkbox <Checkbox ref={doStartTime} size='sm' variant='ontime-ondark' defaultChecked={startTimeIsLastEnd}>
ref={doStartTime}
size='sm'
variant='ontime-ondark'
defaultChecked={startTimeIsLastEnd}
>
Start time is last end Start time is last end
</Checkbox> </Checkbox>
<Checkbox <Checkbox ref={doPublic} size='sm' variant='ontime-ondark' defaultChecked={defaultPublic}>
ref={doPublic}
size='sm'
variant='ontime-ondark'
defaultChecked={defaultPublic}
>
Event is public Event is public
</Checkbox> </Checkbox>
</div> </div>
@@ -10,7 +10,7 @@ import PropTypes from 'prop-types';
import { TableSettingsContext } from '../../common/context/TableSettingsContext'; import { TableSettingsContext } from '../../common/context/TableSettingsContext';
import useFullscreen from '../../common/hooks/useFullscreen'; import useFullscreen from '../../common/hooks/useFullscreen';
import { useTimer } from '../../common/hooks/useSocket'; import { useTimer } from '../../common/hooks/useSocket';
import useEvent from '../../common/hooks-query/useEvent'; import useEventData from '../../common/hooks-query/useEventData';
import { formatDisplay, millisToSeconds } from '../../common/utils/dateConfig'; import { formatDisplay, millisToSeconds } from '../../common/utils/dateConfig';
import { formatTime } from '../../common/utils/time'; import { formatTime } from '../../common/utils/time';
import { tooltipDelayFast } from '../../ontimeConfig'; import { tooltipDelayFast } from '../../ontimeConfig';
@@ -24,7 +24,7 @@ export default function TableHeader({ handleCSVExport, featureData }) {
useContext(TableSettingsContext); useContext(TableSettingsContext);
const { data: timer } = useTimer(); const { data: timer } = useTimer();
const { isFullScreen, toggleFullScreen } = useFullscreen(); const { isFullScreen, toggleFullScreen } = useFullscreen();
const { data: event } = useEvent(); const { data: event } = useEventData();
const selected = !featureData.numEvents const selected = !featureData.numEvents
? 'No events' ? 'No events'
@@ -3,7 +3,7 @@ import { useEffect, useMemo, useState } from 'react';
import { useMessageControl } from '../../common/hooks/useSocket'; import { useMessageControl } from '../../common/hooks/useSocket';
import useSubscription from '../../common/hooks/useSubscription'; import useSubscription from '../../common/hooks/useSubscription';
import useEvent from '../../common/hooks-query/useEvent'; import useEventData from '../../common/hooks-query/useEventData';
import useRundown from '../../common/hooks-query/useRundown'; import useRundown from '../../common/hooks-query/useRundown';
import useViewSettings from '../../common/hooks-query/useViewSettings'; import useViewSettings from '../../common/hooks-query/useViewSettings';
import socket from '../../common/utils/socket'; import socket from '../../common/utils/socket';
@@ -11,13 +11,13 @@ import socket from '../../common/utils/socket';
const withSocket = (Component) => { const withSocket = (Component) => {
return (props) => { return (props) => {
const { data: eventsData } = useRundown(); const { data: eventsData } = useRundown();
const { data: genData } = useEvent(); const { data: genData } = useEventData();
const { data: viewSettings } = useViewSettings(); const { data: viewSettings } = useViewSettings();
const { data: messages } = useMessageControl(); const { data: messageControl } = useMessageControl();
const [publicSelectedId, setPublicSelectedId] = useState(null); const [publicSelectedId, setPublicSelectedId] = useState(null);
const [timer] = useSubscription('ontime-timer', { const [timer] = useSubscription('timer', {
clock: null, clock: null,
current: null, current: null,
elapsed: null , elapsed: null ,
@@ -123,9 +123,9 @@ const withSocket = (Component) => {
return ( return (
<Component <Component
{...props} {...props}
pres={messages.presenter} pres={messageControl.messages.presenter}
publ={messages.public} publ={messageControl.messages.public}
lower={messages.lower} lower={messageControl.messages.lower}
title={titleManager} title={titleManager}
publicTitle={publicTitleManager} publicTitle={publicTitleManager}
time={TimeManagerType} time={TimeManagerType}
@@ -136,7 +136,7 @@ const withSocket = (Component) => {
viewSettings={viewSettings} viewSettings={viewSettings}
nextId={nextId} nextId={nextId}
general={genData} general={genData}
onAir={messages.onAir} onAir={messageControl.onAir}
/> />
); );
}; };
@@ -1,21 +1,21 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { ViewSettings } from 'ontime-types';
import { overrideStylesURL } from '../../../common/api/apiConstants'; import { overrideStylesURL } from '../../../common/api/apiConstants';
import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings'; import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { TimeManagerType } from '../../../common/models/TimeManager.type'; import { TimeManagerType } from '../../../common/models/TimeManager.type';
import { ViewSettingsType } from '../../../common/models/ViewSettings.type'; import { OverridableOptions } from '../../../common/models/View.types';
import { OverridableOptions } from '../../../common/models/ViewTypes';
import { formatTime } from '../../../common/utils/time'; import { formatTime } from '../../../common/utils/time';
import './Clock.scss'; import './Clock.scss';
interface ClockProps { interface ClockProps {
time: TimeManagerType; time: TimeManagerType;
viewSettings: ViewSettingsType; viewSettings: ViewSettings;
} }
const formatOptions = { const formatOptions = {
@@ -1,13 +1,13 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { OntimeEvent, OntimeRundownEntry, SupportedEvent } from 'ontime-types';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { overrideStylesURL } from '../../../common/api/apiConstants'; import { overrideStylesURL } from '../../../common/api/apiConstants';
import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings'; import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { OntimeEvent, OntimeRundownEntry, SupportedEvent } from '../../../common/models/EventTypes';
import { formatDisplay, millisToSeconds } from '../../../common/utils/dateConfig'; import { formatDisplay, millisToSeconds } from '../../../common/utils/dateConfig';
import getDelayTo from '../../../common/utils/getDelayTo'; import getDelayTo from '../../../common/utils/getDelayTo';
import { formatTime } from '../../../common/utils/time'; import { formatTime } from '../../../common/utils/time';
@@ -1,7 +1,7 @@
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import { OntimeEvent, OntimeRundownEntry, SupportedEvent } from 'ontime-types';
import Empty from '../../../common/components/state/Empty'; import Empty from '../../../common/components/state/Empty';
import { OntimeEvent, OntimeRundownEntry, SupportedEvent } from '../../../common/models/EventTypes';
import { formatTime } from '../../../common/utils/time'; import { formatTime } from '../../../common/utils/time';
import { sanitiseTitle } from './countdown.helpers'; import { sanitiseTitle } from './countdown.helpers';
@@ -1,4 +1,5 @@
import { OntimeEvent } from '../../../common/models/EventTypes'; import { OntimeEvent } from 'ontime-types';
import { TimeManagerType } from '../../../common/models/TimeManager.type'; import { TimeManagerType } from '../../../common/models/TimeManager.type';
export enum TimerMessage { export enum TimerMessage {
@@ -1,25 +1,23 @@
import { useEffect } from 'react'; import { useEffect } from 'react';
import { useSearchParams } from 'react-router-dom'; import { useSearchParams } from 'react-router-dom';
import { EventDataType } from 'common/models/EventData.type';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { EventData, Message, ViewSettings } from 'ontime-types';
import { overrideStylesURL } from '../../../common/api/apiConstants'; import { overrideStylesURL } from '../../../common/api/apiConstants';
import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings'; import { mirrorViewersAtom } from '../../../common/atoms/ViewerSettings';
import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu'; import NavigationMenu from '../../../common/components/navigation-menu/NavigationMenu';
import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet'; import { useRuntimeStylesheet } from '../../../common/hooks/useRuntimeStylesheet';
import { PresenterMessageType } from '../../../common/models/PresenterMessage.type';
import { TimeManagerType } from '../../../common/models/TimeManager.type'; import { TimeManagerType } from '../../../common/models/TimeManager.type';
import { ViewSettingsType } from '../../../common/models/ViewSettings.type'; import { OverridableOptions } from '../../../common/models/View.types';
import { OverridableOptions } from '../../../common/models/ViewTypes';
import { formatDisplay, millisToSeconds } from '../../../common/utils/dateConfig'; import { formatDisplay, millisToSeconds } from '../../../common/utils/dateConfig';
import './MinimalTimer.scss'; import './MinimalTimer.scss';
interface MinimalTimerProps { interface MinimalTimerProps {
pres: PresenterMessageType; pres: Message;
time: TimeManagerType; time: TimeManagerType;
viewSettings: ViewSettingsType; viewSettings: ViewSettings;
general: EventDataType; general: EventData;
} }
export default function MinimalTimer(props: MinimalTimerProps) { export default function MinimalTimer(props: MinimalTimerProps) {
+3 -5
View File
@@ -14,7 +14,7 @@ import { OSCSettings } from 'ontime-types';
// Import Routes // Import Routes
import { router as rundownRouter } from './routes/rundownRouter.js'; import { router as rundownRouter } from './routes/rundownRouter.js';
import { router as eventRouter } from './routes/eventRouter.js'; import { router as eventDataRouter } from './routes/eventDataRouter.js';
import { router as ontimeRouter } from './routes/ontimeRouter.js'; import { router as ontimeRouter } from './routes/ontimeRouter.js';
import { router as playbackRouter } from './routes/playbackRouter.js'; import { router as playbackRouter } from './routes/playbackRouter.js';
@@ -54,7 +54,7 @@ app.use(express.json({ limit: '1mb' }));
// Implement route endpoints // Implement route endpoints
app.use('/eventlist', rundownRouter); app.use('/eventlist', rundownRouter);
app.use('/event', eventRouter); app.use('/eventdata', eventDataRouter);
app.use('/ontime', ontimeRouter); app.use('/ontime', ontimeRouter);
app.use('/playback', playbackRouter); app.use('/playback', playbackRouter);
@@ -100,7 +100,7 @@ enum OntimeStartOrder {
} }
let step = OntimeStartOrder.InitDB; let step = OntimeStartOrder.InitDB;
const checkStart = (currentState) => { const checkStart = (currentState: OntimeStartOrder) => {
if (step !== currentState) { if (step !== currentState) {
step = OntimeStartOrder.Error; step = OntimeStartOrder.Error;
throw new Error('Init order error: startDb > startServer > startOsc > startIntegrations'); throw new Error('Init order error: startDb > startServer > startOsc > startIntegrations');
@@ -131,8 +131,6 @@ export const startServer = async () => {
expressServer.listen(serverPort, '0.0.0.0'); expressServer.listen(serverPort, '0.0.0.0');
socketServer.initServer(expressServer); socketServer.initServer(expressServer);
socketServer.info('SERVER', 'Socket initialised');
socketServer.info('SERVER', returnMessage); socketServer.info('SERVER', returnMessage);
socketServer.startListener(); socketServer.startListener();
@@ -2,6 +2,8 @@
* Class Event Provider is a mediator for handling the local db * Class Event Provider is a mediator for handling the local db
* and adds logic specific to ontime data * and adds logic specific to ontime data
*/ */
import { EventData, ViewSettings } from 'ontime-types';
import { data, db } from '../../modules/loadDb.js'; import { data, db } from '../../modules/loadDb.js';
import { safeMerge } from './DataProvider.utils.js'; import { safeMerge } from './DataProvider.utils.js';
@@ -10,14 +12,14 @@ export class DataProvider {
return data; return data;
} }
static async setEventData(newData) { static async setEventData(newData: EventData) {
data.event = { ...data.event, ...newData }; data.eventData = { ...data.eventData, ...newData };
await this.persist(); await this.persist();
return data.event; return data.eventData;
} }
static getEventData() { static getEventData() {
return data.event; return data.eventData;
} }
static async setRundown(newData) { static async setRundown(newData) {
@@ -40,7 +42,6 @@ export class DataProvider {
} }
static async deleteEvent(eventId) { static async deleteEvent(eventId) {
// @ts-expect-error -- this will go away once we type db
data.rundown = Array.from(data.rundown).filter((e) => e.id !== eventId); data.rundown = Array.from(data.rundown).filter((e) => e.id !== eventId);
await this.persist(); await this.persist();
} }
@@ -128,12 +129,12 @@ export class DataProvider {
return { ...data.userFields }; return { ...data.userFields };
} }
static getViews() { static getViewSettings() {
return { ...data.views }; return { ...data.viewSettings };
} }
static async setViews(newData) { static async setViewSettings(newData: ViewSettings) {
data.views = { ...newData }; data.viewSettings = { ...newData };
await this.persist(); await this.persist();
} }
@@ -158,8 +159,9 @@ export class DataProvider {
static async mergeIntoData(newData) { static async mergeIntoData(newData) {
const mergedData = safeMerge(data, newData); const mergedData = safeMerge(data, newData);
data.event = mergedData.event; data.eventData = mergedData.event;
data.settings = mergedData.settings; data.settings = mergedData.settings;
data.viewSettings = mergedData.viewSettings;
data.osc = mergedData.osc; data.osc = mergedData.osc;
data.http = mergedData.http; data.http = mergedData.http;
data.aliases = mergedData.aliases; data.aliases = mergedData.aliases;
@@ -4,15 +4,13 @@
* @param {object} newData * @param {object} newData
*/ */
export function safeMerge(existing, newData) { export function safeMerge(existing, newData) {
const { rundown, event, settings, osc, http, aliases, userFields } = newData || {}; const { rundown, event, settings, viewSettings, osc, http, aliases, userFields } = newData || {};
return { return {
...existing, ...existing,
rundown: rundown ?? existing.rundown, rundown: rundown ?? existing.rundown,
event: { ...existing.event, ...event }, event: { ...existing.event, ...event },
settings: { ...existing.settings, ...settings }, settings: { ...existing.settings, ...settings },
views: { viewSettings: { ...existing.viewSettings, ...viewSettings },
overrideStyles: false,
},
aliases: aliases ?? existing.aliases, aliases: aliases ?? existing.aliases,
userFields: { userFields: {
...existing.userFields, ...existing.userFields,
@@ -1,12 +1,34 @@
import { DataProvider } from '../data-provider/DataProvider.ts'; import { DataProvider } from '../data-provider/DataProvider.js';
import { getRollTimers } from '../../services/rollUtils.js'; import { getRollTimers } from '../../services/rollUtils.js';
import { eventStore } from '../../stores/EventStore.js';
let instance; let instance;
type TitleBlock = {
titleNow: string | null;
subtitleNow: string | null;
presenterNow: string | null;
noteNow: string | null;
titleNext: string | null;
subtitleNext: string | null;
presenterNext: string | null;
noteNext: string | null;
};
/** /**
* Manages business logic around loading events * Manages business logic around loading events
*/ */
export class EventLoader { export class EventLoader {
loadedEvent: object | null;
numEvents: number | null;
selectedEventIndex: number | null;
selectedEventId: string | null;
selectedPublicEventId: string | null;
nextEventId: string | null;
nextPublicEventId: string | null;
titles: TitleBlock;
titlesPublic: TitleBlock;
constructor() { constructor() {
if (instance) { if (instance) {
throw new Error('There can be only one'); throw new Error('There can be only one');
@@ -14,7 +36,7 @@ export class EventLoader {
// eslint-disable-next-line @typescript-eslint/no-this-alias -- this logic is used to ensure singleton // eslint-disable-next-line @typescript-eslint/no-this-alias -- this logic is used to ensure singleton
instance = this; instance = this;
this.reset(); this.reset(false);
this.loadedEvent = null; this.loadedEvent = null;
} }
@@ -119,11 +141,7 @@ export class EventLoader {
*/ */
findNext() { findNext() {
const timedEvents = EventLoader.getPlayableEvents(); const timedEvents = EventLoader.getPlayableEvents();
if ( if (timedEvents === null || !timedEvents.length || this.selectedEventIndex === this.numEvents - 1) {
timedEvents === null ||
!timedEvents.length ||
this.selectedEventIndex === this.numEvents - 1
) {
return null; return null;
} }
@@ -145,15 +163,8 @@ export class EventLoader {
return null; return null;
} }
const { const { nowIndex, timers, timeToNext, nextEvent, nextPublicEvent, currentEvent, currentPublicEvent } =
nowIndex, getRollTimers(timedEvents, timeNow);
timers,
timeToNext,
nextEvent,
nextPublicEvent,
currentEvent,
currentPublicEvent,
} = getRollTimers(timedEvents, timeNow);
this.loadedEvent = currentEvent; this.loadedEvent = currentEvent;
this.selectedEventIndex = nowIndex; this.selectedEventIndex = nowIndex;
@@ -187,7 +198,10 @@ export class EventLoader {
}; };
} }
reset() { /**
* Resets instance state
*/
reset(emit?: boolean) {
this.loadedEvent = null; this.loadedEvent = null;
this.selectedEventIndex = null; this.selectedEventIndex = null;
this.selectedEventId = null; this.selectedEventId = null;
@@ -209,10 +223,17 @@ export class EventLoader {
titleNow: null, titleNow: null,
subtitleNow: null, subtitleNow: null,
presenterNow: null, presenterNow: null,
noteNow: null,
titleNext: null, titleNext: null,
subtitleNext: null, subtitleNext: null,
presenterNext: null, presenterNext: null,
noteNext: null,
}; };
// workaround for socket not being ready in constructor
if (emit) {
this._loadEvent();
}
} }
/** /**
@@ -236,16 +257,26 @@ export class EventLoader {
this._loadTitlesNow(event, playableEvents); this._loadTitlesNow(event, playableEvents);
this._loadTitlesNext(playableEvents); this._loadTitlesNext(playableEvents);
this._loadEvent();
return this.getLoaded(); return this.getLoaded();
} }
/**
* Handle side effects from event loading
*/
private _loadEvent() {
eventStore.set('titles', this.titles);
eventStore.set('titlesPublic', this.titlesPublic);
}
/** /**
* @description loads given title (now) * @description loads given title (now)
* @private * @private
* @param {object} event * @param {object} event
* @param {array} rundown * @param {array} rundown
*/ */
_loadTitlesNow(event, rundown) { private _loadTitlesNow(event, rundown) {
// private title is always current // private title is always current
// check if current is also public // check if current is also public
if (event.isPublic) { if (event.isPublic) {
@@ -276,8 +307,7 @@ export class EventLoader {
* @description look for next titles to load * @description look for next titles to load
* @private * @private
*/ */
_loadTitlesNext(rundown) { private _loadTitlesNext(rundown) {
// Todo: is there a scenario where this gets called without an event?
// maybe there is nothing to load // maybe there is nothing to load
if (this.selectedEventIndex === null) return; if (this.selectedEventIndex === null) return;
@@ -324,7 +354,7 @@ export class EventLoader {
* @param type * @param type
* @private * @private
*/ */
_loadThisTitles(event, type) { private _loadThisTitles(event, type) {
if (!event) { if (!event) {
return; return;
} }
@@ -336,6 +366,7 @@ export class EventLoader {
this.titlesPublic.titleNow = event.title; this.titlesPublic.titleNow = event.title;
this.titlesPublic.subtitleNow = event.subtitle; this.titlesPublic.subtitleNow = event.subtitle;
this.titlesPublic.presenterNow = event.presenter; this.titlesPublic.presenterNow = event.presenter;
this.titlesPublic.noteNow = event.note;
this.selectedPublicEventId = event.id; this.selectedPublicEventId = event.id;
// private // private
@@ -350,6 +381,7 @@ export class EventLoader {
this.titlesPublic.titleNow = event.title; this.titlesPublic.titleNow = event.title;
this.titlesPublic.subtitleNow = event.subtitle; this.titlesPublic.subtitleNow = event.subtitle;
this.titlesPublic.presenterNow = event.presenter; this.titlesPublic.presenterNow = event.presenter;
this.titlesPublic.noteNow = event.note;
this.selectedPublicEventId = event.id; this.selectedPublicEventId = event.id;
break; break;
@@ -367,6 +399,7 @@ export class EventLoader {
this.titlesPublic.titleNext = event.title; this.titlesPublic.titleNext = event.title;
this.titlesPublic.subtitleNext = event.subtitle; this.titlesPublic.subtitleNext = event.subtitle;
this.titlesPublic.presenterNext = event.presenter; this.titlesPublic.presenterNext = event.presenter;
this.titlesPublic.noteNext = event.note;
this.nextPublicEventId = event.id; this.nextPublicEventId = event.id;
// private // private
@@ -381,6 +414,7 @@ export class EventLoader {
this.titlesPublic.titleNext = event.title; this.titlesPublic.titleNext = event.title;
this.titlesPublic.subtitleNext = event.subtitle; this.titlesPublic.subtitleNext = event.subtitle;
this.titlesPublic.presenterNext = event.presenter; this.titlesPublic.presenterNext = event.presenter;
this.titlesPublic.noteNext = event.note;
this.nextPublicEventId = event.id; this.nextPublicEventId = event.id;
break; break;
@@ -1,104 +0,0 @@
let instance;
class MessageService {
constructor() {
if (instance) {
throw new Error('There can be only one');
}
// eslint-disable-next-line @typescript-eslint/no-this-alias -- this logic is used to ensure singleton
instance = this;
this.socket = null;
this.presenter = {
text: '',
visible: false,
};
this.public = {
text: '',
visible: false,
};
this.lower = {
text: '',
visible: false,
};
this.onAir = false;
}
/**
* @description sets message on stage timer screen
* @param payload {string}
*/
setTimerText(payload) {
this.presenter.text = payload;
return this.getAll();
}
/**
* @description sets message visibility on stage timer screen
* @param status {boolean}
*/
setTimerVisibility(status) {
this.presenter.visible = status;
return this.getAll();
}
/**
* @description sets message on public screen
* @param payload {string}
*/
setPublicText(payload) {
this.public.text = payload;
return this.getAll();
}
/**
* @description sets message visibility on public screen
* @param status {boolean}
*/
setPublicVisibility(status) {
this.public.visible = status;
return this.getAll();
}
/**
* @description sets message on lower third screen
* @param payload {string}
*/
setLowerText(payload) {
this.lower.text = payload;
return this.getAll();
}
/**
* @description sets message visibility on lower third screen
* @param status {boolean}
*/
setLowerVisibility(status) {
this.lower.visible = status;
return this.getAll();
}
/**
* @description set state of onAir
* @param status {boolean}
*/
setOnAir(status) {
this.onAir = status;
return this.getAll();
}
/**
* @description Returns feature data
*/
getAll() {
return {
presenter: this.presenter,
public: this.public,
lower: this.lower,
onAir: this.onAir,
};
}
}
export const messageManager = new MessageService();
@@ -0,0 +1,109 @@
import { MessageControl } from 'ontime-types';
import { eventStore } from '../../stores/EventStore.js';
let instance;
class MessageService {
messages: MessageControl;
onAir: boolean;
constructor() {
if (instance) {
throw new Error('There can be only one');
}
// eslint-disable-next-line @typescript-eslint/no-this-alias -- this logic is used to ensure singleton
instance = this;
this.messages = {
presenter: {
text: '',
visible: false,
},
public: {
text: '',
visible: false,
},
lower: {
text: '',
visible: false,
},
};
this.onAir = false;
}
/**
* @description sets message on stage timer screen
*/
setTimerText(payload: string) {
this.messages.presenter.text = payload;
eventStore.set('feat-messagecontrol', { messages: this.messages });
return this.getAll();
}
/**
* @description sets message visibility on stage timer screen
*/
setTimerVisibility(status: boolean) {
this.messages.presenter.visible = status;
eventStore.set('feat-messagecontrol', { messages: this.messages });
return this.getAll();
}
/**
* @description sets message on public screen
*/
setPublicText(payload: string) {
this.messages.public.text = payload;
eventStore.set('feat-messagecontrol', { messages: this.messages });
return this.getAll();
}
/**
* @description sets message visibility on public screen
*/
setPublicVisibility(status: boolean) {
this.messages.public.visible = status;
eventStore.set('feat-messagecontrol', { messages: this.messages });
return this.getAll();
}
/**
* @description sets message on lower third screen
*/
setLowerText(payload: string) {
this.messages.lower.text = payload;
eventStore.set('feat-messagecontrol', { messages: this.messages });
return this.getAll();
}
/**
* @description sets message visibility on lower third screen
*/
setLowerVisibility(status: boolean) {
this.messages.lower.visible = status;
eventStore.set('feat-messagecontrol', { messages: this.messages });
return this.getAll();
}
/**
* @description set state of onAir
*/
setOnAir(status: boolean) {
this.onAir = status;
return this.getAll();
}
/**
* @description Returns feature data
*/
getAll() {
return {
messages: this.messages,
onAir: this.onAir,
};
}
}
export const messageManager = new MessageService();
@@ -6,8 +6,7 @@ import { stringFromMillis } from '../../utils/time.js';
import { messageManager } from '../message-manager/MessageManager.js'; import { messageManager } from '../message-manager/MessageManager.js';
import { PlaybackService } from '../../services/PlaybackService.js'; import { PlaybackService } from '../../services/PlaybackService.js';
import { ADDRESS_MESSAGE_CONTROL } from './socketConfig.js'; import { eventTimer, TimerService } from '../../services/TimerService.js';
import { eventTimer, TimerService } from '../../services/TimerService.ts';
import { EventLoader, eventLoader } from '../event-loader/EventLoader.js'; import { EventLoader, eventLoader } from '../event-loader/EventLoader.js';
class SocketController { class SocketController {
@@ -55,9 +54,7 @@ class SocketController {
// keep track of connections // keep track of connections
this.numClients++; this.numClients++;
this._clientNames[socket.id] = getRandomName(); this._clientNames[socket.id] = getRandomName();
const message = `${this.numClients} Clients with new connection: ${ const message = `${this.numClients} Clients with new connection: ${this._clientNames[socket.id]}`;
this._clientNames[socket.id]
}`;
this.info('CLIENT', message); this.info('CLIENT', message);
// Todo: review in favour of features // Todo: review in favour of features
@@ -78,9 +75,7 @@ class SocketController {
*/ */
socket.on('disconnect', () => { socket.on('disconnect', () => {
this.numClients--; this.numClients--;
const message = `${this.numClients} Clients with disconnection: ${ const message = `${this.numClients} Clients with disconnection: ${this._clientNames[socket.id]}`;
this._clientNames[socket.id]
}`;
delete this._clientNames[socket.id]; delete this._clientNames[socket.id];
this.info('CLIENT', message); this.info('CLIENT', message);
}); });
@@ -215,12 +210,10 @@ class SocketController {
try { try {
const featureData = messageManager.setOnAir(data); const featureData = messageManager.setOnAir(data);
this.info('PLAYBACK', featureData.onAir ? 'Going On Air' : 'Going Off Air'); this.info('PLAYBACK', featureData.onAir ? 'Going On Air' : 'Going Off Air');
this.socket.emit(ADDRESS_MESSAGE_CONTROL, featureData);
} catch (error) { } catch (error) {
this.error('RX', `Failed to parse message ${data} : ${error}`); this.error('RX', `Failed to parse message ${data} : ${error}`);
} }
} }
this.send('onAir', messageManager.onAir);
}); });
// Presenter message // Presenter message
@@ -228,16 +221,14 @@ class SocketController {
if (typeof data !== 'string') { if (typeof data !== 'string') {
return; return;
} }
const featureData = messageManager.setTimerText(data); messageManager.setTimerText(data);
this.socket.emit(ADDRESS_MESSAGE_CONTROL, featureData);
}); });
socket.on('set-timer-message-visible', (data) => { socket.on('set-timer-message-visible', (data) => {
if (typeof data !== 'boolean') { if (typeof data !== 'boolean') {
return; return;
} }
const featureData = messageManager.setTimerVisibility(data); messageManager.setTimerVisibility(data);
this.socket.emit(ADDRESS_MESSAGE_CONTROL, featureData);
}); });
/*******************************************/ /*******************************************/
@@ -246,16 +237,14 @@ class SocketController {
if (typeof data !== 'string') { if (typeof data !== 'string') {
return; return;
} }
const featureData = messageManager.setPublicText(data); messageManager.setPublicText(data);
this.socket.emit(ADDRESS_MESSAGE_CONTROL, featureData);
}); });
socket.on('set-public-message-visible', (data) => { socket.on('set-public-message-visible', (data) => {
if (typeof data !== 'boolean') { if (typeof data !== 'boolean') {
return; return;
} }
const featureData = messageManager.setPublicVisibility(data); messageManager.setPublicVisibility(data);
this.socket.emit(ADDRESS_MESSAGE_CONTROL, featureData);
}); });
/*******************************************/ /*******************************************/
@@ -264,16 +253,14 @@ class SocketController {
if (typeof data !== 'string') { if (typeof data !== 'string') {
return; return;
} }
const featureData = messageManager.setLowerText(data); messageManager.setLowerText(data);
this.socket.emit(ADDRESS_MESSAGE_CONTROL, featureData);
}); });
socket.on('set-lower-message-visible', (data) => { socket.on('set-lower-message-visible', (data) => {
if (typeof data !== 'boolean') { if (typeof data !== 'boolean') {
return; return;
} }
const featureData = messageManager.setLowerVisibility(data); messageManager.setLowerVisibility(data);
this.socket.emit(ADDRESS_MESSAGE_CONTROL, featureData);
}); });
/* MOLECULAR ENDPOINTS /* MOLECULAR ENDPOINTS
@@ -312,8 +299,9 @@ class SocketController {
}); });
// 6. TIMER // 6. TIMER
socket.on('get-ontime-timer', () => { socket.on('get-timer', () => {
this.broadcastTimer(); // TODO: Not ideal workaround
socket.emit('timer', eventTimer.timer);
}); });
}); });
} }
@@ -373,7 +361,7 @@ class SocketController {
*/ */
broadcastFeatureMessageControl() { broadcastFeatureMessageControl() {
const featureData = messageManager.getAll(); const featureData = messageManager.getAll();
this.send(ADDRESS_MESSAGE_CONTROL, featureData); this.send('feat-messagecontrol', featureData);
} }
/** /**
@@ -416,21 +404,14 @@ class SocketController {
this.send('feat-cuesheet', featureData); this.send('feat-cuesheet', featureData);
} }
/** // TODO: ouch, services should update the store
* Broadcast Timer feature // make middleware to maintain the features OR remove the feature endpoints
*/
broadcastTimer() {
const featureData = eventTimer.timer;
this.send('ontime-timer', featureData);
}
broadcastState() { broadcastState() {
this.broadcastFeatureRundown(); this.broadcastFeatureRundown();
this.broadcastFeatureMessageControl(); this.broadcastFeatureMessageControl();
this.broadcastFeaturePlaybackControl(); this.broadcastFeaturePlaybackControl();
this.broadcastFeatureInfo(); this.broadcastFeatureInfo();
this.broadcastFeatureCuesheet(); this.broadcastFeatureCuesheet();
this.broadcastTimer();
} }
/** /**
@@ -1 +0,0 @@
export const ADDRESS_MESSAGE_CONTROL = 'feat-messagecontrol';
+2 -5
View File
@@ -4,7 +4,6 @@ import { OSCSettings } from 'ontime-types';
import { PlaybackService } from '../services/PlaybackService.js'; import { PlaybackService } from '../services/PlaybackService.js';
import { messageManager } from '../classes/message-manager/MessageManager.js'; import { messageManager } from '../classes/message-manager/MessageManager.js';
import { socketProvider } from '../classes/socket/SocketController.js'; import { socketProvider } from '../classes/socket/SocketController.js';
import { ADDRESS_MESSAGE_CONTROL } from '../classes/socket/socketConfig.js';
let oscServer = null; let oscServer = null;
@@ -47,13 +46,11 @@ export const initiateOSC = (config: OSCSettings) => {
switch (path.toLowerCase()) { switch (path.toLowerCase()) {
case 'onair': { case 'onair': {
const featureData = messageManager.setOnAir(true); messageManager.setOnAir(true);
socketProvider.send(ADDRESS_MESSAGE_CONTROL, featureData);
break; break;
} }
case 'offair': { case 'offair': {
const featureData = messageManager.setOnAir(false); messageManager.setOnAir(false);
socketProvider.send(ADDRESS_MESSAGE_CONTROL, featureData);
break; break;
} }
case 'play': { case 'play': {
@@ -2,18 +2,18 @@ import fs from 'fs';
import { networkInterfaces } from 'os'; import { networkInterfaces } from 'os';
import { generateId } from 'ontime-utils'; import { generateId } from 'ontime-utils';
import { fileHandler } from '../utils/parser.js'; import { fileHandler } from '../utils/parser.js';
import { DataProvider } from '../classes/data-provider/DataProvider.ts'; import { DataProvider } from '../classes/data-provider/DataProvider.js';
import { failEmptyObjects, failIsNotArray } from '../utils/routerUtils.js'; import { failEmptyObjects, failIsNotArray } from '../utils/routerUtils.js';
import { mergeObject } from '../utils/parserUtils.js'; import { mergeObject } from '../utils/parserUtils.js';
import { PlaybackService } from '../services/PlaybackService.js'; import { PlaybackService } from '../services/PlaybackService.js';
import { runtimeState } from '../stores/EventStore.js'; import { eventStore } from '../stores/EventStore.js';
import { resolveDbPath } from '../setup.js'; import { resolveDbPath } from '../setup.js';
// Create controller for GET request to '/ontime/poll' // Create controller for GET request to '/ontime/poll'
// Returns data for current state // Returns data for current state
export const poll = async (req, res) => { export const poll = async (req, res) => {
try { try {
const s = runtimeState.poll(); const s = eventStore.poll();
res.status(200).send(s); res.status(200).send(s);
} catch (error) { } catch (error) {
res.status(500).send({ res.status(500).send({
@@ -225,7 +225,7 @@ export const postSettings = async (req, res) => {
* @method GET * @method GET
*/ */
export const getViewSettings = async (req, res) => { export const getViewSettings = async (req, res) => {
const views = DataProvider.getViews(); const views = DataProvider.getViewSettings();
res.status(200).send(views); res.status(200).send(views);
}; };
@@ -240,7 +240,7 @@ export const postViewSettings = async (req, res) => {
try { try {
const newData = { overrideStyles: req.body.overrideStyles }; const newData = { overrideStyles: req.body.overrideStyles };
await DataProvider.setViews(newData); await DataProvider.setViewSettings(newData);
res.status(200).send(newData); res.status(200).send(newData);
} catch (error) { } catch (error) {
res.status(400).send(error); res.status(400).send(error);
@@ -7,7 +7,7 @@ import {
deleteEvent, deleteEvent,
editEvent, editEvent,
reorderEvent, reorderEvent,
} from '../services/RundownService.js'; } from '../services/RundownService.ts';
// Create controller for GET request to '/eventlist' // Create controller for GET request to '/eventlist'
// Returns - // Returns -
+1 -4
View File
@@ -3,10 +3,7 @@ import { startDb, startIntegrations, startOSCServer, startServer } from './app.j
async function startOntime() { async function startOntime() {
try { try {
await startDb(); await startDb();
await startServer();
const loaded = await startServer();
console.log(loaded);
await startOSCServer(); await startOSCServer();
await startIntegrations(); await startIntegrations();
} catch (error) { } catch (error) {
+2 -2
View File
@@ -2,7 +2,7 @@ import { DatabaseModel } from 'ontime-types';
export const dbModel: DatabaseModel = { export const dbModel: DatabaseModel = {
rundown: [], rundown: [],
event: { eventData: {
title: '', title: '',
publicUrl: '', publicUrl: '',
publicInfo: '', publicInfo: '',
@@ -18,7 +18,7 @@ export const dbModel: DatabaseModel = {
pinCode: null, pinCode: null,
timeFormat: '24', timeFormat: '24',
}, },
views: { viewSettings: {
overrideStyles: false, overrideStyles: false,
}, },
aliases: [], aliases: [],
@@ -2,8 +2,8 @@ import express from 'express';
export const router = express.Router(); export const router = express.Router();
// import event controller // import event controller
import { getEvent, postEvent } from '../controllers/eventController.js'; import { getEvent, postEvent } from '../controllers/eventDataController.js';
import { eventSanitizer } from '../controllers/eventController.validate.js'; import { eventSanitizer } from '../controllers/eventDataController.validate.js';
// create route between controller and 'GET /event' endpoint // create route between controller and 'GET /event' endpoint
router.get('/', getEvent); router.get('/', getEvent);
+3 -8
View File
@@ -3,7 +3,8 @@
*/ */
import { socketProvider } from '../classes/socket/SocketController.js'; import { socketProvider } from '../classes/socket/SocketController.js';
import { eventLoader, EventLoader } from '../classes/event-loader/EventLoader.js'; import { eventLoader, EventLoader } from '../classes/event-loader/EventLoader.js';
import { eventTimer, TimerService } from './TimerService.ts'; import { eventTimer, TimerService } from './TimerService.js';
import { eventStore } from '../stores/EventStore.js';
/** /**
* Service manages playback status of app * Service manages playback status of app
@@ -26,7 +27,7 @@ export class PlaybackService {
eventTimer.load(event); eventTimer.load(event);
success = true; success = true;
} }
socketProvider.broadcastState(); eventStore.broadcast();
return success; return success;
} }
@@ -123,7 +124,6 @@ export class PlaybackService {
const newState = eventTimer.playback; const newState = eventTimer.playback;
socketProvider.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`); socketProvider.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`);
} }
socketProvider.broadcastState();
} }
/** /**
@@ -135,7 +135,6 @@ export class PlaybackService {
const newState = eventTimer.playback; const newState = eventTimer.playback;
socketProvider.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`); socketProvider.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`);
} }
socketProvider.broadcastState();
} }
/** /**
@@ -148,7 +147,6 @@ export class PlaybackService {
const newState = eventTimer.playback; const newState = eventTimer.playback;
socketProvider.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`); socketProvider.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`);
} }
socketProvider.broadcastState();
} }
/** /**
@@ -158,7 +156,6 @@ export class PlaybackService {
if (eventLoader.selectedEventId) { if (eventLoader.selectedEventId) {
this.loadById(eventLoader.selectedEventId); this.loadById(eventLoader.selectedEventId);
} }
socketProvider.broadcastState();
} }
/** /**
@@ -186,7 +183,6 @@ export class PlaybackService {
const newState = eventTimer.playback; const newState = eventTimer.playback;
socketProvider.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`); socketProvider.info('PLAYBACK', `Play Mode ${newState.toUpperCase()}`);
socketProvider.broadcastState();
} }
} }
@@ -200,6 +196,5 @@ export class PlaybackService {
eventTimer.delay(delayInMs); eventTimer.delay(delayInMs);
socketProvider.info('PLAYBACK', `Added ${delayTime} min delay`); socketProvider.info('PLAYBACK', `Added ${delayTime} min delay`);
} }
socketProvider.broadcastState();
} }
} }
@@ -1,21 +1,16 @@
import { OntimeBaseEvent, OntimeBlock, OntimeDelay, OntimeEvent } from 'ontime-types';
import { generateId } from 'ontime-utils'; import { generateId } from 'ontime-utils';
import { DataProvider } from '../classes/data-provider/DataProvider.ts'; import { DataProvider } from '../classes/data-provider/DataProvider.js';
import { import { block as blockDef, delay as delayDef, event as eventDef } from '../models/eventsDefinition.js';
block as blockDef,
delay as delayDef,
event as eventDef,
} from '../models/eventsDefinition.js';
import { MAX_EVENTS } from '../settings.js'; import { MAX_EVENTS } from '../settings.js';
import { EventLoader, eventLoader } from '../classes/event-loader/EventLoader.js'; import { EventLoader, eventLoader } from '../classes/event-loader/EventLoader.js';
import { eventTimer } from './TimerService.ts'; import { eventTimer } from './TimerService.js';
import { socketProvider } from '../classes/socket/SocketController.js'; import { eventStore } from '../stores/EventStore.js';
/** /**
* * Checks if a list of IDs is in the current selection
* @param affectedIds
* @returns boolean
*/ */
const affectedLoaded = (affectedIds) => { const affectedLoaded = (affectedIds: string[]) => {
const now = eventLoader.selectedEventId; const now = eventLoader.selectedEventId;
const nowPublic = eventLoader.selectedPublicEventId; const nowPublic = eventLoader.selectedPublicEventId;
const next = eventLoader.nextEventId; const next = eventLoader.nextEventId;
@@ -28,6 +23,9 @@ const affectedLoaded = (affectedIds) => {
); );
}; };
/**
* Checks if timer replaces the loaded next
*/
const isNewNext = () => { const isNewNext = () => {
const timedEvents = EventLoader.getTimedEvents(); const timedEvents = EventLoader.getTimedEvents();
const now = eventLoader.selectedEventId; const now = eventLoader.selectedEventId;
@@ -66,10 +64,9 @@ const isNewNext = () => {
}; };
/** /**
* updates timer object * Updates timer object
* @param {array} [affectedIds]
*/ */
export function updateTimer(affectedIds) { export function updateTimer(affectedIds?: string[]) {
const runningEventId = eventLoader.selectedEventId; const runningEventId = eventLoader.selectedEventId;
if (runningEventId === null) { if (runningEventId === null) {
@@ -121,18 +118,18 @@ export async function addEvent(eventData) {
throw new Error(`ERROR: Reached limit number of ${MAX_EVENTS} events`); throw new Error(`ERROR: Reached limit number of ${MAX_EVENTS} events`);
} }
let newEvent = {}; let newEvent: Partial<OntimeBaseEvent> = {};
const id = generateId(); const id = generateId();
switch (eventData.type) { switch (eventData.type) {
case 'event': case 'event':
newEvent = { ...eventDef, ...eventData, id }; newEvent = { ...eventDef, ...eventData, id } as Partial<OntimeEvent>;
break; break;
case 'delay': case 'delay':
newEvent = { ...delayDef, ...eventData, id }; newEvent = { ...delayDef, ...eventData, id } as Partial<OntimeDelay>;
break; break;
case 'block': case 'block':
newEvent = { ...blockDef, ...eventData, id }; newEvent = { ...blockDef, ...eventData, id } as Partial<OntimeBlock>;
break; break;
} }
@@ -148,7 +145,7 @@ export async function addEvent(eventData) {
throw new Error(error); throw new Error(error);
} }
updateTimer([id]); updateTimer([id]);
socketProvider.broadcastState(); eventStore.broadcast();
return newEvent; return newEvent;
} }
@@ -160,7 +157,7 @@ export async function editEvent(eventData) {
} }
const newEvent = await DataProvider.updateEventById(eventId, eventData); const newEvent = await DataProvider.updateEventById(eventId, eventData);
updateTimer([eventId]); updateTimer([eventId]);
socketProvider.broadcastState(); eventStore.broadcast();
return newEvent; return newEvent;
} }
@@ -172,7 +169,7 @@ export async function editEvent(eventData) {
export async function deleteEvent(eventId) { export async function deleteEvent(eventId) {
await DataProvider.deleteEvent(eventId); await DataProvider.deleteEvent(eventId);
updateTimer([eventId]); updateTimer([eventId]);
socketProvider.broadcastState(); eventStore.broadcast();
} }
/** /**
@@ -182,7 +179,7 @@ export async function deleteEvent(eventId) {
export async function deleteAllEvents() { export async function deleteAllEvents() {
await DataProvider.clearRundown(); await DataProvider.clearRundown();
updateTimer(); updateTimer();
socketProvider.broadcastState(); eventStore.broadcast();
} }
/** /**
@@ -263,5 +260,5 @@ export async function applyDelay(eventId) {
// update rundown // update rundown
await DataProvider.setRundown(rundown); await DataProvider.setRundown(rundown);
updateTimer(); updateTimer();
socketProvider.broadcastState(); eventStore.broadcast();
} }
+15 -15
View File
@@ -1,6 +1,6 @@
import { TimerLifeCycle } from 'ontime-types'; import { TimerLifeCycle } from 'ontime-types';
import { runtimeState } from '../stores/EventStore.js'; import { eventStore } from '../stores/EventStore.js';
import { PlaybackService } from './PlaybackService.js'; import { PlaybackService } from './PlaybackService.js';
import { updateRoll } from './rollUtils.js'; import { updateRoll } from './rollUtils.js';
import { DAY_TO_MS } from '../utils/time.js'; import { DAY_TO_MS } from '../utils/time.js';
@@ -9,9 +9,9 @@ import { integrationService } from './integration-service/IntegrationService.js'
export class TimerService { export class TimerService {
private readonly _interval: NodeJS.Timer; private readonly _interval: NodeJS.Timer;
private playback: string; playback: string;
private loadedTimerId: null; loadedTimerId: null;
private _pausedInterval: number; private _pausedInterval: number;
private _pausedAt: number | null; private _pausedAt: number | null;
private _secondaryTarget: number | null; private _secondaryTarget: number | null;
@@ -163,8 +163,8 @@ export class TimerService {
* @private * @private
*/ */
_onLoad() { _onLoad() {
runtimeState.set('playback', this.playback); eventStore.set('playback', this.playback);
runtimeState.set('ontime-timer', this.timer); eventStore.set('timer', this.timer);
integrationService.dispatch(TimerLifeCycle.onLoad); integrationService.dispatch(TimerLifeCycle.onLoad);
} }
@@ -198,8 +198,8 @@ export class TimerService {
* @private * @private
*/ */
_onStart() { _onStart() {
runtimeState.set('playback', this.playback); eventStore.set('playback', this.playback);
runtimeState.set('ontime-timer', this.timer); eventStore.set('timer', this.timer);
integrationService.dispatch(TimerLifeCycle.onStart); integrationService.dispatch(TimerLifeCycle.onStart);
} }
@@ -215,8 +215,8 @@ export class TimerService {
} }
_onPause() { _onPause() {
runtimeState.set('playback', this.playback); eventStore.set('playback', this.playback);
runtimeState.set('ontime-timer', this.timer); eventStore.set('timer', this.timer);
integrationService.dispatch(TimerLifeCycle.onPause); integrationService.dispatch(TimerLifeCycle.onPause);
} }
@@ -230,8 +230,8 @@ export class TimerService {
} }
_onStop() { _onStop() {
runtimeState.set('playback', this.playback); eventStore.set('playback', this.playback);
runtimeState.set('ontime-timer', this.timer); eventStore.set('timer', this.timer);
integrationService.dispatch(TimerLifeCycle.onStop); integrationService.dispatch(TimerLifeCycle.onStop);
} }
@@ -318,14 +318,14 @@ export class TimerService {
} }
_onUpdate() { _onUpdate() {
runtimeState.set('playback', this.playback); eventStore.set('playback', this.playback);
runtimeState.set('ontime-timer', this.timer); eventStore.set('timer', this.timer);
integrationService.dispatch(TimerLifeCycle.onUpdate); integrationService.dispatch(TimerLifeCycle.onUpdate);
} }
_onFinish() { _onFinish() {
runtimeState.set('playback', this.playback); eventStore.set('playback', this.playback);
runtimeState.set('ontime-timer', this.timer); eventStore.set('timer', this.timer);
integrationService.dispatch(TimerLifeCycle.onFinish); integrationService.dispatch(TimerLifeCycle.onFinish);
} }
@@ -1,5 +1,5 @@
import IIntegration, { TimerLifeCycleKey } from './IIntegration.js'; import IIntegration, { TimerLifeCycleKey } from './IIntegration.js';
import { runtimeState } from '../../stores/EventStore.js'; import { eventStore } from '../../stores/EventStore.js';
class IntegrationService { class IntegrationService {
private integrations: IIntegration[]; private integrations: IIntegration[];
@@ -17,7 +17,7 @@ class IntegrationService {
} }
dispatch(action: TimerLifeCycleKey) { dispatch(action: TimerLifeCycleKey) {
const state = runtimeState.poll(); const state = eventStore.poll();
this.integrations.forEach((integration) => { this.integrations.forEach((integration) => {
integration.dispatch(action, state); integration.dispatch(action, state);
}); });
@@ -1,16 +1,12 @@
/** /**
* Utility variable: 24 hour in milliseconds . * Utility variable: 24 hour in milliseconds .
* @type {number}
*/ */
export const DAY_TO_MS = 86400000; export const DAY_TO_MS = 86400000;
/** /**
* @description handle events that span over midnight * handle events that span over midnight
* @param {number} start - When does the event start
* @param {number} end - When does the event end
* @returns {number} normalised time
*/ */
export const normaliseEndTime = (start, end) => (end < start ? end + DAY_TO_MS : end); export const normaliseEndTime = (start: number, end: number) => (end < start ? end + DAY_TO_MS : end);
/** /**
* @description Sorts an array of objects by given property * @description Sorts an array of objects by given property
@@ -45,15 +41,15 @@ export const replacePlaceholder = (str, values) => {
* @param timeNow * @param timeNow
* @returns {{}} * @returns {{}}
*/ */
export const getRollTimers = (rundown, timeNow) => { export const getRollTimers = (rundown, timeNow: number) => {
let nowIndex = null; // index of event now let nowIndex: number | null = null; // index of event now
let nowId = null; // id of event now let nowId: string | null = null; // id of event now
let publicIndex = null; // index of public event now let publicIndex: string | null = null; // index of public event now
let publicTime = -1; let publicTime = -1;
let nextIndex = null; // index of next event let nextIndex: number | null = null; // index of next event
let publicNextIndex = null; // index of next public event let publicNextIndex: number | null = null; // index of next public event
let timeToNext = null; // counter: time for next event let timeToNext: number | null = null; // counter: time for next event
let publicTimeToNext = null; // counter: time for next public event let publicTimeToNext: number | null = null; // counter: time for next public event
let timers = null; let timers = null;
// Order events by startTime // Order events by startTime
@@ -171,8 +167,7 @@ export const getRollTimers = (rundown, timeNow) => {
* @returns {object} object with selection variables * @returns {object} object with selection variables
*/ */
export const updateRoll = (currentTimers) => { export const updateRoll = (currentTimers) => {
const { selectedEventId, current, _finishAt, clock, secondaryTimer, _secondaryTarget } = const { selectedEventId, current, _finishAt, clock, secondaryTimer, _secondaryTarget } = currentTimers;
currentTimers;
// timers // timers
let updatedTimer = current; let updatedTimer = current;
+5 -1
View File
@@ -5,7 +5,7 @@ const store = {};
/** /**
* A runtime store that broadcasts its payload * A runtime store that broadcasts its payload
*/ */
export const runtimeState = { export const eventStore = {
get(key) { get(key) {
return store[key]; return store[key];
}, },
@@ -16,4 +16,8 @@ export const runtimeState = {
poll() { poll() {
return store; return store;
}, },
broadcast() {
socketProvider.send(store);
socketProvider.broadcastState();
},
}; };
@@ -2,7 +2,7 @@ import { vi } from 'vitest';
import { dbModel } from '../../models/dataModel.js'; import { dbModel } from '../../models/dataModel.js';
import { parseExcel, parseJson, validateEvent } from '../parser.js'; import { parseExcel, parseJson, validateEvent } from '../parser.js';
import { makeString, validateDuration } from '../parserUtils.js'; import { makeString, validateDuration } from '../parserUtils.js';
import { parseAliases, parseUserFields, parseViews } from '../parserFunctions.js'; import { parseAliases, parseUserFields, parseViewSettings } from '../parserFunctions.js';
describe('test json parser with valid def', () => { describe('test json parser with valid def', () => {
const testData = { const testData = {
@@ -829,7 +829,7 @@ describe('test views import', () => {
overrideStyles: true, overrideStyles: true,
}, },
}; };
const parsed = parseViews(testData); const parsed = parseViewSettings(testData);
expect(parsed).toStrictEqual(testData.views); expect(parsed).toStrictEqual(testData.views);
}); });
@@ -841,7 +841,7 @@ describe('test views import', () => {
version: 2, version: 2,
}, },
}; };
const parsed = parseViews(testData, true); const parsed = parseViewSettings(testData, true);
expect(parsed).toStrictEqual(dbModel.views); expect(parsed).toStrictEqual(dbModel.views);
}); });
}); });
+6 -6
View File
@@ -6,13 +6,13 @@ import { dbModel } from '../models/dataModel.js';
import { deleteFile, makeString, validateDuration } from './parserUtils.js'; import { deleteFile, makeString, validateDuration } from './parserUtils.js';
import { import {
parseAliases, parseAliases,
parseEvent, parseEventData,
parseHttp, parseHttp,
parseOsc, parseOsc,
parseRundown, parseRundown,
parseSettings, parseSettings,
parseUserFields, parseUserFields,
parseViews, parseViewSettings,
} from './parserFunctions.js'; } from './parserFunctions.js';
import { parseExcelDate } from './time.js'; import { parseExcelDate } from './time.js';
@@ -238,7 +238,7 @@ export const parseExcel = async (excelData) => {
}); });
return { return {
rundown, rundown,
event: eventData, eventData: eventData,
settings: { settings: {
app: 'ontime', app: 'ontime',
version: 2, version: 2,
@@ -265,11 +265,11 @@ export const parseJson = async (jsonData, enforce = false) => {
// parse Events // parse Events
returnData.rundown = parseRundown(jsonData); returnData.rundown = parseRundown(jsonData);
// parse Event // parse Event
returnData.event = parseEvent(jsonData, enforce); returnData.eventData = parseEventData(jsonData, enforce);
// Settings handled partially // Settings handled partially
returnData.settings = parseSettings(jsonData, enforce); returnData.settings = parseSettings(jsonData, enforce);
// View settings handled partially // View settings handled partially
returnData.views = parseViews(jsonData, enforce); returnData.viewSettings = parseViewSettings(jsonData, enforce);
// Import OSC settings if any // Import OSC settings if any
returnData.osc = parseOsc(jsonData, enforce); returnData.osc = parseOsc(jsonData, enforce);
// Import HTTP settings if any // Import HTTP settings if any
@@ -358,7 +358,7 @@ export const fileHandler = async (file) => {
const dataFromExcel = await parseExcel(excelData.data); const dataFromExcel = await parseExcel(excelData.data);
res.data = {}; res.data = {};
res.data.rundown = parseRundown(dataFromExcel); res.data.rundown = parseRundown(dataFromExcel);
res.data.event = parseEvent(dataFromExcel, true); res.data.eventData = parseEventData(dataFromExcel, true);
res.data.userFields = parseUserFields(dataFromExcel); res.data.userFields = parseUserFields(dataFromExcel);
res.message = 'success'; res.message = 'success';
} else { } else {
+20 -20
View File
@@ -64,26 +64,26 @@ export const parseRundown = (data) => {
* @param {boolean} enforce - whether to create a definition if one is missing * @param {boolean} enforce - whether to create a definition if one is missing
* @returns {object} - event object data * @returns {object} - event object data
*/ */
export const parseEvent = (data, enforce) => { export const parseEventData = (data, enforce) => {
let newEvent = {}; let newEventData = {};
if ('event' in data) { if ('eventData' in data) {
console.log('Found event data, importing...'); console.log('Found event data, importing...');
const e = data.event; const e = data.eventData;
// filter known properties and write to db // filter known properties and write to db
newEvent = { newEventData = {
...dbModel.event, ...dbModel.eventData,
title: e.title || dbModel.event.title, title: e.title || dbModel.eventData.title,
publicUrl: e.publicUrl || dbModel.event.publicUrl, publicUrl: e.publicUrl || dbModel.eventData.publicUrl,
publicInfo: e.publicInfo || dbModel.event.publicInfo, publicInfo: e.publicInfo || dbModel.eventData.publicInfo,
backstageUrl: e.backstageUrl || dbModel.event.backstageUrl, backstageUrl: e.backstageUrl || dbModel.eventData.backstageUrl,
backstageInfo: e.backstageInfo || dbModel.event.backstageInfo, backstageInfo: e.backstageInfo || dbModel.eventData.backstageInfo,
endMessage: e.endMessage || dbModel.event.endMessage, endMessage: e.endMessage || dbModel.eventData.endMessage,
}; };
} else if (enforce) { } else if (enforce) {
newEvent = { ...dbModel.event }; newEventData = { ...dbModel.eventData };
console.log(`Created event object in db`); console.log(`Created event object in db`);
} }
return newEvent; return newEventData;
}; };
/** /**
@@ -127,14 +127,14 @@ export const parseSettings = (data, enforce) => {
* @param {boolean} enforce - whether to create a definition if one is missing * @param {boolean} enforce - whether to create a definition if one is missing
* @returns {object} - event object data * @returns {object} - event object data
*/ */
export const parseViews = (data, enforce) => { export const parseViewSettings = (data, enforce) => {
let newViews = {}; let newViews = {};
if ('views' in data) { if ('viewSettings' in data) {
console.log('Found view definition, importing...'); console.log('Found view definition, importing...');
const v = data.views; const v = data.viewSettings;
const viewSettings = { const viewSettings = {
overrideStyles: v.overrideStyles ?? dbModel.views.overrideStyles, overrideStyles: v.overrideStyles ?? dbModel.viewSettings.overrideStyles,
}; };
// write to db // write to db
@@ -142,8 +142,8 @@ export const parseViews = (data, enforce) => {
...viewSettings, ...viewSettings,
}; };
} else if (enforce) { } else if (enforce) {
newViews = dbModel.views; newViews = dbModel.viewSettings;
console.log(`Created view object in db`); console.log(`Created viewSettings object in db`);
} }
return newViews; return newViews;
}; };
+160 -12
View File
@@ -7,9 +7,23 @@
"note": "Maybe a running note for the operator?", "note": "Maybe a running note for the operator?",
"timeStart": 28800000, "timeStart": 28800000,
"timeEnd": 30600000, "timeEnd": 30600000,
"timeType": "start-end",
"duration": 1800000,
"isPublic": false, "isPublic": false,
"skip": false,
"colour": "",
"user0": "",
"user1": "",
"user2": "",
"user3": "",
"user4": "",
"user5": "",
"user6": "",
"user7": "",
"user8": "",
"user9": "",
"type": "event", "type": "event",
"revision": 7, "revision": 0,
"id": "5946" "id": "5946"
}, },
{ {
@@ -19,9 +33,23 @@
"note": "", "note": "",
"timeStart": 30600000, "timeStart": 30600000,
"timeEnd": 34200000, "timeEnd": 34200000,
"timeType": "start-end",
"duration": 3600000,
"isPublic": false, "isPublic": false,
"skip": false,
"colour": "",
"user0": "",
"user1": "",
"user2": "",
"user3": "",
"user4": "",
"user5": "",
"user6": "",
"user7": "",
"user8": "",
"user9": "",
"type": "event", "type": "event",
"revision": 4, "revision": 0,
"id": "c2e7" "id": "c2e7"
}, },
{ {
@@ -31,9 +59,23 @@
"note": "", "note": "",
"timeStart": 34200000, "timeStart": 34200000,
"timeEnd": 34800000, "timeEnd": 34800000,
"timeType": "start-end",
"duration": 600000,
"isPublic": false, "isPublic": false,
"skip": false,
"colour": "",
"user0": "",
"user1": "",
"user2": "",
"user3": "",
"user4": "",
"user5": "",
"user6": "",
"user7": "",
"user8": "",
"user9": "",
"type": "event", "type": "event",
"revision": 4, "revision": 0,
"id": "cc0f" "id": "cc0f"
}, },
{ {
@@ -43,9 +85,23 @@
"note": "In green, below", "note": "In green, below",
"timeStart": 34800000, "timeStart": 34800000,
"timeEnd": 35400000, "timeEnd": 35400000,
"timeType": "start-end",
"duration": 600000,
"isPublic": false, "isPublic": false,
"skip": false,
"colour": "",
"user0": "",
"user1": "",
"user2": "",
"user3": "",
"user4": "",
"user5": "",
"user6": "",
"user7": "",
"user8": "",
"user9": "",
"type": "event", "type": "event",
"revision": 7, "revision": 0,
"id": "8ee5" "id": "8ee5"
}, },
{ {
@@ -55,15 +111,29 @@
"note": "", "note": "",
"timeStart": 0, "timeStart": 0,
"timeEnd": 600000, "timeEnd": 600000,
"timeType": "start-end",
"duration": 600000,
"isPublic": false, "isPublic": false,
"skip": false,
"colour": "",
"user0": "",
"user1": "",
"user2": "",
"user3": "",
"user4": "",
"user5": "",
"user6": "",
"user7": "",
"user8": "",
"user9": "",
"type": "event", "type": "event",
"revision": 2, "revision": 0,
"id": "8222" "id": "8222"
}, },
{ {
"duration": 900000, "duration": 900000,
"type": "delay", "type": "delay",
"revision": 1, "revision": 0,
"id": "a386" "id": "a386"
}, },
{ {
@@ -73,9 +143,23 @@
"note": "* Until a block is found", "note": "* Until a block is found",
"timeStart": 35400000, "timeStart": 35400000,
"timeEnd": 36600000, "timeEnd": 36600000,
"timeType": "start-end",
"duration": 1200000,
"isPublic": false, "isPublic": false,
"skip": false,
"colour": "",
"user0": "",
"user1": "",
"user2": "",
"user3": "",
"user4": "",
"user5": "",
"user6": "",
"user7": "",
"user8": "",
"user9": "",
"type": "event", "type": "event",
"revision": 6, "revision": 0,
"id": "6dce" "id": "6dce"
}, },
{ {
@@ -85,9 +169,23 @@
"note": "Orange and red buttons on the right", "note": "Orange and red buttons on the right",
"timeStart": 36600000, "timeStart": 36600000,
"timeEnd": 43200000, "timeEnd": 43200000,
"timeType": "start-end",
"duration": 6600000,
"isPublic": false, "isPublic": false,
"skip": false,
"colour": "",
"user0": "",
"user1": "",
"user2": "",
"user3": "",
"user4": "",
"user5": "",
"user6": "",
"user7": "",
"user8": "",
"user9": "",
"type": "event", "type": "event",
"revision": 9, "revision": 0,
"id": "2651" "id": "2651"
}, },
{ {
@@ -101,13 +199,27 @@
"note": "Blue button on the right", "note": "Blue button on the right",
"timeStart": 46800000, "timeStart": 46800000,
"timeEnd": 57600000, "timeEnd": 57600000,
"timeType": "start-end",
"duration": 10800000,
"isPublic": false, "isPublic": false,
"skip": false,
"colour": "",
"user0": "",
"user1": "",
"user2": "",
"user3": "",
"user4": "",
"user5": "",
"user6": "",
"user7": "",
"user8": "",
"user9": "",
"type": "event", "type": "event",
"revision": 6, "revision": 0,
"id": "1358" "id": "1358"
} }
], ],
"event": { "eventData": {
"title": "All about Carlos demo event", "title": "All about Carlos demo event",
"publicUrl": "www.getontime.no", "publicUrl": "www.getontime.no",
"publicInfo": "WiFi: demoproject \nPassword: ontimeproject", "publicInfo": "WiFi: demoproject \nPassword: ontimeproject",
@@ -117,17 +229,53 @@
}, },
"settings": { "settings": {
"app": "ontime", "app": "ontime",
"version": 2, "version": 1,
"serverPort": 4001, "serverPort": 4001,
"lock": null, "lock": null,
"pinCode": "1234" "pinCode": "1234"
}, },
"viewSettings": {
"overrideStyles": false
},
"osc": { "osc": {
"port": 8888, "port": 8888,
"portOut": 9999, "portOut": 9999,
"targetIP": "127.0.0.1", "targetIP": "127.0.0.1",
"enabled": true "enabled": true
}, },
"http": {
"http": {
"user": null,
"pwd": null,
"messages": {
"onLoad": {
"url": "",
"enabled": false
},
"onStart": {
"url": "",
"enabled": false
},
"onUpdate": {
"url": "",
"enabled": false
},
"onPause": {
"url": "",
"enabled": false
},
"onStop": {
"url": "",
"enabled": false
},
"onFinish": {
"url": "",
"enabled": false
}
},
"enabled": true
}
},
"aliases": [ "aliases": [
{ {
"id": "0b0b3", "id": "0b0b3",
@@ -148,4 +296,4 @@
"user8": "user8", "user8": "user8",
"user9": "user9" "user9": "user9"
} }
} }
+3 -3
View File
@@ -1,5 +1,5 @@
{ {
"events": [ "rundown": [
{ {
"title": "Welcome to Ontime", "title": "Welcome to Ontime",
"subtitle": "Subtitles are useful", "subtitle": "Subtitles are useful",
@@ -219,7 +219,7 @@
"id": "1358" "id": "1358"
} }
], ],
"event": { "eventData": {
"title": "All about Carlos demo event", "title": "All about Carlos demo event",
"publicUrl": "www.getontime.no", "publicUrl": "www.getontime.no",
"publicInfo": "WiFi: demoproject \nPassword: ontimeproject", "publicInfo": "WiFi: demoproject \nPassword: ontimeproject",
@@ -234,7 +234,7 @@
"lock": null, "lock": null,
"pinCode": "1234" "pinCode": "1234"
}, },
"views": { "viewSettings": {
"overrideStyles": false "overrideStyles": false
}, },
"osc": { "osc": {
@@ -1,12 +1,18 @@
import { Alias } from './core/Alias.type.js';
import { EventData } from './core/EventData.type.js';
import { OntimeRundown } from './core/Rundown.type.js';
import { OSCSettings } from './core/OscSettings.type.js'; import { OSCSettings } from './core/OscSettings.type.js';
import { Settings } from './core/Settings.type.js';
import { UserFields } from './core/UserFields.type.js';
import { ViewSettings } from './core/Views.type.js';
export type DatabaseModel = { export type DatabaseModel = {
rundown: any; rundown: OntimeRundown;
event: any; eventData: EventData;
settings: any; settings: Settings;
views: any; viewSettings: ViewSettings;
aliases: any; aliases: Alias[];
userFields: any; userFields: UserFields;
osc: OSCSettings; osc: OSCSettings;
http: any; http: any;
}; };
@@ -1,7 +1,5 @@
enum TimerTypeType { export enum TimerTypeType {
CountDown = 'count-down', CountDown = 'count-down',
CountUp = 'count-up', CountUp = 'count-up',
Clock = 'clock' Clock = 'clock'
} }
export default TimerTypeType
@@ -0,0 +1,5 @@
export type Alias = {
enabled: boolean;
alias: string;
pathAndParams: string;
}
@@ -0,0 +1,8 @@
export type EventData = {
title: string;
publicUrl: string;
publicInfo: string;
backstageUrl: string;
backstageInfo: string;
endMessage: string;
};
@@ -0,0 +1,47 @@
export enum SupportedEvent {
Event = 'event',
Delay = 'delay',
Block = 'block',
}
export type OntimeBaseEvent = {
type: SupportedEvent;
id: string;
after?: string; // used when creating an event to indicate its position in rundown
};
export type OntimeDelay = OntimeBaseEvent & {
type: SupportedEvent.Delay;
duration: number;
revision: number;
};
export type OntimeBlock = OntimeBaseEvent & {
type: SupportedEvent.Block;
};
export type OntimeEvent = OntimeBaseEvent & {
type: SupportedEvent.Event;
title: string;
subtitle: string;
presenter: string;
note: string;
timeType?: string;
timeStart: number;
timeEnd: number;
duration: number;
isPublic: boolean;
skip: boolean;
colour: string;
user0: string;
user1: string;
user2: string;
user3: string;
user4: string;
user5: string;
user6: string;
user7: string;
user8: string;
user9: string;
revision: number;
};
@@ -0,0 +1,4 @@
import { OntimeBlock, OntimeDelay, OntimeEvent } from './OntimeEvent.type';
export type OntimeRundownEntry = OntimeDelay | OntimeBlock | OntimeEvent;
export type OntimeRundown = OntimeRundownEntry[];
@@ -0,0 +1,10 @@
import { TimeFormat } from './TimeFormat.type';
export type Settings = {
app: 'ontime';
version: 2;
serverPort: 4001;
lock: null | boolean;
pinCode: null | number | string;
timeFormat: TimeFormat;
};
@@ -0,0 +1 @@
export type TimeFormat = '12' | '24';
@@ -0,0 +1,13 @@
export type UserFields = {
user0: string;
user1: string;
user2: string;
user3: string;
user4: string;
user5: string;
user6: string;
user7: string;
user8: string;
user9: string;
}
@@ -0,0 +1,3 @@
export type ViewSettings = {
overrideStyles: boolean;
}
@@ -0,0 +1,10 @@
export type Message = {
text: string;
visible: boolean;
};
export type MessageControl = {
presenter: Message;
public: Message;
lower: Message;
};

Some files were not shown because too many files have changed in this diff Show More