mirror of
https://github.com/cpvalente/ontime.git
synced 2026-08-15 20:33:47 +00:00
Alpha revision (#811)
* style: less space for direction button * chore: cleanup unused * refactor: prevent CSS pollution * refactor: force render on swatch * refactor: force render on swatch * refactor: improve import of blocks and skip import * chore: remove unused * chore: clarify effect * chore: updata dataset * fix: integrations not triggering * chore: remove unused * fix: onair derives from playback * fix: gap day after
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { OntimeEvent, RuntimeStore } from 'ontime-types';
|
||||
import { OntimeEvent, Playback, RuntimeStore } from 'ontime-types';
|
||||
|
||||
import { deepEqual } from 'fast-equals';
|
||||
|
||||
@@ -149,7 +149,10 @@ function broadcastResult(_target: any, _propertyKey: string, descriptor: Propert
|
||||
const hasImmediateChanges = hasNewLoaded || hasSkippedBack || justStarted || hasChangedPlayback;
|
||||
|
||||
if (hasImmediateChanges || (isTimeToUpdate && !deepEqual(TimerService.previousState?.timer, state.timer))) {
|
||||
eventStore.set('timer', state.timer);
|
||||
eventStore.batchSet({
|
||||
timer: state.timer,
|
||||
onAir: state.timer.playback !== Playback.Stop,
|
||||
});
|
||||
TimerService.previousState.timer = { ...state.timer };
|
||||
}
|
||||
|
||||
|
||||
@@ -1518,7 +1518,7 @@ describe('getRuntimeOffset()', () => {
|
||||
secondaryTimer: null,
|
||||
startedAt: null,
|
||||
},
|
||||
_timer: { pausedAt: null, secondaryTarget: null, finishedNow: false },
|
||||
_timer: { pausedAt: null, secondaryTarget: null },
|
||||
} as RuntimeState;
|
||||
|
||||
const offset = getRuntimeOffset(state);
|
||||
@@ -1570,7 +1570,7 @@ describe('getRuntimeOffset()', () => {
|
||||
secondaryTimer: null,
|
||||
startedAt: null,
|
||||
},
|
||||
_timer: { pausedAt: null, secondaryTarget: null, finishedNow: false },
|
||||
_timer: { pausedAt: null, secondaryTarget: null },
|
||||
} as RuntimeState;
|
||||
|
||||
const offset = getRuntimeOffset(state);
|
||||
@@ -1622,7 +1622,7 @@ describe('getRuntimeOffset()', () => {
|
||||
secondaryTimer: null,
|
||||
startedAt: 78000000,
|
||||
},
|
||||
_timer: { pausedAt: null, secondaryTarget: null, finishedNow: false },
|
||||
_timer: { pausedAt: null, secondaryTarget: null },
|
||||
} as RuntimeState;
|
||||
|
||||
const offset = getRuntimeOffset(state);
|
||||
@@ -1674,7 +1674,7 @@ describe('getRuntimeOffset()', () => {
|
||||
secondaryTimer: null,
|
||||
startedAt: 78000000,
|
||||
},
|
||||
_timer: { pausedAt: null, secondaryTarget: null, finishedNow: false },
|
||||
_timer: { pausedAt: null, secondaryTarget: null },
|
||||
} as RuntimeState;
|
||||
|
||||
const offset = getRuntimeOffset(state);
|
||||
|
||||
@@ -34,7 +34,7 @@ export class HttpIntegration implements IIntegration<HttpSubscription> {
|
||||
|
||||
dispatch(action: TimerLifeCycleKey, state?: object) {
|
||||
// noop
|
||||
if (!this.enabled || !action) {
|
||||
if (!this.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ export class OscIntegration implements IIntegration<OscSubscription> {
|
||||
|
||||
dispatch(action: TimerLifeCycleKey, state?: object) {
|
||||
// noop
|
||||
if (!this.oscClient || !action) {
|
||||
if (!this.oscClient) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@ class RuntimeService {
|
||||
/** Checks result of an update and notifies integrations as needed */
|
||||
checkTimerUpdate({ shouldCallRoll, hasTimerFinished }: runtimeState.UpdateResult) {
|
||||
const newState = runtimeState.getState();
|
||||
|
||||
if (hasTimerFinished) {
|
||||
integrationService.dispatch(TimerLifeCycle.onFinish);
|
||||
|
||||
@@ -37,11 +36,11 @@ class RuntimeService {
|
||||
// actions are added to the queue stack to ensure that the order of operations is maintained
|
||||
if (newState.timer.playback === Playback.Play && newState.eventNow) {
|
||||
if (newState.eventNow.endAction === EndAction.Stop) {
|
||||
setTimeout(this.stop, 0);
|
||||
setTimeout(this.stop.bind(this), 0);
|
||||
} else if (newState.eventNow.endAction === EndAction.LoadNext) {
|
||||
setTimeout(this.loadNext, 0);
|
||||
setTimeout(this.loadNext.bind(this), 0);
|
||||
} else if (newState.eventNow.endAction === EndAction.PlayNext) {
|
||||
setTimeout(this.startNext, 0);
|
||||
setTimeout(this.startNext.bind(this), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,6 @@ export type RuntimeState = {
|
||||
// private properties of the timer calculations
|
||||
_timer: {
|
||||
pausedAt: MaybeNumber;
|
||||
finishedNow: boolean;
|
||||
secondaryTarget: MaybeNumber;
|
||||
};
|
||||
};
|
||||
@@ -63,9 +62,6 @@ const runtimeState: RuntimeState = {
|
||||
_timer: {
|
||||
pausedAt: null,
|
||||
secondaryTarget: null,
|
||||
get finishedNow() {
|
||||
return this.current <= 0 && this.finishedAt === null;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
@@ -92,7 +88,6 @@ export function clear() {
|
||||
runtimeState._timer = {
|
||||
pausedAt: null,
|
||||
secondaryTarget: null,
|
||||
finishedNow: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -411,8 +406,9 @@ export function update(): UpdateResult {
|
||||
function onPlayUpdate() {
|
||||
let isFinished = false;
|
||||
runtimeState.timer.current = getCurrent(runtimeState);
|
||||
const finishedNow = runtimeState.timer.current <= 0 && runtimeState.timer.finishedAt === null;
|
||||
|
||||
if (runtimeState.timer.playback === Playback.Play && runtimeState._timer.finishedNow) {
|
||||
if (runtimeState.timer.playback === Playback.Play && finishedNow) {
|
||||
runtimeState.timer.finishedAt = runtimeState.clock;
|
||||
isFinished = true;
|
||||
} else {
|
||||
|
||||
@@ -1106,5 +1106,261 @@ describe('parseExcel()', () => {
|
||||
expect(parsedData.rundown[1]).toMatchObject(expectedParsedRundown[1]);
|
||||
});
|
||||
|
||||
it.todo('imports events and blocks, ignores otherwise', () => {});
|
||||
it('ignores unknown event types', () => {
|
||||
const testdata = [
|
||||
[
|
||||
'Time Start',
|
||||
'Time End',
|
||||
'Title',
|
||||
'End Action',
|
||||
'Timer type',
|
||||
'Public',
|
||||
'Skip',
|
||||
'Notes',
|
||||
'test0',
|
||||
'test1',
|
||||
'test2',
|
||||
'test3',
|
||||
'test4',
|
||||
'test5',
|
||||
'test6',
|
||||
'test7',
|
||||
'test8',
|
||||
'test9',
|
||||
'Colour',
|
||||
'cue',
|
||||
],
|
||||
[
|
||||
'1899-12-30T07:00:00.000Z',
|
||||
'1899-12-30T08:00:10.000Z',
|
||||
'Guest Welcome',
|
||||
'',
|
||||
'skip',
|
||||
'x',
|
||||
'',
|
||||
'Ballyhoo',
|
||||
'a0',
|
||||
'a1',
|
||||
'a2',
|
||||
'a3',
|
||||
'a4',
|
||||
'a5',
|
||||
'a6',
|
||||
'a7',
|
||||
'a8',
|
||||
'a9',
|
||||
'red',
|
||||
101,
|
||||
],
|
||||
[
|
||||
'1899-12-30T08:00:00.000Z',
|
||||
'1899-12-30T08:30:00.000Z',
|
||||
'A song from the hearth',
|
||||
'load-next',
|
||||
'clock',
|
||||
'',
|
||||
'x',
|
||||
'Rainbow chase',
|
||||
'b0',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'b5',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'#F00',
|
||||
102,
|
||||
],
|
||||
[],
|
||||
];
|
||||
|
||||
const importMap = {
|
||||
worksheet: 'event schedule',
|
||||
timeStart: 'time start',
|
||||
timeEnd: 'time end',
|
||||
duration: 'duration',
|
||||
cue: 'cue',
|
||||
title: 'title',
|
||||
isPublic: 'public',
|
||||
skip: 'skip',
|
||||
note: 'notes',
|
||||
colour: 'colour',
|
||||
endAction: 'end action',
|
||||
timerType: 'timer type',
|
||||
timeWarning: 'warning time',
|
||||
timeDanger: 'danger time',
|
||||
custom: {},
|
||||
};
|
||||
const result = parseExcel(testdata, importMap);
|
||||
expect(result.rundown.length).toBe(1);
|
||||
expect((result.rundown.at(0) as OntimeEvent).title).toBe('A song from the hearth');
|
||||
});
|
||||
it('imports blocks', () => {
|
||||
const testdata = [
|
||||
[
|
||||
'Time Start',
|
||||
'Time End',
|
||||
'Title',
|
||||
'End Action',
|
||||
'Timer type',
|
||||
'Public',
|
||||
'Skip',
|
||||
'Notes',
|
||||
'test0',
|
||||
'test1',
|
||||
'test2',
|
||||
'test3',
|
||||
'test4',
|
||||
'test5',
|
||||
'test6',
|
||||
'test7',
|
||||
'test8',
|
||||
'test9',
|
||||
'Colour',
|
||||
'cue',
|
||||
],
|
||||
[
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'block',
|
||||
'x',
|
||||
'',
|
||||
'Ballyhoo',
|
||||
'a0',
|
||||
'a1',
|
||||
'a2',
|
||||
'a3',
|
||||
'a4',
|
||||
'a5',
|
||||
'a6',
|
||||
'a7',
|
||||
'a8',
|
||||
'a9',
|
||||
'red',
|
||||
101,
|
||||
],
|
||||
[
|
||||
'1899-12-30T08:00:00.000Z',
|
||||
'1899-12-30T08:30:00.000Z',
|
||||
'A song from the hearth',
|
||||
'load-next',
|
||||
'clock',
|
||||
'',
|
||||
'x',
|
||||
'Rainbow chase',
|
||||
'b0',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'b5',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'#F00',
|
||||
102,
|
||||
],
|
||||
[],
|
||||
];
|
||||
|
||||
const importMap = {
|
||||
worksheet: 'event schedule',
|
||||
timeStart: 'time start',
|
||||
timeEnd: 'time end',
|
||||
duration: 'duration',
|
||||
cue: 'cue',
|
||||
title: 'title',
|
||||
isPublic: 'public',
|
||||
skip: 'skip',
|
||||
note: 'notes',
|
||||
colour: 'colour',
|
||||
endAction: 'end action',
|
||||
timerType: 'timer type',
|
||||
timeWarning: 'warning time',
|
||||
timeDanger: 'danger time',
|
||||
custom: {},
|
||||
};
|
||||
const result = parseExcel(testdata, importMap);
|
||||
expect(result.rundown.length).toBe(2);
|
||||
expect(result.rundown.at(0).type).toBe(SupportedEvent.Block);
|
||||
});
|
||||
|
||||
it('imports as events if there is no timer type column', () => {
|
||||
const testdata = [
|
||||
[
|
||||
'Time Start',
|
||||
'Time End',
|
||||
'Title',
|
||||
'End Action',
|
||||
'Public',
|
||||
'Skip',
|
||||
'Notes',
|
||||
'test0',
|
||||
'test1',
|
||||
'test2',
|
||||
'test3',
|
||||
'test4',
|
||||
'test5',
|
||||
'test6',
|
||||
'test7',
|
||||
'test8',
|
||||
'test9',
|
||||
'Colour',
|
||||
'cue',
|
||||
],
|
||||
['', '', '', '', 'x', '', 'Ballyhoo', 'a0', 'a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9', 'red', 101],
|
||||
[
|
||||
'1899-12-30T08:00:00.000Z',
|
||||
'1899-12-30T08:30:00.000Z',
|
||||
'A song from the hearth',
|
||||
'load-next',
|
||||
'',
|
||||
'x',
|
||||
'Rainbow chase',
|
||||
'b0',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'b5',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'#F00',
|
||||
102,
|
||||
],
|
||||
[],
|
||||
];
|
||||
|
||||
const importMap = {
|
||||
worksheet: 'event schedule',
|
||||
timeStart: 'time start',
|
||||
timeEnd: 'time end',
|
||||
duration: 'duration',
|
||||
cue: 'cue',
|
||||
title: 'title',
|
||||
isPublic: 'public',
|
||||
skip: 'skip',
|
||||
note: 'notes',
|
||||
colour: 'colour',
|
||||
endAction: 'end action',
|
||||
timerType: 'timer type',
|
||||
timeWarning: 'warning time',
|
||||
timeDanger: 'danger time',
|
||||
custom: {},
|
||||
};
|
||||
const result = parseExcel(testdata, importMap);
|
||||
expect(result.rundown.length).toBe(2);
|
||||
expect(result.rundown.at(0).type).toBe(SupportedEvent.Event);
|
||||
expect((result.rundown.at(0) as OntimeEvent).timerType).toBe(TimerType.CountDown);
|
||||
expect(result.rundown.at(1).type).toBe(SupportedEvent.Event);
|
||||
expect((result.rundown.at(1) as OntimeEvent).timerType).toBe(TimerType.CountDown);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,6 +18,7 @@ import {
|
||||
TimeStrategy,
|
||||
CustomFields,
|
||||
EventCustomFields,
|
||||
TimerType,
|
||||
} from 'ontime-types';
|
||||
|
||||
import xlsx from 'node-xlsx';
|
||||
@@ -176,23 +177,24 @@ export const parseExcel = (excelData: unknown[][], options?: Partial<ImportMap>)
|
||||
const event: any = {};
|
||||
const eventCustomFields: EventCustomFields = {};
|
||||
|
||||
row.forEach((column, j) => {
|
||||
for (let j = 0; j < row.length; j++) {
|
||||
const column = row[j];
|
||||
// 1. we check if we have set a flag for a known field
|
||||
if (j === timerTypeIndex) {
|
||||
if (column === 'block') {
|
||||
event.type = SupportedEvent.Block;
|
||||
}
|
||||
if (column === '' || isKnownTimerType(column)) {
|
||||
} else if (column === '' || isKnownTimerType(column)) {
|
||||
event.type = SupportedEvent.Event;
|
||||
event.timerType = validateTimerType(column);
|
||||
} else {
|
||||
// if it is not a block or a known type, we dont import it
|
||||
return;
|
||||
}
|
||||
// if it is not a block or a known type, we dont import it
|
||||
return;
|
||||
} else if (j === titleIndex) {
|
||||
event.title = makeString(column, '');
|
||||
// if this is a block, we have nothing else to import
|
||||
if (event.type === SupportedEvent.Block) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
} else if (j === timeStartIndex) {
|
||||
event.timeStart = parseExcelDate(column);
|
||||
@@ -225,7 +227,7 @@ export const parseExcel = (excelData: unknown[][], options?: Partial<ImportMap>)
|
||||
if (typeof column === 'string') {
|
||||
// we cant deal with empty content
|
||||
if (column.length === 0) {
|
||||
return;
|
||||
continue;
|
||||
}
|
||||
const columnText = column.toLowerCase();
|
||||
|
||||
@@ -243,11 +245,15 @@ export const parseExcel = (excelData: unknown[][], options?: Partial<ImportMap>)
|
||||
// just ignore it
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// if any data was found in row, push to array
|
||||
const keysFound = Object.keys(event).length + Object.keys(eventCustomFields).length;
|
||||
if (keysFound > 0) {
|
||||
if (timerTypeIndex === null) {
|
||||
event.timerType = TimerType.CountDown;
|
||||
event.type = SupportedEvent.Event;
|
||||
}
|
||||
rundown.push({ ...event, custom: { ...eventCustomFields } });
|
||||
}
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user