✨ Implemented ClassQuizController-logic
This commit is contained in:
@@ -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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user