+
{title}
0) {
- if (nextStart == null || wait < nextStart) {
- nextStart = wait;
- nextIndex = index;
- }
- }
- }
+ const {
+ nowIndex,
+ nowId,
+ publicIndex,
+ nextIndex,
+ publicNextIndex,
+ timers,
+ timeToNext,
+ } = getSelectionByRoll(this._eventlist, now);
// nothing to play, unload
- if (foundNow == null && nextIndex == null) {
+ if (nowIndex === null && nextIndex === null) {
this.unload();
console.log('Roll: no events found');
return;
}
- // we found something to play next
- if (nextIndex != null) {
- const e = this._eventlist[nextIndex];
- this._loadThisTitles(e, 'next');
+ // there is something running, load
+ if (nowIndex !== null) {
+ // clear secondary timers
+ this.secondaryTimer = null;
+ this._secondaryTarget = null;
- if (foundNow == null) {
+ // set timers
+ this._startedAt = timers._startedAt;
+ this._finishAt = timers._finishAt;
+ this.duration = timers.duration;
+ this.current = timers.current;
+
+ // set selection
+ this.selectedEventId = nowId;
+ this.selectedEventIndex = nowIndex;
+ }
+
+ // found something to run next
+ if (nextIndex != null) {
+ // Set running timers
+ if (nowIndex === null) {
// only warn the first time
- if (this.secondaryTimer == null)
+ if (this.secondaryTimer === null)
console.log('Roll: waiting for event start');
// reset running timer
+ // ??? should this not have been reset?
this.current = null;
- // timer counts to nextStart
- this.secondaryTimer = nextStart;
- this._secondaryTarget = e.timeStart;
+ // timer counts to next event
+ this.secondaryTimer = timeToNext;
+ this._secondaryTarget = this._eventlist[nextIndex].timeStart;
}
+
+ // TITLES: Load next private
+ this._loadThisTitles(this._eventlist[nextIndex], 'next-private');
+ }
+
+ // TITLES: Load next public
+ if (publicNextIndex !== null) {
+ this._loadThisTitles(this._eventlist[publicNextIndex], 'next-public');
+ }
+
+ // TITLES: Load now private
+ if (nowIndex !== null) {
+ this._loadThisTitles(this._eventlist[nowIndex], 'now-private');
+ }
+ // TITLES: Load now public
+ if (publicIndex !== null) {
+ this._loadThisTitles(this._eventlist[publicIndex], 'now-public');
}
}
diff --git a/server/src/classes/__tests__/classUtils.test.js b/server/src/classes/__tests__/classUtils.test.js
new file mode 100644
index 000000000..fa666372d
--- /dev/null
+++ b/server/src/classes/__tests__/classUtils.test.js
@@ -0,0 +1,244 @@
+import { getSelectionByRoll, sortArrayByProperty } from '../classUtils.js';
+
+// test sortArrayByProperty()
+describe('sort simple arrays of objects', () => {
+ it('sort array 1-5', () => {
+ const arr1 = [
+ { timeStart: 1 },
+ { timeStart: 5 },
+ { timeStart: 3 },
+ { timeStart: 2 },
+ { timeStart: 4 },
+ ];
+
+ const arr1Expected = [
+ { timeStart: 1 },
+ { timeStart: 2 },
+ { timeStart: 3 },
+ { timeStart: 4 },
+ { timeStart: 5 },
+ ];
+
+ const sorted = sortArrayByProperty(arr1, 'timeStart');
+ expect(sorted).toStrictEqual(arr1Expected);
+ });
+
+ it('sort array 1-5 with null', () => {
+ const arr1 = [
+ { timeStart: 1 },
+ { timeStart: 5 },
+ { timeStart: 3 },
+ { timeStart: 2 },
+ { timeStart: 4 },
+ { timeStart: null },
+ ];
+
+ const arr1Expected = [
+ { timeStart: null },
+ { timeStart: 1 },
+ { timeStart: 2 },
+ { timeStart: 3 },
+ { timeStart: 4 },
+ { timeStart: 5 },
+ ];
+
+ const sorted = sortArrayByProperty(arr1, 'timeStart');
+ expect(sorted).toStrictEqual(arr1Expected);
+ });
+});
+
+// test getSelectionByRoll()
+describe('test that roll loads selection in right order', () => {
+ const eventlist = [
+ {
+ id: 1,
+ timeStart: 0,
+ timeEnd: 10,
+ isPublic: false,
+ },
+ {
+ id: 2,
+ timeStart: 10,
+ timeEnd: 20,
+ isPublic: true,
+ },
+ {
+ id: 3,
+ timeStart: 20,
+ timeEnd: 30,
+ isPublic: false,
+ },
+ {
+ id: 4,
+ timeStart: 30,
+ timeEnd: 40,
+ isPublic: false,
+ },
+ {
+ id: 5,
+ timeStart: 40,
+ timeEnd: 50,
+ isPublic: true,
+ },
+ {
+ id: 6,
+ timeStart: 50,
+ timeEnd: 60,
+ isPublic: false,
+ },
+ {
+ id: 7,
+ timeStart: 60,
+ timeEnd: 70,
+ isPublic: true,
+ },
+ {
+ id: 8,
+ timeStart: 70,
+ timeEnd: 80,
+ isPublic: false,
+ },
+ ];
+
+ it('if timer is at 0', () => {
+ const now = 0;
+ const expected = {
+ nowIndex: 0,
+ nowId: 1,
+ publicIndex: null,
+ nextIndex: 1,
+ publicNextIndex: 1,
+ timers: {
+ _finishAt: 10,
+ _startedAt: 0,
+ current: 10,
+ duration: 10,
+ },
+ timeToNext: 10,
+ };
+
+ const state = getSelectionByRoll(eventlist, now);
+ expect(state).toStrictEqual(expected);
+ });
+
+ it('if timer is at 15', () => {
+ const now = 15;
+ const expected = {
+ nowIndex: 1,
+ nowId: 2,
+ publicIndex: 1,
+ nextIndex: 2,
+ publicNextIndex: 4,
+ timers: {
+ _finishAt: 20,
+ _startedAt: 10,
+ current: 5,
+ duration: 10,
+ },
+ timeToNext: 5,
+ };
+
+ const state = getSelectionByRoll(eventlist, now);
+ expect(state).toStrictEqual(expected);
+ });
+
+ it('if timer is at 20', () => {
+ const now = 20;
+ const expected = {
+ nowIndex: 2,
+ nowId: 3,
+ publicIndex: 1,
+ nextIndex: 3,
+ publicNextIndex: 4,
+ timers: {
+ _startedAt: 20,
+ _finishAt: 30,
+ current: 10,
+ duration: 10,
+ },
+ timeToNext: 10,
+ };
+
+ const state = getSelectionByRoll(eventlist, now);
+ expect(state).toStrictEqual(expected);
+ });
+
+ it('if timer is at 49', () => {
+ const now = 49;
+ const expected = {
+ nowIndex: 4,
+ nowId: 5,
+ publicIndex: 4,
+ nextIndex: 5,
+ publicNextIndex: 6,
+ timers: {
+ _startedAt: 40,
+ _finishAt: 50,
+ current: 1,
+ duration: 10,
+ },
+ timeToNext: 1,
+ };
+
+ const state = getSelectionByRoll(eventlist, now);
+ expect(state).toStrictEqual(expected);
+ });
+
+ it('if timer is at 63', () => {
+ const now = 63;
+ const expected = {
+ nowIndex: 6,
+ nowId: 7,
+ publicIndex: 6,
+ nextIndex: 7,
+ publicNextIndex: null,
+ timers: {
+ _startedAt: 60,
+ _finishAt: 70,
+ current: 7,
+ duration: 10,
+ },
+ timeToNext: 7,
+ };
+
+ const state = getSelectionByRoll(eventlist, now);
+ expect(state).toStrictEqual(expected);
+ });
+
+ it('if timer is at 75', () => {
+ const now = 75;
+ const expected = {
+ nowIndex: 7,
+ nowId: 8,
+ publicIndex: 6,
+ nextIndex: null,
+ publicNextIndex: null,
+ timers: {
+ _startedAt: 70,
+ _finishAt: 80,
+ current: 5,
+ duration: 10,
+ },
+ timeToNext: null,
+ };
+
+ const state = getSelectionByRoll(eventlist, now);
+ expect(state).toStrictEqual(expected);
+ });
+
+ it('if timer is at 100', () => {
+ const now = 100;
+ const expected = {
+ nowIndex: null,
+ nowId: null,
+ publicIndex: null,
+ nextIndex: null,
+ publicNextIndex: null,
+ timers: null,
+ timeToNext: null,
+ };
+
+ const state = getSelectionByRoll(eventlist, now);
+ expect(state).toStrictEqual(expected);
+ });
+});
diff --git a/server/src/classes/classUtils.js b/server/src/classes/classUtils.js
new file mode 100644
index 000000000..3c2ab2bfc
--- /dev/null
+++ b/server/src/classes/classUtils.js
@@ -0,0 +1,120 @@
+/**
+ * @description Sorts an array of objects by given property
+ * @param {array} arr - array to be sorted
+ * @param {string} property - property to compare
+ * @returns {array} copy of array sorted in ascending order
+ */
+
+export const sortArrayByProperty = (arr, property) => {
+ return [...arr].sort((a, b) => {
+ return a[property] - b[property];
+ });
+};
+
+/**
+ * @description Used in roll mode, returns selection variables from array
+ * @param {array} arr - event list
+ * @param {number} now - time now in millis
+ * @returns {object} object with selection variables
+ */
+
+export const getSelectionByRoll = (arr, now) => {
+ // Events now
+ let nowIndex = null; // index of event now
+ let nowId = null; // id of event now
+ let publicIndex = null; // index of public event now
+ let publicTime = -1; // counter:
+
+ // Events next
+ let nextIndex = null; // index of next event
+ let publicNextIndex = null; // index of next public event
+ let timeToNext = null; // counter: time for next event
+ let publicTimeToNext = null; // counter: time for next public event
+
+ // current timer
+ let timers = null;
+
+ // Order events by startTime
+ const orderedEvents = sortArrayByProperty(arr, 'timeStart');
+
+ // exit early if we are past the events
+ const lastEventEnd = orderedEvents[orderedEvents.length - 1].timeEnd;
+ if (now > lastEventEnd) {
+ return {
+ nowIndex,
+ nowId,
+ publicIndex,
+ nextIndex,
+ publicNextIndex,
+ timers,
+ timeToNext,
+ };
+ }
+
+ // loop through events, look for where we should be
+ for (const e of orderedEvents) {
+ // When does the event end (handle midnight)
+ const normalEnd =
+ e.timeEnd < e.timeStart ? (e.timeEnd += this.DAYMS) : e.timeEnd;
+
+ if (normalEnd < now) {
+ // event ran already
+
+ // public event might not be the one running
+ if (e.isPublic && normalEnd > publicTime) {
+ publicTime = normalEnd;
+ publicIndex = arr.findIndex((a) => a.id === e.id);
+ }
+ } else if (normalEnd >= now && now >= e.timeStart) {
+ // event is running
+
+ // it could also be public
+ if (e.isPublic) {
+ publicTime = normalEnd;
+ publicIndex = arr.findIndex((a) => a.id === e.id);
+ }
+
+ nowIndex = arr.findIndex((a) => a.id === e.id);
+ nowId = e.id;
+
+ // set timers
+ timers = {
+ _startedAt: e.timeStart,
+ _finishAt: normalEnd,
+ duration: normalEnd - e.timeStart,
+ current: normalEnd - now,
+ };
+ } else if (normalEnd > now) {
+ // event will run
+
+ // no need to look after found first
+ if (nextIndex !== null && publicNextIndex !== null) continue;
+
+ // look for next events
+ // check how far the start is from now
+ const wait = e.timeStart - now;
+ if (wait > 0) {
+ if (nextIndex === null || wait < timeToNext) {
+ timeToNext = wait;
+ nextIndex = arr.findIndex((a) => a.id === e.id);
+ }
+ if (
+ (publicNextIndex === null || wait < publicTimeToNext) &&
+ e.isPublic
+ ) {
+ publicTimeToNext = wait;
+ publicNextIndex = arr.findIndex((a) => a.id === e.id);
+ }
+ }
+ }
+ }
+ return {
+ nowIndex,
+ nowId,
+ publicIndex,
+ nextIndex,
+ publicNextIndex,
+ timers,
+ timeToNext,
+ };
+};