mirror of
https://github.com/randyjc/Rahoot.git
synced 2026-03-14 04:25:35 +01:00
Add Start cooldown & Patch socket event
This commit is contained in:
@@ -17,6 +17,10 @@ io.listen(5157)
|
|||||||
io.on("connection", (socket) => {
|
io.on("connection", (socket) => {
|
||||||
console.log(`A user connected ${socket.id}`)
|
console.log(`A user connected ${socket.id}`)
|
||||||
|
|
||||||
|
socket.on("player:checkRoom", (roomId) =>
|
||||||
|
Player.checkRoom(gameState, io, socket, roomId),
|
||||||
|
)
|
||||||
|
|
||||||
socket.on("player:join", (player) =>
|
socket.on("player:join", (player) =>
|
||||||
Player.join(gameState, io, socket, player),
|
Player.join(gameState, io, socket, player),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { GAME_STATE_INIT } from "../quizz.config.js"
|
import { GAME_STATE_INIT } from "../quizz.config.js"
|
||||||
import { startRound } from "../utils/round.js"
|
import { startRound } from "../utils/round.js"
|
||||||
import generateRoomId from "../utils/generateRoomId.js"
|
import generateRoomId from "../utils/generateRoomId.js"
|
||||||
|
import { cooldown, sleep } from "../utils/cooldown.js"
|
||||||
|
|
||||||
const Manager = {
|
const Manager = {
|
||||||
createRoom: (game, io, socket) => {
|
createRoom: (game, io, socket) => {
|
||||||
@@ -31,13 +32,24 @@ const Manager = {
|
|||||||
io.to(game.manager).emit("manager:playerKicked", player.id)
|
io.to(game.manager).emit("manager:playerKicked", player.id)
|
||||||
},
|
},
|
||||||
|
|
||||||
startGame: (game, io, socket) => {
|
startGame: async (game, io, socket) => {
|
||||||
if (game.started || !game.room) {
|
if (game.started || !game.room) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
game.started = true
|
game.started = true
|
||||||
io.to(game.room).emit("startGame", game.room)
|
io.to(game.room).emit("game:status", {
|
||||||
|
name: "SHOW_START",
|
||||||
|
data: {
|
||||||
|
time: 3,
|
||||||
|
subject: "Adobe",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
await sleep(3)
|
||||||
|
io.to(game.room).emit("game:startCooldown")
|
||||||
|
|
||||||
|
await cooldown(3, io, game.room)
|
||||||
startRound(game, io, socket)
|
startRound(game, io, socket)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,17 @@
|
|||||||
import convertTimeToPoint from "../utils/convertTimeToPoint.js"
|
import convertTimeToPoint from "../utils/convertTimeToPoint.js"
|
||||||
import { abortCooldown } from "../utils/round.js"
|
import { abortCooldown } from "../utils/cooldown.js"
|
||||||
|
|
||||||
const Player = {
|
const Player = {
|
||||||
|
checkRoom: (game, io, socket, roomId) => {
|
||||||
|
if (!game.room || roomId !== game.room) {
|
||||||
|
io.to(socket.id).emit("game:errorMessage", "Room not found")
|
||||||
|
console.log("zaza")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
io.to(socket.id).emit("game:successRoom", roomId)
|
||||||
|
},
|
||||||
|
|
||||||
join: (game, io, socket, player) => {
|
join: (game, io, socket, player) => {
|
||||||
if (!player.room || !player.room || game.started) {
|
if (!player.room || !player.room || game.started) {
|
||||||
return
|
return
|
||||||
|
|||||||
26
socket/src/utils/cooldown.js
Normal file
26
socket/src/utils/cooldown.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
let cooldownTimeout
|
||||||
|
let cooldownResolve
|
||||||
|
|
||||||
|
export const abortCooldown = () => {
|
||||||
|
clearInterval(cooldownTimeout)
|
||||||
|
cooldownResolve()
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cooldown = (ms, io, room) => {
|
||||||
|
let count = ms - 1
|
||||||
|
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
cooldownResolve = resolve
|
||||||
|
|
||||||
|
cooldownTimeout = setInterval(() => {
|
||||||
|
if (!count) {
|
||||||
|
clearInterval(cooldownTimeout)
|
||||||
|
resolve()
|
||||||
|
}
|
||||||
|
io.to(room).emit("game:cooldown", count)
|
||||||
|
count -= 1
|
||||||
|
}, 1000)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const sleep = (sec) => new Promise((r) => setTimeout(r, sec * 1000))
|
||||||
@@ -1,29 +1,4 @@
|
|||||||
let cooldownTimeout
|
import { cooldown, sleep } from "./cooldown.js"
|
||||||
let cooldownResolve
|
|
||||||
|
|
||||||
export const abortCooldown = () => {
|
|
||||||
clearInterval(cooldownTimeout)
|
|
||||||
cooldownResolve()
|
|
||||||
}
|
|
||||||
|
|
||||||
function cooldown(ms, io, room) {
|
|
||||||
let count = ms - 1
|
|
||||||
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
cooldownResolve = resolve
|
|
||||||
|
|
||||||
cooldownTimeout = setInterval(() => {
|
|
||||||
if (!count) {
|
|
||||||
clearInterval(cooldownTimeout)
|
|
||||||
resolve()
|
|
||||||
}
|
|
||||||
io.to(room).emit("game:cooldown", count)
|
|
||||||
count -= 1
|
|
||||||
}, 1000)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const sleep = (sec) => new Promise((r) => setTimeout(r, sec * 1000))
|
|
||||||
|
|
||||||
export const startRound = async (game, io, socket) => {
|
export const startRound = async (game, io, socket) => {
|
||||||
const question = game.questions[game.currentQuestion]
|
const question = game.questions[game.currentQuestion]
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import Button from "@/components/Button"
|
|||||||
import background from "@/assets/background.webp"
|
import background from "@/assets/background.webp"
|
||||||
import { usePlayerContext } from "@/context/player"
|
import { usePlayerContext } from "@/context/player"
|
||||||
import { useSocketContext } from "@/context/socket"
|
import { useSocketContext } from "@/context/socket"
|
||||||
import { useState } from "react"
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
export default function GameWrapper({ children, textNext, onNext, manager }) {
|
export default function GameWrapper({ children, textNext, onNext, manager }) {
|
||||||
const { socket } = useSocketContext()
|
const { socket } = useSocketContext()
|
||||||
@@ -11,6 +11,7 @@ export default function GameWrapper({ children, textNext, onNext, manager }) {
|
|||||||
|
|
||||||
const [questionState, setQuestionState] = useState()
|
const [questionState, setQuestionState] = useState()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
socket.on("game:updateQuestion", ({ current, total }) => {
|
socket.on("game:updateQuestion", ({ current, total }) => {
|
||||||
setQuestionState({
|
setQuestionState({
|
||||||
current,
|
current,
|
||||||
@@ -18,12 +19,18 @@ export default function GameWrapper({ children, textNext, onNext, manager }) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off("game:updateQuestion")
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="relative flex min-h-screen w-full flex-col justify-between">
|
<section className="relative flex min-h-screen w-full flex-col justify-between">
|
||||||
<div className="fixed left-0 top-0 -z-10 h-full w-full bg-orange-600 opacity-70">
|
<div className="fixed left-0 top-0 -z-10 h-full w-full bg-orange-600 opacity-70">
|
||||||
<Image
|
<Image
|
||||||
className="pointer-events-none h-full w-full object-cover opacity-60"
|
className="pointer-events-none h-full w-full object-cover opacity-60"
|
||||||
src={background}
|
src={background}
|
||||||
|
alt="background"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -47,7 +54,7 @@ export default function GameWrapper({ children, textNext, onNext, manager }) {
|
|||||||
{children}
|
{children}
|
||||||
|
|
||||||
{!manager && (
|
{!manager && (
|
||||||
<div className="flex items-center justify-between bg-white px-4 py-2 text-lg font-bold text-white">
|
<div className="z-50 flex items-center justify-between bg-white px-4 py-2 text-lg font-bold text-white">
|
||||||
<p className="text-gray-800">{!!player && player.username}</p>
|
<p className="text-gray-800">{!!player && player.username}</p>
|
||||||
<div className="rounded-sm bg-gray-800 px-3 py-1 text-lg">
|
<div className="rounded-sm bg-gray-800 px-3 py-1 text-lg">
|
||||||
{!!player && player.points}
|
{!!player && player.points}
|
||||||
|
|||||||
@@ -2,17 +2,28 @@ import { usePlayerContext } from "@/context/player"
|
|||||||
import Form from "@/components/Form"
|
import Form from "@/components/Form"
|
||||||
import Button from "@/components/Button"
|
import Button from "@/components/Button"
|
||||||
import Input from "@/components/Input"
|
import Input from "@/components/Input"
|
||||||
import { useState } from "react"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
|
import { socket } from "@/context/socket"
|
||||||
|
|
||||||
export default function Room() {
|
export default function Room() {
|
||||||
const { player, dispatch } = usePlayerContext()
|
const { player, dispatch } = usePlayerContext()
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [roomId, setRoomId] = useState("207223")
|
const [roomId, setRoomId] = useState("")
|
||||||
|
|
||||||
const handleLogin = () => {
|
const handleLogin = () => {
|
||||||
dispatch({ type: "JOIN", payload: roomId })
|
socket.emit("player:checkRoom", roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
socket.on("game:successRoom", (roomId) => {
|
||||||
|
dispatch({ type: "JOIN", payload: roomId })
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off("game:successRoom")
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{loading && (
|
{loading && (
|
||||||
|
|||||||
@@ -1,21 +1,9 @@
|
|||||||
import Circle from "@/components/icons/Circle"
|
|
||||||
import Triangle from "@/components/icons/Triangle"
|
|
||||||
import Square from "@/components/icons/Square"
|
|
||||||
import Rhombus from "@/components/icons/Rhombus"
|
|
||||||
import AnswerButton from "../../AnswerButton"
|
import AnswerButton from "../../AnswerButton"
|
||||||
import { useSocketContext } from "@/context/socket"
|
import { useSocketContext } from "@/context/socket"
|
||||||
import Image from "next/image"
|
import Image from "next/image"
|
||||||
import { useEffect, useRef, useState } from "react"
|
import { useEffect, useRef, useState } from "react"
|
||||||
import clsx from "clsx"
|
import clsx from "clsx"
|
||||||
|
import { ANSWERS_COLORS, ANSWERS_ICONS } from "@/constants"
|
||||||
const answersColors = [
|
|
||||||
"bg-red-500",
|
|
||||||
"bg-blue-500",
|
|
||||||
"bg-yellow-500",
|
|
||||||
"bg-green-500",
|
|
||||||
]
|
|
||||||
|
|
||||||
const answersIcons = [Triangle, Rhombus, Circle, Square]
|
|
||||||
|
|
||||||
const calculatePercentages = (objectResponses) => {
|
const calculatePercentages = (objectResponses) => {
|
||||||
const keys = Object.keys(objectResponses)
|
const keys = Object.keys(objectResponses)
|
||||||
@@ -36,8 +24,6 @@ const calculatePercentages = (objectResponses) => {
|
|||||||
result[key] = ((objectResponses[key] / totalSum) * 100).toFixed() + "%"
|
result[key] = ((objectResponses[key] / totalSum) * 100).toFixed() + "%"
|
||||||
})
|
})
|
||||||
|
|
||||||
console.log(result)
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,6 +44,7 @@ export default function Answers({
|
|||||||
setPercentages(calculatePercentages(responses))
|
setPercentages(calculatePercentages(responses))
|
||||||
}, [responses])
|
}, [responses])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
socket.on("game:cooldown", (sec) => {
|
socket.on("game:cooldown", (sec) => {
|
||||||
setCooldown(sec)
|
setCooldown(sec)
|
||||||
})
|
})
|
||||||
@@ -66,6 +53,12 @@ export default function Answers({
|
|||||||
setTotalAnswer(count)
|
setTotalAnswer(count)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off("game:cooldown")
|
||||||
|
socket.off("game:playerAnswer")
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-1 flex-col justify-between">
|
<div className="flex h-full flex-1 flex-col justify-between">
|
||||||
<div className="mx-auto inline-flex h-full w-full max-w-7xl flex-1 flex-col items-center justify-center gap-5">
|
<div className="mx-auto inline-flex h-full w-full max-w-7xl flex-1 flex-col items-center justify-center gap-5">
|
||||||
@@ -84,7 +77,7 @@ export default function Answers({
|
|||||||
key={key}
|
key={key}
|
||||||
className={clsx(
|
className={clsx(
|
||||||
"flex flex-col justify-end self-end overflow-hidden rounded-md",
|
"flex flex-col justify-end self-end overflow-hidden rounded-md",
|
||||||
answersColors[key],
|
ANSWERS_COLORS[key],
|
||||||
)}
|
)}
|
||||||
style={{ height: percentages[key] }}
|
style={{ height: percentages[key] }}
|
||||||
>
|
>
|
||||||
@@ -115,10 +108,10 @@ export default function Answers({
|
|||||||
{answers.map((answer, key) => (
|
{answers.map((answer, key) => (
|
||||||
<AnswerButton
|
<AnswerButton
|
||||||
key={key}
|
key={key}
|
||||||
className={clsx(answersColors[key], {
|
className={clsx(ANSWERS_COLORS[key], {
|
||||||
"opacity-65": responses && correct !== key,
|
"opacity-65": responses && correct !== key,
|
||||||
})}
|
})}
|
||||||
icon={answersIcons[key]}
|
icon={ANSWERS_ICONS[key]}
|
||||||
onClick={() => socket.emit("player:selectedAnswer", key)}
|
onClick={() => socket.emit("player:selectedAnswer", key)}
|
||||||
>
|
>
|
||||||
{answer}
|
{answer}
|
||||||
|
|||||||
@@ -1,27 +1,25 @@
|
|||||||
import Circle from "@/components/icons/Circle"
|
import { ANSWERS_COLORS, ANSWERS_ICONS } from "@/constants"
|
||||||
import Rhombus from "@/components/icons/Rhombus"
|
import clsx from "clsx"
|
||||||
import Square from "@/components/icons/Square"
|
import { createElement } from "react"
|
||||||
import Triangle from "@/components/icons/Triangle"
|
|
||||||
|
|
||||||
export default function Prepared({ data: { totalAnswers, questionNumber } }) {
|
export default function Prepared({ data: { totalAnswers, questionNumber } }) {
|
||||||
return (
|
return (
|
||||||
<section className="anim-show relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-center">
|
<section className="anim-show relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-center">
|
||||||
<h2 className="anim-show mb-10 text-center text-2xl font-bold text-white drop-shadow-lg md:text-4xl lg:text-5xl">
|
<h2 className="anim-show mb-20 text-center text-3xl font-bold text-white drop-shadow-lg md:text-4xl lg:text-5xl">
|
||||||
Question #{questionNumber}
|
Question #{questionNumber}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="anim-quizz grid h-[300px] w-60 grid-cols-2 gap-4 rounded-2xl bg-gray-700 p-5 md:h-[400px] md:w-80 ">
|
<div className="anim-quizz grid aspect-square w-60 grid-cols-2 gap-4 rounded-2xl bg-gray-700 p-5 md:w-60">
|
||||||
<div className="button shadow-inset flex aspect-square h-full w-full items-center justify-center rounded-2xl bg-red-500">
|
{[...Array(totalAnswers)].map((_, key) => (
|
||||||
<Triangle className="h-10 md:h-14" />
|
<div
|
||||||
</div>
|
key={key}
|
||||||
<div className="button shadow-inset flex aspect-square h-full w-full items-center justify-center rounded-2xl bg-blue-500">
|
className={clsx(
|
||||||
<Rhombus className="h-10 md:h-14" />
|
"button shadow-inset flex aspect-square h-full w-full items-center justify-center rounded-2xl",
|
||||||
</div>
|
ANSWERS_COLORS[key],
|
||||||
<div className="button shadow-inset flex aspect-square h-full w-full items-center justify-center rounded-2xl bg-yellow-500">
|
)}
|
||||||
<Square className="h-10 md:h-14" />
|
>
|
||||||
</div>
|
{createElement(ANSWERS_ICONS[key], { className: "h-10 md:h-14" })}
|
||||||
<div className="button shadow-inset flex aspect-square h-full w-full items-center justify-center rounded-2xl bg-green-500">
|
|
||||||
<Circle className="h-10 md:h-14" />
|
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export default function Question({ data: { question } }) {
|
|||||||
return (
|
return (
|
||||||
<section className="relative mx-auto flex h-full w-full max-w-7xl flex-1 flex-col items-center px-4">
|
<section className="relative mx-auto flex h-full w-full max-w-7xl flex-1 flex-col items-center px-4">
|
||||||
<div className="flex flex-1 items-center">
|
<div className="flex flex-1 items-center">
|
||||||
<h2 className="anim-show text-center text-2xl font-bold text-white drop-shadow-lg md:text-4xl lg:text-5xl">
|
<h2 className="anim-show text-center text-3xl font-bold text-white drop-shadow-lg md:text-4xl lg:text-5xl">
|
||||||
{question}
|
{question}
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
43
src/components/game/states/Room.jsx
Normal file
43
src/components/game/states/Room.jsx
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { useSocketContext } from "@/context/socket"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
|
export default function Room({ data: { text, inviteCode } }) {
|
||||||
|
const { socket } = useSocketContext()
|
||||||
|
const [playerList, setPlayerList] = useState([])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
socket.on("manager:newPlayer", (player) => {
|
||||||
|
setPlayerList([...playerList, player])
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off("manager:newPlayer")
|
||||||
|
}
|
||||||
|
}, [playerList])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-center px-2">
|
||||||
|
<div className="mb-10 rotate-3 rounded-md bg-white px-6 py-4 text-6xl font-extrabold">
|
||||||
|
{inviteCode}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h2 className="mb-4 text-4xl font-bold text-white drop-shadow-lg">
|
||||||
|
{text}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap gap-3">
|
||||||
|
{playerList.map((player) => (
|
||||||
|
<div
|
||||||
|
key={player.id}
|
||||||
|
className="shadow-inset rounded-md bg-primary px-4 py-3 font-bold text-white"
|
||||||
|
onClick={() => socket.emit("manager:kickPlayer", player.id)}
|
||||||
|
>
|
||||||
|
<span className="cursor-pointer text-xl drop-shadow-md hover:line-through">
|
||||||
|
{player.username}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,37 +1,48 @@
|
|||||||
import { useSocketContext } from "@/context/socket"
|
import { useSocketContext } from "@/context/socket"
|
||||||
import { useState } from "react"
|
import clsx from "clsx"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
export default function Start({ data: { text, inviteCode } }) {
|
export default function Start({ data: { time, subject } }) {
|
||||||
const { socket } = useSocketContext()
|
const { socket } = useSocketContext()
|
||||||
const [playerList, setPlayerList] = useState([])
|
const [showTitle, setShowTitle] = useState(true)
|
||||||
|
const [cooldown, setCooldown] = useState(time)
|
||||||
|
|
||||||
socket.on("manager:newPlayer", (player) => {
|
useEffect(() => {
|
||||||
setPlayerList([...playerList, player])
|
socket.on("game:startCooldown", () => {
|
||||||
|
setShowTitle(false)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
socket.on("game:cooldown", (sec) => {
|
||||||
|
setCooldown(sec)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off("game:startCooldown")
|
||||||
|
socket.off("game:cooldown")
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-center px-2">
|
<section className="relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-center">
|
||||||
<div className="mb-10 rotate-3 rounded-md bg-white px-6 py-4 text-6xl font-extrabold">
|
{showTitle ? (
|
||||||
{inviteCode}
|
<h2 className="anim-show text-center text-3xl font-bold text-white drop-shadow-lg md:text-4xl lg:text-5xl">
|
||||||
</div>
|
{subject}
|
||||||
|
|
||||||
<h2 className="mb-4 text-4xl font-bold text-white drop-shadow-lg">
|
|
||||||
{text}
|
|
||||||
</h2>
|
</h2>
|
||||||
|
) : (
|
||||||
<div className="flex flex-wrap gap-3">
|
<>
|
||||||
{playerList.map((player) => (
|
|
||||||
<div
|
<div
|
||||||
key={player.id}
|
className={clsx(
|
||||||
className="shadow-inset rounded-md bg-primary px-4 py-3 font-bold text-white"
|
`anim-show aspect-square h-32 bg-primary transition-all md:h-60`,
|
||||||
onClick={() => socket.emit("manager:kickPlayer", player.id)}
|
)}
|
||||||
>
|
style={{
|
||||||
<span className="cursor-pointer text-xl drop-shadow-md hover:line-through">
|
transform: `rotate(${45 * (time - cooldown)}deg)`,
|
||||||
{player.username}
|
}}
|
||||||
|
></div>
|
||||||
|
<span className="absolute text-6xl font-bold text-white drop-shadow-md md:text-8xl">
|
||||||
|
{cooldown}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</>
|
||||||
))}
|
)}
|
||||||
</div>
|
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export default function Wait({ data: { text } }) {
|
|||||||
return (
|
return (
|
||||||
<section className="relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-center">
|
<section className="relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-center">
|
||||||
<Loader />
|
<Loader />
|
||||||
<h2 className="mt-5 text-4xl font-bold text-white drop-shadow-lg">
|
<h2 className="mt-5 text-center text-3xl font-bold text-white drop-shadow-lg md:text-4xl lg:text-5xl">
|
||||||
{text}
|
{text}
|
||||||
</h2>
|
</h2>
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -1 +1,15 @@
|
|||||||
|
import Circle from "@/components/icons/Circle"
|
||||||
|
import Triangle from "@/components/icons/Triangle"
|
||||||
|
import Square from "@/components/icons/Square"
|
||||||
|
import Rhombus from "@/components/icons/Rhombus"
|
||||||
|
|
||||||
export const WEBSOCKET_URL = "http://localhost:5157" ///"https://rahoot.ralex.app"
|
export const WEBSOCKET_URL = "http://localhost:5157" ///"https://rahoot.ralex.app"
|
||||||
|
|
||||||
|
export const ANSWERS_COLORS = [
|
||||||
|
"bg-red-500",
|
||||||
|
"bg-blue-500",
|
||||||
|
"bg-yellow-500",
|
||||||
|
"bg-green-500",
|
||||||
|
]
|
||||||
|
|
||||||
|
export const ANSWERS_ICONS = [Triangle, Rhombus, Circle, Square]
|
||||||
|
|||||||
@@ -5,15 +5,17 @@ import Prepared from "@/components/game/states/Prepared"
|
|||||||
import Question from "@/components/game/states/Question"
|
import Question from "@/components/game/states/Question"
|
||||||
import Result from "@/components/game/states/Result"
|
import Result from "@/components/game/states/Result"
|
||||||
import Wait from "@/components/game/states/Wait"
|
import Wait from "@/components/game/states/Wait"
|
||||||
|
import Start from "@/components/game/states/Start"
|
||||||
import { usePlayerContext } from "@/context/player"
|
import { usePlayerContext } from "@/context/player"
|
||||||
import { useSocketContext } from "@/context/socket"
|
import { useSocketContext } from "@/context/socket"
|
||||||
import { useRouter } from "next/router"
|
import { useRouter } from "next/router"
|
||||||
import { createElement, useState } from "react"
|
import { createElement, useMemo, useState } from "react"
|
||||||
|
|
||||||
const gameStateComponent = {
|
const gameStateComponent = {
|
||||||
SELECT_ANSWER: Answers,
|
SELECT_ANSWER: Answers,
|
||||||
SHOW_QUESTION: Question,
|
SHOW_QUESTION: Question,
|
||||||
WAIT: Wait,
|
WAIT: Wait,
|
||||||
|
SHOW_START: Start,
|
||||||
SHOW_RESULT: Result,
|
SHOW_RESULT: Result,
|
||||||
SHOW_PREPARED: Prepared,
|
SHOW_PREPARED: Prepared,
|
||||||
}
|
}
|
||||||
@@ -40,6 +42,7 @@ export default function Game() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useMemo(() => {
|
||||||
socket.on("game:status", (status) => {
|
socket.on("game:status", (status) => {
|
||||||
setState({
|
setState({
|
||||||
...state,
|
...state,
|
||||||
@@ -51,6 +54,11 @@ export default function Game() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off("game:status")
|
||||||
|
}
|
||||||
|
}, [state])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GameWrapper>
|
<GameWrapper>
|
||||||
{gameStateComponent[state.status.name] &&
|
{gameStateComponent[state.status.name] &&
|
||||||
|
|||||||
@@ -4,16 +4,28 @@ import Form from "@/components/Form"
|
|||||||
import Button from "@/components/Button"
|
import Button from "@/components/Button"
|
||||||
import Input from "@/components/Input"
|
import Input from "@/components/Input"
|
||||||
import logo from "@/assets/logo.svg"
|
import logo from "@/assets/logo.svg"
|
||||||
import { useState } from "react"
|
import { useEffect, useMemo, useState } from "react"
|
||||||
import Loader from "@/components/Loader"
|
import Loader from "@/components/Loader"
|
||||||
import { usePlayerContext } from "@/context/player"
|
import { usePlayerContext } from "@/context/player"
|
||||||
import Room from "@/components/game/join/Room"
|
import Room from "@/components/game/join/Room"
|
||||||
import Username from "@/components/game/join/Username"
|
import Username from "@/components/game/join/Username"
|
||||||
|
import { useSocketContext } from "@/context/socket"
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
const { player } = usePlayerContext()
|
const { player, dispatch } = usePlayerContext()
|
||||||
|
const { socket } = useSocketContext()
|
||||||
|
|
||||||
|
useMemo(() => {
|
||||||
|
socket.on("game:errorMessage", (message) => {
|
||||||
|
console.log(message)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off("game:errorMessage")
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="relative flex min-h-screen flex-col items-center justify-center">
|
<section className="relative flex min-h-screen flex-col items-center justify-center">
|
||||||
@@ -28,7 +40,7 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Image src={logo} className="mb-6 h-32" />
|
<Image src={logo} className="mb-6 h-32" alt="logo" />
|
||||||
|
|
||||||
{!player ? <Room /> : <Username />}
|
{!player ? <Room /> : <Username />}
|
||||||
</section>
|
</section>
|
||||||
|
|||||||
@@ -4,14 +4,16 @@ import Answers from "@/components/game/states/Answers"
|
|||||||
import Leaderboard from "@/components/game/states/Leaderboard"
|
import Leaderboard from "@/components/game/states/Leaderboard"
|
||||||
import Prepared from "@/components/game/states/Prepared"
|
import Prepared from "@/components/game/states/Prepared"
|
||||||
import Question from "@/components/game/states/Question"
|
import Question from "@/components/game/states/Question"
|
||||||
|
import Room from "@/components/game/states/Room"
|
||||||
import Start from "@/components/game/states/Start"
|
import Start from "@/components/game/states/Start"
|
||||||
import Wait from "@/components/game/states/Wait"
|
import Wait from "@/components/game/states/Wait"
|
||||||
import { usePlayerContext } from "@/context/player"
|
import { usePlayerContext } from "@/context/player"
|
||||||
import { useSocketContext } from "@/context/socket"
|
import { useSocketContext } from "@/context/socket"
|
||||||
import { useRouter } from "next/router"
|
import { useRouter } from "next/router"
|
||||||
import { createElement, useState } from "react"
|
import { createElement, useEffect, useState } from "react"
|
||||||
|
|
||||||
const gameStateComponent = {
|
const gameStateComponent = {
|
||||||
|
SHOW_ROOM: Room,
|
||||||
SHOW_START: Start,
|
SHOW_START: Start,
|
||||||
SELECT_ANSWER: Answers,
|
SELECT_ANSWER: Answers,
|
||||||
SHOW_QUESTION: Question,
|
SHOW_QUESTION: Question,
|
||||||
@@ -23,13 +25,12 @@ const gameStateComponent = {
|
|||||||
|
|
||||||
export default function Manager() {
|
export default function Manager() {
|
||||||
const { socket } = useSocketContext()
|
const { socket } = useSocketContext()
|
||||||
const { player } = usePlayerContext()
|
|
||||||
|
|
||||||
const [nextText, setNextText] = useState("Start")
|
const [nextText, setNextText] = useState("Start")
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
created: false,
|
created: false,
|
||||||
status: {
|
status: {
|
||||||
name: "SHOW_START",
|
name: "SHOW_ROOM",
|
||||||
data: { text: "Waiting for the players" },
|
data: { text: "Waiting for the players" },
|
||||||
},
|
},
|
||||||
question: {
|
question: {
|
||||||
@@ -38,6 +39,7 @@ export default function Manager() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
socket.on("game:status", (status) => {
|
socket.on("game:status", (status) => {
|
||||||
setState({
|
setState({
|
||||||
...state,
|
...state,
|
||||||
@@ -63,6 +65,12 @@ export default function Manager() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off("game:status")
|
||||||
|
socket.off("manager:inviteCode")
|
||||||
|
}
|
||||||
|
}, [state])
|
||||||
|
|
||||||
const handleCreate = () => {
|
const handleCreate = () => {
|
||||||
socket.emit("manager:createRoom")
|
socket.emit("manager:createRoom")
|
||||||
}
|
}
|
||||||
@@ -71,7 +79,7 @@ export default function Manager() {
|
|||||||
setNextText("Skip")
|
setNextText("Skip")
|
||||||
|
|
||||||
switch (state.status.name) {
|
switch (state.status.name) {
|
||||||
case "SHOW_START":
|
case "SHOW_ROOM":
|
||||||
socket.emit("manager:startGame")
|
socket.emit("manager:startGame")
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.anim-quizz .button {
|
.anim-quizz .button {
|
||||||
|
box-shadow: rgba(0, 0, 0, 0.25) -4px -4px inset;
|
||||||
animation: quizzButton 0.8s ease-out;
|
animation: quizzButton 0.8s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +95,14 @@
|
|||||||
0% {
|
0% {
|
||||||
transform: scale(0) perspective(1200px) rotateY(-60deg) rotateX(60deg)
|
transform: scale(0) perspective(1200px) rotateY(-60deg) rotateX(60deg)
|
||||||
translateZ(100px);
|
translateZ(100px);
|
||||||
box-shadow: 40px 10px 0 rgba(20, 24, 29, 1);
|
}
|
||||||
|
60% {
|
||||||
|
transform: scale(1) perspective(1200px) rotateY(-15deg) rotateX(15deg)
|
||||||
|
translateZ(100px);
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
transform: scale(0.8) perspective(1200px) rotateY(-15deg) rotateX(15deg)
|
||||||
|
translateZ(100px);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: scale(1) perspective(1200px) rotateY(-15deg) rotateX(15deg)
|
transform: scale(1) perspective(1200px) rotateY(-15deg) rotateX(15deg)
|
||||||
@@ -106,6 +114,12 @@
|
|||||||
0% {
|
0% {
|
||||||
transform: scale(0);
|
transform: scale(0);
|
||||||
}
|
}
|
||||||
|
60% {
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
80% {
|
||||||
|
transform: scale(0.8);
|
||||||
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
safelist: ["grid-cols-4", "grid-cols-3", "grid-cols-2"],
|
safelist: ["grid-cols-4", "grid-cols-3", "grid-cols-2", {
|
||||||
|
pattern: /bg-(red|blue|yellow|green)/}],
|
||||||
plugins: [],
|
plugins: [],
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user