fix(game): fix leaderboard old state

This commit is contained in:
Ralex
2025-10-26 18:30:49 +01:00
parent 349c9337e6
commit 0cd2e8d8b7
2 changed files with 54 additions and 13 deletions

View File

@@ -25,7 +25,9 @@ class Game {
managerStatus: { name: Status; data: StatusDataMap[Status] } | null = null managerStatus: { name: Status; data: StatusDataMap[Status] } | null = null
playerStatus: Map<string, { name: Status; data: StatusDataMap[Status] }> = playerStatus: Map<string, { name: Status; data: StatusDataMap[Status] }> =
new Map() new Map()
leaderboard: Player[] leaderboard: Player[]
tempOldLeaderboard: Player[] | null
quizz: Quizz quizz: Quizz
players: Player[] players: Player[]
@@ -59,7 +61,9 @@ class Game {
this.lastBroadcastStatus = null this.lastBroadcastStatus = null
this.managerStatus = null this.managerStatus = null
this.playerStatus = new Map() this.playerStatus = new Map()
this.leaderboard = [] this.leaderboard = []
this.tempOldLeaderboard = null
this.players = [] this.players = []
@@ -370,6 +374,11 @@ class Game {
} }
showResults(question: any) { showResults(question: any) {
const oldLeaderboard =
this.leaderboard.length === 0
? this.players.map((p) => ({ ...p }))
: this.leaderboard.map((p) => ({ ...p }))
const totalType = this.round.playersAnswers.reduce( const totalType = this.round.playersAnswers.reduce(
(acc: Record<number, number>, { answerId }) => { (acc: Record<number, number>, { answerId }) => {
acc[answerId] = (acc[answerId] || 0) + 1 acc[answerId] = (acc[answerId] || 0) + 1
@@ -422,9 +431,11 @@ class Game {
image: question.image, image: question.image,
}) })
this.leaderboard = sortedPlayers
this.tempOldLeaderboard = oldLeaderboard
this.round.playersAnswers = [] this.round.playersAnswers = []
} }
selectAnswer(socket: Socket, answerId: number) { selectAnswer(socket: Socket, answerId: number) {
const player = this.players.find((player) => player.id === socket.id) const player = this.players.find((player) => player.id === socket.id)
const question = this.quizz.questions[this.round.currentQuestion] const question = this.quizz.questions[this.round.currentQuestion]
@@ -491,11 +502,6 @@ class Game {
const isLastRound = const isLastRound =
this.round.currentQuestion + 1 === this.quizz.questions.length this.round.currentQuestion + 1 === this.quizz.questions.length
const sortedPlayers = this.players.sort((a, b) => b.points - a.points)
const oldLeaderboard =
this.leaderboard.length === 0 ? sortedPlayers : this.leaderboard
this.leaderboard = this.players.sort((a, b) => b.points - a.points)
if (isLastRound) { if (isLastRound) {
this.started = false this.started = false
@@ -507,10 +513,16 @@ class Game {
return return
} }
const oldLeaderboard = this.tempOldLeaderboard
? this.tempOldLeaderboard
: this.leaderboard
this.sendStatus(this.manager.id, STATUS.SHOW_LEADERBOARD, { this.sendStatus(this.manager.id, STATUS.SHOW_LEADERBOARD, {
oldLeaderboard: oldLeaderboard.slice(0, 5), oldLeaderboard: oldLeaderboard.slice(0, 5),
leaderboard: this.leaderboard.slice(0, 5), leaderboard: this.leaderboard.slice(0, 5),
}) })
this.tempOldLeaderboard = null
} }
} }

View File

@@ -1,35 +1,57 @@
import { ManagerStatusDataMap } from "@rahoot/common/types/game/status" import { ManagerStatusDataMap } from "@rahoot/common/types/game/status"
import { AnimatePresence, motion } from "motion/react" import { AnimatePresence, motion, useSpring, useTransform } from "motion/react"
import { useEffect, useState } from "react" import { useEffect, useState } from "react"
type Props = { type Props = {
data: ManagerStatusDataMap["SHOW_LEADERBOARD"] data: ManagerStatusDataMap["SHOW_LEADERBOARD"]
} }
const AnimatedPoints = ({ from, to }: { from: number; to: number }) => {
const spring = useSpring(from, { stiffness: 1000, damping: 30 })
const display = useTransform(spring, (value) => Math.round(value))
const [displayValue, setDisplayValue] = useState(from)
useEffect(() => {
spring.set(to)
const unsubscribe = display.on("change", (latest) => {
setDisplayValue(latest)
})
return unsubscribe
}, [to, spring, display])
return <span className="drop-shadow-md">{displayValue}</span>
}
const Leaderboard = ({ data: { oldLeaderboard, leaderboard } }: Props) => { const Leaderboard = ({ data: { oldLeaderboard, leaderboard } }: Props) => {
const [displayedLeaderboard, setDisplayedLeaderboard] = const [displayedLeaderboard, setDisplayedLeaderboard] =
useState(oldLeaderboard) useState(oldLeaderboard)
const [isAnimating, setIsAnimating] = useState(false)
useEffect(() => { useEffect(() => {
setDisplayedLeaderboard(oldLeaderboard) setDisplayedLeaderboard(oldLeaderboard)
setIsAnimating(false)
const timer = setTimeout(() => { const timer = setTimeout(() => {
setIsAnimating(true)
setDisplayedLeaderboard(leaderboard) setDisplayedLeaderboard(leaderboard)
}, 2000) }, 1600)
return () => clearTimeout(timer) return () => {
clearTimeout(timer)
}
}, [oldLeaderboard, leaderboard]) }, [oldLeaderboard, leaderboard])
return ( return (
<section className="relative mx-auto flex w-full max-w-3xl flex-1 flex-col items-center justify-center px-2"> <section className="relative mx-auto flex w-full max-w-4xl flex-1 flex-col items-center justify-center px-2">
<h2 className="mb-6 text-5xl font-bold text-white drop-shadow-md"> <h2 className="mb-6 text-5xl font-bold text-white drop-shadow-md">
Leaderboard Leaderboard
</h2> </h2>
<div className="flex w-full flex-col gap-2"> <div className="flex w-full flex-col gap-2">
<AnimatePresence mode="popLayout"> <AnimatePresence mode="popLayout">
{displayedLeaderboard.map(({ username, points }) => ( {displayedLeaderboard.map(({ id, username, points }) => (
<motion.div <motion.div
key={username} key={id}
layout layout
initial={{ opacity: 0, y: 50 }} initial={{ opacity: 0, y: 50 }}
animate={{ animate={{
@@ -51,7 +73,14 @@ const Leaderboard = ({ data: { oldLeaderboard, leaderboard } }: Props) => {
className="bg-primary flex w-full justify-between rounded-md p-3 text-2xl font-bold text-white" className="bg-primary flex w-full justify-between rounded-md p-3 text-2xl font-bold text-white"
> >
<span className="drop-shadow-md">{username}</span> <span className="drop-shadow-md">{username}</span>
<span className="drop-shadow-md">{points}</span> {isAnimating ? (
<AnimatedPoints
from={oldLeaderboard.find((u) => u.id === id)?.points || 0}
to={leaderboard.find((u) => u.id === id)?.points || 0}
/>
) : (
<span className="drop-shadow-md">{points}</span>
)}
</motion.div> </motion.div>
))} ))}
</AnimatePresence> </AnimatePresence>