mirror of
https://github.com/randyjc/Rahoot.git
synced 2026-03-13 20:15:35 +01:00
Add leaderboard & Manager some features
This commit is contained in:
@@ -17,7 +17,7 @@ const Player = {
|
|||||||
id: socket.id,
|
id: socket.id,
|
||||||
points: 0,
|
points: 0,
|
||||||
}
|
}
|
||||||
socket.to(player.room).emit("manager:newPlayer", playerData)
|
socket.to(player.room).emit("manager:newPlayer", { ...playerData })
|
||||||
|
|
||||||
game.players.push(playerData)
|
game.players.push(playerData)
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ 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]
|
||||||
|
|
||||||
|
io.to(game.room).emit("game:updateQuestion", {
|
||||||
|
current: game.currentQuestion + 1,
|
||||||
|
total: game.questions.length,
|
||||||
|
})
|
||||||
|
|
||||||
io.to(game.room).emit("game:status", {
|
io.to(game.room).emit("game:status", {
|
||||||
name: "SHOW_QUESTION",
|
name: "SHOW_QUESTION",
|
||||||
data: {
|
data: {
|
||||||
|
|||||||
@@ -2,10 +2,22 @@ import Image from "next/image"
|
|||||||
import Button from "@/components/Button"
|
import Button from "@/components/Button"
|
||||||
import background from "@/assets/2238431_1694.jpg"
|
import background from "@/assets/2238431_1694.jpg"
|
||||||
import { usePlayerContext } from "@/context/player"
|
import { usePlayerContext } from "@/context/player"
|
||||||
|
import { useSocketContext } from "@/context/socket"
|
||||||
|
import { useState } from "react"
|
||||||
|
|
||||||
export default function GameWrapper({ children }) {
|
export default function GameWrapper({ children, onNext, manager }) {
|
||||||
|
const { socket } = useSocketContext()
|
||||||
const { player } = usePlayerContext()
|
const { player } = usePlayerContext()
|
||||||
|
|
||||||
|
const [questionState, setQuestionState] = useState()
|
||||||
|
|
||||||
|
socket.on("game:updateQuestion", ({ current, total }) => {
|
||||||
|
setQuestionState({
|
||||||
|
current,
|
||||||
|
total,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="relative flex justify-between flex-col w-full min-h-screen">
|
<section className="relative flex justify-between flex-col w-full min-h-screen">
|
||||||
<div className="fixed h-full w-full top-0 left-0 bg-orange-600 opacity-70 -z-10">
|
<div className="fixed h-full w-full top-0 left-0 bg-orange-600 opacity-70 -z-10">
|
||||||
@@ -16,10 +28,20 @@ export default function GameWrapper({ children }) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="p-4 w-full flex justify-between">
|
<div className="p-4 w-full flex justify-between">
|
||||||
|
{questionState && (
|
||||||
<div className="bg-white shadow-inset text-black px-4 font-bold rounded-md flex items-center text-lg">
|
<div className="bg-white shadow-inset text-black px-4 font-bold rounded-md flex items-center text-lg">
|
||||||
1/10
|
{`${questionState.current} / ${questionState.total}`}
|
||||||
</div>
|
</div>
|
||||||
<Button className="bg-white !text-black px-4">Skip</Button>
|
)}
|
||||||
|
|
||||||
|
{manager && (
|
||||||
|
<Button
|
||||||
|
className="bg-white !text-black px-4 self-end"
|
||||||
|
onClick={() => onNext()}
|
||||||
|
>
|
||||||
|
Skip
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{children}
|
{children}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export default function Username() {
|
|||||||
payload: username,
|
payload: username,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
socket.emit("player:join", { username: username, room: player.room })
|
||||||
router.push("/game")
|
router.push("/game")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,24 +1,16 @@
|
|||||||
export default function Leaderboard({ data }) {
|
export default function Leaderboard({ data: { leaderboard } }) {
|
||||||
return (
|
return (
|
||||||
<section className="max-w-7xl mx-auto w-full flex-1 flex-col relative items-center justify-center flex">
|
<section className="max-w-7xl mx-auto w-full flex-1 flex-col relative items-center justify-center flex px-2">
|
||||||
<h2 className="text-white drop-shadow-md text-5xl font-bold mb-6">
|
<h2 className="text-white drop-shadow-md text-5xl font-bold mb-6">
|
||||||
Leaderboard
|
Leaderboard
|
||||||
</h2>
|
</h2>
|
||||||
<div className="w-full flex-col flex gap-2">
|
<div className="w-full flex-col flex gap-2">
|
||||||
<div className=" bg-orange-500 rounded-md p-3 flex justify-between w-full text-white font-bold text-2xl">
|
{leaderboard.map(({ username, points }) => (
|
||||||
<span>Username</span>
|
<div className="bg-primary rounded-md p-3 flex justify-between w-full text-white font-bold text-2xl">
|
||||||
<span>10000</span>
|
<span className="drop-shadow-md">{username}</span>
|
||||||
</div>
|
<span className="drop-shadow-md">{points}</span>
|
||||||
|
|
||||||
<div className="bg-orange-500 rounded-md p-3 flex justify-between w-full text-white font-bold text-2xl">
|
|
||||||
<span>Username</span>
|
|
||||||
<span>10000</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="bg-orange-500 rounded-md p-3 flex justify-between w-full text-white font-bold text-2xl">
|
|
||||||
<span>Username</span>
|
|
||||||
<span>10000</span>
|
|
||||||
</div>
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ export default function Question({ data: { question } }) {
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
ref={barRef}
|
ref={barRef}
|
||||||
className="h-6 bg-primary mb-32 rounded-full w-0 self-start justify-self-end"
|
className="h-6 bg-primary mb-32 rounded-full self-start justify-self-end"
|
||||||
style={{ transition: `width ${6}s linear` }}
|
style={{ transition: `width ${6}s linear`, width: "0%" }}
|
||||||
></div>
|
></div>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
|
|||||||
38
src/components/game/states/Start.jsx
Normal file
38
src/components/game/states/Start.jsx
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import Loader from "@/components/Loader"
|
||||||
|
import { useSocketContext } from "@/context/socket"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
|
||||||
|
export default function Start({ data: { text } }) {
|
||||||
|
const { socket } = useSocketContext()
|
||||||
|
const [playerList, setPlayerList] = useState([])
|
||||||
|
|
||||||
|
socket.on("manager:newPlayer", (player) => {
|
||||||
|
setPlayerList([...playerList, player])
|
||||||
|
})
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
socket.emit("manager:createRoom")
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="max-w-7xl mx-auto w-full flex-1 relative items-center justify-center flex flex-col px-2">
|
||||||
|
<h2 className="text-white font-bold text-4xl drop-shadow-lg mb-4">
|
||||||
|
{text}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className="flex flex-wrap gap-3">
|
||||||
|
{playerList.map((player) => (
|
||||||
|
<div
|
||||||
|
key={player.id}
|
||||||
|
className="py-3 px-4 bg-primary shadow-inset rounded-md text-white font-bold"
|
||||||
|
onClick={() => socket.emit("manager:kickPlayer", player.id)}
|
||||||
|
>
|
||||||
|
<span className="drop-shadow-md text-xl hover:line-through cursor-pointer">
|
||||||
|
{player.username}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -14,6 +14,7 @@ const gameStateComponent = {
|
|||||||
SHOW_QUESTION: Question,
|
SHOW_QUESTION: Question,
|
||||||
WAIT: Wait,
|
WAIT: Wait,
|
||||||
SHOW_RESULT: Result,
|
SHOW_RESULT: Result,
|
||||||
|
SHOW_LEADERBOARD: Leaderboard,
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Game() {
|
export default function Game() {
|
||||||
@@ -27,8 +28,6 @@ export default function Game() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
socket.emit("player:join", { username: "Test", room: player.room })
|
|
||||||
|
|
||||||
const [state, setState] = useState({
|
const [state, setState] = useState({
|
||||||
status: {
|
status: {
|
||||||
name: "WAIT",
|
name: "WAIT",
|
||||||
@@ -41,17 +40,15 @@ export default function Game() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
socket.on("game:status", (status) => {
|
socket.on("game:status", (status) => {
|
||||||
setState({
|
|
||||||
...gameState,
|
|
||||||
status: status,
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
/*socket.on("game:", (question) => {
|
|
||||||
setState({
|
setState({
|
||||||
...state,
|
...state,
|
||||||
|
status: status,
|
||||||
})*/
|
question: {
|
||||||
|
...state.question,
|
||||||
|
current: status.question,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<GameWrapper>
|
<GameWrapper>
|
||||||
|
|||||||
@@ -2,62 +2,71 @@ import GameWrapper from "@/components/game/GameWrapper"
|
|||||||
import Answers from "@/components/game/states/Answers"
|
import Answers from "@/components/game/states/Answers"
|
||||||
import Leaderboard from "@/components/game/states/Leaderboard"
|
import Leaderboard from "@/components/game/states/Leaderboard"
|
||||||
import Question from "@/components/game/states/Question"
|
import Question from "@/components/game/states/Question"
|
||||||
|
import Result from "@/components/game/states/Result"
|
||||||
|
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 { useSocketContext } from "@/context/socket"
|
import { useSocketContext } from "@/context/socket"
|
||||||
import { createElement, useEffect, useMemo, useState } from "react"
|
import { useRouter } from "next/router"
|
||||||
|
import { createElement, useState } from "react"
|
||||||
|
|
||||||
export default function Manager({ children }) {
|
const gameStateComponent = {
|
||||||
|
SHOW_START: Start,
|
||||||
|
SELECT_ANSWER: Answers,
|
||||||
|
SHOW_QUESTION: Question,
|
||||||
|
WAIT: Wait,
|
||||||
|
SHOW_RESULT: Result,
|
||||||
|
SHOW_LEADERBOARD: Leaderboard,
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Manager() {
|
||||||
const { socket } = useSocketContext()
|
const { socket } = useSocketContext()
|
||||||
|
const { player } = usePlayerContext()
|
||||||
|
|
||||||
const [gameState, setGameState] = useState({
|
const [state, setState] = useState({
|
||||||
status: {
|
status: {
|
||||||
name: "PENDING",
|
name: "SHOW_START",
|
||||||
|
data: { text: "Waiting for the players" },
|
||||||
|
},
|
||||||
|
question: {
|
||||||
|
current: 1,
|
||||||
|
total: null,
|
||||||
},
|
},
|
||||||
cooldown: 0,
|
|
||||||
answers: [],
|
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("game:status", (status) => {
|
socket.on("game:status", (status) => {
|
||||||
setGameState({
|
setState({
|
||||||
...gameState,
|
...state,
|
||||||
status: status,
|
status: status,
|
||||||
cooldown: 0,
|
question: {
|
||||||
|
...state.question,
|
||||||
|
current: status.question,
|
||||||
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const handleSkip = () => {
|
||||||
|
switch (state.status.name) {
|
||||||
|
case "SHOW_START":
|
||||||
|
socket.emit("manager:startGame")
|
||||||
|
break
|
||||||
|
|
||||||
|
case "SHOW_RESPONSES":
|
||||||
|
socket.emit("manager:showLeaderboard")
|
||||||
|
break
|
||||||
|
|
||||||
|
case "SHOW_LEADERBOARD":
|
||||||
|
socket.emit("manager:nextQuestion")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<GameWrapper textNext="" onNext={handleSkip} manager>
|
||||||
<p className="text-white">{JSON.stringify(gameState)}</p>
|
{gameStateComponent[state.status.name] &&
|
||||||
<hr></hr>
|
createElement(gameStateComponent[state.status.name], {
|
||||||
<div className="flex gap-2">
|
data: state.status.data,
|
||||||
<button
|
})}
|
||||||
className="bg-primary p-2"
|
</GameWrapper>
|
||||||
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>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
63
src/pages/managerold.jsx
Normal file
63
src/pages/managerold.jsx
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
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, useMemo, 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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
import Image from "next/image"
|
|
||||||
import Form from "@/components/Form"
|
|
||||||
import Button from "@/components/Button"
|
|
||||||
import Input from "@/components/Input"
|
|
||||||
import logo from "@/assets/logo.svg"
|
|
||||||
import { useEffect, useRef, useState } from "react"
|
|
||||||
import Loader from "@/components/Loader"
|
|
||||||
import { useSocketContext } from "@/context/socket"
|
|
||||||
import background from "@/assets/2238431_1694.jpg"
|
|
||||||
|
|
||||||
export default function Questionasas() {
|
|
||||||
const [loading, setLoading] = useState(false)
|
|
||||||
|
|
||||||
const socket = useSocketContext()
|
|
||||||
const barRef = useRef(null)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
barRef.current.style.width = "100%"
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section className="min-h-screen relative flex justify-center items-center flex-col max-w-7xl mx-auto">
|
|
||||||
<div className="fixed h-full w-full top-0 left-0 bg-orange-600 opacity-70 -z-10">
|
|
||||||
<Image
|
|
||||||
className="object-cover h-full w-full opacity-60 pointer-events-none"
|
|
||||||
src={background}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<h2 className="text-white text-5xl font-bold drop-shadow-lg text-center">
|
|
||||||
Lorem ipsum dolor sit ametorrupti. Alias, recusandae officia.
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div
|
|
||||||
ref={barRef}
|
|
||||||
className="absolute h-6 bg-primary bottom-0 mb-32 rounded-full w-0 self-start"
|
|
||||||
style={{ transition: "width 5s linear" }}
|
|
||||||
></div>
|
|
||||||
</section>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user