Implemented ClassQuizController-logic

This commit is contained in:
Mawoka
2023-03-27 21:04:08 +02:00
parent 603939fb20
commit 46ff32a986
13 changed files with 488 additions and 98 deletions
+47 -5
View File
@@ -10,12 +10,21 @@
import { fade } from 'svelte/transition';
import Spinner from '$lib/Spinner.svelte';
import { onMount } from 'svelte';
import { createTippy } from 'svelte-tippy';
export let quiz_id;
let captcha_selected = false;
let selected_game_mode = 'kahoot';
let loading = false;
let custom_field = '';
let cqcs_enabled = false;
const tippy = createTippy({
arrow: true,
animation: 'perspective-subtle',
placement: 'top-start',
allowHTML: true
});
onMount(() => {
const ls_data = localStorage.getItem('custom_field');
@@ -26,16 +35,17 @@
let res;
loading = true;
localStorage.setItem('custom_field', custom_field);
const cqcs_enabled_parsed = cqcs_enabled ? 'True' : 'False';
if (captcha_enabled && captcha_selected) {
res = await fetch(
`/api/v1/quiz/start/${id}?captcha_enabled=True&game_mode=${selected_game_mode}&custom_field=${custom_field}`,
`/api/v1/quiz/start/${id}?captcha_enabled=True&game_mode=${selected_game_mode}&custom_field=${custom_field}&cqcs_enabled=${cqcs_enabled_parsed}`,
{
method: 'POST'
}
);
} else {
res = await fetch(
`/api/v1/quiz/start/${id}?captcha_enabled=False&game_mode=${selected_game_mode}&custom_field=${custom_field}`,
`/api/v1/quiz/start/${id}?captcha_enabled=False&game_mode=${selected_game_mode}&custom_field=${custom_field}&cqcs_enabled=${cqcs_enabled_parsed}`,
{
method: 'POST'
}
@@ -54,7 +64,9 @@
const data = await res.json();
// eslint-disable-next-line no-undef
plausible('Started Game', { props: { quiz_id: id, game_id: data.game_id } });
window.location.assign(`/admin?token=${data.game_id}&pin=${data.game_pin}&connect=1`);
window.location.assign(
`/admin?token=${data.game_id}&pin=${data.game_pin}&connect=1&cqc_code=${data.cqc_code}`
);
}
};
</script>
@@ -89,7 +101,7 @@
</label>
</div>
{#if captcha_selected}
<div class="flex justify-center mt-2" transition:fade>
<div class="flex justify-center mt-2" in:fade>
<p class="w-1/3">
If enabled, Google's ReCaptcha will load in the browser of players. Only enable
if you really need it, since you need the consent of <b>EVERY</b> player to load
@@ -128,7 +140,7 @@
</p>
</div>
</div>
<div class="flex justify-center items-center">
<div class="flex justify-center items-center my-auto">
<label class="mr-4">Custom Field</label>
<input
bind:value={custom_field}
@@ -136,6 +148,36 @@
placeholder="Phone Number or Email"
/>
</div>
<div class="flex justify-center w-full my-auto">
<label
for="cqc-toggle"
class="inline-flex relative items-center cursor-pointer"
class:pointer-events-none={!captcha_enabled}
class:opacity-50={!captcha_enabled}
>
<input
type="checkbox"
bind:checked={cqcs_enabled}
id="cqc-toggle"
class="sr-only peer"
/>
<span
class="w-14 h-7 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:left-[4px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-6 after:w-6 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"
/>
<span class="ml-3 text-sm font-medium text-gray-900"
><a
href="/controller"
target="_blank"
use:tippy={{
content:
'ClassQuizControllers are small physical devices to play ClassQuiz. Click to learn more.'
}}
class="decoration-dashed underline cursor-help">ClassQuizControllers</a
>
are {cqcs_enabled ? 'enabled' : 'disabled'}</span
>
</label>
</div>
<button
class="mt-auto mx-auto bg-green-500 p-4 rounded-lg shadow-lg hover:bg-green-400 transition-all marck-script text-2xl"
@@ -0,0 +1,104 @@
<!--
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
-->
<script lang="ts">
import AudioPlayer from '$lib/play/audio_player.svelte';
import { getLocalization } from '$lib/i18n';
export let game_pin: string;
export let players;
export let socket;
export let cqc_code: string;
const { t } = getLocalization();
let play_music = false;
if (cqc_code === 'null') {
cqc_code = null;
}
const kick_player = (username: string) => {
socket.emit('kick_player', { username: username });
for (let i = 0; i < players.length; i++) {
console.log(players[i].username, username);
if (players[i].username === username) {
players.splice(i, 1);
break;
}
}
players = players;
};
const color_map = {
r: 'red',
g: 'green',
y: 'yellow',
b: 'blue'
};
</script>
<div class="w-full h-full">
<AudioPlayer bind:play={play_music} />
<div class="grid grid-cols-3 mt-12">
<div class="flex justify-center">
<p class="m-auto text-2xl">
Join at <b>{window.location.host}/play</b> and enter <b>{game_pin}</b>.
</p>
</div>
<img
alt="QR code to join the game"
src="/api/v1/utils/qr/{game_pin}"
class="block mx-auto w-1/2 dark:bg-white"
/>
{#if cqc_code}
<div class="m-auto">
<div class="flex-col flex justify-center">
<p class="mx-auto">Join by entering the following code</p>
<div class="flex flex-row gap-2 mx-auto">
{#each cqc_code as c}
<div class="flex flex-col">
<p class="text-center">{c}</p>
<span
style="background-color: {color_map[
c.toLowerCase()
]}; width: 2rem; height: {c.toLowerCase() == c ? '2' : '4'}rem"
/>
</div>
{/each}
</div>
</div>
</div>
{/if}
</div>
<p class="text-3xl text-center ">{$t('words.pin')}: {game_pin}</p>
<div class="flex justify-center w-full mt-4">
<ul class="list-disc pl-8">
{#if players.length > 0}
{#each players as player}
<li>
<span
class="hover:line-through"
on:click={() => {
kick_player(player.username);
}}>{player.username}</span
>
<!-- <button>{$t('words.kick')}</button>-->
</li>
{/each}
{/if}
</ul>
</div>
{#if players.length > 0}
<div class="flex justify-center w-full mt-4">
<button
class="ml-4 admin-button"
id="startGame"
on:click={() => {
socket.emit('start_game', '');
}}
>{$t('admin_page.start_game')}
</button>
</div>
{/if}
</div>
+8 -53
View File
@@ -11,11 +11,12 @@
import { navbarVisible } from '$lib/stores';
import type { PlayerAnswer, Player } from '$lib/admin.ts';
import SomeAdminScreen from '$lib/admin.svelte';
import AudioPlayer from '$lib/play/audio_player.svelte';
import GameNotStarted from '$lib/play/admin/game_not_started.svelte';
import { browser } from '$app/environment';
import { onMount } from 'svelte';
import FinalResults from '$lib/play/admin/final_results.svelte';
import GrayButton from '$lib/components/buttons/gray.svelte';
import { page } from '$app/stores';
navbarVisible.set(false);
@@ -41,7 +42,6 @@
let success = false;
let dataexport_download_a;
let warnToLeave = true;
let play_music = false;
const connect = async () => {
socket.emit('register_as_admin', {
@@ -127,17 +127,6 @@
window.matchMedia('(prefers-color-scheme: dark)').matches);
}
const kick_player = (username: string) => {
socket.emit('kick_player', { username: username });
for (let i = 0; i < players.length; i++) {
console.log(players[i].username, username);
if (players[i].username === username) {
players.splice(i, 1);
break;
}
}
players = players;
};
let bg_color;
let bg_image;
let results_saved = false;
@@ -201,46 +190,12 @@
<p class="text-red-700">{errorMessage}</p>
{/if}
{:else if !game_started}
<div class="w-full h-full">
<AudioPlayer bind:play={play_music} />
<div>
<img
alt="QR code to join the game"
src="/api/v1/utils/qr/{quiz_data.game_pin}"
class="block mx-auto w-1/6 mt-12 dark:bg-white"
/>
</div>
<p class="text-3xl text-center ">{$t('words.pin')}: {quiz_data.game_pin}</p>
<div class="flex justify-center w-full mt-4">
<ul class="list-disc pl-8">
{#if players.length > 0}
{#each players as player}
<li>
<span
class="hover:line-through"
on:click={() => {
kick_player(player.username);
}}>{player.username}</span
>
<!-- <button>{$t('words.kick')}</button>-->
</li>
{/each}
{/if}
</ul>
</div>
{#if players.length > 0}
<div class="flex justify-center w-full mt-4">
<button
class="ml-4 admin-button"
id="startGame"
on:click={() => {
socket.emit('start_game', '');
}}
>{$t('admin_page.start_game')}
</button>
</div>
{/if}
</div>
<GameNotStarted
{game_pin}
bind:players
{socket}
cqc_code={$page.url.searchParams.get('cqc_code')}
/>
{:else}
<SomeAdminScreen
bind:final_results
@@ -0,0 +1,13 @@
<!--
- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
-->
<div>
<h1 class="text-center text-8xl marck-script mt-12">ClassQuizController</h1>
<div>
<p class="text-center">Play a quiz with a physical controller, not on a touchscreen!</p>
</div>
<p class="text-center text-4xl m-12">More infos will follow soon</p>
</div>