mirror of
https://github.com/randyjc/Rahoot.git
synced 2026-03-13 20:15:35 +01:00
Large patch
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
"next/core-web-vitals",
|
"next/core-web-vitals",
|
||||||
"plugin:react/recommended"
|
"plugin:react/recommended"
|
||||||
],
|
],
|
||||||
"plugins": ["react", "react-hooks"],
|
"plugins": ["react"],
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
"browser": true,
|
||||||
"node": true,
|
"node": true,
|
||||||
|
|||||||
@@ -9,6 +9,16 @@ const nextConfig = {
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
},*/
|
},*/
|
||||||
|
images: {
|
||||||
|
remotePatterns: [
|
||||||
|
{
|
||||||
|
protocol: "https",
|
||||||
|
hostname: "images.unsplash.com",
|
||||||
|
port: "",
|
||||||
|
pathname: "/**",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
export default nextConfig
|
export default nextConfig
|
||||||
|
|||||||
@@ -2,8 +2,10 @@ import { Server } from "socket.io"
|
|||||||
import { GAME_STATE_INIT } from "./quizz.config.js"
|
import { GAME_STATE_INIT } from "./quizz.config.js"
|
||||||
import Manager from "./roles/manager.js"
|
import Manager from "./roles/manager.js"
|
||||||
import Player from "./roles/player.js"
|
import Player from "./roles/player.js"
|
||||||
|
import { abortCooldown } from "./utils/cooldown.js"
|
||||||
|
import deepClone from "./utils/deepClone.js"
|
||||||
|
|
||||||
let gameState = GAME_STATE_INIT
|
let gameState = deepClone(GAME_STATE_INIT)
|
||||||
|
|
||||||
const io = new Server({
|
const io = new Server({
|
||||||
cors: {
|
cors: {
|
||||||
@@ -25,8 +27,8 @@ io.on("connection", (socket) => {
|
|||||||
Player.join(gameState, io, socket, player),
|
Player.join(gameState, io, socket, player),
|
||||||
)
|
)
|
||||||
|
|
||||||
socket.on("manager:createRoom", () =>
|
socket.on("manager:createRoom", (password) =>
|
||||||
Manager.createRoom(gameState, io, socket),
|
Manager.createRoom(gameState, io, socket, password),
|
||||||
)
|
)
|
||||||
socket.on("manager:kickPlayer", (playerId) =>
|
socket.on("manager:kickPlayer", (playerId) =>
|
||||||
Manager.kickPlayer(gameState, io, socket, playerId),
|
Manager.kickPlayer(gameState, io, socket, playerId),
|
||||||
@@ -48,9 +50,21 @@ io.on("connection", (socket) => {
|
|||||||
|
|
||||||
socket.on("disconnect", () => {
|
socket.on("disconnect", () => {
|
||||||
console.log(`user disconnected ${socket.id}`)
|
console.log(`user disconnected ${socket.id}`)
|
||||||
/*if (gameState.manager === socket.id) {
|
if (gameState.manager === socket.id) {
|
||||||
console.log("Reset game")
|
console.log("Reset game")
|
||||||
gameState = gameStateInit
|
io.to(gameState.room).emit("game:reset")
|
||||||
}*/
|
gameState.started = false
|
||||||
|
gameState = deepClone(GAME_STATE_INIT)
|
||||||
|
|
||||||
|
abortCooldown()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const player = gameState.players.find((p) => p.id === socket.id)
|
||||||
|
|
||||||
|
if (player) {
|
||||||
|
gameState.players = gameState.players.filter((p) => p.id !== socket.id)
|
||||||
|
socket.to(gameState.manager).emit("manager:removePlayer", player.id)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
export const GAME_STATE_INIT = {
|
export const GAME_STATE_INIT = {
|
||||||
started: false,
|
started: false,
|
||||||
|
password: "PASSWORD",
|
||||||
players: [],
|
players: [],
|
||||||
playersAnswer: [],
|
playersAnswer: [],
|
||||||
manager: null,
|
manager: null,
|
||||||
room: null,
|
room: null,
|
||||||
currentQuestion: 0,
|
currentQuestion: 0,
|
||||||
roundStartTime: 0,
|
roundStartTime: 0,
|
||||||
|
subject: "Adobe",
|
||||||
questions: [
|
questions: [
|
||||||
{
|
{
|
||||||
question: "Who are the founders of Adobe?",
|
question: "Who are the founders of Adobe?",
|
||||||
@@ -15,21 +17,24 @@ export const GAME_STATE_INIT = {
|
|||||||
"Jhon Jonse and Charles Geskie",
|
"Jhon Jonse and Charles Geskie",
|
||||||
"Bill Gate",
|
"Bill Gate",
|
||||||
],
|
],
|
||||||
image: "/question/Adobe-Logo.png",
|
|
||||||
solution: 1,
|
solution: 1,
|
||||||
|
cooldow: 5,
|
||||||
time: 15,
|
time: 15,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
question: "What is Adobe's most famous software?",
|
question: "What is Adobe's most famous software?",
|
||||||
answers: ["Encore", "AfterEffect", "Creative Cloud", "Photoshop"],
|
answers: ["Encore", "AfterEffect", "Creative Cloud", "Photoshop"],
|
||||||
image: "/question/Adobe-Packages.webp",
|
image:
|
||||||
|
"https://images.unsplash.com/photo-1626785774573-4b799315345d?q=80&w=500&auto=webp",
|
||||||
solution: 3,
|
solution: 3,
|
||||||
|
cooldown: 5,
|
||||||
time: 15,
|
time: 15,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
question: "When was Adobe created?",
|
question: "When was Adobe created?",
|
||||||
answers: ["2000", "1982", "2003", "1987"],
|
answers: ["2000", "1982", "2003", "1987"],
|
||||||
solution: 1,
|
solution: 1,
|
||||||
|
cooldown: 5,
|
||||||
time: 15,
|
time: 15,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -40,8 +45,8 @@ export const GAME_STATE_INIT = {
|
|||||||
"DowTown, Texas",
|
"DowTown, Texas",
|
||||||
"Tokyo, Japan",
|
"Tokyo, Japan",
|
||||||
],
|
],
|
||||||
image: "/question/Adobe_World_Headquarters.jpg",
|
|
||||||
solution: 0,
|
solution: 0,
|
||||||
|
cooldown: 5,
|
||||||
time: 15,
|
time: 15,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -52,8 +57,10 @@ export const GAME_STATE_INIT = {
|
|||||||
"25,988 employees",
|
"25,988 employees",
|
||||||
"5,073 employees",
|
"5,073 employees",
|
||||||
],
|
],
|
||||||
image: "/question/000012204568_Large.jpg",
|
image:
|
||||||
|
"https://images.unsplash.com/photo-1504384308090-c894fdcc538d?q=80&w=500&auto=webp",
|
||||||
solution: 2,
|
solution: 2,
|
||||||
|
cooldown: 5,
|
||||||
time: 15,
|
time: 15,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -64,8 +71,10 @@ export const GAME_STATE_INIT = {
|
|||||||
"Mark Java",
|
"Mark Java",
|
||||||
"Shantanu Narayen",
|
"Shantanu Narayen",
|
||||||
],
|
],
|
||||||
image: "/question/guess-the-person.png",
|
image:
|
||||||
|
"https://images.unsplash.com/photo-1435348773030-a1d74f568bc2?q=80&w=500&auto=webp",
|
||||||
solution: 3,
|
solution: 3,
|
||||||
|
cooldown: 5,
|
||||||
time: 15,
|
time: 15,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -76,7 +85,10 @@ export const GAME_STATE_INIT = {
|
|||||||
"Logistics software",
|
"Logistics software",
|
||||||
"Other",
|
"Other",
|
||||||
],
|
],
|
||||||
|
image:
|
||||||
|
"https://images.unsplash.com/photo-1582736317407-371893d9e146?q=80&w=500&auto=webp",
|
||||||
solution: 0,
|
solution: 0,
|
||||||
|
cooldown: 5,
|
||||||
time: 15,
|
time: 15,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -2,11 +2,17 @@ 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"
|
import { cooldown, sleep } from "../utils/cooldown.js"
|
||||||
|
import deepClone from "../utils/deepClone.js"
|
||||||
|
|
||||||
const Manager = {
|
const Manager = {
|
||||||
createRoom: (game, io, socket) => {
|
createRoom: (game, io, socket, password) => {
|
||||||
|
if (game.password !== password) {
|
||||||
|
io.to(socket.id).emit("game:errorMessage", "Bad Password")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (game.manager || game.room) {
|
if (game.manager || game.room) {
|
||||||
io.to(socket.id).emit("message", "Already manager")
|
io.to(socket.id).emit("game:errorMessage", "Already manager")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,6 +34,7 @@ const Manager = {
|
|||||||
const player = game.players.find((p) => p.id === playerId)
|
const player = game.players.find((p) => p.id === playerId)
|
||||||
game.players = game.players.filter((p) => p.id !== playerId)
|
game.players = game.players.filter((p) => p.id !== playerId)
|
||||||
|
|
||||||
|
io.in(playerId).socketsLeave(game.room)
|
||||||
io.to(player.id).emit("game:kick")
|
io.to(player.id).emit("game:kick")
|
||||||
io.to(game.manager).emit("manager:playerKicked", player.id)
|
io.to(game.manager).emit("manager:playerKicked", player.id)
|
||||||
},
|
},
|
||||||
@@ -72,18 +79,19 @@ const Manager = {
|
|||||||
|
|
||||||
showLoaderboard: (game, io, socket) => {
|
showLoaderboard: (game, io, socket) => {
|
||||||
if (!game.questions[game.currentQuestion + 1]) {
|
if (!game.questions[game.currentQuestion + 1]) {
|
||||||
io.to(socket).emit("game:status", {
|
socket.emit("game:status", {
|
||||||
name: "FINISH",
|
name: "FINISH",
|
||||||
data: {
|
data: {
|
||||||
winners: game.players.slice(0, 3).sort((a, b) => b.points - a.points),
|
subject: game.subject,
|
||||||
|
top: game.players.slice(0, 3).sort((a, b) => b.points - a.points),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
game = GAME_STATE_INIT
|
game = deepClone(GAME_STATE_INIT)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
io.to(socket.id).emit("game:status", {
|
socket.emit("game:status", {
|
||||||
name: "SHOW_LEADERBOARD",
|
name: "SHOW_LEADERBOARD",
|
||||||
data: {
|
data: {
|
||||||
leaderboard: game.players
|
leaderboard: game.players
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ const Player = {
|
|||||||
|
|
||||||
game.players.push(playerData)
|
game.players.push(playerData)
|
||||||
|
|
||||||
io.to(socket.id).emit("game:successJoin")
|
socket.emit("game:successJoin")
|
||||||
},
|
},
|
||||||
|
|
||||||
selectedAnswer: (game, io, socket, answerKey) => {
|
selectedAnswer: (game, io, socket, answerKey) => {
|
||||||
@@ -72,7 +72,7 @@ const Player = {
|
|||||||
points: convertTimeToPoint(game.roundStartTime, question.time),
|
points: convertTimeToPoint(game.roundStartTime, question.time),
|
||||||
})
|
})
|
||||||
|
|
||||||
io.to(socket.id).emit("game:status", {
|
socket.emit("game:status", {
|
||||||
name: "WAIT",
|
name: "WAIT",
|
||||||
data: { text: "Waiting for the players to answer" },
|
data: { text: "Waiting for the players to answer" },
|
||||||
})
|
})
|
||||||
|
|||||||
20
socket/src/utils/deepClone.js
Normal file
20
socket/src/utils/deepClone.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
const deepClone = (obj) => {
|
||||||
|
if (obj === null || typeof obj !== "object") {
|
||||||
|
return obj
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(obj)) {
|
||||||
|
return obj.map(deepClone)
|
||||||
|
}
|
||||||
|
|
||||||
|
const clonedObj = {}
|
||||||
|
for (const key in obj) {
|
||||||
|
if (obj.hasOwnProperty(key)) {
|
||||||
|
clonedObj[key] = deepClone(obj[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clonedObj
|
||||||
|
}
|
||||||
|
|
||||||
|
export default deepClone
|
||||||
@@ -3,6 +3,8 @@ import { cooldown, sleep } from "./cooldown.js"
|
|||||||
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]
|
||||||
|
|
||||||
|
if (!game.started) return
|
||||||
|
|
||||||
io.to(game.room).emit("game:updateQuestion", {
|
io.to(game.room).emit("game:updateQuestion", {
|
||||||
current: game.currentQuestion + 1,
|
current: game.currentQuestion + 1,
|
||||||
total: game.questions.length,
|
total: game.questions.length,
|
||||||
@@ -16,19 +18,22 @@ export const startRound = async (game, io, socket) => {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
await sleep(4)
|
await sleep(2)
|
||||||
|
|
||||||
|
if (!game.started) return
|
||||||
|
|
||||||
io.to(game.room).emit("game:status", {
|
io.to(game.room).emit("game:status", {
|
||||||
name: "SHOW_QUESTION",
|
name: "SHOW_QUESTION",
|
||||||
data: {
|
data: {
|
||||||
question: question.question,
|
question: question.question,
|
||||||
number: game.currentQuestion + 1,
|
|
||||||
image: question.image,
|
image: question.image,
|
||||||
cooldown: 6,
|
cooldown: question.cooldown,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
await sleep(6)
|
await sleep(question.cooldown)
|
||||||
|
|
||||||
|
if (!game.started) return
|
||||||
|
|
||||||
game.roundStartTime = Date.now()
|
game.roundStartTime = Date.now()
|
||||||
|
|
||||||
@@ -45,6 +50,8 @@ export const startRound = async (game, io, socket) => {
|
|||||||
|
|
||||||
await cooldown(question.time, io, game.room)
|
await cooldown(question.time, io, game.room)
|
||||||
|
|
||||||
|
if (!game.started) return
|
||||||
|
|
||||||
game.players.map(async (player) => {
|
game.players.map(async (player) => {
|
||||||
let playerAnswer = await game.playersAnswer.find((p) => p.id === player.id)
|
let playerAnswer = await game.playersAnswer.find((p) => p.id === player.id)
|
||||||
|
|
||||||
@@ -55,9 +62,8 @@ export const startRound = async (game, io, socket) => {
|
|||||||
let points =
|
let points =
|
||||||
(isCorrect && Math.round(playerAnswer && playerAnswer.points)) || 0
|
(isCorrect && Math.round(playerAnswer && playerAnswer.points)) || 0
|
||||||
|
|
||||||
game.players.find((p) => p.id === player.id).points += points
|
player.points += points
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
let sortPlayers = game.players.sort((a, b) => b.points - a.points)
|
let sortPlayers = game.players.sort((a, b) => b.points - a.points)
|
||||||
|
|
||||||
let rank = sortPlayers.findIndex((p) => p.id === player.id) + 1
|
let rank = sortPlayers.findIndex((p) => p.id === player.id) + 1
|
||||||
@@ -74,13 +80,12 @@ export const startRound = async (game, io, socket) => {
|
|||||||
aheadOfMe: aheadPlayer ? aheadPlayer.username : null,
|
aheadOfMe: aheadPlayer ? aheadPlayer.username : null,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}, 200)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
let totalParType = {}
|
let totalType = {}
|
||||||
|
|
||||||
game.playersAnswer.forEach(({ answer }) => {
|
game.playersAnswer.forEach(({ answer }) => {
|
||||||
totalParType[answer] = (totalParType[answer] || 0) + 1
|
totalType[answer] = (totalType[answer] || 0) + 1
|
||||||
})
|
})
|
||||||
|
|
||||||
// Manager
|
// Manager
|
||||||
@@ -88,7 +93,7 @@ export const startRound = async (game, io, socket) => {
|
|||||||
name: "SHOW_RESPONSES",
|
name: "SHOW_RESPONSES",
|
||||||
data: {
|
data: {
|
||||||
question: game.questions[game.currentQuestion].question,
|
question: game.questions[game.currentQuestion].question,
|
||||||
responses: totalParType,
|
responses: totalType,
|
||||||
correct: game.questions[game.currentQuestion].solution,
|
correct: game.questions[game.currentQuestion].solution,
|
||||||
answers: game.questions[game.currentQuestion].answers,
|
answers: game.questions[game.currentQuestion].answers,
|
||||||
image: game.questions[game.currentQuestion].image,
|
image: game.questions[game.currentQuestion].image,
|
||||||
|
|||||||
47
src/components/ManagerPassword.jsx
Normal file
47
src/components/ManagerPassword.jsx
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import Image from "next/image"
|
||||||
|
import { usePlayerContext } from "@/context/player"
|
||||||
|
import Form from "@/components/Form"
|
||||||
|
import Button from "@/components/Button"
|
||||||
|
import Input from "@/components/Input"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
import { socket } from "@/context/socket"
|
||||||
|
import logo from "@/assets/logo.svg"
|
||||||
|
import toast from "react-hot-toast"
|
||||||
|
|
||||||
|
export default function ManagerPassword() {
|
||||||
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [password, setPassword] = useState("")
|
||||||
|
|
||||||
|
const handleCreate = () => {
|
||||||
|
socket.emit("manager:createRoom", password)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
socket.on("game:errorMessage", (message) => {
|
||||||
|
toast.error(message)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.off("game:errorMessage")
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="relative flex min-h-screen flex-col items-center justify-center">
|
||||||
|
<div className="absolute h-full w-full overflow-hidden">
|
||||||
|
<div className="absolute -left-[15vmin] -top-[15vmin] min-h-[75vmin] min-w-[75vmin] rounded-full bg-primary/15"></div>
|
||||||
|
<div className="absolute -bottom-[15vmin] -right-[15vmin] min-h-[75vmin] min-w-[75vmin] rotate-45 bg-primary/15"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Image src={logo} className="mb-6 h-32" alt="logo" />
|
||||||
|
|
||||||
|
<Form>
|
||||||
|
<Input
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
placeholder="Manager password"
|
||||||
|
/>
|
||||||
|
<Button onClick={() => handleCreate()}>Submit</Button>
|
||||||
|
</Form>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
import clsx from "clsx"
|
|
||||||
import { Toaster as ToasterRaw, ToastBar } from "react-hot-toast"
|
import { Toaster as ToasterRaw, ToastBar } from "react-hot-toast"
|
||||||
|
|
||||||
const Toaster = () => (
|
const Toaster = () => (
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ 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 [roomId, setRoomId] = useState("")
|
const [roomId, setRoomId] = useState("")
|
||||||
|
|
||||||
const handleLogin = () => {
|
const handleLogin = () => {
|
||||||
@@ -25,13 +24,6 @@ export default function Room() {
|
|||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
{loading && (
|
|
||||||
<div className="absolute z-30 flex h-full w-full items-center justify-center bg-black/40">
|
|
||||||
<Loader />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form>
|
<Form>
|
||||||
<Input
|
<Input
|
||||||
onChange={(e) => setRoomId(e.target.value)}
|
onChange={(e) => setRoomId(e.target.value)}
|
||||||
@@ -39,6 +31,5 @@ export default function Room() {
|
|||||||
/>
|
/>
|
||||||
<Button onClick={() => handleLogin()}>Submit</Button>
|
<Button onClick={() => handleLogin()}>Submit</Button>
|
||||||
</Form>
|
</Form>
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { useRouter } from "next/router"
|
|||||||
export default function Username() {
|
export default function Username() {
|
||||||
const { socket } = useSocketContext()
|
const { socket } = useSocketContext()
|
||||||
const { player, dispatch } = usePlayerContext()
|
const { player, dispatch } = usePlayerContext()
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const [username, setUsername] = useState("")
|
const [username, setUsername] = useState("")
|
||||||
|
|
||||||
@@ -33,13 +32,6 @@ export default function Username() {
|
|||||||
}, [username])
|
}, [username])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
{loading && (
|
|
||||||
<div className="absolute z-30 flex h-full w-full items-center justify-center bg-black/40">
|
|
||||||
<Loader />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Form>
|
<Form>
|
||||||
<Input
|
<Input
|
||||||
onChange={(e) => setUsername(e.target.value)}
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
@@ -47,6 +39,5 @@ export default function Username() {
|
|||||||
/>
|
/>
|
||||||
<Button onClick={() => handleJoin()}>Submit</Button>
|
<Button onClick={() => handleJoin()}>Submit</Button>
|
||||||
</Form>
|
</Form>
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import AnswerButton from "../../AnswerButton"
|
import AnswerButton from "../../AnswerButton"
|
||||||
import { useSocketContext } from "@/context/socket"
|
import { useSocketContext } from "@/context/socket"
|
||||||
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"
|
import { ANSWERS_COLORS, ANSWERS_ICONS } from "@/constants"
|
||||||
@@ -66,7 +65,9 @@ export default function Answers({
|
|||||||
{question}
|
{question}
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
{/*<Image src={image} className="h-60 w-auto rounded-md" />*/}
|
{!!image && !responses && (
|
||||||
|
<img src={image} className="h-48 max-h-60 w-auto rounded-md" />
|
||||||
|
)}
|
||||||
|
|
||||||
{responses && (
|
{responses && (
|
||||||
<div
|
<div
|
||||||
|
|||||||
106
src/components/game/states/Podium.jsx
Normal file
106
src/components/game/states/Podium.jsx
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
import Loader from "@/components/Loader"
|
||||||
|
import clsx from "clsx"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
|
export default function Podium({ data: { subject, top } }) {
|
||||||
|
const [apparition, setApparition] = useState(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (top.length < 3) {
|
||||||
|
setApparition(4)
|
||||||
|
}
|
||||||
|
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
if (apparition > 4) {
|
||||||
|
clearInterval(interval)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setApparition((value) => value + 1)
|
||||||
|
}, 2000)
|
||||||
|
|
||||||
|
return () => clearInterval(interval)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="relative mx-auto flex w-full max-w-7xl flex-1 flex-col items-center justify-between">
|
||||||
|
<h2 className="anim-show text-center text-3xl font-bold text-white drop-shadow-lg md:text-4xl lg:text-5xl">
|
||||||
|
{subject}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`grid max-w-[500px] flex-1 grid-cols-${top.length} w-full items-end justify-center justify-self-end overflow-x-visible`}
|
||||||
|
>
|
||||||
|
{top[1] && (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"z-20 flex h-[50%] w-full flex-col items-center justify-center gap-3 opacity-0",
|
||||||
|
{ "opacity-100": apparition >= 2 },
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<p className="overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white drop-shadow-lg">
|
||||||
|
{top[1].username}
|
||||||
|
</p>
|
||||||
|
<div className="flex h-full w-full flex-col items-center gap-4 rounded-t-md bg-primary pt-6 text-center shadow-2xl">
|
||||||
|
<p className="flex aspect-square h-14 items-center justify-center rounded-full border-4 border-zinc-400 bg-zinc-500 text-3xl font-bold text-white drop-shadow-lg">
|
||||||
|
<span className="drop-shadow-md">2</span>
|
||||||
|
</p>
|
||||||
|
<p className="text-2xl font-bold text-white drop-shadow-lg">
|
||||||
|
{top[1].points}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"z-30 flex h-[60%] w-full flex-col items-center gap-3 opacity-0",
|
||||||
|
{
|
||||||
|
"opacity-100": apparition >= 3,
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
className={clsx(
|
||||||
|
"overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white opacity-0 drop-shadow-lg",
|
||||||
|
{ "opacity-100": apparition >= 4 },
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{top[0].username}
|
||||||
|
</p>
|
||||||
|
<div className="flex h-full w-full flex-col items-center gap-4 rounded-t-md bg-primary pt-6 text-center shadow-2xl">
|
||||||
|
<p className="flex aspect-square h-14 items-center justify-center rounded-full border-4 border-amber-400 bg-amber-300 text-3xl font-bold text-white drop-shadow-lg">
|
||||||
|
<span className="drop-shadow-md">1</span>
|
||||||
|
</p>
|
||||||
|
<p className="text-2xl font-bold text-white drop-shadow-lg">
|
||||||
|
{top[0].points}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{top[2] && (
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
"z-10 flex h-[40%] w-full flex-col items-center gap-3 opacity-0",
|
||||||
|
{
|
||||||
|
"opacity-100": apparition >= 1,
|
||||||
|
},
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<p className="overflow-visible whitespace-nowrap text-center text-2xl font-bold text-white drop-shadow-lg">
|
||||||
|
{top[2].username}
|
||||||
|
</p>
|
||||||
|
<div className="flex h-full w-full flex-col items-center gap-4 rounded-t-md bg-primary pt-6 text-center shadow-2xl">
|
||||||
|
<p className="flex aspect-square h-14 items-center justify-center rounded-full border-4 border-amber-800 bg-amber-700 text-3xl font-bold text-white drop-shadow-lg">
|
||||||
|
<span className="drop-shadow-md">3</span>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p className="text-2xl font-bold text-white drop-shadow-lg">
|
||||||
|
{top[2].points}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,19 +1,23 @@
|
|||||||
import { useRef } from "react"
|
import { useRef } from "react"
|
||||||
|
|
||||||
export default function Question({ data: { question } }) {
|
export default function Question({ data: { question, image, cooldown } }) {
|
||||||
const barRef = useRef(null)
|
const barRef = useRef(null)
|
||||||
|
|
||||||
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 flex-col items-center justify-center gap-5">
|
||||||
<h2 className="anim-show text-center text-3xl 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>
|
||||||
|
|
||||||
|
{!!image && (
|
||||||
|
<img src={image} className="h-48 max-h-60 w-auto rounded-md" />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
ref={barRef}
|
ref={barRef}
|
||||||
className="mb-20 h-4 self-start justify-self-end rounded-full bg-primary"
|
className="mb-20 h-4 self-start justify-self-end rounded-full bg-primary"
|
||||||
style={{ animation: `progressBar ${6}s linear forwards` }}
|
style={{ animation: `progressBar ${cooldown}s linear forwards` }}
|
||||||
></div>
|
></div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -10,12 +10,17 @@ export default function Room({ data: { text, inviteCode } }) {
|
|||||||
setPlayerList([...playerList, player])
|
setPlayerList([...playerList, player])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
socket.on("manager:removePlayer", (playerId) => {
|
||||||
|
setPlayerList(playerList.filter((p) => p.id !== playerId))
|
||||||
|
})
|
||||||
|
|
||||||
socket.on("manager:playerKicked", (playerId) => {
|
socket.on("manager:playerKicked", (playerId) => {
|
||||||
setPlayerList(playerList.filter((p) => p.id !== playerId))
|
setPlayerList(playerList.filter((p) => p.id !== playerId))
|
||||||
})
|
})
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
socket.off("manager:newPlayer")
|
socket.off("manager:newPlayer")
|
||||||
|
socket.off("manager:removePlayer")
|
||||||
socket.off("manager:playerKicked")
|
socket.off("manager:playerKicked")
|
||||||
}
|
}
|
||||||
}, [playerList])
|
}, [playerList])
|
||||||
|
|||||||
39
src/components/icons/Pentagon.jsx
Normal file
39
src/components/icons/Pentagon.jsx
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
export default function Pentagon({ className, fill, stroke }) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
fill={fill}
|
||||||
|
height="800px"
|
||||||
|
width="800px"
|
||||||
|
version="1.1"
|
||||||
|
id="Layer_1"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
viewBox="-40.96 -40.96 593.93 593.93"
|
||||||
|
transform="rotate(180)"
|
||||||
|
stroke={fill}
|
||||||
|
stroke-width="0.005120100000000001"
|
||||||
|
>
|
||||||
|
<g stroke-width="0" />
|
||||||
|
|
||||||
|
<g
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
stroke={stroke}
|
||||||
|
strokeWidth="71.6814"
|
||||||
|
>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M507.804,200.28L262.471,12.866c-3.84-2.923-9.131-2.923-12.949,0L4.188,200.28c-3.605,2.773-5.077,7.531-3.648,11.84 l93.717,281.92c1.451,4.373,5.525,7.296,10.133,7.296h303.253c4.587,0,8.683-2.944,10.133-7.296l93.717-281.92 C512.882,207.789,511.41,203.053,507.804,200.28z" />{" "}
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<path d="M507.804,200.28L262.471,12.866c-3.84-2.923-9.131-2.923-12.949,0L4.188,200.28c-3.605,2.773-5.077,7.531-3.648,11.84 l93.717,281.92c1.451,4.373,5.525,7.296,10.133,7.296h303.253c4.587,0,8.683-2.944,10.133-7.296l93.717-281.92 C512.882,207.789,511.41,203.053,507.804,200.28z" />{" "}
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,9 +1,19 @@
|
|||||||
|
import Answers from "@/components/game/states/Answers"
|
||||||
|
import Leaderboard from "@/components/game/states/Leaderboard"
|
||||||
|
import Prepared from "@/components/game/states/Prepared"
|
||||||
|
import Question from "@/components/game/states/Question"
|
||||||
|
import Result from "@/components/game/states/Result"
|
||||||
|
import Room from "./components/game/states/Room"
|
||||||
|
import Podium from "./components/game/states/Podium"
|
||||||
|
import Wait from "@/components/game/states/Wait"
|
||||||
|
import Start from "@/components/game/states/Start"
|
||||||
|
|
||||||
import Circle from "@/components/icons/Circle"
|
import Circle from "@/components/icons/Circle"
|
||||||
import Triangle from "@/components/icons/Triangle"
|
import Triangle from "@/components/icons/Triangle"
|
||||||
import Square from "@/components/icons/Square"
|
import Square from "@/components/icons/Square"
|
||||||
import Rhombus from "@/components/icons/Rhombus"
|
import Rhombus from "@/components/icons/Rhombus"
|
||||||
|
|
||||||
export const WEBSOCKET_URL = "http://localhost:5157" ///"https://rahoot.ralex.app"
|
export const WEBSOCKET_URL = "http://localhost:5157"
|
||||||
|
|
||||||
export const ANSWERS_COLORS = [
|
export const ANSWERS_COLORS = [
|
||||||
"bg-red-500",
|
"bg-red-500",
|
||||||
@@ -13,3 +23,31 @@ export const ANSWERS_COLORS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export const ANSWERS_ICONS = [Triangle, Rhombus, Circle, Square]
|
export const ANSWERS_ICONS = [Triangle, Rhombus, Circle, Square]
|
||||||
|
|
||||||
|
export const GAME_STATES = {
|
||||||
|
status: {
|
||||||
|
name: "WAIT",
|
||||||
|
data: { text: "Waiting for the players" },
|
||||||
|
},
|
||||||
|
question: {
|
||||||
|
current: 1,
|
||||||
|
total: null,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GAME_STATE_COMPONENTS = {
|
||||||
|
SELECT_ANSWER: Answers,
|
||||||
|
SHOW_QUESTION: Question,
|
||||||
|
WAIT: Wait,
|
||||||
|
SHOW_START: Start,
|
||||||
|
SHOW_RESULT: Result,
|
||||||
|
SHOW_PREPARED: Prepared,
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GAME_STATE_COMPONENTS_MANAGER = {
|
||||||
|
...GAME_STATE_COMPONENTS,
|
||||||
|
SHOW_ROOM: Room,
|
||||||
|
SHOW_RESPONSES: Answers,
|
||||||
|
SHOW_LEADERBOARD: Leaderboard,
|
||||||
|
FINISH: Podium,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
|
||||||
|
|
||||||
export default function handler(req, res) {
|
|
||||||
res.status(200).json({ name: "John Doe" })
|
|
||||||
}
|
|
||||||
@@ -1,30 +1,16 @@
|
|||||||
import GameWrapper from "@/components/game/GameWrapper"
|
import GameWrapper from "@/components/game/GameWrapper"
|
||||||
import Answers from "@/components/game/states/Answers"
|
import { GAME_STATES, GAME_STATE_COMPONENTS } from "@/constants"
|
||||||
import Leaderboard from "@/components/game/states/Leaderboard"
|
|
||||||
import Prepared from "@/components/game/states/Prepared"
|
|
||||||
import Question from "@/components/game/states/Question"
|
|
||||||
import Result from "@/components/game/states/Result"
|
|
||||||
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, useEffect, useState } from "react"
|
import { createElement, useEffect, useState } from "react"
|
||||||
|
import toast from "react-hot-toast"
|
||||||
const gameStateComponent = {
|
|
||||||
SELECT_ANSWER: Answers,
|
|
||||||
SHOW_QUESTION: Question,
|
|
||||||
WAIT: Wait,
|
|
||||||
SHOW_START: Start,
|
|
||||||
SHOW_RESULT: Result,
|
|
||||||
SHOW_PREPARED: Prepared,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Game() {
|
export default function Game() {
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const { socket } = useSocketContext()
|
const { socket } = useSocketContext()
|
||||||
const { player } = usePlayerContext()
|
const { player, dispatch } = usePlayerContext()
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!player) {
|
if (!player) {
|
||||||
@@ -32,16 +18,7 @@ export default function Game() {
|
|||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState(GAME_STATES)
|
||||||
status: {
|
|
||||||
name: "WAIT",
|
|
||||||
data: { text: "Waiting for the players" },
|
|
||||||
},
|
|
||||||
question: {
|
|
||||||
current: 1,
|
|
||||||
total: null,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
socket.on("game:status", (status) => {
|
socket.on("game:status", (status) => {
|
||||||
@@ -55,15 +32,25 @@ export default function Game() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
socket.on("game:reset", () => {
|
||||||
|
router.replace("/")
|
||||||
|
|
||||||
|
dispatch({ type: "LOGOUT" })
|
||||||
|
setState(GAME_STATES)
|
||||||
|
|
||||||
|
toast("The game has been reset by the host")
|
||||||
|
})
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
socket.off("game:status")
|
socket.off("game:status")
|
||||||
|
socket.off("game:reset")
|
||||||
}
|
}
|
||||||
}, [state])
|
}, [state])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GameWrapper>
|
<GameWrapper>
|
||||||
{gameStateComponent[state.status.name] &&
|
{GAME_STATE_COMPONENTS[state.status.name] &&
|
||||||
createElement(gameStateComponent[state.status.name], {
|
createElement(GAME_STATE_COMPONENTS[state.status.name], {
|
||||||
data: state.status.data,
|
data: state.status.data,
|
||||||
})}
|
})}
|
||||||
</GameWrapper>
|
</GameWrapper>
|
||||||
|
|||||||
@@ -13,8 +13,6 @@ import { useSocketContext } from "@/context/socket"
|
|||||||
import toast from "react-hot-toast"
|
import toast from "react-hot-toast"
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
|
|
||||||
const { player, dispatch } = usePlayerContext()
|
const { player, dispatch } = usePlayerContext()
|
||||||
const { socket } = useSocketContext()
|
const { socket } = useSocketContext()
|
||||||
|
|
||||||
@@ -35,12 +33,6 @@ export default function Home() {
|
|||||||
<div className="absolute -bottom-[15vmin] -right-[15vmin] min-h-[75vmin] min-w-[75vmin] rotate-45 bg-primary/15"></div>
|
<div className="absolute -bottom-[15vmin] -right-[15vmin] min-h-[75vmin] min-w-[75vmin] rotate-45 bg-primary/15"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{loading && (
|
|
||||||
<div className="absolute z-30 flex h-full w-full items-center justify-center bg-black/40">
|
|
||||||
<Loader />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<Image src={logo} className="mb-6 h-32" alt="logo" />
|
<Image src={logo} className="mb-6 h-32" alt="logo" />
|
||||||
|
|
||||||
{!player ? <Room /> : <Username />}
|
{!player ? <Room /> : <Username />}
|
||||||
|
|||||||
@@ -1,41 +1,21 @@
|
|||||||
import Button from "@/components/Button"
|
import Button from "@/components/Button"
|
||||||
import GameWrapper from "@/components/game/GameWrapper"
|
import GameWrapper from "@/components/game/GameWrapper"
|
||||||
import Answers from "@/components/game/states/Answers"
|
import ManagerPassword from "@/components/ManagerPassword"
|
||||||
import Leaderboard from "@/components/game/states/Leaderboard"
|
import { GAME_STATES, GAME_STATE_COMPONENTS_MANAGER } from "@/constants"
|
||||||
import Prepared from "@/components/game/states/Prepared"
|
|
||||||
import Question from "@/components/game/states/Question"
|
|
||||||
import Room from "@/components/game/states/Room"
|
|
||||||
import Start from "@/components/game/states/Start"
|
|
||||||
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, useEffect, useState } from "react"
|
import { createElement, useEffect, useState } from "react"
|
||||||
|
|
||||||
const gameStateComponent = {
|
|
||||||
SHOW_ROOM: Room,
|
|
||||||
SHOW_START: Start,
|
|
||||||
SELECT_ANSWER: Answers,
|
|
||||||
SHOW_QUESTION: Question,
|
|
||||||
WAIT: Wait,
|
|
||||||
SHOW_RESPONSES: Answers,
|
|
||||||
SHOW_LEADERBOARD: Leaderboard,
|
|
||||||
SHOW_PREPARED: Prepared,
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Manager() {
|
export default function Manager() {
|
||||||
const { socket } = useSocketContext()
|
const { socket } = useSocketContext()
|
||||||
|
|
||||||
const [nextText, setNextText] = useState("Start")
|
const [nextText, setNextText] = useState("Start")
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
created: false,
|
...GAME_STATES,
|
||||||
status: {
|
status: {
|
||||||
|
...GAME_STATES.status,
|
||||||
name: "SHOW_ROOM",
|
name: "SHOW_ROOM",
|
||||||
data: { text: "Waiting for the players" },
|
|
||||||
},
|
|
||||||
question: {
|
|
||||||
current: 1,
|
|
||||||
total: null,
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -97,13 +77,13 @@ export default function Manager() {
|
|||||||
<>
|
<>
|
||||||
{!state.created ? (
|
{!state.created ? (
|
||||||
<div>
|
<div>
|
||||||
<Button onClick={handleCreate}>Create Room</Button>
|
<ManagerPassword />
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<GameWrapper textNext={nextText} onNext={handleSkip} manager>
|
<GameWrapper textNext={nextText} onNext={handleSkip} manager>
|
||||||
{gameStateComponent[state.status.name] &&
|
{GAME_STATE_COMPONENTS_MANAGER[state.status.name] &&
|
||||||
createElement(gameStateComponent[state.status.name], {
|
createElement(GAME_STATE_COMPONENTS_MANAGER[state.status.name], {
|
||||||
data: state.status.data,
|
data: state.status.data,
|
||||||
})}
|
})}
|
||||||
</GameWrapper>
|
</GameWrapper>
|
||||||
|
|||||||
@@ -1,63 +0,0 @@
|
|||||||
import GameWrapper from "@/components/game/GameWrapper"
|
|
||||||
import Answers from "@/components/game/states/Answers"
|
|
||||||
import Leaderboard from "@/components/game/states/Leaderboard"
|
|
||||||
import Question from "@/components/game/states/Question"
|
|
||||||
import Wait from "@/components/game/states/Wait"
|
|
||||||
import { useSocketContext } from "@/context/socket"
|
|
||||||
import { createElement, useEffect, useState } from "react"
|
|
||||||
|
|
||||||
export default function Manager({ children }) {
|
|
||||||
const { socket } = useSocketContext()
|
|
||||||
|
|
||||||
const [gameState, setGameState] = useState({
|
|
||||||
status: {
|
|
||||||
name: "PENDING",
|
|
||||||
},
|
|
||||||
cooldown: 0,
|
|
||||||
answers: [],
|
|
||||||
})
|
|
||||||
|
|
||||||
socket.on("game:status", (status) => {
|
|
||||||
setGameState({
|
|
||||||
...gameState,
|
|
||||||
status: status,
|
|
||||||
cooldown: 0,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<p className="text-white">{JSON.stringify(gameState)}</p>
|
|
||||||
<hr></hr>
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<button
|
|
||||||
className="bg-primary p-2"
|
|
||||||
onClick={() => socket.emit("manager:createRoom")}
|
|
||||||
>
|
|
||||||
Create Room
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
className="bg-primary p-2"
|
|
||||||
onClick={() => socket.emit("manager:startGame")}
|
|
||||||
>
|
|
||||||
Start Game
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
className="bg-primary p-2"
|
|
||||||
onClick={() => socket.emit("manager:showLeaderboard")}
|
|
||||||
>
|
|
||||||
Leaderboard
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
className="bg-primary p-2"
|
|
||||||
onClick={() => socket.emit("manager:nextQuestion")}
|
|
||||||
>
|
|
||||||
Next Question
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user