Change the timer data structure to fix network bugs and refactored shared timer functions
This commit is contained in:
parent
0c1ec22234
commit
4199d7ab6a
@ -58,7 +58,15 @@ function Party({
|
|||||||
stream={partyStreams[id]}
|
stream={partyStreams[id]}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
<Timer timer={timer} />
|
{timer && <Timer timer={timer} index={0} />}
|
||||||
|
{Object.entries(partyTimers).map(([id, partyTimer], index) => (
|
||||||
|
<Timer
|
||||||
|
timer={partyTimer}
|
||||||
|
key={id}
|
||||||
|
// Put party timers above your timer if there is one
|
||||||
|
index={timer ? index + 1 : index}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
<Flex sx={{ flexDirection: "column" }}>
|
<Flex sx={{ flexDirection: "column" }}>
|
||||||
<ChangeNicknameButton nickname={nickname} onChange={onNicknameChange} />
|
<ChangeNicknameButton nickname={nickname} onChange={onNicknameChange} />
|
||||||
|
@ -1,33 +1,18 @@
|
|||||||
import React, { useEffect, useState, useRef } from "react";
|
import React, { useEffect, useRef } from "react";
|
||||||
import ReactDOM from "react-dom";
|
import ReactDOM from "react-dom";
|
||||||
import { Box, Progress } from "theme-ui";
|
import { Box, Progress } from "theme-ui";
|
||||||
|
|
||||||
import usePortal from "../../helpers/usePortal";
|
import usePortal from "../../helpers/usePortal";
|
||||||
|
|
||||||
function getTimerDuration(t) {
|
function Timer({ timer, index }) {
|
||||||
if (!t) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return t.hour * 3600000 + t.minute * 60000 + t.second * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
function Timer({ timer }) {
|
|
||||||
const [maxDuration, setMaxDuration] = useState(0);
|
|
||||||
|
|
||||||
const previousTimerRef = useRef(timer);
|
|
||||||
useEffect(() => {
|
|
||||||
if (!previousTimerRef.current && timer) {
|
|
||||||
setMaxDuration(getTimerDuration(timer));
|
|
||||||
}
|
|
||||||
previousTimerRef.current = timer;
|
|
||||||
});
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
progressBarRef.current.value = getTimerDuration(timer);
|
|
||||||
}, [timer]);
|
|
||||||
|
|
||||||
const progressBarRef = useRef();
|
const progressBarRef = useRef();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (progressBarRef.current && timer) {
|
||||||
|
progressBarRef.current.value = timer.current;
|
||||||
|
}
|
||||||
|
}, [timer]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let request = requestAnimationFrame(animate);
|
let request = requestAnimationFrame(animate);
|
||||||
let previousTime = performance.now();
|
let previousTime = performance.now();
|
||||||
@ -36,13 +21,15 @@ function Timer({ timer }) {
|
|||||||
const deltaTime = time - previousTime;
|
const deltaTime = time - previousTime;
|
||||||
previousTime = time;
|
previousTime = time;
|
||||||
|
|
||||||
progressBarRef.current.value -= deltaTime;
|
if (progressBarRef.current && progressBarRef.current.value > 0) {
|
||||||
|
progressBarRef.current.value -= deltaTime;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
cancelAnimationFrame(request);
|
cancelAnimationFrame(request);
|
||||||
};
|
};
|
||||||
}, [maxDuration]);
|
}, []);
|
||||||
|
|
||||||
const timerContainer = usePortal("root");
|
const timerContainer = usePortal("root");
|
||||||
|
|
||||||
@ -59,15 +46,15 @@ function Timer({ timer }) {
|
|||||||
borderRadius: "28px",
|
borderRadius: "28px",
|
||||||
left: "50%",
|
left: "50%",
|
||||||
maxWidth: "500px",
|
maxWidth: "500px",
|
||||||
width: "60%",
|
width: "40%",
|
||||||
transform: "translateX(-50%)",
|
transform: `translate(-50%, -${index * 36}px)`,
|
||||||
padding: "0 8px",
|
padding: "0 8px",
|
||||||
margin: "8px",
|
margin: "8px",
|
||||||
}}
|
}}
|
||||||
bg="overlay"
|
bg="overlay"
|
||||||
>
|
>
|
||||||
<Progress
|
<Progress
|
||||||
max={maxDuration}
|
max={timer && timer.max}
|
||||||
m={2}
|
m={2}
|
||||||
sx={{ width: "100%" }}
|
sx={{ width: "100%" }}
|
||||||
ref={progressBarRef}
|
ref={progressBarRef}
|
||||||
|
33
src/helpers/timer.js
Normal file
33
src/helpers/timer.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
const MILLISECONDS_IN_HOUR = 3600000;
|
||||||
|
const MILLISECONDS_IN_MINUTE = 60000;
|
||||||
|
const MILLISECONDS_IN_SECOND = 1000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a timers duration in milliseconds
|
||||||
|
* @param {Object} t The object with an hour, minute and second property
|
||||||
|
*/
|
||||||
|
export function getHMSDuration(t) {
|
||||||
|
if (!t) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
t.hour * MILLISECONDS_IN_HOUR +
|
||||||
|
t.minute * MILLISECONDS_IN_MINUTE +
|
||||||
|
t.second * MILLISECONDS_IN_SECOND
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an object with an hour, minute and second property
|
||||||
|
* @param {number} duration The duration in milliseconds
|
||||||
|
*/
|
||||||
|
export function getDurationHMS(duration) {
|
||||||
|
let workingDuration = duration;
|
||||||
|
const hour = Math.floor(workingDuration / MILLISECONDS_IN_HOUR);
|
||||||
|
workingDuration -= hour * MILLISECONDS_IN_HOUR;
|
||||||
|
const minute = Math.floor(workingDuration / MILLISECONDS_IN_MINUTE);
|
||||||
|
workingDuration -= minute * MILLISECONDS_IN_MINUTE;
|
||||||
|
const second = Math.floor(workingDuration / MILLISECONDS_IN_SECOND);
|
||||||
|
|
||||||
|
return { hour, minute, second };
|
||||||
|
}
|
@ -3,6 +3,8 @@ import { Box, Label, Input, Button, Flex, Text } from "theme-ui";
|
|||||||
|
|
||||||
import Modal from "../components/Modal";
|
import Modal from "../components/Modal";
|
||||||
|
|
||||||
|
import { getHMSDuration, getDurationHMS } from "../helpers/timer";
|
||||||
|
|
||||||
function StartTimerModal({
|
function StartTimerModal({
|
||||||
isOpen,
|
isOpen,
|
||||||
onRequestClose,
|
onRequestClose,
|
||||||
@ -24,7 +26,8 @@ function StartTimerModal({
|
|||||||
if (timer) {
|
if (timer) {
|
||||||
onTimerStop();
|
onTimerStop();
|
||||||
} else {
|
} else {
|
||||||
onTimerStart({ hour, minute, second });
|
const duration = getHMSDuration({ hour, minute, second });
|
||||||
|
onTimerStart({ current: duration, max: duration });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,6 +50,8 @@ function StartTimerModal({
|
|||||||
return Math.min(num, max);
|
return Math.min(num, max);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const timerHMS = timer && getDurationHMS(timer.current);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isOpen={isOpen}
|
isOpen={isOpen}
|
||||||
@ -70,7 +75,7 @@ function StartTimerModal({
|
|||||||
</Text>
|
</Text>
|
||||||
<Input
|
<Input
|
||||||
sx={inputStyle}
|
sx={inputStyle}
|
||||||
value={`${timer ? timer.hour : hour}`}
|
value={`${timer ? timerHMS.hour : hour}`}
|
||||||
onChange={(e) => setHour(parseValue(e.target.value, 24))}
|
onChange={(e) => setHour(parseValue(e.target.value, 24))}
|
||||||
type="number"
|
type="number"
|
||||||
disabled={timer}
|
disabled={timer}
|
||||||
@ -82,7 +87,7 @@ function StartTimerModal({
|
|||||||
</Text>
|
</Text>
|
||||||
<Input
|
<Input
|
||||||
sx={inputStyle}
|
sx={inputStyle}
|
||||||
value={`${timer ? timer.minute : minute}`}
|
value={`${timer ? timerHMS.minute : minute}`}
|
||||||
onChange={(e) => setMinute(parseValue(e.target.value, 59))}
|
onChange={(e) => setMinute(parseValue(e.target.value, 59))}
|
||||||
type="number"
|
type="number"
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
@ -95,7 +100,7 @@ function StartTimerModal({
|
|||||||
</Text>
|
</Text>
|
||||||
<Input
|
<Input
|
||||||
sx={inputStyle}
|
sx={inputStyle}
|
||||||
value={`${timer ? timer.second : second}`}
|
value={`${timer ? timerHMS.second : second}`}
|
||||||
onChange={(e) => setSecond(parseValue(e.target.value, 59))}
|
onChange={(e) => setSecond(parseValue(e.target.value, 59))}
|
||||||
type="number"
|
type="number"
|
||||||
disabled={timer}
|
disabled={timer}
|
||||||
|
@ -3,7 +3,7 @@ import React, { useContext, useState, useEffect, useCallback } from "react";
|
|||||||
// Load session for auto complete
|
// Load session for auto complete
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
import Session from "../helpers/Session";
|
import Session from "../helpers/Session";
|
||||||
import { isStreamStopped, omit } from "../helpers/shared";
|
import { isStreamStopped, omit, fromEntries } from "../helpers/shared";
|
||||||
|
|
||||||
import AuthContext from "../contexts/AuthContext";
|
import AuthContext from "../contexts/AuthContext";
|
||||||
|
|
||||||
@ -73,23 +73,18 @@ function NetworkedParty({ gameId, session }) {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function decreaseTimer(previousTimer) {
|
function decreaseTimer(previousTimer) {
|
||||||
if (previousTimer.second > 0) {
|
return { ...previousTimer, current: previousTimer.current - 1000 };
|
||||||
return { ...previousTimer, second: previousTimer.second - 1 };
|
|
||||||
} else if (previousTimer.minute > 0) {
|
|
||||||
return {
|
|
||||||
...previousTimer,
|
|
||||||
minute: previousTimer.minute - 1,
|
|
||||||
second: 59,
|
|
||||||
};
|
|
||||||
} else if (previousTimer.hour > 0) {
|
|
||||||
return { hour: previousTimer.hour - 1, minute: 59, second: 59 };
|
|
||||||
} else return { hour: 0, minute: 0, second: 0 };
|
|
||||||
}
|
}
|
||||||
function updateTimers() {
|
function updateTimers() {
|
||||||
if (timer) {
|
if (timer) {
|
||||||
const newTimer = decreaseTimer(timer);
|
const newTimer = decreaseTimer(timer);
|
||||||
setTimer(newTimer);
|
if (newTimer.current < 0) {
|
||||||
session.send("timer", { [session.id]: newTimer });
|
setTimer(null);
|
||||||
|
session.send("timer", { [session.id]: null });
|
||||||
|
} else {
|
||||||
|
setTimer(newTimer);
|
||||||
|
session.send("timer", { [session.id]: newTimer });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const interval = setInterval(updateTimers, 1000);
|
const interval = setInterval(updateTimers, 1000);
|
||||||
@ -122,10 +117,14 @@ function NetworkedParty({ gameId, session }) {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
if (id === "timer") {
|
if (id === "timer") {
|
||||||
setPartyTimers((prevTimers) => ({
|
setPartyTimers((prevTimers) => {
|
||||||
...prevTimers,
|
const newTimers = { ...prevTimers, ...data };
|
||||||
...data,
|
// filter out timers that are null
|
||||||
}));
|
const filtered = Object.entries(newTimers).filter(
|
||||||
|
([, value]) => value !== null
|
||||||
|
);
|
||||||
|
return fromEntries(filtered);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user