Compare commits
No commits in common. "de65fddba2fe9007f815b08fe2e478c062c8ccd6" and "9e03336d1a4e67a81ebdb1cc48ab378061e453a9" have entirely different histories.
de65fddba2
...
9e03336d1a
@ -1,20 +1,19 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import { debounce } from "./utils";
|
||||
import { socket } from "./socket";
|
||||
import { useAppSelector } from "./hooks";
|
||||
import { selectSignature } from "./store/contestantSlice";
|
||||
import { selectCanBuzz, selectSignature } from "./store/contestantSlice";
|
||||
|
||||
const Contestant = () => {
|
||||
const { room } = useParams();
|
||||
|
||||
const signature = useAppSelector(selectSignature);
|
||||
const [canBuzz, setCanBuzz] = useState<boolean>(false);
|
||||
const canBuzz = useAppSelector(selectCanBuzz);
|
||||
|
||||
useEffect(() => {
|
||||
socket.emit("contestant-join", { room, signature });
|
||||
socket.on("clue-clock-on", () => setCanBuzz(true));
|
||||
}, []);
|
||||
|
||||
const handleBuzz = debounce(() => {
|
||||
|
@ -4,7 +4,7 @@ import { isEmpty } from "lodash";
|
||||
|
||||
import { socket } from "./socket";
|
||||
import { useAppSelector } from "./hooks";
|
||||
import { selectRoomCode } from "./store/commonSlice";
|
||||
import { selectRoomCode } from "./store/socketSlice";
|
||||
import { selectContestants } from "./store/displaySlice";
|
||||
import { selectCategories, selectActiveClue } from "./store/cluesSlice";
|
||||
import ContestantWidget from "./ContestantWidget";
|
||||
|
@ -1,48 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { clamp } from "lodash";
|
||||
|
||||
interface Props {
|
||||
startTime: number | null;
|
||||
length: number;
|
||||
onTimeout?: () => unknown;
|
||||
}
|
||||
|
||||
const invLerp = (a: number, b: number, x: number): number =>
|
||||
clamp((x - b) / (a - b), 0, 1);
|
||||
|
||||
const Timer = ({ startTime, length, onTimeout }: Props) => {
|
||||
const [segs, setSegs] = useState<number>(0);
|
||||
const updateTimer = () => {
|
||||
if (!startTime) return;
|
||||
const endTime = startTime + length;
|
||||
const currentTime = Date.now();
|
||||
const segs = invLerp(startTime, endTime, currentTime) * 5;
|
||||
setSegs(segs);
|
||||
if (segs > 0) {
|
||||
setTimeout(updateTimer, 100);
|
||||
} else if (onTimeout) {
|
||||
onTimeout();
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
setTimeout(updateTimer, 100);
|
||||
}, [startTime, length]);
|
||||
|
||||
return (
|
||||
<svg viewBox="0 0 90 50" style={{ width: "100%", height: "100%" }}>
|
||||
{[5, 4, 3, 2, 1, 2, 3, 4, 5].map((seg, i) => (
|
||||
<rect
|
||||
x={i * 10}
|
||||
y="0"
|
||||
width="10"
|
||||
height="5"
|
||||
key={i}
|
||||
fill={seg <= segs ? "yellow" : "black"}
|
||||
stroke="blue"
|
||||
/>
|
||||
))}
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
|
||||
export default Timer;
|
@ -1,24 +1,11 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
import Timer from "../Timer";
|
||||
import type { Clue } from "../store/cluesSlice";
|
||||
import { socket } from "../socket";
|
||||
|
||||
interface Props {
|
||||
activeClue: Clue;
|
||||
}
|
||||
|
||||
const ClueDisplay = ({ activeClue }: Props) => {
|
||||
const [startTime, setStartTime] = useState<number | null>(null);
|
||||
useEffect(() => {
|
||||
socket.on("clue-clock-on", () => setStartTime(Date.now()));
|
||||
}, []);
|
||||
return (
|
||||
<>
|
||||
<div className="text-center fs-1">{activeClue.question}</div>
|
||||
<Timer startTime={startTime} length={5000} />
|
||||
</>
|
||||
);
|
||||
return <div className="text-center fs-1">{activeClue.question}</div>;
|
||||
};
|
||||
|
||||
export default ClueDisplay;
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { Container, Stack, Button } from "react-bootstrap";
|
||||
|
||||
import { useAppDispatch } from "../hooks";
|
||||
import { setActiveClue } from "../store/cluesSlice";
|
||||
import type { Clue } from "../store/cluesSlice";
|
||||
import { socket } from "../socket";
|
||||
|
||||
interface Props {
|
||||
activeClue: Clue;
|
||||
@ -10,11 +11,12 @@ interface Props {
|
||||
// TODO implement timer
|
||||
|
||||
const ActiveClue = ({ activeClue }: Props) => {
|
||||
const dispatch = useAppDispatch();
|
||||
return (
|
||||
<Container>
|
||||
<p>{activeClue.question}</p>
|
||||
<Stack gap={3} className="text-center">
|
||||
<Button onClick={() => socket.emit("start-clue-clock")}>
|
||||
<Button onClick={() => dispatch(setActiveClue(null))}>
|
||||
Start Timer
|
||||
</Button>
|
||||
</Stack>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { io } from "socket.io-client";
|
||||
import store from "./store";
|
||||
import { setRoomCode } from "./store/commonSlice";
|
||||
import { setRoomCode } from "./store/socketSlice";
|
||||
import { addContestant } from "./store/displaySlice";
|
||||
import { setCategories, setActiveClue } from "./store/cluesSlice";
|
||||
import type { Clue } from "./store/cluesSlice";
|
||||
|
@ -3,10 +3,12 @@ import type { RootState } from "./";
|
||||
|
||||
interface ContestantState {
|
||||
signature: number[][];
|
||||
canBuzz: boolean;
|
||||
}
|
||||
|
||||
const initialState: ContestantState = {
|
||||
signature: [],
|
||||
canBuzz: false,
|
||||
};
|
||||
|
||||
export const contestantSlice = createSlice({
|
||||
@ -16,10 +18,14 @@ export const contestantSlice = createSlice({
|
||||
addPathToSignature: (state, { payload }: PayloadAction<number[]>) => {
|
||||
state.signature = [...state.signature, payload];
|
||||
},
|
||||
setCanBuzz: (state, { payload }: PayloadAction<boolean>) => {
|
||||
state.canBuzz = payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { addPathToSignature } = contestantSlice.actions;
|
||||
export const selectSignature = (state: RootState) => state.contestant.signature;
|
||||
export const selectCanBuzz = (state: RootState) => state.contestant.canBuzz;
|
||||
|
||||
export default contestantSlice.reducer;
|
||||
|
@ -1,12 +1,12 @@
|
||||
import { configureStore } from "@reduxjs/toolkit";
|
||||
import commonReducer from "./commonSlice";
|
||||
import socketReducer from "./socketSlice";
|
||||
import contestantReducer from "./contestantSlice";
|
||||
import displayReducer from "./displaySlice";
|
||||
import cluesReducer from "./cluesSlice";
|
||||
|
||||
const store = configureStore({
|
||||
reducer: {
|
||||
common: commonReducer,
|
||||
socket: socketReducer,
|
||||
contestant: contestantReducer,
|
||||
display: displayReducer,
|
||||
clues: cluesReducer,
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
|
||||
import type { RootState } from "./";
|
||||
|
||||
interface CommonState {
|
||||
interface SocketState {
|
||||
roomCode: string;
|
||||
}
|
||||
|
||||
const initialState: CommonState = {
|
||||
const initialState: SocketState = {
|
||||
roomCode: "",
|
||||
};
|
||||
|
||||
export const commonSlice = createSlice({
|
||||
name: "common",
|
||||
export const socketSlice = createSlice({
|
||||
name: "socket",
|
||||
initialState,
|
||||
reducers: {
|
||||
setRoomCode: (state, { payload }: PayloadAction<string>) => {
|
||||
@ -19,7 +19,7 @@ export const commonSlice = createSlice({
|
||||
},
|
||||
});
|
||||
|
||||
export const { setRoomCode } = commonSlice.actions;
|
||||
export const selectRoomCode = (state: RootState) => state.common.roomCode;
|
||||
export const { setRoomCode } = socketSlice.actions;
|
||||
export const selectRoomCode = (state: RootState) => state.socket.roomCode;
|
||||
|
||||
export default commonSlice.reducer;
|
||||
export default socketSlice.reducer;
|
Loading…
Reference in New Issue
Block a user