Key Topics

Creating a Real-Time Scoreboard

A significant part of our project involved creating a real-time scoreboard that displays the highest scores of the top 10 users. Here's how we achieved this:

First, we established a scoreboard function in the Firebase database and set an initial state for the scores:

import { onValue, ref } from "firebase/database";
import { useEffect, useState } from "react";
import { database } from "../../utils/Firebase/Firebase";
import { Navbar } from "../../components/Navbar/Navbar"

export function Scoreboard() {
  const [scores, setScores] = useState([]);
...
}

We then created a function that would determine the highest score of a user, comparing their song score and quiz score:

  function getHighestScore(user) {
    const songScore = user.highScore.song || 0;
    const quizScore = user.highScore.quiz || 0;
    if (songScore > quizScore) {
      return {
        name: `${user.firstName} ${user.lastName}`,
        type: "Song",
        score: songScore
      }
    } else {
      return {
        name: `${user.firstName} ${user.lastName}`,
        type: "Quiz",
        score: quizScore
      }
    }
  }

Next, we utilized the useEffect function to listen for value changes in the /users node of our database. For each change, we would sort and slice the top 10 highest scores, and update the state accordingly:

  useEffect(() => {
    onValue(ref(database, "/users"), (snapshot) => {
      const users = Object.values(snapshot.val())
      if (users) {
        const highestScores = users
          .map(user => getHighestScore(user))
          .sort((a, b) => b.score - a.score)
          .slice(0, 10)
        setScores(highestScores);
      }
    })
  }, [])

Lastly, we rendered the scores in a table format:

  return (
    <div>
      <Navbar />
      <main className="loggedIn">
        <h1> Scoreboard </h1>
        <table className="table">
          ...
          <tbody className="tableBody">
            {scores.map((score, index) => {
              return (
                <tr key={index} className="tableBodyRow">
                  <td className="tableBodyIndex"></td>
                  <td className="tableBodyName"><b>{index + 1}</b><span className="tabBody" />{score.name}</td>
                  <td className="tableBodyItem">{score.type}</td>
                  <td className="tableBodyItem">{score.score}</td>
                </tr>
              )
            })}
          </tbody>
        </table>
      </main>
    </div>
  );
};

We also set up functionalities to update the song and quiz scores for each user in the database as they achieved new highs:

export async function setSongScore(newScore) {
    const user = await getUser();
    let currentScore = user.highScore.song;

    if (newScore > currentScore) {
        await updateUser({
            highScore: {
                song: newScore,
                quiz: user.highScore.quiz
            }
        })
    }
}

export async function setQuizScore(newScore) {
    const user = await getUser();
    let currentScore = user.highScore.quiz;

    if (newScore > currentScore) {
        await updateUser({
            highScore: {
                song: user.highScore.song,
                quiz: newScore
            }
        })
    }
}

With these functionalities in place, we're able to provide a live, competitive environment for our users, encouraging them to improve their skills and reach higher scores.