Build a typing game with Next.js and Appwrite
Appwrite Hashnode Hackathon - 2023
Table of contents
For the 2023 Appwrite + Hashnode Hackathon, I decided to build a daily typing challenge where users can sign in and attempt to accurately type out the sentence for the day as quickly as they can. Here's how I built it.
Tech Stack
Frontend - Next.js (using app-router to try out React server components)
Styling - Tailwind CSS (by go-to for building fast)
Database (to store user scores and daily challenges) - Appwrite cloud
Authentication - Clerk (out-of-the-box UI components for auth)
Deployment/hosting - Vercel
Challenges Encountered
The main challenge for an app like this is visually showing the user how much of the sentence they've accurately typed to provide a good UX as shown in the screenshot:
Here's how this effect was accomplished using Regular expressions:
// sentenceChallenge = <daily challenge fetched from the DB>
const regex = new RegExp("^" + userInput, ""); // create a RegEx from the user's input in the text area
// replace the matching portion of the challenge with the highlighted text
const highlightedSentence = sentenceChallenge.replace(regex, function (str) {
return "<span class='text-pink-600 font-semibold'>" + str + "</span>";
});
And, that's it!
The other task is properly calculating and storing the user's typing speed averages after every test.
To accomplish this, if the user was signed in when they land on the /daily-challenge
route, I fetch the user's typing data from the Appwrite database. This is used to calculate the new average after the new test is completed.
Here's how the new typing speed and average are calculated thereafter:
const completeChallenge = async () => {
const newTypingSpeed = parseFloat(
(sentenceChallenge.length / 5 / (timer / 60)).toFixed(2)
);
if (!user) // exit early
try {
const averageScore =
(userInfo.average_score * userInfo.number_of_challenges +
newTypingSpeed) /
(userInfo.number_of_challenges + 1);
await databases.updateDocument(
process.env.NEXT_PUBLIC_APPWRITE_DATABASE_ID!,
process.env.NEXT_PUBLIC_APPWRITE_SCORES_COLLECTION_ID!,
userInfo.$id,
{
average_score: averageScore,
last_challenge_day: today,
number_of_challenges: userInfo.number_of_challenges + 1,
last_challenge_time: timer,
}
);
} catch (error) {
// deal with error
}
};
Conclusion
Overall, this was a fun project to hack on. Feel free to clone the Github repo (linked below) and see if you can make any improvements or modifications!