2023-05-11 04:03:20 +08:00
|
|
|
import { Meteor } from 'meteor/meteor';
|
|
|
|
import Timer from '/imports/api/timer';
|
|
|
|
import Auth from '/imports/ui/services/auth';
|
|
|
|
import { makeCall } from '/imports/ui/services/api';
|
2020-04-26 03:03:35 +08:00
|
|
|
import { Session } from 'meteor/session';
|
|
|
|
import Users from '/imports/api/users';
|
2020-04-27 01:44:01 +08:00
|
|
|
import Logger from '/imports/startup/client/logger';
|
2020-04-26 03:03:35 +08:00
|
|
|
import { ACTIONS, PANELS } from '../layout/enums';
|
2023-05-11 04:03:20 +08:00
|
|
|
|
|
|
|
const TIMER_CONFIG = Meteor.settings.public.timer;
|
2020-04-26 03:03:35 +08:00
|
|
|
const ROLE_MODERATOR = Meteor.settings.public.user.role_moderator;
|
2020-06-11 01:29:30 +08:00
|
|
|
const OFFSET_INTERVAL = TIMER_CONFIG.interval.offset;
|
2020-04-26 03:03:35 +08:00
|
|
|
|
|
|
|
const MILLI_IN_HOUR = 3600000;
|
|
|
|
const MILLI_IN_MINUTE = 60000;
|
|
|
|
const MILLI_IN_SECOND = 1000;
|
2023-05-11 04:03:20 +08:00
|
|
|
|
2020-04-27 01:44:01 +08:00
|
|
|
const MAX_HOURS = 23;
|
|
|
|
|
2022-02-02 03:05:02 +08:00
|
|
|
const TRACKS = [
|
|
|
|
'noTrack',
|
|
|
|
'track1',
|
|
|
|
'track2',
|
|
|
|
'track3',
|
|
|
|
];
|
|
|
|
|
2023-06-09 03:24:25 +08:00
|
|
|
const isMusicEnabled = () => TIMER_CONFIG.music.enabled;
|
|
|
|
|
|
|
|
const getCurrentTrack = () => {
|
|
|
|
const timer = Timer.findOne(
|
|
|
|
{ meetingId: Auth.meetingID },
|
|
|
|
{ fields: { track: 1 } },
|
|
|
|
);
|
|
|
|
|
|
|
|
if (timer) return isMusicEnabled() && timer.track;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
const isEnabled = () => TIMER_CONFIG.enabled;
|
|
|
|
|
2020-04-27 01:44:01 +08:00
|
|
|
const getMaxHours = () => MAX_HOURS;
|
|
|
|
|
2020-06-13 02:00:24 +08:00
|
|
|
const isAlarmEnabled = () => isEnabled() && TIMER_CONFIG.alarm;
|
2020-04-27 04:18:43 +08:00
|
|
|
|
2022-02-02 03:05:02 +08:00
|
|
|
const isMusicActive = () => getCurrentTrack() !== TRACKS[0];
|
|
|
|
|
2021-09-03 20:44:16 +08:00
|
|
|
const getMusicVolume = () => TIMER_CONFIG.music.volume;
|
|
|
|
|
|
|
|
const getMusicTrack = () => TIMER_CONFIG.music.track;
|
|
|
|
|
2020-06-13 02:00:24 +08:00
|
|
|
const isActive = () => {
|
2023-05-11 04:03:20 +08:00
|
|
|
const timer = Timer.findOne(
|
|
|
|
{ meetingId: Auth.meetingID },
|
|
|
|
{ fields: { active: 1 } },
|
|
|
|
);
|
|
|
|
|
|
|
|
if (timer) return timer.active;
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
2020-04-26 03:03:35 +08:00
|
|
|
const getDefaultTime = () => TIMER_CONFIG.time * MILLI_IN_MINUTE;
|
|
|
|
|
2020-06-11 01:29:30 +08:00
|
|
|
const getInterval = () => TIMER_CONFIG.interval.clock;
|
2020-04-26 03:03:35 +08:00
|
|
|
|
2023-05-11 04:03:20 +08:00
|
|
|
const isRunning = () => {
|
|
|
|
const timer = Timer.findOne(
|
|
|
|
{ meetingId: Auth.meetingID },
|
|
|
|
{ fields: { running: 1 } },
|
|
|
|
);
|
|
|
|
|
|
|
|
if (timer) return timer.running;
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
|
|
|
const isStopwatch = () => {
|
|
|
|
const timer = Timer.findOne(
|
|
|
|
{ meetingId: Auth.meetingID },
|
|
|
|
{ fields: { stopwatch: 1 } },
|
|
|
|
);
|
|
|
|
|
|
|
|
if (timer) return timer.stopwatch;
|
|
|
|
return false;
|
2020-04-26 03:03:35 +08:00
|
|
|
};
|
2023-05-11 04:03:20 +08:00
|
|
|
|
2020-04-26 03:03:35 +08:00
|
|
|
const startTimer = () => makeCall('startTimer');
|
2023-05-11 04:03:20 +08:00
|
|
|
|
|
|
|
const stopTimer = () => makeCall('stopTimer');
|
|
|
|
|
2020-04-26 03:03:35 +08:00
|
|
|
const switchTimer = (stopwatch) => makeCall('switchTimer', stopwatch);
|
|
|
|
|
|
|
|
const setTimer = (time) => makeCall('setTimer', time);
|
|
|
|
|
2023-05-11 04:03:20 +08:00
|
|
|
const resetTimer = () => makeCall('resetTimer');
|
|
|
|
|
2023-06-22 03:32:06 +08:00
|
|
|
const activateTimer = (layoutContextDispatch) => {
|
|
|
|
makeCall('activateTimer');
|
|
|
|
//Set an observer to switch to timer tab as soon as the timer is activated
|
|
|
|
const handle =Timer.find({ meetingId: Auth.meetingID }).observeChanges({
|
|
|
|
changed(id, timer) {
|
|
|
|
if (timer.active === true) {
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
|
|
|
|
value: true,
|
|
|
|
});
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL,
|
|
|
|
value: PANELS.TIMER,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
handle.stop();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
2023-05-11 04:03:20 +08:00
|
|
|
|
|
|
|
const deactivateTimer = () => makeCall('deactivateTimer');
|
|
|
|
|
2021-09-14 02:16:07 +08:00
|
|
|
const timerEnded = () => makeCall('timerEnded');
|
|
|
|
|
2022-02-02 03:05:02 +08:00
|
|
|
const setTrack = (track) => {
|
|
|
|
makeCall('setTrack', track);
|
2021-09-03 20:44:16 +08:00
|
|
|
};
|
|
|
|
|
2020-06-11 01:29:30 +08:00
|
|
|
const fetchTimeOffset = () => {
|
2020-06-13 02:50:42 +08:00
|
|
|
const t0 = Date.now();
|
2020-06-11 01:29:30 +08:00
|
|
|
|
2023-06-09 03:24:25 +08:00
|
|
|
makeCall('getServerTime').then((result) => {
|
|
|
|
if (result === 0) return;
|
2020-06-13 02:50:42 +08:00
|
|
|
const t3 = Date.now();
|
2020-06-11 01:29:30 +08:00
|
|
|
|
2020-06-13 02:50:42 +08:00
|
|
|
const ts = result;
|
|
|
|
const rtt = t3 - t0;
|
2023-06-09 03:24:25 +08:00
|
|
|
const timeOffset = Math.round(ts - rtt / 2 - t0);
|
2020-06-11 01:29:30 +08:00
|
|
|
|
2020-06-13 02:50:42 +08:00
|
|
|
Session.set('timeOffset', timeOffset);
|
2020-06-11 01:29:30 +08:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2020-06-13 02:50:42 +08:00
|
|
|
const getTimeOffset = () => {
|
|
|
|
const timeOffset = Session.get('timeOffset');
|
|
|
|
|
|
|
|
if (timeOffset) return timeOffset;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
const getElapsedTime = (running, timestamp, timeOffset, accumulated) => {
|
2020-06-11 01:29:30 +08:00
|
|
|
if (!running) return accumulated;
|
|
|
|
|
|
|
|
const now = Date.now();
|
|
|
|
|
2020-06-13 02:50:42 +08:00
|
|
|
return accumulated + Math.abs(now - timestamp + timeOffset);
|
2020-06-11 01:29:30 +08:00
|
|
|
};
|
2023-05-11 04:03:20 +08:00
|
|
|
|
2023-05-16 03:46:44 +08:00
|
|
|
const getStopwatch = () => {
|
|
|
|
const timer = Timer.findOne(
|
|
|
|
{ meetingId: Auth.meetingID },
|
|
|
|
{ fields: { stopwatch: 1 } },
|
|
|
|
);
|
|
|
|
|
|
|
|
if (timer) return timer.stopwatch;
|
|
|
|
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2023-05-11 04:03:20 +08:00
|
|
|
const getTimer = () => {
|
|
|
|
const timer = Timer.findOne(
|
|
|
|
{ meetingId: Auth.meetingID },
|
2023-06-09 03:24:25 +08:00
|
|
|
{
|
|
|
|
fields:
|
2023-05-11 04:03:20 +08:00
|
|
|
{
|
|
|
|
stopwatch: 1,
|
|
|
|
running: 1,
|
|
|
|
time: 1,
|
|
|
|
accumulated: 1,
|
|
|
|
timestamp: 1,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
);
|
|
|
|
|
|
|
|
if (timer) {
|
|
|
|
const {
|
|
|
|
stopwatch,
|
|
|
|
running,
|
|
|
|
time,
|
|
|
|
accumulated,
|
|
|
|
timestamp,
|
|
|
|
} = timer;
|
|
|
|
|
|
|
|
return {
|
|
|
|
stopwatch,
|
|
|
|
running,
|
|
|
|
time,
|
|
|
|
accumulated,
|
|
|
|
timestamp,
|
2023-06-09 03:24:25 +08:00
|
|
|
};
|
2023-05-11 04:03:20 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
stopwatch: true,
|
|
|
|
running: false,
|
2020-04-26 03:03:35 +08:00
|
|
|
time: getDefaultTime(),
|
2023-05-11 04:03:20 +08:00
|
|
|
accumulated: 0,
|
|
|
|
timestamp: 0,
|
2023-06-09 03:24:25 +08:00
|
|
|
};
|
2020-04-26 03:03:35 +08:00
|
|
|
};
|
2023-05-11 04:03:20 +08:00
|
|
|
|
2023-06-09 03:24:25 +08:00
|
|
|
const getTimeAsString = (time) => {
|
|
|
|
const milliseconds = time;
|
2023-05-11 04:03:20 +08:00
|
|
|
|
2023-06-09 03:24:25 +08:00
|
|
|
const hours = Math.floor(milliseconds / MILLI_IN_HOUR);
|
2020-04-26 03:03:35 +08:00
|
|
|
const mHours = hours * MILLI_IN_HOUR;
|
|
|
|
|
2023-06-09 03:24:25 +08:00
|
|
|
const minutes = Math.floor((milliseconds - mHours) / MILLI_IN_MINUTE);
|
2020-04-26 03:03:35 +08:00
|
|
|
const mMinutes = minutes * MILLI_IN_MINUTE;
|
|
|
|
|
2023-06-09 03:24:25 +08:00
|
|
|
const seconds = Math.floor((milliseconds - mHours - mMinutes) / MILLI_IN_SECOND);
|
2020-04-26 03:03:35 +08:00
|
|
|
|
2020-04-27 01:44:01 +08:00
|
|
|
let timeAsString = '';
|
2020-04-26 03:03:35 +08:00
|
|
|
|
2023-05-16 03:46:44 +08:00
|
|
|
if (hours < 10) {
|
|
|
|
timeAsString += `0${hours}:`;
|
|
|
|
} else {
|
|
|
|
timeAsString += `${hours}:`;
|
2020-04-26 03:03:35 +08:00
|
|
|
}
|
|
|
|
|
2023-05-16 03:46:44 +08:00
|
|
|
if (minutes < 10) {
|
|
|
|
timeAsString += `0${minutes}:`;
|
|
|
|
} else {
|
|
|
|
timeAsString += `${minutes}:`;
|
2020-04-26 03:03:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (seconds < 10) {
|
2020-04-27 01:44:01 +08:00
|
|
|
timeAsString += `0${seconds}`;
|
2020-04-26 03:03:35 +08:00
|
|
|
} else {
|
2020-04-27 01:44:01 +08:00
|
|
|
timeAsString += `${seconds}`;
|
2020-04-26 03:03:35 +08:00
|
|
|
}
|
|
|
|
|
2020-04-27 01:44:01 +08:00
|
|
|
return timeAsString;
|
2020-04-26 03:03:35 +08:00
|
|
|
};
|
|
|
|
|
2020-04-27 01:44:01 +08:00
|
|
|
const closePanel = (layoutContextDispatch) => {
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
|
|
|
|
value: false,
|
|
|
|
});
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL,
|
|
|
|
value: PANELS.NONE,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2020-04-26 03:03:35 +08:00
|
|
|
const togglePanel = (sidebarContentPanel, layoutContextDispatch) => {
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_CONTENT_IS_OPEN,
|
|
|
|
value: sidebarContentPanel !== PANELS.TIMER,
|
|
|
|
});
|
|
|
|
layoutContextDispatch({
|
|
|
|
type: ACTIONS.SET_SIDEBAR_CONTENT_PANEL,
|
|
|
|
value: sidebarContentPanel === PANELS.TIMER
|
|
|
|
? PANELS.NONE
|
|
|
|
: PANELS.TIMER,
|
|
|
|
});
|
|
|
|
};
|
2023-05-11 04:03:20 +08:00
|
|
|
|
2023-06-09 03:24:25 +08:00
|
|
|
const isModerator = () => Users.findOne(
|
|
|
|
{ userId: Auth.userID },
|
|
|
|
{ fields: { role: 1 } },
|
|
|
|
).role === ROLE_MODERATOR;
|
2023-05-11 04:03:20 +08:00
|
|
|
|
2020-04-27 01:44:01 +08:00
|
|
|
const setHours = (hours, time) => {
|
2023-06-09 03:24:25 +08:00
|
|
|
if (!Number.isNaN(hours) && hours >= 0 && hours <= MAX_HOURS) {
|
2020-04-27 01:44:01 +08:00
|
|
|
const currentHours = Math.floor(time / MILLI_IN_HOUR);
|
|
|
|
|
|
|
|
const diff = (hours - currentHours) * MILLI_IN_HOUR;
|
|
|
|
setTimer(time + diff);
|
|
|
|
} else {
|
|
|
|
Logger.warn('Invalid time');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const setMinutes = (minutes, time) => {
|
2023-06-09 03:24:25 +08:00
|
|
|
if (!Number.isNaN(minutes) && minutes >= 0 && minutes <= 59) {
|
2020-04-27 01:44:01 +08:00
|
|
|
const currentHours = Math.floor(time / MILLI_IN_HOUR);
|
|
|
|
const mHours = currentHours * MILLI_IN_HOUR;
|
|
|
|
|
|
|
|
const currentMinutes = Math.floor((time - mHours) / MILLI_IN_MINUTE);
|
|
|
|
|
|
|
|
const diff = (minutes - currentMinutes) * MILLI_IN_MINUTE;
|
|
|
|
setTimer(time + diff);
|
|
|
|
} else {
|
|
|
|
Logger.warn('Invalid time');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const setSeconds = (seconds, time) => {
|
2023-06-09 03:24:25 +08:00
|
|
|
if (!Number.isNaN(seconds) && seconds >= 0 && seconds <= 59) {
|
2020-04-27 01:44:01 +08:00
|
|
|
const currentHours = Math.floor(time / MILLI_IN_HOUR);
|
|
|
|
const mHours = currentHours * MILLI_IN_HOUR;
|
|
|
|
|
|
|
|
const currentMinutes = Math.floor((time - mHours) / MILLI_IN_MINUTE);
|
|
|
|
const mMinutes = currentMinutes * MILLI_IN_MINUTE;
|
|
|
|
|
|
|
|
const currentSeconds = Math.floor((time - mHours - mMinutes) / MILLI_IN_SECOND);
|
|
|
|
|
|
|
|
const diff = (seconds - currentSeconds) * MILLI_IN_SECOND;
|
|
|
|
setTimer(time + diff);
|
|
|
|
} else {
|
|
|
|
Logger.warn('Invalid time');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-05-11 04:03:20 +08:00
|
|
|
export default {
|
2020-06-11 01:29:30 +08:00
|
|
|
OFFSET_INTERVAL,
|
2022-02-02 03:05:02 +08:00
|
|
|
TRACKS,
|
2023-05-11 04:03:20 +08:00
|
|
|
isActive,
|
|
|
|
isEnabled,
|
2021-09-03 20:44:16 +08:00
|
|
|
isMusicEnabled,
|
|
|
|
isMusicActive,
|
2022-02-02 03:05:02 +08:00
|
|
|
getCurrentTrack,
|
2021-09-03 20:44:16 +08:00
|
|
|
getMusicVolume,
|
|
|
|
getMusicTrack,
|
2023-05-11 04:03:20 +08:00
|
|
|
isRunning,
|
|
|
|
isStopwatch,
|
2020-04-27 04:18:43 +08:00
|
|
|
isAlarmEnabled,
|
2023-05-11 04:03:20 +08:00
|
|
|
startTimer,
|
|
|
|
stopTimer,
|
2020-04-26 03:03:35 +08:00
|
|
|
switchTimer,
|
2020-04-27 01:44:01 +08:00
|
|
|
setHours,
|
|
|
|
setMinutes,
|
|
|
|
setSeconds,
|
2023-05-11 04:03:20 +08:00
|
|
|
resetTimer,
|
|
|
|
activateTimer,
|
|
|
|
deactivateTimer,
|
2020-06-11 01:29:30 +08:00
|
|
|
fetchTimeOffset,
|
2022-02-02 03:05:02 +08:00
|
|
|
setTrack,
|
2020-06-13 02:50:42 +08:00
|
|
|
getTimeOffset,
|
2020-06-11 01:29:30 +08:00
|
|
|
getElapsedTime,
|
2023-05-11 04:03:20 +08:00
|
|
|
getInterval,
|
2020-04-27 01:44:01 +08:00
|
|
|
getMaxHours,
|
2023-05-16 03:46:44 +08:00
|
|
|
getStopwatch,
|
2023-05-11 04:03:20 +08:00
|
|
|
getTimer,
|
|
|
|
getTimeAsString,
|
2020-04-27 01:44:01 +08:00
|
|
|
closePanel,
|
2020-04-26 03:03:35 +08:00
|
|
|
togglePanel,
|
|
|
|
isModerator,
|
2021-09-14 02:16:07 +08:00
|
|
|
timerEnded,
|
2023-05-11 04:03:20 +08:00
|
|
|
};
|