Quiz is no loner stored in player's browser and more improvements under the hood

This commit is contained in:
Mawoka
2022-09-19 19:21:24 +02:00
parent 8039269cf3
commit 2c02f2ef54
9 changed files with 209 additions and 97 deletions
+2 -1
View File
@@ -159,6 +159,7 @@ class PlayGame(BaseModel):
captcha_enabled: bool = False captcha_enabled: bool = False
cover_image: str | None cover_image: str | None
game_mode: str | None game_mode: str | None
current_question: int = -1
class GamePlayer(BaseModel): class GamePlayer(BaseModel):
@@ -180,7 +181,7 @@ class GameAnswer1(BaseModel):
class GameSession(BaseModel): class GameSession(BaseModel):
admin: str admin: str
game_id: str game_id: str
players: list[GamePlayer | None] # players: list[GamePlayer | None]
answers: list[GameAnswer1 | None] answers: list[GameAnswer1 | None]
+1 -1
View File
@@ -114,7 +114,7 @@ async def start_quiz(
cover_image=quiz.cover_image, cover_image=quiz.cover_image,
game_mode=game_mode, game_mode=game_mode,
) )
await redis.set(f"game:{str(game.game_pin)}", (game.json()), ex=18000) await redis.set(f"game:{str(game.game_pin)}", game.json(), ex=18000)
return {**quiz.dict(exclude={"id"}), **game.dict(exclude={"questions"})} return {**quiz.dict(exclude={"id"}), **game.dict(exclude={"questions"})}
+66 -14
View File
@@ -8,10 +8,9 @@ import os
import aiohttp import aiohttp
import socketio import socketio
from typing import Any
from classquiz.config import redis, settings from classquiz.config import redis, settings
from classquiz.db.models import PlayGame, QuizQuestionType, GameSession, GamePlayer from classquiz.db.models import PlayGame, QuizQuestionType, GameSession, GamePlayer, RangeQuizAnswer, QuizQuestion
from pydantic import BaseModel, ValidationError from pydantic import BaseModel, ValidationError, validator
sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins=[]) sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins=[])
settings = settings() settings = settings()
@@ -22,7 +21,7 @@ async def generate_final_results(game_data: PlayGame, game_pin: str) -> dict:
for i in range(len(game_data.questions)): for i in range(len(game_data.questions)):
redis_res = await redis.get(f"game_session:{game_pin}:{i}") redis_res = await redis.get(f"game_session:{game_pin}:{i}")
if redis_res is None: if redis_res is None:
break continue
else: else:
results[str(i)] = json.loads(redis_res) results[str(i)] = json.loads(redis_res)
return results return results
@@ -46,10 +45,14 @@ async def join_game(sid: str, data: dict):
await sio.emit("error", room=sid) await sio.emit("error", room=sid)
print(e) print(e)
return return
game_data = PlayGame.parse_raw(redis_res)
if game_data.started:
await sio.emit("game_already_started", room=sid)
return
# +++ START checking captcha +++ # +++ START checking captcha +++
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
try: try:
if json.loads(redis_res)["captcha_enabled"]: if game_data.captcha_enabled:
try: try:
async with session.post( async with session.post(
"https://hcaptcha.com/siteverify", "https://hcaptcha.com/siteverify",
@@ -72,15 +75,19 @@ async def join_game(sid: str, data: dict):
"admin": False, "admin": False,
} }
await sio.save_session(sid, session) await sio.save_session(sid, session)
await sio.emit("joined_game", redis_res, room=sid) await sio.emit(
"joined_game",
{**json.loads(game_data.json(exclude={"quiz_id", "questions"})), "question_count": len(game_data.questions)},
room=sid,
)
redis_res = await redis.get(f"game_session:{data.game_pin}") redis_res = await redis.get(f"game_session:{data.game_pin}")
redis_res = GameSession.parse_raw(redis_res) redis_res = GameSession.parse_raw(redis_res)
redis_res.players.append(GamePlayer(username=data.username, sid=sid)) await redis.sadd(f"game_session:{data.game_pin}:players", GamePlayer(username=data.username, sid=sid).json())
await redis.set( # await redis.set(
f"game_session:{data.game_pin}", # f"game_session:{data.game_pin}",
GameSession(admin=redis_res.admin, game_id=redis_res.game_id, players=redis_res.players, answers=[]).json(), # GameSession(admin=redis_res.admin, game_id=redis_res.game_id, answers=[]).json(),
ex=18000, # ex=18000,
) # )
await sio.emit( await sio.emit(
"player_joined", "player_joined",
{"username": data.username, "sid": sid}, {"username": data.username, "sid": sid},
@@ -93,6 +100,9 @@ async def join_game(sid: str, data: dict):
async def start_game(sid: str, _data: dict): async def start_game(sid: str, _data: dict):
session = await sio.get_session(sid) session = await sio.get_session(sid)
if session["admin"]: if session["admin"]:
game_data = PlayGame.parse_raw(await redis.get(f"game:{session['game_pin']}"))
game_data.started = True
await redis.set(f"game:{session['game_pin']}", game_data.json())
await sio.emit("start_game", room=session["game_pin"]) await sio.emit("start_game", room=session["game_pin"])
@@ -114,7 +124,7 @@ async def register_as_admin(sid: str, data: dict):
if (await redis.get(f"game_session:{game_pin}")) is None: if (await redis.get(f"game_session:{game_pin}")) is None:
await redis.set( await redis.set(
f"game_session:{game_pin}", f"game_session:{game_pin}",
GameSession(admin=sid, game_id=game_id, answers=[], players=[]).json(), GameSession(admin=sid, game_id=game_id, answers=[]).json(),
ex=18000, ex=18000,
) )
@@ -140,13 +150,45 @@ async def get_question_results(sid: str, data: dict):
await sio.emit("question_results", redis_res, room=game_pin) await sio.emit("question_results", redis_res, room=game_pin)
class ABCDQuizAnswerWithoutSolution(BaseModel):
answer: str
color: str | None
class RangeQuizAnswerWithoutSolution(BaseModel):
min: int
max: int
class ReturnQuestion(QuizQuestion):
answers: list[ABCDQuizAnswerWithoutSolution] | RangeQuizAnswerWithoutSolution
@validator("answers")
def answers_not_none_if_abcd_type(cls, v, values):
if values["type"] == QuizQuestionType.ABCD and len(v) == 0:
raise ValueError("Answers can't be none if type is ABCD")
if values["type"] == QuizQuestionType.RANGE and type(v) != RangeQuizAnswerWithoutSolution:
raise ValueError("Answer must be from type RangeQuizAnswer if type is RANGE")
return v
@sio.event @sio.event
async def set_question_number(sid, data: str): async def set_question_number(sid, data: str):
# data is just a number (as a str) of the question # data is just a number (as a str) of the question
session = await sio.get_session(sid) session = await sio.get_session(sid)
if session["admin"]: if session["admin"]:
game_pin = session["game_pin"] game_pin = session["game_pin"]
await sio.emit("set_question_number", data, room=game_pin) game_data = PlayGame.parse_raw(await redis.get(f"game:{session['game_pin']}"))
game_data.current_question = int(data)
await redis.set(f"game:{session['game_pin']}", game_data.json())
await sio.emit(
"set_question_number",
{
"question_index": int(data),
"question": ReturnQuestion(**game_data.dict(include={"questions"})["questions"][int(data)]).dict(),
},
room=game_pin,
)
class _SubmitAnswerData(BaseModel): class _SubmitAnswerData(BaseModel):
@@ -219,6 +261,7 @@ async def get_final_results(sid: str, _data: dict):
if not session["admin"]: if not session["admin"]:
return return
results = await generate_final_results(game_data, session["game_pin"]) results = await generate_final_results(game_data, session["game_pin"])
print(results)
await sio.emit("final_results", results, room=session["game_pin"]) await sio.emit("final_results", results, room=session["game_pin"])
@@ -232,3 +275,12 @@ async def get_export_token(sid):
token = os.urandom(32).hex() token = os.urandom(32).hex()
await redis.set(f"export_token:{token}", json.dumps(results)) await redis.set(f"export_token:{token}", json.dumps(results))
await sio.emit("export_token", token, room=sid) await sio.emit("export_token", token, room=sid)
@sio.event
async def show_solutions(sid: str, _data: dict):
session: dict = await sio.get_session(sid)
game_data = PlayGame(**json.loads(await redis.get(f"game:{session['game_pin']}")))
if not session["admin"]:
return
await sio.emit("solutions", game_data.questions[game_data.current_question].dict(), room=session["game_pin"])
+29 -2
View File
@@ -43,6 +43,10 @@
}); });
timer_res = '0'; timer_res = '0';
}; };
const show_solutions = () => {
socket.emit('show_solutions', {});
timer_res = '0';
};
const get_final_results = () => { const get_final_results = () => {
socket.emit('get_final_results', {}); socket.emit('get_final_results', {});
@@ -67,6 +71,7 @@
let timer_interval = setInterval(() => { let timer_interval = setInterval(() => {
if (timer_res === '0') { if (timer_res === '0') {
clearInterval(timer_interval); clearInterval(timer_interval);
socket.emit('show_solutions', {});
return; return;
} else { } else {
seconds--; seconds--;
@@ -85,6 +90,7 @@
circular_prgoress = 0; circular_prgoress = 0;
} }
} }
$: console.log(quiz_data.questions.length, 'length of quiz');
</script> </script>
{#if game_mode === 'kahoot'} {#if game_mode === 'kahoot'}
@@ -100,6 +106,24 @@
</div> </div>
{/if} {/if}
<div class="justify-self-end ml-auto mr-0 col-start-3 col-end-3"> <div class="justify-self-end ml-auto mr-0 col-start-3 col-end-3">
{#if selected_question + 1 === quiz_data.questions.length && timer_res === '0'}
{#if JSON.stringify(final_results) === JSON.stringify([null])}
<button on:click={get_final_results}>Get final results</button>
{/if}
{:else if timer_res === '0' || selected_question === -1}
<button
on:click={() => {
set_question_number(selected_question + 1);
}}
>Next Question ({selected_question + 2})
</button>
{#if question_results === null && selected_question !== -1}
<button on:click={get_question_results}>Show results</button>
{/if}
{:else if selected_question !== -1}
<button on:click={show_solutions}>Stop time and show solutions</button>
{:else}
<p>!OK!</p>
<button <button
on:click={() => { on:click={() => {
set_question_number(selected_question + 1); set_question_number(selected_question + 1);
@@ -107,6 +131,7 @@
>Next Question ({selected_question + 2} >Next Question ({selected_question + 2}
) )
</button> </button>
{/if}
</div> </div>
</div> </div>
{#if timer_res !== '0' && selected_question >= 0} {#if timer_res !== '0' && selected_question >= 0}
@@ -228,10 +253,12 @@
{#if game_mode === 'normal'} {#if game_mode === 'normal'}
<div class="w-full flex justify-center"> <div class="w-full flex justify-center">
<button <button
on:click={get_question_results} on:click={() => {
timer_res = '0';
}}
id="GetQuestionResultsAndStopTime" id="GetQuestionResultsAndStopTime"
class="px-4 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded text-center hover:bg-gray-600 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50" class="px-4 py-2 leading-5 text-white transition-colors duration-200 transform bg-gray-700 rounded text-center hover:bg-gray-600 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
>{$t('admin_page.get_results_and_stop_time')}</button >{$t('admin_page.stop_time')}</button
> >
</div> </div>
{/if} {/if}
+13 -6
View File
@@ -9,7 +9,7 @@
const { t } = getLocalization(); const { t } = getLocalization();
export let quiz_data: QuizData; export let question_count: number;
export let final_results: Array<null> | Array<Array<PlayerAnswer>>; export let final_results: Array<null> | Array<Array<PlayerAnswer>>;
interface PlayerAnswer { interface PlayerAnswer {
@@ -23,7 +23,7 @@
const getWinnersSorted = () => { const getWinnersSorted = () => {
let winners = {}; let winners = {};
let q_count = quiz_data.questions.length; let q_count = question_count;
console.log( console.log(
JSON.stringify(final_results), JSON.stringify(final_results),
JSON.stringify(final_results) === '{}', JSON.stringify(final_results) === '{}',
@@ -42,9 +42,15 @@
try { try {
for (let i = 0; i < q_count; i++) { for (let i = 0; i < q_count; i++) {
let q_res = final_results[i]; let q_res = final_results[i];
if (q_res === null) { if (!q_res) {
continue;
} else {
q_res = final_results[String(i)];
if (!q_res) {
continue; continue;
} }
}
console.log(q_res);
for (let j = 0; j < q_res.length; j++) { for (let j = 0; j < q_res.length; j++) {
let res = q_res[j]; let res = q_res[j];
if (res['right']) { if (res['right']) {
@@ -68,6 +74,7 @@
data_available = true; data_available = true;
return close_to_res; return close_to_res;
} catch (e) { } catch (e) {
console.log(e);
data_available = false; data_available = false;
} }
}; };
@@ -93,7 +100,7 @@
<span <span
>{$t('play_page.with_out_of', { >{$t('play_page.with_out_of', {
correct_questions: winners_arr[0][1] ?? 0, correct_questions: winners_arr[0][1] ?? 0,
total_question_count: quiz_data.questions.length total_question_count: question_count
})}</span })}</span
> >
</p> </p>
@@ -106,7 +113,7 @@
<span <span
>{$t('play_page.with_out_of', { >{$t('play_page.with_out_of', {
correct_questions: winners_arr[1][1] ?? 0, correct_questions: winners_arr[1][1] ?? 0,
total_question_count: quiz_data.questions.length total_question_count: question_count
})}</span })}</span
> >
</p> </p>
@@ -120,7 +127,7 @@
<span> <span>
{$t('play_page.with_out_of', { {$t('play_page.with_out_of', {
correct_questions: winners_arr[2][1] ?? 0, correct_questions: winners_arr[2][1] ?? 0,
total_question_count: quiz_data.questions.length total_question_count: question_count
})} })}
</span> </span>
</p> </p>
+32 -6
View File
@@ -17,12 +17,22 @@
export let question: Question; export let question: Question;
export let game_mode; export let game_mode;
export let question_index: string | number; export let question_index: string | number;
export let solution;
if (typeof question_index === 'string') { $: console.log(question_index, question, 'hi!');
console.log(question);
if (question.type === undefined) {
question.type = QuizQuestionType.ABCD;
} else {
question.type = QuizQuestionType[question.type];
}
/* if (typeof question_index === 'string') {
question_index = parseInt(question_index); question_index = parseInt(question_index);
} else { } else {
throw new Error('question_index must be a string or number'); throw new Error('question_index must be a string or number');
} }*/
let timer_res = question.time; let timer_res = question.time;
let selected_answer: string; let selected_answer: string;
@@ -44,6 +54,12 @@
timer(question.time); timer(question.time);
$: {
if (solution !== undefined) {
timer_res = '0';
}
}
const selectAnswer = (answer: string) => { const selectAnswer = (answer: string) => {
selected_answer = answer; selected_answer = answer;
//timer_res = '0'; //timer_res = '0';
@@ -75,6 +91,8 @@
circular_prgoress = 0; circular_prgoress = 0;
} }
} }
$: console.log(solution);
</script> </script>
<div class="flex flex-col justify-center w-screen h-1/6"> <div class="flex flex-col justify-center w-screen h-1/6">
@@ -149,8 +167,11 @@
{/await} {/await}
{/if} {/if}
{:else if question.type === QuizQuestionType.ABCD} {:else if question.type === QuizQuestionType.ABCD}
{#if solution === undefined}
<Spinner />
{:else}
<div class="flex flex-wrap"> <div class="flex flex-wrap">
{#each question.answers as answer} {#each solution.answers as answer}
{#if answer.right} {#if answer.right}
<button <button
class="w-1/2 text-3xl bg-green-600 border border-white" class="w-1/2 text-3xl bg-green-600 border border-white"
@@ -166,14 +187,19 @@
{/if} {/if}
{/each} {/each}
</div> </div>
{/if}
{:else if question.type === QuizQuestionType.RANGE} {:else if question.type === QuizQuestionType.RANGE}
{#if solution === undefined}
<Spinner />
{:else}
<p class="text-center"> <p class="text-center">
Every number between {question.answers.min_correct} and {question.answers.max_correct} was correct. Every number between {solution.answers.min_correct} and {solution.answers.max_correct} was
You got {selected_answer}, so you have been correct. You got {selected_answer}, so you have been
{#if question.answers.min_correct <= parseInt(selected_answer) && parseInt(selected_answer) <= question.answers.max_correct} {#if solution.answers.min_correct <= parseInt(selected_answer) && parseInt(selected_answer) <= solution.answers.max_correct}
correct correct
{:else} {:else}
wrong. wrong.
{/if} {/if}
</p> </p>
{/if}
{/if} {/if}
+12 -14
View File
@@ -4,7 +4,7 @@
- file, You can obtain one at https://mozilla.org/MPL/2.0/. - file, You can obtain one at https://mozilla.org/MPL/2.0/.
--> -->
<script lang="ts"> <script lang="ts">
import type { Answer, QuizData } from '$lib/quiz_types'; import type { Answer, Question, QuizData } from '$lib/quiz_types';
import { getLocalization } from '$lib/i18n'; import { getLocalization } from '$lib/i18n';
import { QuizQuestionType } from '../quiz_types.js'; import { QuizQuestionType } from '../quiz_types.js';
import Spinner from '$lib/Spinner.svelte'; import Spinner from '$lib/Spinner.svelte';
@@ -12,10 +12,11 @@
const { t } = getLocalization(); const { t } = getLocalization();
export let results: Array<Answer>; export let results: Array<Answer>;
export let game_data: QuizData; export let game_data: QuizData;
export let solution: Question;
export let question_index: string; export let question_index: string;
let data_store = {}; let data_store = {};
const question = game_data.questions[parseInt(question_index)].answers; const question = solution.answers;
for (let i = 0; i < question.length; i++) { for (let i = 0; i < question.length; i++) {
data_store[question[i].answer] = 0; data_store[question[i].answer] = 0;
} }
@@ -24,11 +25,8 @@
data_store[results[i].answer] += 1; data_store[results[i].answer] += 1;
} }
let slider_values = [ let slider_values = [solution.answers.min_correct ?? 0, solution.answers.max_correct ?? 0];
game_data.questions[parseInt(question_index)].answers.min_correct ?? 0, console.log(slider_values, solution.answers);
game_data.questions[parseInt(question_index)].answers.max_correct ?? 0
];
console.log(slider_values, game_data.questions[parseInt(question_index)].answers);
</script> </script>
<!-- Show the results from the results object --> <!-- Show the results from the results object -->
@@ -38,7 +36,7 @@
<div> <div>
<h2 class="text-center text-3xl mb-8">{$t('words.result', { count: 2 })}</h2> <h2 class="text-center text-3xl mb-8">{$t('words.result', { count: 2 })}</h2>
<div class="w-screen flex justify-center"> <div class="w-screen flex justify-center">
{#if game_data.questions[parseInt(question_index)].type === QuizQuestionType.ABCD} {#if solution.type === QuizQuestionType.ABCD}
<div class="relative overflow-x-auto shadow-md rounded-lg"> <div class="relative overflow-x-auto shadow-md rounded-lg">
<table class="w-fit text-sm text-left text-gray-500 dark:text-gray-400"> <table class="w-fit text-sm text-left text-gray-500 dark:text-gray-400">
<thead class="bg-gray-50 dark:bg-gray-700"> <thead class="bg-gray-50 dark:bg-gray-700">
@@ -64,7 +62,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{#each game_data.questions[parseInt(question_index)].answers as answer} {#each solution.answers as answer}
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700"> <tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
<td <td
class="py-4 px-6 text-sm font-medium text-gray-900 whitespace-nowrap dark:text-white" class="py-4 px-6 text-sm font-medium text-gray-900 whitespace-nowrap dark:text-white"
@@ -90,10 +88,10 @@
</tbody> </tbody>
</table> </table>
</div> </div>
{:else if game_data.questions[parseInt(question_index)].type === QuizQuestionType.RANGE} {:else if solution.type === QuizQuestionType.RANGE}
<!--<p class="text-center"> <!--<p class="text-center">
Every number between {game_data.questions[parseInt(question_index)].answers Every number between {solution.answers
.min_correct} and {game_data.questions[parseInt(question_index)].answers .min_correct} and {solution.answers
.max_correct} was correct. .max_correct} was correct.
</p>--> </p>-->
{#await import('svelte-range-slider-pips')} {#await import('svelte-range-slider-pips')}
@@ -103,8 +101,8 @@
<svelte:component <svelte:component
this={c.default} this={c.default}
bind:values={slider_values} bind:values={slider_values}
bind:min={game_data.questions[parseInt(question_index)].answers.min} bind:min={solution.answers.min}
bind:max={game_data.questions[parseInt(question_index)].answers.max} bind:max={solution.answers.max}
pips pips
float float
all="label" all="label"
+5 -1
View File
@@ -119,7 +119,11 @@
> >
</div> </div>
{#await import('$lib/play/end.svelte') then c} {#await import('$lib/play/end.svelte') then c}
<svelte:component this={c.default} bind:final_results bind:quiz_data /> <svelte:component
this={c.default}
bind:final_results
bind:question_count={quiz_data.questions.length}
/>
{/await} {/await}
{/if} {/if}
{#if !success} {#if !success}
+19 -22
View File
@@ -2,7 +2,7 @@
<script lang="ts"> <script lang="ts">
import { socket } from '$lib/socket'; import { socket } from '$lib/socket';
import JoinGame from '$lib/play/join.svelte'; import JoinGame from '$lib/play/join.svelte';
import type { Answer, QuizData } from '$lib/quiz_types'; import type { Answer, QuizData, Question } from '$lib/quiz_types';
import ShowTitle from '$lib/play/title.svelte'; import ShowTitle from '$lib/play/title.svelte';
import Question from '$lib/play/question.svelte'; import Question from '$lib/play/question.svelte';
import ShowResults from '$lib/play/show_results.svelte'; import ShowResults from '$lib/play/show_results.svelte';
@@ -37,11 +37,14 @@
navbarVisible.set(false); navbarVisible.set(false);
let game_pin_valid: boolean; let game_pin_valid: boolean;
let answer_results: Array<Answer>; let answer_results: Array<Answer>;
let gameData: QuizData; let gameData;
let solution: Question;
let gameMeta: GameMeta = { let gameMeta: GameMeta = {
started: false started: false
}; };
let question;
// Functions // Functions
function restart() { function restart() {
unique = {}; unique = {};
@@ -56,17 +59,7 @@
// Socket-events // Socket-events
socket.on('joined_game', (data) => { socket.on('joined_game', (data) => {
console.log('joined_game', data); gameData = data;
let temp_data = JSON.parse(data);
for (let i = 0; i < temp_data.questions.length; i++) {
let question = temp_data.questions[i];
if (question.type === undefined) {
temp_data.questions[i].type = QuizQuestionType.ABCD;
} else {
temp_data.questions[i].type = QuizQuestionType[question.type];
}
}
gameData = temp_data;
// eslint-disable-next-line no-undef // eslint-disable-next-line no-undef
plausible('Joined Game', { props: { quiz_id: gameData.quiz_id } }); plausible('Joined Game', { props: { quiz_id: gameData.quiz_id } });
}); });
@@ -76,9 +69,12 @@
}); });
socket.on('set_question_number', (data) => { socket.on('set_question_number', (data) => {
solution = undefined;
restart(); restart();
console.log(data, data.question_index);
question = data.question;
question_index = data.question_index;
answer_results = undefined; answer_results = undefined;
question_index = data;
}); });
socket.on('start_game', () => { socket.on('start_game', () => {
@@ -97,6 +93,10 @@
socket.on('final_results', (data) => { socket.on('final_results', (data) => {
final_results = data; final_results = data;
}); });
socket.on('solutions', (data) => {
solution = data;
});
// The rest // The rest
</script> </script>
@@ -109,19 +109,19 @@
<svelte:window on:beforeunload={confirmUnload} /> <svelte:window on:beforeunload={confirmUnload} />
<svelte:head> <svelte:head>
<title>ClassQuiz - Play</title> <title>ClassQuiz - Play</title>
{#if gameData !== undefined && game_mode !== 'kahoot'} <!-- {#if gameData !== undefined && game_mode !== 'kahoot'}
{#each gameData.questions as question} {#each gameData.questions as question}
{#if question.image !== undefined} {#if question.image !== undefined}
<link rel="preload" as="image" href={question.image} /> <link rel="preload" as="image" href={question.image} />
{/if} {/if}
{/each} {/each}
{/if} {/if}-->
</svelte:head> </svelte:head>
<div> <div>
{#if !gameMeta.started && gameData === undefined} {#if !gameMeta.started && gameData === undefined}
<JoinGame {game_pin} bind:game_mode /> <JoinGame {game_pin} bind:game_mode />
{:else if JSON.stringify(final_results) !== JSON.stringify([null])} {:else if JSON.stringify(final_results) !== JSON.stringify([null])}
<ShowEndScreen bind:final_results bind:quiz_data={gameData} /> <ShowEndScreen bind:final_results bind:question_count={gameData.question_count} />
{:else if gameData !== undefined && question_index === ''} {:else if gameData !== undefined && question_index === ''}
<ShowTitle <ShowTitle
bind:title={gameData.title} bind:title={gameData.title}
@@ -130,11 +130,7 @@
/> />
{:else if gameMeta.started && gameData !== undefined && question_index !== '' && answer_results === undefined} {:else if gameMeta.started && gameData !== undefined && question_index !== '' && answer_results === undefined}
{#key unique} {#key unique}
<Question <Question bind:game_mode bind:question bind:question_index bind:solution />
bind:game_mode
bind:question={gameData.questions[parseInt(question_index)]}
bind:question_index
/>
{/key} {/key}
{:else if gameMeta.started && answer_results !== undefined} {:else if gameMeta.started && answer_results !== undefined}
{#if answer_results === null} {#if answer_results === null}
@@ -147,6 +143,7 @@
bind:results={answer_results} bind:results={answer_results}
bind:game_data={gameData} bind:game_data={gameData}
bind:question_index bind:question_index
bind:solution
/> />
{/key} {/key}
{/if} {/if}