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.CountUp}>Count up</option>
<option value={TimerType.TimeToEnd}>Time to end</option>
<option value={TimerType.Clock}>Clock</option>
</Select>
<label className={style.inputLabel}>End Action</label>
@@ -1,5 +1,6 @@
import { memo, useCallback, useEffect, useState } from '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 { IoArrowUp } from '@react-icons/all-files/io5/IoArrowUp';
import { IoOptions } from '@react-icons/all-files/io5/IoOptions';
@@ -196,5 +197,8 @@ function TimerIcon(props: { type: TimerType; className: string }) {
if (type === TimerType.Clock) {
return <IoTime className={className} />;
}
if (type === TimerType.TimeToEnd) {
return <BiArrowToBottom className={className} />;
}
return <IoArrowDown className={className} />;
}
@@ -17,7 +17,7 @@ export function getTimerByType(timerObject?: TimerTypeParams): string | number |
return timer;
}
if (timerObject.timerType === TimerType.CountDown) {
if (timerObject.timerType === TimerType.CountDown || timerObject.timerType === TimerType.TimeToEnd) {
timer = timerObject.current;
} else if (timerObject.timerType === TimerType.CountUp) {
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 { eventStore } from '../stores/EventStore.js';
@@ -24,6 +24,9 @@ export class TimerService {
timer: TimerState;
loadedTimerId: string | null;
private loadedTimerStart: number | null;
private loadedTimerEnd: number | null;
private pausedTime: number;
private pausedAt: number | null;
private secondaryTarget: number | null;
@@ -61,6 +64,9 @@ export class TimerService {
endAction: null,
};
this.loadedTimerId = null;
this.loadedTimerStart = null;
this.loadedTimerEnd = null;
this.pausedTime = 0;
this.pausedAt = null;
this.secondaryTarget = null;
@@ -93,6 +99,8 @@ export class TimerService {
this.timer.duration = calculateDuration(timer.timeStart, timer.timeEnd);
this.timer.timerType = timer.timerType;
this.timer.endAction = timer.endAction;
this.loadedTimerStart = timer.timeStart;
this.loadedTimerEnd = timer.timeEnd;
// this might not be ideal
this.timer.finishedAt = null;
@@ -102,6 +110,8 @@ export class TimerService {
this.timer.duration,
this.pausedTime,
this.timer.addedTime,
this.loadedTimerEnd,
this.timer.timerType,
);
if (this.timer.startedAt === null) {
this.timer.current = this.timer.duration;
@@ -129,14 +139,22 @@ export class TimerService {
this._clear();
this.loadedTimerId = timer.id;
this.loadedTimerStart = timer.timeStart;
this.loadedTimerEnd = timer.timeEnd;
this.timer.duration = calculateDuration(timer.timeStart, timer.timeEnd);
this.timer.current = this.timer.duration;
this.playback = Playback.Armed;
this.timer.timerType = timer.timerType;
this.timer.endAction = timer.endAction;
this.pausedTime = 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') {
this.timer = { ...this.timer, ...initialData };
}
@@ -188,6 +206,8 @@ export class TimerService {
this.timer.duration,
this.pausedTime,
this.timer.addedTime,
this.loadedTimerEnd,
this.timer.timerType,
);
this._onStart();
}
@@ -310,6 +330,8 @@ export class TimerService {
this.timer.duration,
this.pausedTime,
this.timer.addedTime,
this.loadedTimerEnd,
this.timer.timerType,
);
}
this.timer.current = getCurrent(
@@ -318,6 +340,8 @@ export class TimerService {
this.timer.addedTime,
this.pausedTime,
this.timer.clock,
this.loadedTimerEnd,
this.timer.timerType,
);
this.timer.elapsed = this.timer.duration - this.timer.current;
}
@@ -1,4 +1,5 @@
import { dayInMs } from 'ontime-utils';
import { TimerType } from 'ontime-types';
import { getCurrent, getExpectedFinish } from '../timerUtils.js';
@@ -9,7 +10,17 @@ describe('getExpectedFinish()', () => {
const duration = 10;
const pausedTime = 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);
});
it('is finishedAt if defined', () => {
@@ -18,7 +29,17 @@ describe('getExpectedFinish()', () => {
const duration = 10;
const pausedTime = 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);
});
it('calculates the finish time', () => {
@@ -27,7 +48,17 @@ describe('getExpectedFinish()', () => {
const duration = 10;
const pausedTime = 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);
});
it('adds paused and added times', () => {
@@ -36,7 +67,17 @@ describe('getExpectedFinish()', () => {
const duration = 10;
const pausedTime = 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);
});
it('added time could be negative', () => {
@@ -45,7 +86,17 @@ describe('getExpectedFinish()', () => {
const duration = 10;
const pausedTime = 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);
});
it('user could add enough time for it to be negative', () => {
@@ -54,7 +105,17 @@ describe('getExpectedFinish()', () => {
const duration = 10;
const pausedTime = 0;
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);
});
it('timer can have no duration', () => {
@@ -63,7 +124,17 @@ describe('getExpectedFinish()', () => {
const duration = 0;
const pausedTime = 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);
});
it('finish can be the day after', () => {
@@ -72,9 +143,60 @@ describe('getExpectedFinish()', () => {
const duration = dayInMs;
const pausedTime = 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);
});
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()', () => {
@@ -84,7 +206,9 @@ describe('getCurrent()', () => {
const pausedTime = 0;
const addedTime = 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);
});
it('is the remaining time in clock', () => {
@@ -93,7 +217,9 @@ describe('getCurrent()', () => {
const pausedTime = 0;
const addedTime = 0;
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);
});
it('accounts for added times', () => {
@@ -102,7 +228,9 @@ describe('getCurrent()', () => {
const pausedTime = 5;
const addedTime = 5;
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);
});
it('counts over midnight', () => {
@@ -111,7 +239,9 @@ describe('getCurrent()', () => {
const pausedTime = 0;
const addedTime = 0;
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);
});
it('rolls over midnight', () => {
@@ -120,7 +250,9 @@ describe('getCurrent()', () => {
const pausedTime = 0;
const addedTime = 0;
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);
});
it('midnight holds delays', () => {
@@ -129,9 +261,46 @@ describe('getCurrent()', () => {
const pausedTime = 10;
const addedTime = 10;
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);
});
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', () => {
@@ -142,8 +311,18 @@ describe('getExpectedFinish() and getCurrentTime() combined', () => {
const pausedTime = 0;
const addedTime = 0;
const clock = 0;
const expectedFinish = getExpectedFinish(startedAt, finishedAt, duration, pausedTime, addedTime);
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock);
const endTime = 10;
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;
expect(expectedFinish).toBe(10);
expect(elapsed).toBe(0);
@@ -157,8 +336,18 @@ describe('getExpectedFinish() and getCurrentTime() combined', () => {
const pausedTime = 1;
const addedTime = 2;
const clock = 5;
const expectedFinish = getExpectedFinish(startedAt, finishedAt, duration, pausedTime, addedTime);
const current = getCurrent(startedAt, duration, addedTime, pausedTime, clock);
const endTime = 10;
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;
expect(expectedFinish).toBe(13);
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';
/**
@@ -10,6 +10,8 @@ export function getExpectedFinish(
duration: number,
pausedTime: number,
addedTime: number,
timeEnd: number,
timerType: TimerType,
) {
if (startedAt === null) {
return null;
@@ -19,6 +21,10 @@ export function getExpectedFinish(
return finishedAt;
}
if (timerType === TimerType.TimeToEnd) {
return timeEnd + addedTime + pausedTime;
}
// handle events that finish the day after
const expectedFinish = startedAt + duration + pausedTime + addedTime;
if (expectedFinish > dayInMs) {
@@ -38,11 +44,22 @@ export function getCurrent(
addedTime: number,
pausedTime: number,
clock: number,
timeEnd: number,
timerType: TimerType,
) {
if (startedAt === null) {
return null;
}
if (timerType === TimerType.TimeToEnd) {
if (startedAt > timeEnd) {
return timeEnd + addedTime + pausedTime + dayInMs - clock;
}
return timeEnd + addedTime + pausedTime - 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;
@@ -1,5 +1,6 @@
export enum TimerType {
CountDown = 'count-down',
CountUp = 'count-up',
Clock = 'clock'
TimeToEnd = 'time-to-end',
Clock = 'clock',
}