Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

🍻 A pub quiz web application built using the MERN stack with websockets for near real-time quiz action

License

NotificationsYou must be signed in to change notification settings

voax/quizzer

Repository files navigation

Made byIvo Breukers andOktay Dinler as our final assignment for the DWA course in the first semester of 2019 at the HAN University of Applied Sciences (Arnhem).

Contents

1 Introduction

The Quizzer is a web application that can be used in bars, sports canteens and maybe even prisons to play quizzes as a team. A pub quiz, basically.

There are 3 main roles in this application: The Quizz Master, The Team and The Scoreboard. The idea is that the Quizz Master can host a room which players can join. A game of Quizzer can be played with a minimum of 2 players and a maximum of 6. The game consists of 12 questions per round and can be played indefinitely untill the Quizz Master ends the quiz after the last question of a round. The question, team answers and points are all shown on the scoreboard screen which is updated in near real-time.

2 Wireframes / resources / system reactions

Clickhere for our wireframes.

3 Communitation protocols

3.1 WebSocket

Client receives:

  • TEAM_APPLIED
  • APPLICATION_ACCEPTED
  • APPLICATION_REJECTED
  • CATEGORIES_SELECTED
  • QUESTION_SELECTED
  • GUESS_SUBMITTED
  • ROOM_CLOSED
  • QUESTION_CLOSED
  • SCOREBOARD_REFRESH

Clients sends:

  • TEAM_APPLIED

3.2 Rest Endpoints

MethodUrl
GET/categories/
GET/categories/:categoryID/questions
POST/rooms
GET/rooms/:roomCode
PATCH/rooms/:roomCode
DELETE/rooms/:roomCode
GET/rooms/:roomCode/applications
POST/rooms/:roomCode/applications
DELETE/rooms/:roomCode/applications/:applicationId
POST/rooms/:roomCode/teams
PATCH/rooms/:roomCode/teams/:teamID
PUT/rooms/:roomCode/teams/question
PUT/rooms/:roomCode/categories
POST/rooms/:roomCode/scoreboards

4 Data Schema

4.1 Mongoose Schema

4.1.1 Question

PropertyTypeDefaultRequired
questionString✔️
answerString✔️
categoryString✔️
languageString✔️

4.1.2 Team

PropertyTypeDefaultRequired
sessionIDString✔️
nameString✔️
roundPointsNumber0
roundScoreNumber0
guessString
guessCorrectBoolean

4.1.3 Room

PropertyTypeDefaultRequired
codeString✔️
hostString✔️
languageString✔️
roundNumber0
questionNoNumber0
roundStartedBooleanfalse
teams[Team]
applications[Team]
categories[String]
askedQuestionsref:[Question]
currentQuestionQuestionQuestion
questionClosedBooleantrue
roomClosedBooleanfalse
scoreboards[String]
endedBooleanfalse
questionCompletedBooleanfalse

5 Clientside State

5.1 websocket

{  connected:false;}

5.2 team-app

{teamID:null,roomCode:{value:'',valid:false,},team:{value:'',valid:false,},roundNo:0,question:{open:false,number:0,question:'',category:'',},guess:{value:'',valid:false,},}

5.3 scoreboard

{roomCode:null,connectedToRoom:false,connectingToRoom:false,triedConnectingToRoom:false,round:null,teams:null,category:null,question:null,questionNo:null,questionClosed:null,}

5.4 popup

{title:'',message:'',button:'',active:false,}

5.5 loader

{active:false,text:'',}

5.6 qm (Quizz Master)

{roomCode:null,language:null,selectedTeamApplication:null,teamApplications:[],approvedTeamApplications:[],roomClosed:false,round:0,roundStarted:false,selectedCategory:null,categories:[],selectedCategories:[],question:0,questions:[],questionsAsked:[],currentQuestion:null,questionClosed:true,selectedQuestion:null,approvingATeamGuess:false,},

6 Server Structure

6.1 Middleware

6.1.1 catch-errors.js

module.exports =(fn: (req, res, next) => Promise ): (req, res, next) => void

This module exports a router wrapper for async route handlers. It allows the user to use async route handlers without having to use try/catch. When this wrapper 'catches' an error it passes tonext(error).

6.1.2 error-handler.js

module.exports =({ defaultStatusCode: Number, defaultMessage: String }): (err, req, res, next) => void

This module exports a basic error handler middleware creator. If the user does not pass both initial arguments it will use thestatusCode andmessage props from the object passed fromnext(). Furthermore the closure will also log the error usingconsole.error to the console.

6.1.3 http-ws-upgrade.js

module.exports =(sessionParser): (wss): (request, socket, head) => void

An application specific function for this project which is used to prevent users who do not have a role connecting to our WebSocket server.

6.1.4 role.js

module.exports.isRole =(...conditions: any): (req, res, next) => void

TheisRole function export is a middleware creator function. This function takes a 0.* arguments which are used to check for the users'ssession.role value.

However if the type is a function it will be passed thereq object and whether the function returns true or false it will respond with an error ornext() it.

If any condition results into false it will execute the following code:

res.status(400).json({message:'You are not allowed to perform this action.',});

To combine multiple role middleware you can simply put them in an array. To use it like an array in your route handlers you can simply use the spread operator:

constisHost=isRole(req=>req.room&&req.sessionID===req.room.host)constisQM=isRole('QM')constisQMAndHost=[isQuizzMaster,isHost]//                       spread like sorouter.get('/protected', ...isQMAndHost,(req,res)=>{...})// orapp.use('/protected', ...isQMAndHost)

6.1.5 socket.js

module.exports.sessionHasWSConnect =(errorMsg: String): (req, res, next) => void

The returned middleware closure checks for whether the request is already connected to the WebSocket server.If the user is already connected it will respond with a400 status code and with the passed inerrorMsg parameter.

6.2 Mongoose methods

6.2.1 Room

6.2.2 .pingTeams(msg: String)

Ping the WebSocket for all team connections with the givenmsg.

6.2.3 .pingScoreboards(msg: String)

Ping the WebSocket for all scoreboard connections with the givenmsg.

6.2.4 .pingApplications(msg: String)

Ping the WebSocket for all team-application connections with the givenmsg.

6.2.5 .pingHost(msg: String)

Ping the host's WebSocket connection with the givenmsg.

6.2.6 async .calculateRP()

This method determines the winnner(s) and respectively gives all the teams their points.

6.2.7 async .nextRound()

UpdatesroundStarted tofalse andquestionNo to0. Then it will callcalculateRP()

6.2.8 async .nextQuestion()

  1. Updates for all teams their.roundScore property if.guessCorrect is true
  2. SetscurrentQuestion tonull andquestionCompleted totrue
  3. calls.nextRound() if the current question is>= MAX_QUESTIONS_PER_ROUND defined inserver/.env

6.2.9 async .startRound(categories)

Incrementsround , updatesquestionNo to0,categories tocategories,roundStarted totrue and updates the team'sroundScore to0. Then it will ping the teams with'CATEGORIES_SELECTED' and returnsroundStarted,round andquestionNo.

6.2.10 async .startQuestion(question)

IncrementsquestionNo , updatesquestionCompleted tofalse,questionClosed tofalse,currentQuestion toquestion, adds thequestion._id to theaskedQuestions array, next it updates the team'sguess to'' andguessCorrect tofalse. Then it will ping the teams with'QUESTION_SELECTED' and the scoreboards with'SCOREBOARD_REFRESH'. Then it returnsquestionClosed andquestionNo.

6.3 Team

6.3.1 .ping(msg)

Ping the team's WebSocket connection with the givenmsg.


[8]ページ先頭

©2009-2025 Movatter.jp