feat: count to end (#500)

This commit is contained in:
Carlos Valente
2023-08-27 19:25:40 +02:00
committed by GitHub
parent cd9dbcdc68
commit 5323e627fb
7 changed files with 259 additions and 23 deletions
@@ -130,6 +130,7 @@ const EventEditorTimes = (props: EventEditorTimesProps) => {
> >
<option value={TimerType.CountDown}>Count down</option> <option value={TimerType.CountDown}>Count down</option>
<option value={TimerType.CountUp}>Count up</option> <option value={TimerType.CountUp}>Count up</option>
<option value={TimerType.TimeToEnd}>Time to end</option>
<option value={TimerType.Clock}>Clock</option> <option value={TimerType.Clock}>Clock</option>
</Select> </Select>
<label className={style.inputLabel}>End Action</label> <label className={style.inputLabel}>End Action</label>
@@ -1,5 +1,6 @@
import { memo, useCallback, useEffect, useState } from 'react'; import { memo, useCallback, useEffect, useState } from 'react';
import { Tooltip } from '@chakra-ui/react'; import { Tooltip } from '@chakra-ui/react';
import { BiArrowToBottom } from '@react-icons/all-files/bi/BiArrowToBottom';
import { IoArrowDown } from '@react-icons/all-files/io5/IoArrowDown'; import { IoArrowDown } from '@react-icons/all-files/io5/IoArrowDown';
import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp'; import { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp';
import { IoOptions } from '@react-icons/all-files/io5/IoOptions'; import { IoOptions } from '@react-icons/all-files/io5/IoOptions';
@@ -196,5 +197,8 @@ function TimerIcon(props: { type: TimerType; className: string }) {
if (type === TimerType.Clock) { if (type === TimerType.Clock) {
return <IoTime className={className} />; return <IoTime className={className} />;
} }
if (type === TimerType.TimeToEnd) {
return <BiArrowToBottom className={className} />;
}
return <IoArrowDown className={className} />; return <IoArrowDown className={className} />;
} }
@@ -17,7 +17,7 @@ export function getTimerByType(timerObject?: TimerTypeParams): string | number |
return timer; return timer;
} }
if (timerObject.timerType === TimerType.CountDown) { if (timerObject.timerType === TimerType.CountDown || timerObject.timerType === TimerType.TimeToEnd) {
timer = timerObject.current; timer = timerObject.current;
} else if (timerObject.timerType === TimerType.CountUp) { } else if (timerObject.timerType === TimerType.CountUp) {
timer = timerObject.elapsed; timer = timerObject.elapsed;
+26 -2
View File
@@ -1,4 +1,4 @@
import { EndAction, OntimeEvent, Playback, TimerLifeCycle, TimerState } from 'ontime-types'; import { EndAction, OntimeEvent, Playback, TimerLifeCycle, TimerState, TimerType } from 'ontime-types';
import { calculateDuration, dayInMs } from 'ontime-utils'; import { calculateDuration, dayInMs } from 'ontime-utils';
import { eventStore } from '../stores/EventStore.js'; import { eventStore } from '../stores/EventStore.js';
@@ -24,6 +24,9 @@ export class TimerService {
timer: TimerState; timer: TimerState;
loadedTimerId: string | null; loadedTimerId: string | null;
private loadedTimerStart: number | null;
private loadedTimerEnd: number | null;
private pausedTime: number; private pausedTime: number;
private pausedAt: number | null; private pausedAt: number | null;
private secondaryTarget: number | null; private secondaryTarget: number | null;
@@ -61,6 +64,9 @@ export class TimerService {
endAction: null, endAction: null,
}; };
this.loadedTimerId = null; this.loadedTimerId = null;
this.loadedTimerStart = null;
this.loadedTimerEnd = null;
this.pausedTime = 0; this.pausedTime = 0;
this.pausedAt = null; this.pausedAt = null;
this.secondaryTarget = null; this.secondaryTarget = null;
@@ -93,6 +99,8 @@ export class TimerService {
this.timer.duration = calculateDuration(timer.timeStart, timer.timeEnd); this.timer.duration = calculateDuration(timer.timeStart, timer.timeEnd);
this.timer.timerType = timer.timerType; this.timer.timerType = timer.timerType;
this.timer.endAction = timer.endAction; this.timer.endAction = timer.endAction;
this.loadedTimerStart = timer.timeStart;
this.loadedTimerEnd = timer.timeEnd;
// this might not be ideal // this might not be ideal
this.timer.finishedAt = null; this.timer.finishedAt = null;
@@ -102,6 +110,8 @@ export class TimerService {
this.timer.duration, this.timer.duration,
this.pausedTime, this.pausedTime,
this.timer.addedTime, this.timer.addedTime,
this.loadedTimerEnd,
this.timer.timerType,
); );
if (this.timer.startedAt === null) { if (this.timer.startedAt === null) {
this.timer.current = this.timer.duration; this.timer.current = this.timer.duration;
@@ -129,14 +139,22 @@ export class TimerService {
this._clear(); this._clear();
this.loadedTimerId = timer.id; this.loadedTimerId = timer.id;
this.loadedTimerStart = timer.timeStart;
this.loadedTimerEnd = timer.timeEnd;
this.timer.duration = calculateDuration(timer.timeStart, timer.timeEnd); this.timer.duration = calculateDuration(timer.timeStart, timer.timeEnd);
this.timer.current = this.timer.duration;
this.playback = Playback.Armed; this.playback = Playback.Armed;
this.timer.timerType = timer.timerType; this.timer.timerType = timer.timerType;
this.timer.endAction = timer.endAction; this.timer.endAction = timer.endAction;
this.pausedTime = 0; this.pausedTime = 0;
this.pausedAt = 0; this.pausedAt = 0;
this.timer.current = this.timer.duration;
if (this.timer.timerType === TimerType.TimeToEnd) {
const now = clock.timeNow();
this.timer.current = getCurrent(now, this.timer.duration, 0, 0, now, timer.timeEnd, this.timer.timerType);
}
if (typeof initialData !== 'undefined') { if (typeof initialData !== 'undefined') {
this.timer = { ...this.timer, ...initialData }; this.timer = { ...this.timer, ...initialData };
} }
@@ -188,6 +206,8 @@ export class TimerService {
this.timer.duration, this.timer.duration,
this.pausedTime, this.pausedTime,
this.timer.addedTime, this.timer.addedTime,
this.loadedTimerEnd,
this.timer.timerType,
); );
this._onStart(); this._onStart();
} }
@@ -310,6 +330,8 @@ export class TimerService {
this.timer.duration, this.timer.duration,
this.pausedTime, this.pausedTime,
this.timer.addedTime, this.timer.addedTime,
this.loadedTimerEnd,
this.timer.timerType,
); );
} }
this.timer.current = getCurrent( this.timer.current = getCurrent(
@@ -318,6 +340,8 @@ export class TimerService {
this.timer.addedTime, this.timer.addedTime,
this.pausedTime, this.pausedTime,
this.timer.clock, this.timer.clock,
this.loadedTimerEnd,
this.timer.timerType,
); );
this.timer.elapsed = this.timer.duration - this.timer.current; this.timer.elapsed = this.timer.duration - this.timer.current;
} }
@@ -1,4 +1,5 @@
import { dayInMs } from 'ontime-utils'; import { dayInMs } from 'ontime-utils';
import { TimerType } from 'ontime-types';
import { getCurrent, getExpectedFinish } from '../timerUtils.js'; import { getCurrent, getExpectedFinish } from '../timerUtils.js';
@@ -9,7 +10,17 @@ describe('getExpectedFinish()', () => {
const duration = 10; const duration = 10;
const pausedTime = 0; const pausedTime = 0;
const addedTime = 0; const addedTime = 0;
const calculatedFinish = getExpectedFinish(startedAt, finishedAt, duration, pausedTime, addedTime); const endTime = 10;
const timerType = TimerType.CountDown;
const calculatedFinish = getExpectedFinish(
startedAt,
finishedAt,
duration,
pausedTime,
addedTime,
endTime,
timerType,
);
expect(calculatedFinish).toBe(null); expect(calculatedFinish).toBe(null);
}); });
it('is finishedAt if defined', () => { it('is finishedAt if defined', () => {
@@ -18,7 +29,17 @@ describe('getExpectedFinish()', () => {
const duration = 10; const duration = 10;
const pausedTime = 0; const pausedTime = 0;
const addedTime = 0; const addedTime = 0;
const calculatedFinish = getExpectedFinish(startedAt, finishedAt, duration, pausedTime, addedTime); const endTime = 20;
const timerType = TimerType.CountDown;
const calculatedFinish = getExpectedFinish(
startedAt,
finishedAt,
duration,
pausedTime,
addedTime,
endTime,
timerType,
);
expect(calculatedFinish).toBe(finishedAt); expect(calculatedFinish).toBe(finishedAt);
}); });
it('calculates the finish time', () => { it('calculates the finish time', () => {
@@ -27,7 +48,17 @@ describe('getExpectedFinish()', () => {
const duration = 10; const duration = 10;
const pausedTime = 0; const pausedTime = 0;
const addedTime = 0; const addedTime = 0;
const calculatedFinish = getExpectedFinish(startedAt, finishedAt, duration, pausedTime, addedTime); const endTime = 11;
const timerType = TimerType.CountDown;
const calculatedFinish = getExpectedFinish(
startedAt,
finishedAt,
duration,
pausedTime,
addedTime,
endTime,
timerType,
);
expect(calculatedFinish).toBe(11); expect(calculatedFinish).toBe(11);
}); });
it('adds paused and added times', () => { it('adds paused and added times', () => {
@@ -36,7 +67,17 @@ describe('getExpectedFinish()', () => {
const duration = 10; const duration = 10;
const pausedTime = 10; const pausedTime = 10;
const addedTime = 10; const addedTime = 10;
const calculatedFinish = getExpectedFinish(startedAt, finishedAt, duration, pausedTime, addedTime); const endTime = 11;
const timerType = TimerType.CountDown;
const calculatedFinish = getExpectedFinish(
startedAt,
finishedAt,
duration,
pausedTime,
addedTime,
endTime,
timerType,
);
expect(calculatedFinish).toBe(31); expect(calculatedFinish).toBe(31);
}); });
it('added time could be negative', () => { it('added time could be negative', () => {
@@ -45,7 +86,17 @@ describe('getExpectedFinish()', () => {
const duration = 10; const duration = 10;
const pausedTime = 10; const pausedTime = 10;
const addedTime = -10; const addedTime = -10;
const calculatedFinish = getExpectedFinish(startedAt, finishedAt, duration, pausedTime, addedTime); const endTime = 11;
const timerType = TimerType.CountDown;
const calculatedFinish = getExpectedFinish(
startedAt,
finishedAt,
duration,
pausedTime,
addedTime,
endTime,
timerType,
);
expect(calculatedFinish).toBe(11); expect(calculatedFinish).toBe(11);
}); });
it('user could add enough time for it to be negative', () => { it('user could add enough time for it to be negative', () => {
@@ -54,7 +105,17 @@ describe('getExpectedFinish()', () => {
const duration = 10; const duration = 10;
const pausedTime = 0; const pausedTime = 0;
const addedTime = -100; const addedTime = -100;
const calculatedFinish = getExpectedFinish(startedAt, finishedAt, duration, pausedTime, addedTime); const endTime = 11;
const timerType = TimerType.CountDown;
const calculatedFinish = getExpectedFinish(
startedAt,
finishedAt,
duration,
pausedTime,
addedTime,
endTime,
timerType,
);
expect(calculatedFinish).toBe(1); expect(calculatedFinish).toBe(1);
}); });
it('timer can have no duration', () => { it('timer can have no duration', () => {
@@ -63,7 +124,17 @@ describe('getExpectedFinish()', () => {
const duration = 0; const duration = 0;
const pausedTime = 0; const pausedTime = 0;
const addedTime = 0; const addedTime = 0;
const calculatedFinish = getExpectedFinish(startedAt, finishedAt, duration, pausedTime, addedTime); const endTime = 0;
const timerType = TimerType.CountDown;
const calculatedFinish = getExpectedFinish(
startedAt,
finishedAt,
duration,
pausedTime,
addedTime,
endTime,
timerType,
);
expect(calculatedFinish).toBe(1); expect(calculatedFinish).toBe(1);
}); });
it('finish can be the day after', () => { it('finish can be the day after', () => {
@@ -72,9 +143,60 @@ describe('getExpectedFinish()', () => {
const duration = dayInMs; const duration = dayInMs;
const pausedTime = 0; const pausedTime = 0;
const addedTime = 0; const addedTime = 0;
const calculatedFinish = getExpectedFinish(startedAt, finishedAt, duration, pausedTime, addedTime); const endTime = 10;
const timerType = TimerType.CountDown;
const calculatedFinish = getExpectedFinish(
startedAt,
finishedAt,
duration,
pausedTime,
addedTime,
endTime,
timerType,
);
expect(calculatedFinish).toBe(10); expect(calculatedFinish).toBe(10);
}); });
describe('on timers of type time-to-end', () => {
it('finish time is as schedule + added time', () => {
const startedAt = 10;
const finishedAt = null;
const duration = dayInMs;
const pausedTime = 0;
const addedTime = 10;
const endTime = 30;
const timerType = TimerType.TimeToEnd;
const calculatedFinish = getExpectedFinish(
startedAt,
finishedAt,
duration,
pausedTime,
addedTime,
endTime,
timerType,
);
expect(calculatedFinish).toBe(40);
});
it('handles events that finish the day after', () => {
const startedAt = 79200000; // 22:00:00
const finishedAt = null;
const duration = Infinity; // not relevant
const pausedTime = 0;
const addedTime = 0;
const endTime = 600000; // 00:10:00
const timerType = TimerType.TimeToEnd;
const calculatedFinish = getExpectedFinish(
startedAt,
finishedAt,
duration,
pausedTime,
addedTime,
endTime,
timerType,
);
// expected finish is not a duration but a timetag
expect(calculatedFinish).toBe(600000);
});
});
}); });
describe('getCurrent()', () => { describe('getCurrent()', () => {
@@ -84,7 +206,9 @@ describe('getCurrent()', () => {
const pausedTime = 0; const pausedTime = 0;
const addedTime = 0; const addedTime = 0;
const clock = 0; const clock = 0;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock); const endTime = 0;
const timerType = TimerType.CountDown;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock, endTime, timerType);
expect(current).toBe(null); expect(current).toBe(null);
}); });
it('is the remaining time in clock', () => { it('is the remaining time in clock', () => {
@@ -93,7 +217,9 @@ describe('getCurrent()', () => {
const pausedTime = 0; const pausedTime = 0;
const addedTime = 0; const addedTime = 0;
const clock = 1; const clock = 1;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock); const endTime = 10;
const timerType = TimerType.CountDown;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock, endTime, timerType);
expect(current).toBe(9); expect(current).toBe(9);
}); });
it('accounts for added times', () => { it('accounts for added times', () => {
@@ -102,7 +228,9 @@ describe('getCurrent()', () => {
const pausedTime = 5; const pausedTime = 5;
const addedTime = 5; const addedTime = 5;
const clock = 1; const clock = 1;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock); const endTime = 10;
const timerType = TimerType.CountDown;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock, endTime, timerType);
expect(current).toBe(19); expect(current).toBe(19);
}); });
it('counts over midnight', () => { it('counts over midnight', () => {
@@ -111,7 +239,9 @@ describe('getCurrent()', () => {
const pausedTime = 0; const pausedTime = 0;
const addedTime = 0; const addedTime = 0;
const clock = 10; const clock = 10;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock); const endTime = 20;
const timerType = TimerType.CountDown;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock, endTime, timerType);
expect(current).toBe(dayInMs + 10); expect(current).toBe(dayInMs + 10);
}); });
it('rolls over midnight', () => { it('rolls over midnight', () => {
@@ -120,7 +250,9 @@ describe('getCurrent()', () => {
const pausedTime = 0; const pausedTime = 0;
const addedTime = 0; const addedTime = 0;
const clock = 5; const clock = 5;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock); const endTime = 20;
const timerType = TimerType.CountDown;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock, endTime, timerType);
expect(current).toBe(15); expect(current).toBe(15);
}); });
it('midnight holds delays', () => { it('midnight holds delays', () => {
@@ -129,9 +261,46 @@ describe('getCurrent()', () => {
const pausedTime = 10; const pausedTime = 10;
const addedTime = 10; const addedTime = 10;
const clock = 5; const clock = 5;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock); const endTime = 20;
const timerType = TimerType.CountDown;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock, endTime, timerType);
expect(current).toBe(35); expect(current).toBe(35);
}); });
describe('on timers of type time-to-end', () => {
it('current time is the time to end', () => {
const startedAt = 10;
const duration = 100;
const pausedTime = 0;
const addedTime = 0;
const clock = 30;
const endTime = 100;
const timerType = TimerType.TimeToEnd;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock, endTime, timerType);
expect(current).toBe(70);
});
it('current time is the time to end + added time', () => {
const startedAt = 10;
const duration = 100;
const pausedTime = 3;
const addedTime = 4;
const clock = 30;
const endTime = 100;
const timerType = TimerType.TimeToEnd;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock, endTime, timerType);
expect(current).toBe(77);
});
it('handles events that finish the day after', () => {
const startedAt = 79200000; // 22:00:00
const duration = Infinity; // not relevant
const pausedTime = 0;
const addedTime = 0;
const clock = 79500000; // 22:05:00
const endTime = 600000; // 00:10:00
const timerType = TimerType.TimeToEnd;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock, endTime, timerType);
expect(current).toBe(600000 + dayInMs - clock);
});
});
}); });
describe('getExpectedFinish() and getCurrentTime() combined', () => { describe('getExpectedFinish() and getCurrentTime() combined', () => {
@@ -142,8 +311,18 @@ describe('getExpectedFinish() and getCurrentTime() combined', () => {
const pausedTime = 0; const pausedTime = 0;
const addedTime = 0; const addedTime = 0;
const clock = 0; const clock = 0;
const expectedFinish = getExpectedFinish(startedAt, finishedAt, duration, pausedTime, addedTime); const endTime = 10;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock); const timerType = TimerType.CountDown;
const expectedFinish = getExpectedFinish(
startedAt,
finishedAt,
duration,
pausedTime,
addedTime,
endTime,
timerType,
);
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock, endTime, timerType);
const elapsed = duration - current; const elapsed = duration - current;
expect(expectedFinish).toBe(10); expect(expectedFinish).toBe(10);
expect(elapsed).toBe(0); expect(elapsed).toBe(0);
@@ -157,8 +336,18 @@ describe('getExpectedFinish() and getCurrentTime() combined', () => {
const pausedTime = 1; const pausedTime = 1;
const addedTime = 2; const addedTime = 2;
const clock = 5; const clock = 5;
const expectedFinish = getExpectedFinish(startedAt, finishedAt, duration, pausedTime, addedTime); const endTime = 10;
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock); const timerType = TimerType.CountDown;
const expectedFinish = getExpectedFinish(
startedAt,
finishedAt,
duration,
pausedTime,
addedTime,
endTime,
timerType,
);
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock, endTime, timerType);
const elapsed = duration - current; const elapsed = duration - current;
expect(expectedFinish).toBe(13); expect(expectedFinish).toBe(13);
expect(elapsed).toBe(2); expect(elapsed).toBe(2);
+18 -1
View File
@@ -1,4 +1,4 @@
import { MaybeNumber } from 'ontime-types'; import { MaybeNumber, TimerType } from 'ontime-types';
import { dayInMs } from 'ontime-utils'; import { dayInMs } from 'ontime-utils';
/** /**
@@ -10,6 +10,8 @@ export function getExpectedFinish(
duration: number, duration: number,
pausedTime: number, pausedTime: number,
addedTime: number, addedTime: number,
timeEnd: number,
timerType: TimerType,
) { ) {
if (startedAt === null) { if (startedAt === null) {
return null; return null;
@@ -19,6 +21,10 @@ export function getExpectedFinish(
return finishedAt; return finishedAt;
} }
if (timerType === TimerType.TimeToEnd) {
return timeEnd + addedTime + pausedTime;
}
// handle events that finish the day after // handle events that finish the day after
const expectedFinish = startedAt + duration + pausedTime + addedTime; const expectedFinish = startedAt + duration + pausedTime + addedTime;
if (expectedFinish > dayInMs) { if (expectedFinish > dayInMs) {
@@ -38,11 +44,22 @@ export function getCurrent(
addedTime: number, addedTime: number,
pausedTime: number, pausedTime: number,
clock: number, clock: number,
timeEnd: number,
timerType: TimerType,
) { ) {
if (startedAt === null) { if (startedAt === null) {
return null; return null;
} }
if (timerType === TimerType.TimeToEnd) {
if (startedAt > timeEnd) {
return timeEnd + addedTime + pausedTime + dayInMs - clock;
}
return timeEnd + addedTime + pausedTime - clock;
}
if (startedAt > clock) { if (startedAt > clock) {
// we are the day after the event was started
return startedAt + duration + addedTime + pausedTime - clock - dayInMs; return startedAt + duration + addedTime + pausedTime - clock - dayInMs;
} }
return startedAt + duration + addedTime + pausedTime - clock; return startedAt + duration + addedTime + pausedTime - clock;
@@ -1,5 +1,6 @@
export enum TimerType { export enum TimerType {
CountDown = 'count-down', CountDown = 'count-down',
CountUp = 'count-up', CountUp = 'count-up',
Clock = 'clock' TimeToEnd = 'time-to-end',
Clock = 'clock',
} }