Further cleanup, mostly with svelte 5 migration placeholder cleanup
This commit is contained in:
@@ -9,7 +9,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="description" content="" />
|
||||
<!-- <link rel="icon" href="%sveltekit.assets%/favicon.png" />-->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<script>
|
||||
window.plausible =
|
||||
|
||||
Vendored
+9
-2
@@ -5,8 +5,15 @@
|
||||
/// <reference types="vite/client" />
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_APP_TITLE: string;
|
||||
// more env variables...
|
||||
readonly VITE_GOOGLE_AUTH_ENABLED?: string;
|
||||
readonly VITE_GITHUB_AUTH_ENABLED?: string;
|
||||
readonly VITE_CAPTCHA_ENABLED?: string;
|
||||
readonly VITE_CUSTOM_OAUTH_NAME?: string;
|
||||
readonly VITE_REGISTRATION_DISABLED?: string;
|
||||
readonly VITE_HCAPTCHA?: string;
|
||||
readonly VITE_RECAPTCHA?: string;
|
||||
readonly VITE_SENTRY?: string;
|
||||
readonly VITE_PLAUSIBLE_DATA_URL?: string;
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
|
||||
@@ -36,10 +36,3 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
event.locals.email = jwt.payload.sub;
|
||||
return resolve(event);
|
||||
};
|
||||
|
||||
/*export const getSession: GetSession = async (event) => {
|
||||
return {
|
||||
email: event.locals.email,
|
||||
authenticated: Boolean(event.locals.email)
|
||||
};
|
||||
};*/
|
||||
|
||||
@@ -15,7 +15,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
import Controls from '$lib/play/admin/controls.svelte';
|
||||
import Question from '$lib/play/admin/question.svelte';
|
||||
|
||||
|
||||
const { t } = getLocalization();
|
||||
const default_colors = ['#D6EDC9', '#B07156', '#7F7057', '#4E6E58'];
|
||||
|
||||
@@ -24,13 +23,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
let timer_res: string = $state();
|
||||
let shown_question_now: number = $state();
|
||||
let final_results_clicked = $state(false);
|
||||
let timer_interval;
|
||||
let timer_interval: NodeJS.Timeout;
|
||||
let answer_count = $state(0);
|
||||
|
||||
interface Props {
|
||||
game_token: string;
|
||||
quiz_data: QuizData;
|
||||
game_mode: string;
|
||||
bg_color: string;
|
||||
final_results?: Array<null> | Array<Array<PlayerAnswer>>;
|
||||
control_visible: boolean;
|
||||
@@ -40,7 +38,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
let {
|
||||
game_token,
|
||||
quiz_data = $bindable(),
|
||||
game_mode,
|
||||
bg_color,
|
||||
final_results = $bindable([null]),
|
||||
control_visible,
|
||||
@@ -92,7 +89,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
timer_interval = setInterval(() => {
|
||||
if (timer_res === '0') {
|
||||
clearInterval(timer_interval);
|
||||
// socket.emit('show_solutions', {});
|
||||
return;
|
||||
} else {
|
||||
seconds--;
|
||||
@@ -132,9 +128,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
{#await import('$lib/play/admin/slide.svelte')}
|
||||
<Spinner my_20={false} />
|
||||
{:then c}
|
||||
<c.default
|
||||
bind:question={quiz_data.questions[selected_question]}
|
||||
/>
|
||||
<c.default question={quiz_data.questions[selected_question]} />
|
||||
{/await}
|
||||
{:else}
|
||||
<Question {quiz_data} {selected_question} {timer_res} {answer_count} {default_colors} />
|
||||
@@ -153,8 +147,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<Spinner />
|
||||
{:then c}
|
||||
<c.default
|
||||
bind:data={question_results}
|
||||
bind:question={quiz_data.questions[selected_question]}
|
||||
data={question_results}
|
||||
question={quiz_data.questions[selected_question]}
|
||||
/>
|
||||
{/await}
|
||||
{:else}
|
||||
@@ -164,7 +158,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<c.default
|
||||
bind:data={player_scores}
|
||||
question={quiz_data.questions[selected_question]}
|
||||
bind:new_data={question_results}
|
||||
new_data={question_results}
|
||||
/>
|
||||
{/await}
|
||||
{/if}
|
||||
|
||||
@@ -8,8 +8,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
This should be okay, right?
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import { tinykeys } from '$lib/tinykeys';
|
||||
import { fade } from 'svelte/transition';
|
||||
@@ -104,7 +102,6 @@ This should be okay, right?
|
||||
const toggle_open = (e: KeyboardEvent | undefined) => {
|
||||
e.preventDefault();
|
||||
open = !open;
|
||||
console.log('TOGGLE!');
|
||||
};
|
||||
|
||||
const close_cp = (e: KeyboardEvent | undefined) => {
|
||||
@@ -159,7 +156,6 @@ This should be okay, right?
|
||||
|
||||
visible_items = [];
|
||||
|
||||
console.log(res);
|
||||
for (const quiz_data of res) {
|
||||
visible_items.push(actions[quiz_data.id]);
|
||||
}
|
||||
@@ -206,13 +202,6 @@ This should be okay, right?
|
||||
input = '';
|
||||
};
|
||||
|
||||
run(() => {
|
||||
search(input);
|
||||
});
|
||||
// $: input = lower_input(input)
|
||||
run(() => {
|
||||
input = input.toLowerCase();
|
||||
});
|
||||
onMount(async () => {
|
||||
tinykeys(window, {
|
||||
'$mod+k': toggle_open,
|
||||
@@ -240,7 +229,9 @@ This should be okay, right?
|
||||
class="fixed top-0 left-0 w-screen h-screen flex bg-black/50 z-50"
|
||||
onclick={close_on_outside}
|
||||
onkeyup={close_on_outside}
|
||||
role="generic"
|
||||
role="button"
|
||||
aria-label="Close"
|
||||
tabindex="0"
|
||||
transition:fade|global={{ duration: 60 }}
|
||||
>
|
||||
<div class="m-auto w-1/3 h-2/3 rounded-sm bg-black flex flex-col">
|
||||
@@ -252,8 +243,9 @@ This should be okay, right?
|
||||
</p>
|
||||
<input
|
||||
type="text"
|
||||
class="col-start-1 row-start-1 bg-transparent w-full p-4 outline-hidden bg-gray-700 rounded-sm"
|
||||
class="col-start-1 row-start-1 w-full p-4 outline-hidden bg-gray-700 rounded-sm"
|
||||
bind:value={input}
|
||||
oninput={() => search(input)}
|
||||
autofocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -5,17 +5,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { fly } from 'svelte/transition';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { PopoverTypes } from './smalltop';
|
||||
|
||||
const { t } = getLocalization();
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
interface Props {
|
||||
open?: boolean;
|
||||
type: PopoverTypes;
|
||||
@@ -23,10 +18,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
|
||||
let { open = $bindable(false), type, data = undefined }: Props = $props();
|
||||
|
||||
run(() => {
|
||||
dispatch('open', open);
|
||||
});
|
||||
</script>
|
||||
|
||||
{#if open}
|
||||
|
||||
@@ -5,9 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, preventDefault } from 'svelte/legacy';
|
||||
|
||||
// import { mint } from '$lib/hashcash';
|
||||
import { dataSchema } from '$lib/yupSchemas';
|
||||
import type { EditorData, Question } from './quiz_types';
|
||||
import Sidebar from '$lib/editor/sidebar.svelte';
|
||||
@@ -28,52 +25,25 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
let { data = $bindable(), quiz_id }: Props = $props();
|
||||
let selected_question = $state(-1);
|
||||
let imgur_links_valid = $state(false);
|
||||
|
||||
const validateInput = async (data: EditorData) => {
|
||||
// console.log("input", data)
|
||||
try {
|
||||
await dataSchema.validate(data, { abortEarly: false });
|
||||
schemaInvalid = false;
|
||||
yupErrorMessage = '';
|
||||
} catch (err) {
|
||||
console.log('erro!', err.errors);
|
||||
schemaInvalid = true;
|
||||
yupErrorMessage = err.errors ? err.errors[0] : '';
|
||||
}
|
||||
};
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
validateInput(data);
|
||||
});
|
||||
|
||||
const checkIfAllQuestionImagesComplyWithRegex = (questions: Array<Question>) => {
|
||||
let NoteverythingValid = false;
|
||||
// const regex = /^https:\/\/i\.imgur\.com\/.{7}.(jpg|png|gif)$/;
|
||||
// const local_regex = /^http(|s):\/\/\w*(|:)\d*\/api\/v1\/storage\/download\/.{36}--.{36}$/g;
|
||||
const main_regex =
|
||||
/^(http(|s):\/\/.*(|:)\d*\/api\/v1\/storage\/download\/.{36}--.{36}|https:\/\/i\.imgur\.com\/.{7}.(jpg|png|gif))$/;
|
||||
for (let i = 0; i < questions.length; i++) {
|
||||
const question = questions[i];
|
||||
|
||||
if (question.image && !main_regex.test(question.image)) {
|
||||
NoteverythingValid = true;
|
||||
}
|
||||
}
|
||||
return NoteverythingValid;
|
||||
};
|
||||
|
||||
run(() => {
|
||||
imgur_links_valid = checkIfAllQuestionImagesComplyWithRegex(data.questions);
|
||||
});
|
||||
let edit_id = $state();
|
||||
let edit_id: string = $state();
|
||||
let confirm_to_leave = true;
|
||||
|
||||
run(() => {
|
||||
console.log('data', data);
|
||||
});
|
||||
|
||||
const getEditID = async () => {
|
||||
let res;
|
||||
let res: Response;
|
||||
if (quiz_id === null) {
|
||||
res = await fetch(`/api/v1/editor/start?edit=false`, {
|
||||
method: 'POST'
|
||||
@@ -91,8 +61,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
};
|
||||
|
||||
const confirmUnload = (event) => {
|
||||
console.log(confirm_to_leave);
|
||||
const confirmUnload = (event: BeforeUnloadEvent) => {
|
||||
if (!confirm_to_leave) {
|
||||
return;
|
||||
}
|
||||
@@ -101,7 +70,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
localStorage.setItem('edit_game', JSON.stringify(data));
|
||||
return 'unload';
|
||||
};
|
||||
const saveQuiz = async () => {
|
||||
const saveQuiz = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (schemaInvalid) {
|
||||
return;
|
||||
}
|
||||
@@ -126,7 +96,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
{#await getEditID()}
|
||||
<Spinner />
|
||||
{:then _}
|
||||
<form onsubmit={preventDefault(saveQuiz)}>
|
||||
<form onsubmit={saveQuiz}>
|
||||
<div class="grid grid-cols-6 h-screen w-screen">
|
||||
<div>
|
||||
<Sidebar bind:data bind:selected_question />
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { browser } from '$app/environment';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { thumbHashToDataURL } from 'thumbhash';
|
||||
@@ -36,7 +34,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
return Uint8Array.from(binString, (m) => m.codePointAt(0));
|
||||
}
|
||||
|
||||
const get_media = async () => {
|
||||
const get_media = async (_: string) => {
|
||||
if (!browser) {
|
||||
return;
|
||||
}
|
||||
@@ -55,14 +53,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
thumbhash_data = undefined;
|
||||
}
|
||||
};
|
||||
const update_url = () => {
|
||||
media = get_media();
|
||||
};
|
||||
let media = $state(get_media());
|
||||
run(() => {
|
||||
src;
|
||||
update_url();
|
||||
});
|
||||
let media = $derived(get_media(src));
|
||||
|
||||
let fullscreen_open = $state(false);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, preventDefault } from 'svelte/legacy';
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import type { EditorData, OrderQuizAnswer } from '$lib/quiz_types';
|
||||
@@ -170,9 +170,10 @@ SPDX-License-Identifier: MPL-2.0
|
||||
class="rounded-lg p-1 border-black border"
|
||||
type="color"
|
||||
bind:value={answer.color}
|
||||
oncontextmenu={preventDefault(() => {
|
||||
oncontextmenu={(e) => {
|
||||
e.preventDefault();
|
||||
answer.color = null;
|
||||
})}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
@@ -27,19 +27,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
const correct_numbers = (data: number[]) => {
|
||||
console.log(data, data[1] <= data[0])
|
||||
if (data[1] <= data[0]) {
|
||||
range_arr[1] = range_arr[0] + 2
|
||||
}
|
||||
if (data[0] <= data[1]) {
|
||||
range_arr[0] = range_arr[1] - 2
|
||||
}
|
||||
}
|
||||
$: correct_numbers(range_arr)
|
||||
|
||||
*/
|
||||
let answer = question.answers;
|
||||
let range_arr = $state([answer.min_correct, answer.max_correct]);
|
||||
run(() => {
|
||||
|
||||
@@ -5,7 +5,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, preventDefault } from 'svelte/legacy';
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import type { EditorData } from '../quiz_types';
|
||||
import { fade } from 'svelte/transition';
|
||||
@@ -103,9 +103,10 @@ SPDX-License-Identifier: MPL-2.0
|
||||
class="rounded-lg p-1 border-black border"
|
||||
type="color"
|
||||
bind:value={answer.color}
|
||||
oncontextmenu={preventDefault(() => {
|
||||
oncontextmenu={(e) => {
|
||||
e.preventDefault();
|
||||
answer.color = default_colors[index];
|
||||
})}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, preventDefault } from 'svelte/legacy';
|
||||
|
||||
import type { EditorData } from '$lib/quiz_types';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import Spinner from '$lib/Spinner.svelte';
|
||||
@@ -30,7 +28,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
animation: 'perspective-subtle'
|
||||
});
|
||||
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
data.background_color = custom_bg_color ? data.background_color : undefined;
|
||||
});
|
||||
</script>
|
||||
@@ -57,11 +55,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
: `unset`}"
|
||||
>
|
||||
<div class="flex justify-center pt-10 w-full">
|
||||
<!--<input
|
||||
type="text"
|
||||
bind:value={data.title}
|
||||
class="p-3 rounded-lg border-gray-500 border text-center w-1/3 text-lg font-semibold dark:bg-gray-500"
|
||||
/>-->
|
||||
{#await import('$lib/inline-editor.svelte')}
|
||||
<Spinner my_20={false} />
|
||||
{:then c}
|
||||
@@ -70,7 +63,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
<div class="flex justify-center pt-10 w-full max-h-32">
|
||||
<textarea
|
||||
type="text"
|
||||
placeholder="Description"
|
||||
bind:value={data.description}
|
||||
class="p-3 rounded-lg border-gray-500 border text-center w-1/3 h-20 resize-none dark:bg-gray-500 outline-hidden focus:shadow-2xl transition-all"
|
||||
@@ -78,26 +70,22 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
|
||||
{#if data.cover_image != undefined && data.cover_image !== ''}
|
||||
<div class="flex justify-center pt-10 w-full max-h-72 w-full">
|
||||
<div class="flex justify-center pt-10 w-full max-h-72">
|
||||
<img
|
||||
src="/api/v1/storage/download/{data.cover_image}"
|
||||
alt="not available"
|
||||
class="max-h-72 h-auto w-auto"
|
||||
oncontextmenu={preventDefault(() => {
|
||||
oncontextmenu={(e: Event) => {
|
||||
e.preventDefault();
|
||||
data.cover_image = null;
|
||||
})}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
{#await import('$lib/editor/uploader.svelte')}
|
||||
<Spinner my_20={false} />
|
||||
{:then c}
|
||||
<c.default
|
||||
bind:modalOpen={uppyOpen}
|
||||
bind:edit_id
|
||||
bind:data
|
||||
video_upload={false}
|
||||
/>
|
||||
<c.default bind:modalOpen={uppyOpen} {data} video_upload={false} />
|
||||
{/await}
|
||||
{/if}
|
||||
<div class="pt-10 w-full flex justify-center">
|
||||
@@ -154,8 +142,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
class="bg-gray-200 rounded-lg w-full h-full p-1"
|
||||
class:pointer-events-none={custom_bg_color}
|
||||
>
|
||||
<span
|
||||
class="inline-block w-full h-full bg-[#d6edc9] dark:bg-[#4e6e58]"
|
||||
<span class="inline-block w-full h-full bg-[#d6edc9] dark:bg-[#4e6e58]"
|
||||
></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -209,8 +196,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
{:then c}
|
||||
<c.default
|
||||
bind:modalOpen={bg_uppy_open}
|
||||
bind:edit_id
|
||||
bind:data
|
||||
{data}
|
||||
selected_question={-1}
|
||||
video_upload={false}
|
||||
/>
|
||||
|
||||
@@ -5,14 +5,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { fade } from 'svelte/transition';
|
||||
|
||||
let { title = $bindable(), time = $bindable() } = $props();
|
||||
let time_local = $state(120);
|
||||
/*eslint no-unused-vars: ["error", { "argsIgnorePattern": "^_" }]*/
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
time = String(time_local);
|
||||
});
|
||||
if (time) {
|
||||
|
||||
@@ -27,19 +27,17 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
const { t } = getLocalization();
|
||||
let {
|
||||
modalOpen = false,
|
||||
edit_id,
|
||||
modalOpen = $bindable(),
|
||||
data,
|
||||
selected_question,
|
||||
video_upload = false,
|
||||
library_enabled = true
|
||||
}: {
|
||||
modalOpen: boolean;
|
||||
edit_id: string;
|
||||
data: EditorData;
|
||||
selected_question: number;
|
||||
selected_question?: number;
|
||||
video_upload: boolean;
|
||||
library_enabled: boolean;
|
||||
library_enabled?: boolean;
|
||||
} = $props();
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
@@ -83,7 +81,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
// allowedFileTypes: ['.gif', '.jpg', '.jpeg', '.png', '.svg', '.webp']
|
||||
}
|
||||
};
|
||||
let image_id;
|
||||
let image_id: string;
|
||||
uppy.on('upload-success', (file, response) => {
|
||||
image_id = response.body.id;
|
||||
});
|
||||
@@ -142,6 +140,10 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<div
|
||||
class="w-screen h-screen fixed top-0 left-0 bg-black/50 z-20 flex justify-center"
|
||||
onclick={handle_on_click}
|
||||
tabindex="0"
|
||||
role="button"
|
||||
aria-label="Close modal"
|
||||
onkeydown={(e) => (e.key === 'Enter' || e.key === ' ' ? handle_on_click(e) : null)}
|
||||
transition:fade={{ duration: 100 }}
|
||||
>
|
||||
{#if selected_type === null}
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
|
||||
import type { EditorData } from '$lib/quiz_types';
|
||||
import Spinner from '$lib/Spinner.svelte';
|
||||
import BrownButton from '$lib/components/buttons/brown.svelte';
|
||||
@@ -70,7 +68,10 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
<form
|
||||
class="w-full flex gap-2"
|
||||
onsubmit={preventDefault(() => (fetched_data = fetch_data()))}
|
||||
onsubmit={(e) => {
|
||||
e.preventDefault();
|
||||
fetched_data = fetch_data();
|
||||
}}
|
||||
>
|
||||
<input
|
||||
class="w-full outline-hidden p-1 rounded-sm dark:bg-gray-500 bg-gray-300"
|
||||
|
||||
@@ -22,24 +22,17 @@ import vi from './locales/vi.json';
|
||||
import ta from './locales/ta.json';
|
||||
import pt_BR from './locales/pt_BR.json';
|
||||
import ja from './locales/ja.json';
|
||||
// import uz from './locales/uz.json'
|
||||
// import zh_Hans from './locales/zh_Hans.json';
|
||||
import LanguageDetector from 'i18next-browser-languagedetector';
|
||||
|
||||
import type { i18n } from 'i18next';
|
||||
|
||||
export class I18nService {
|
||||
// expose i18next
|
||||
i18n: i18n;
|
||||
|
||||
constructor() {
|
||||
this.i18n = i18next;
|
||||
this.initialize();
|
||||
// this.changeLanguage("de")
|
||||
//this.changeLanguage(INITIAL_LANGUAGE);
|
||||
}
|
||||
|
||||
// Our translation function
|
||||
t(key: string, replacements?: Record<string, unknown>): string {
|
||||
return this.i18n.t(key, replacements);
|
||||
}
|
||||
@@ -57,10 +50,6 @@ export class I18nService {
|
||||
},
|
||||
returnEmptyString: false,
|
||||
simplifyPluralSuffix: true,
|
||||
// detection: {
|
||||
// order: ['browser', 'querystring', 'navigator', 'localStorage', 'htmlTag'],
|
||||
// lookupQuerystring: 'lng'
|
||||
// }
|
||||
detection: {
|
||||
order: ['querystring', 'cookie', 'localStorage', 'navigator'],
|
||||
lookupQuerystring: 'lng',
|
||||
@@ -88,8 +77,6 @@ export class I18nService {
|
||||
this.i18n.addResourceBundle('ta', 'translation', ta);
|
||||
this.i18n.addResourceBundle('pt_BR', 'translation', pt_BR);
|
||||
this.i18n.addResourceBundle('ja', 'translation', ja);
|
||||
|
||||
// this.i18n.addResourceBundle('uz', 'translation', uz);
|
||||
}
|
||||
|
||||
changeLanguage(language: string): void {
|
||||
|
||||
@@ -20,14 +20,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
Strikethrough
|
||||
} from 'ckeditor5';
|
||||
import 'ckeditor5/ckeditor5.css';
|
||||
// import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
|
||||
// import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold.js';
|
||||
// import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic.js';
|
||||
// import Strikethrough from '@ckeditor/ckeditor5-basic-styles/src/strikethrough';
|
||||
// import Subscript from '@ckeditor/ckeditor5-basic-styles/src/subscript.js';
|
||||
// import Superscript from '@ckeditor/ckeditor5-basic-styles/src/superscript.js';
|
||||
|
||||
|
||||
|
||||
const triggerChange = () => {
|
||||
text = editor.getData();
|
||||
@@ -46,33 +38,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
run(() => {
|
||||
text = text.replace('<p>', '').replace('</p>', '');
|
||||
});
|
||||
|
||||
/* Editor.builtinPlugins = [
|
||||
Autoformat,
|
||||
Bold,
|
||||
Essentials,
|
||||
Italic,
|
||||
Paragraph,
|
||||
Strikethrough,
|
||||
Subscript,
|
||||
Superscript,
|
||||
TextTransformation
|
||||
];*/
|
||||
/*Editor.defaultConfig = {
|
||||
toolbar: {
|
||||
items: [
|
||||
'bold',
|
||||
'italic',
|
||||
'strikethrough',
|
||||
'superscript',
|
||||
'subscript',
|
||||
'|',
|
||||
'undo',
|
||||
'redo'
|
||||
]
|
||||
},
|
||||
language: 'en'
|
||||
};*/
|
||||
let editor;
|
||||
onMount(() => {
|
||||
class Editor extends BalloonEditor {
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
let { text } = $props();
|
||||
let internal_text = $state('');
|
||||
/*
|
||||
const markSelection = (function() {
|
||||
const markerTextChar = '\ufeff';
|
||||
const markerTextCharEntity = '';
|
||||
|
||||
let markerEl, markerId = 'sel_' + new Date().getTime() + '_' + Math.random().toString().substr(2);
|
||||
|
||||
let selectionEl;
|
||||
|
||||
return function(win) {
|
||||
win = win || window;
|
||||
const doc = win.document;
|
||||
let sel, range;
|
||||
// Branch for IE <= 8
|
||||
if (doc.selection && doc.selection.createRange) {
|
||||
// Clone the TextRange and collapse
|
||||
range = doc.selection.createRange().duplicate();
|
||||
range.collapse(false);
|
||||
|
||||
// Create the marker element containing a single invisible character by creating literal HTML and insert it
|
||||
range.pasteHTML('<span id="' + markerId + '" style="position: relative;">' + markerTextCharEntity + '</span>');
|
||||
markerEl = doc.getElementById(markerId);
|
||||
} else if (win.getSelection) {
|
||||
sel = win.getSelection();
|
||||
range = sel.getRangeAt(0).cloneRange();
|
||||
range.collapse(false);
|
||||
|
||||
// Create the marker element containing a single invisible character using DOM methods and insert it
|
||||
markerEl = doc.createElement('span');
|
||||
markerEl.id = markerId;
|
||||
markerEl.appendChild(doc.createTextNode(markerTextChar));
|
||||
range.insertNode(markerEl);
|
||||
}
|
||||
|
||||
if (markerEl) {
|
||||
// Lazily create element to be placed next to the selection
|
||||
if (!selectionEl) {
|
||||
selectionEl = doc.createElement('div');
|
||||
selectionEl.style.border = 'solid darkblue 1px';
|
||||
selectionEl.style.backgroundColor = 'lightgoldenrodyellow';
|
||||
selectionEl.innerHTML = '<- selection';
|
||||
selectionEl.style.position = 'absolute';
|
||||
|
||||
doc.body.appendChild(selectionEl);
|
||||
}
|
||||
|
||||
// Find markerEl position http://www.quirksmode.org/js/findpos.html
|
||||
var obj = markerEl;
|
||||
var left = 0, top = 0;
|
||||
do {
|
||||
left += obj.offsetLeft;
|
||||
top += obj.offsetTop;
|
||||
} while (obj = obj.offsetParent);
|
||||
|
||||
// Move the button into place.
|
||||
// Substitute your jQuery stuff in here
|
||||
selectionEl.style.left = left + 'px';
|
||||
selectionEl.style.top = top + 'px';
|
||||
|
||||
markerEl.parentNode.removeChild(markerEl);
|
||||
}
|
||||
};
|
||||
})();*/
|
||||
// $: console.log(document.getSelection().toString())
|
||||
/*
|
||||
const markSelection = () => {
|
||||
const sel = document.getSelection();
|
||||
console.log(sel.toString());
|
||||
};
|
||||
*/
|
||||
const bold_regex = /.*\*\*(.+)\*\*.*/gm; // **bold**
|
||||
const italic_regex = /.*\|\|(.+)\|\|.*/gm; // *italic*
|
||||
const strikethrough_regex = /.*~~(.+)~~.*/gm; // ~~strikethrough~~
|
||||
const sub_regex = /.*__(.+)__.*/gm; // __sub__
|
||||
const sup_regex = /.*--(.+)--.*/gm; // --sup--
|
||||
const process_input = () => {
|
||||
let output = internal_text;
|
||||
output = output.replace(bold_regex, '<b>$1</b>');
|
||||
output = output.replace(italic_regex, '<i>$1</i>');
|
||||
output = output.replace(strikethrough_regex, '<del>$1</del>');
|
||||
output = output.replace(sub_regex, '<sub>$1</sub>');
|
||||
output = output.replace(sup_regex, '<sup>$1</sup>');
|
||||
console.log(output);
|
||||
};
|
||||
|
||||
run(() => {
|
||||
process_input();
|
||||
internal_text;
|
||||
});
|
||||
</script>
|
||||
|
||||
<input bind:value={internal_text} />
|
||||
@@ -3,53 +3,6 @@ SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<!--
|
||||
<script context='module' lang='ts'>
|
||||
export const prerender = true;
|
||||
export const load = async ({}) => {
|
||||
return {};
|
||||
const languages = [
|
||||
{
|
||||
code: 'de',
|
||||
name: 'Deutsch',
|
||||
flag: '🇩🇪'
|
||||
},
|
||||
{
|
||||
code: 'en',
|
||||
name: 'English',
|
||||
flag: '🇺🇲'
|
||||
},
|
||||
{
|
||||
code: 'tr',
|
||||
name: 'Türkçe',
|
||||
flag: '🇹🇷'
|
||||
},
|
||||
{
|
||||
code: 'fr',
|
||||
name: 'Français',
|
||||
flag: '🇫🇷'
|
||||
}
|
||||
];
|
||||
let final_arr = [];
|
||||
const set_percents = async () => {
|
||||
for (const lang of languages) {
|
||||
const res = await fetch(`https://translate.mawoka.eu/api/translations/classquiz/frontend/${lang.code}/?format=json`);
|
||||
const json = await res.json();
|
||||
console.log(json);
|
||||
// return Math.floor(json.translated_percent);
|
||||
final_arr.push({ ...lang, percent: json.translated_percent });
|
||||
}
|
||||
};
|
||||
await set_percents();
|
||||
return {
|
||||
slot: {
|
||||
final_arr
|
||||
}
|
||||
};
|
||||
};
|
||||
</script>
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
@@ -164,7 +117,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
const get_selected_language = (): string => {
|
||||
return localStorage.getItem('language');
|
||||
};
|
||||
let selected_language = $state();
|
||||
let selected_language: string = $state();
|
||||
onMount(() => {
|
||||
selected_language = get_selected_language();
|
||||
});
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { onMount } from 'svelte';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
|
||||
@@ -16,13 +14,14 @@ SPDX-License-Identifier: MPL-2.0
|
||||
import confetti from 'canvas-confetti';
|
||||
interface Props {
|
||||
data: any;
|
||||
username: any;
|
||||
username?: any;
|
||||
show_final_results: boolean;
|
||||
}
|
||||
|
||||
let { data = $bindable(), username, show_final_results }: Props = $props();
|
||||
let sorted_data = $derived(sortObjectbyValue(data));
|
||||
|
||||
function sortObjectbyValue(obj) {
|
||||
function sortObjectbyValue(obj: object) {
|
||||
const ret = {};
|
||||
Object.keys(obj)
|
||||
.sort((a, b) => obj[b] - obj[a])
|
||||
@@ -30,30 +29,22 @@ SPDX-License-Identifier: MPL-2.0
|
||||
return ret;
|
||||
}
|
||||
|
||||
run(() => {
|
||||
data = sortObjectbyValue(data);
|
||||
});
|
||||
run(() => {
|
||||
console.log(data, 'sorted, fina');
|
||||
});
|
||||
|
||||
let player_names = $derived(Object.keys(data).sort(function (a, b) {
|
||||
return data[b] - data[a];
|
||||
}));
|
||||
|
||||
let player_names = $derived(Object.keys(sorted_data));
|
||||
|
||||
let player_count_or_five = $derived(player_names.length >= 5 ? 5 : player_names.length);
|
||||
|
||||
|
||||
let canvas = $state();
|
||||
let canvas: HTMLCanvasElement = $state();
|
||||
onMount(() => {
|
||||
setTimeout(() => {
|
||||
confetti.create(canvas, {
|
||||
resize: true,
|
||||
useWorker: true
|
||||
});
|
||||
confetti({ particleCount: 200, spread: 160 });
|
||||
}, player_count_or_five * 1200 - 800);
|
||||
setTimeout(
|
||||
() => {
|
||||
confetti.create(canvas, {
|
||||
resize: true,
|
||||
useWorker: true
|
||||
});
|
||||
confetti({ particleCount: 200, spread: 160 });
|
||||
},
|
||||
player_count_or_five * 1200 - 800
|
||||
);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -70,13 +61,13 @@ SPDX-License-Identifier: MPL-2.0
|
||||
{$t('play_page.final_result_rank', {
|
||||
place: i + 1,
|
||||
username: player,
|
||||
points: data[player]
|
||||
points: sorted_data[player]
|
||||
})}
|
||||
</p>
|
||||
{/if}
|
||||
{/each}
|
||||
</div>
|
||||
{#if data[username]}
|
||||
{#if sorted_data[username]}
|
||||
<div class="fixed bottom-0 left-0 flex justify-center w-full mb-6">
|
||||
<div class="mx-auto p-2 border-[#B07156] border-4 rounded-sm">
|
||||
<p class="text-center">{$t('play_page.your_score', { score: data[username] })}</p>
|
||||
|
||||
@@ -121,6 +121,15 @@ SPDX-License-Identifier: MPL-2.0
|
||||
class="fixed top-0 left-0 z-50 w-screen h-screen bg-black/50 flex p-2"
|
||||
transition:fade|global={{ duration: 80 }}
|
||||
onclick={() => (fullscreen_open = false)}
|
||||
tabindex="0"
|
||||
role="button"
|
||||
aria-label="Close modal"
|
||||
onkeydown={(e) =>
|
||||
e.key === 'Enter' || e.key === ' '
|
||||
? () => {
|
||||
fullscreen_open = false;
|
||||
}
|
||||
: null}
|
||||
>
|
||||
<img
|
||||
alt="QR code to join the game"
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { QuizQuestionType } from '$lib/quiz_types';
|
||||
import type { QuizData } from '$lib/quiz_types';
|
||||
import { get_foreground_color } from '$lib/helpers.js';
|
||||
@@ -15,7 +13,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
import MediaComponent from '$lib/editor/MediaComponent.svelte';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
|
||||
|
||||
interface Props {
|
||||
quiz_data: QuizData;
|
||||
selected_question: number;
|
||||
@@ -34,16 +31,16 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
const { t } = getLocalization();
|
||||
|
||||
let circular_progress = $state(0);
|
||||
run(() => {
|
||||
let circular_progress = $derived.by(() => {
|
||||
try {
|
||||
circular_progress =
|
||||
return (
|
||||
1 -
|
||||
((100 / parseInt(quiz_data.questions[selected_question].time)) *
|
||||
parseInt(timer_res)) /
|
||||
100;
|
||||
100
|
||||
);
|
||||
} catch {
|
||||
circular_progress = 0;
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@@ -56,11 +53,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<div class="grid grid-cols-3 my-2">
|
||||
<span></span>
|
||||
<div class="m-auto">
|
||||
<CircularTimer
|
||||
bind:text={timer_res}
|
||||
bind:progress={circular_progress}
|
||||
color="#ef4444"
|
||||
/>
|
||||
<CircularTimer text={timer_res} progress={circular_progress} color="#ef4444" />
|
||||
</div>
|
||||
<p class="m-auto text-3xl">
|
||||
{$t('admin_page.answers_submitted', { answer_count: answer_count })}
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import VotingResults from './voting_results.svelte';
|
||||
import { flip } from 'svelte/animate';
|
||||
import { fly } from 'svelte/transition';
|
||||
@@ -31,7 +29,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
let { data = $bindable(), question, new_data }: Props = $props();
|
||||
|
||||
function sortObjectbyValue(obj) {
|
||||
function sortObjectbyValue(obj: object) {
|
||||
const ret = {};
|
||||
Object.keys(obj)
|
||||
.sort((a, b) => obj[b] - obj[a])
|
||||
@@ -39,26 +37,20 @@ SPDX-License-Identifier: MPL-2.0
|
||||
return ret;
|
||||
}
|
||||
|
||||
let sorted_data = $derived(sortObjectbyValue(data));
|
||||
|
||||
// let data_by_username = {};
|
||||
let score_by_username = $state({});
|
||||
|
||||
const do_sth = () => {
|
||||
const group_username_by_score = (new_d: any[]): object => {
|
||||
let ret_data = {};
|
||||
for (const i of new_data) {
|
||||
score_by_username[i.username] = i.score;
|
||||
ret_data[i.username] = i.score;
|
||||
}
|
||||
return ret_data;
|
||||
};
|
||||
let score_by_username = $derived(group_username_by_score(new_data));
|
||||
|
||||
run(() => {
|
||||
new_data;
|
||||
score_by_username;
|
||||
do_sth();
|
||||
});
|
||||
|
||||
let player_names = $derived(
|
||||
Object.keys(data).sort(function (a, b) {
|
||||
return data[b] - data[a];
|
||||
})
|
||||
);
|
||||
let player_names = $derived(Object.keys(sorted_data));
|
||||
|
||||
if (JSON.stringify(data) === '{}') {
|
||||
for (const i of new_data) {
|
||||
@@ -66,15 +58,9 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
}
|
||||
|
||||
run(() => {
|
||||
data = sortObjectbyValue(data);
|
||||
});
|
||||
|
||||
let show_new_score_clicked = $state(false);
|
||||
|
||||
const show_new_score = () => {
|
||||
// for (let i = 0; i++; i < player_names.length) {
|
||||
// console.log(data)
|
||||
for (const i of player_names) {
|
||||
if (isNaN(data[i])) {
|
||||
data[i] = 0;
|
||||
@@ -91,8 +77,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
setTimeout(() => {
|
||||
data = data;
|
||||
}, 800);
|
||||
|
||||
// console.log(data)
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
@@ -127,7 +111,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<td class:hidden={i > 3} class="p-2 border-r border-r-black"
|
||||
>{player}</td
|
||||
>
|
||||
<td class:hidden={i > 3} class="p-2">{data[player]}</td>
|
||||
<td class:hidden={i > 3} class="p-2">{sorted_data[player]}</td>
|
||||
{#if show_new_score_clicked}
|
||||
<td
|
||||
in:fly|global={{ x: 300 }}
|
||||
|
||||
@@ -40,6 +40,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<div bind:this={canvas_el} class="w-full h-full block"></div>
|
||||
</div>
|
||||
<div class="w-full h-full flex justify-center">
|
||||
<img src={img_src} />
|
||||
<img src={img_src} alt="Slide image" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import Audio1 from '$lib/assets/music/1-128.mp3';
|
||||
|
||||
interface Props {
|
||||
@@ -25,10 +23,10 @@ SPDX-License-Identifier: MPL-2.0
|
||||
audio.pause();
|
||||
}
|
||||
};
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
audio.volume = volume / 100;
|
||||
});
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
control_audio(play);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
interface Props {
|
||||
progress: number;
|
||||
text: string;
|
||||
@@ -14,25 +12,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
|
||||
let { progress, text, color }: Props = $props();
|
||||
let angle = $state(360 * progress);
|
||||
|
||||
run(() => {
|
||||
angle = 360 * progress;
|
||||
});
|
||||
run(() => {
|
||||
console.log(angle);
|
||||
});
|
||||
let angle = $derived(360 * progress);
|
||||
|
||||
// Adapt the logic according to the approach
|
||||
let background = $state(`radial-gradient(white 50%, transparent 51%),
|
||||
conic-gradient(transparent 0deg ${angle}deg, gainsboro ${angle}deg 360deg),
|
||||
conic-gradient(green 0deg, green 90deg, green 180deg, green);`);
|
||||
|
||||
run(() => {
|
||||
background = `radial-gradient(white 50%, transparent 51%),
|
||||
let background = $derived(`radial-gradient(white 50%, transparent 51%),
|
||||
conic-gradient(transparent 0deg ${angle}deg, gainsboro ${angle}deg 360deg),
|
||||
conic-gradient(${color} 0deg, ${color} 90deg, ${color} 180deg, ${color});`;
|
||||
});
|
||||
conic-gradient(${color} 0deg, ${color} 90deg, ${color} 180deg, ${color});`);
|
||||
|
||||
let cssVarStyles = $derived(`--background:${background}`);
|
||||
</script>
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
|
||||
const { t } = getLocalization();
|
||||
@@ -85,9 +83,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
};
|
||||
let winners = getWinnersSorted();
|
||||
run(() => {
|
||||
console.log(winners, winners_arr);
|
||||
});
|
||||
</script>
|
||||
|
||||
<div>
|
||||
|
||||
@@ -5,14 +5,10 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, createBubbler, preventDefault } from 'svelte/legacy';
|
||||
|
||||
const bubble = createBubbler();
|
||||
import { socket } from '$lib/socket';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { browser } from '$app/environment';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
// import { alertModal } from '../stores';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import Cookies from 'js-cookie';
|
||||
import BrownButton from '$lib/components/buttons/brown.svelte';
|
||||
@@ -116,14 +112,14 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
};
|
||||
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
if (game_pin.length > 5) {
|
||||
console.log('Setting game pin');
|
||||
set_game_pin();
|
||||
}
|
||||
});
|
||||
|
||||
const setUsername = async () => {
|
||||
const setUsername = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (username.length <= 3) {
|
||||
return;
|
||||
}
|
||||
@@ -189,12 +185,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
alert('Game not found');
|
||||
}
|
||||
});
|
||||
|
||||
run(() => {
|
||||
console.log(game_pin, game_pin.length > 6);
|
||||
});
|
||||
run(() => {
|
||||
game_pin = game_pin.replace(/\D/g, '');
|
||||
$effect(() => {
|
||||
const cleaned = game_pin.replace(/\D/g, '');
|
||||
if (game_pin.replace(/\D/g, '') === game_pin) {
|
||||
return;
|
||||
}
|
||||
game_pin = cleaned;
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -211,10 +207,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
{#if game_pin === '' || game_pin.length < 6}
|
||||
<div class="flex flex-col justify-center align-center w-screen h-screen">
|
||||
<form
|
||||
onsubmit={preventDefault(bubble('submit'))}
|
||||
class="flex-col flex justify-center align-center mx-auto"
|
||||
>
|
||||
<form class="flex-col flex justify-center align-center mx-auto">
|
||||
<h1 class="text-lg text-center">{$t('words.game_pin')}</h1>
|
||||
<input
|
||||
class="border border-gray-400 self-center text-center text-black ring-0 outline-hidden p-2 rounded-lg focus:shadow-2xl transition-all"
|
||||
@@ -232,10 +225,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex flex-col justify-center align-center w-screen h-screen">
|
||||
<form
|
||||
onsubmit={preventDefault(setUsername)}
|
||||
class="flex-col flex justify-center align-center mx-auto"
|
||||
>
|
||||
<form onsubmit={setUsername} class="flex-col flex justify-center align-center mx-auto">
|
||||
<h1 class="text-lg text-center">{$t('words.username')}</h1>
|
||||
<input
|
||||
class="border border-gray-400 self-center text-center text-black ring-0 outline-hidden p-2 rounded-lg focus:shadow-2xl transition-all"
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import type { Question } from '$lib/quiz_types';
|
||||
import { QuizQuestionType } from '$lib/quiz_types';
|
||||
import { socket } from '$lib/socket';
|
||||
@@ -35,23 +33,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
solution
|
||||
}: Props = $props();
|
||||
|
||||
run(() => {
|
||||
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);
|
||||
} else {
|
||||
throw new Error('question_index must be a string or number');
|
||||
}*/
|
||||
|
||||
let timer_res = $state(question.time);
|
||||
let selected_answer: string = $state();
|
||||
|
||||
@@ -75,7 +62,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
timer(question.time);
|
||||
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
if (solution !== undefined) {
|
||||
timer_res = '0';
|
||||
}
|
||||
@@ -83,7 +70,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
const selectAnswer = (answer: string) => {
|
||||
selected_answer = answer;
|
||||
//timer_res = '0';
|
||||
socket.emit('submit_answer', {
|
||||
question_index: question_index,
|
||||
answer: answer
|
||||
@@ -132,15 +118,14 @@ SPDX-License-Identifier: MPL-2.0
|
||||
_arr[b] = temp;
|
||||
return _arr;
|
||||
};
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
set_answer_if_not_set_range(timer_res);
|
||||
});
|
||||
let circular_progress = $state(0);
|
||||
run(() => {
|
||||
let circular_progress = $derived.by(() => {
|
||||
try {
|
||||
circular_progress = 1 - ((100 / question.time) * parseInt(timer_res)) / 100;
|
||||
return 1 - ((100 / question.time) * parseInt(timer_res)) / 100;
|
||||
} catch {
|
||||
circular_progress = 0;
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -155,9 +140,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
return '100';
|
||||
}
|
||||
};
|
||||
run(() => {
|
||||
console.log(slider_value, 'values');
|
||||
});
|
||||
const default_colors = ['#D6EDC9', '#B07156', '#7F7057', '#4E6E58'];
|
||||
</script>
|
||||
|
||||
@@ -189,11 +171,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<div
|
||||
class="absolute top-0 bottom-0 left-0 right-0 m-auto rounded-full h-fit w-fit border-2 border-black shadow-2xl z-40"
|
||||
>
|
||||
<CircularTimer
|
||||
bind:text={timer_res}
|
||||
bind:progress={circular_progress}
|
||||
color="#ef4444"
|
||||
/>
|
||||
<CircularTimer text={timer_res} progress={circular_progress} color="#ef4444" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-rows-2 grid-flow-col auto-cols-auto gap-2 w-full p-4 h-full">
|
||||
@@ -279,20 +257,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else if question.type === QuizQuestionType.RANGE}
|
||||
{#if solution === undefined}
|
||||
<Spinner />
|
||||
{:else}
|
||||
<p class="text-center">
|
||||
Every number between {solution.answers.min_correct} and {solution.answers
|
||||
.max_correct} was correct. You got {selected_answer}, so you have been
|
||||
{#if solution.answers.min_correct <= parseInt(selected_answer) && parseInt(selected_answer) <= solution.answers.max_correct}
|
||||
correct
|
||||
{:else}
|
||||
wrong.
|
||||
{/if}
|
||||
</p>
|
||||
{/if}
|
||||
{:else if question.type === QuizQuestionType.ORDER}
|
||||
<!-- {#if solution === undefined}
|
||||
<Spinner />
|
||||
@@ -314,7 +278,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}}
|
||||
class="disabled:opacity-50 shadow-lg bg-black/30 w-full flex justify-center rounded-lg p-2 hover:bg-black/20 transition"
|
||||
type="button"
|
||||
disabled={i === 0 || selected_answer}
|
||||
aria-label="Move item up"
|
||||
disabled={i === 0 || Boolean(selected_answer)}
|
||||
>
|
||||
<svg
|
||||
class="w-8 h-8"
|
||||
@@ -341,7 +306,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}}
|
||||
class="disabled:opacity-50 shadow-lg bg-black/30 w-full flex justify-center rounded-lg p-2 hover:bg-black/20 transition"
|
||||
type="button"
|
||||
disabled={i === question.answers.length - 1 || selected_answer}
|
||||
aria-label="Move item down"
|
||||
disabled={i === question.answers.length - 1 || Boolean(selected_answer)}
|
||||
>
|
||||
<svg
|
||||
class="w-8 h-8"
|
||||
@@ -365,7 +331,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<div class="w-full mt-2">
|
||||
<BrownButton
|
||||
type="button"
|
||||
disabled={selected_answer}
|
||||
disabled={Boolean(selected_answer)}
|
||||
onclick={() => {
|
||||
select_complex_answer(question.answers);
|
||||
}}>{$t('words.submit')}</BrownButton
|
||||
@@ -396,13 +362,5 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
{/await}
|
||||
{/if}
|
||||
|
||||
<!--{:else if question.type === QuizQuestionType.VOTING}
|
||||
{#await import('$lib/play/admin/voting_results.svelte')}
|
||||
<Spinner />
|
||||
{:then c}
|
||||
<svelte:component this={c.default} bind:data={question_results}
|
||||
bind:question={quiz_data.questions[selected_question]} />
|
||||
{/await}-->
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
@@ -5,10 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
|
||||
|
||||
function sortObjectbyValue(obj) {
|
||||
const ret = {};
|
||||
Object.keys(obj)
|
||||
@@ -20,12 +16,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
interface Props {
|
||||
scores: any;
|
||||
question_results: Array<{
|
||||
username: string;
|
||||
answer: string;
|
||||
right: boolean;
|
||||
time_taken: number;
|
||||
score: number;
|
||||
}>;
|
||||
username: string;
|
||||
answer: string;
|
||||
right: boolean;
|
||||
time_taken: number;
|
||||
score: number;
|
||||
}>;
|
||||
username: any;
|
||||
}
|
||||
|
||||
@@ -37,26 +33,14 @@ SPDX-License-Identifier: MPL-2.0
|
||||
scores[i.username] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
run(() => {
|
||||
console.log(score_by_username, scores, 'dieter');
|
||||
});
|
||||
for (const i of question_results) {
|
||||
score_by_username[i.username] = i.score;
|
||||
}
|
||||
|
||||
run(() => {
|
||||
scores = sortObjectbyValue(scores);
|
||||
});
|
||||
|
||||
const do_sth = () => {
|
||||
for (const i of Object.keys(score_by_username)) {
|
||||
scores[i] = (score_by_username[i] ?? 0) + (scores[i] ?? 0);
|
||||
}
|
||||
scores = scores;
|
||||
};
|
||||
|
||||
do_sth();
|
||||
for (const username of Object.keys(score_by_username)) {
|
||||
scores[username] = (score_by_username[username] ?? 0) + (scores[username] ?? 0);
|
||||
}
|
||||
scores = scores;
|
||||
let sorted_scores = $derived(sortObjectbyValue(scores));
|
||||
</script>
|
||||
|
||||
<div>
|
||||
@@ -65,7 +49,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<p class="p-4 bg-black/40 rounded-lg text-2xl">
|
||||
+{score_by_username[username] ?? '0'}
|
||||
</p>
|
||||
<p>Total score: {scores[username] ?? '0'}</p>
|
||||
<p>Total score: {sorted_scores[username] ?? '0'}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import type { Answer, Question, QuizData } from '$lib/quiz_types';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import { QuizQuestionType } from '../quiz_types.js';
|
||||
import Spinner from '$lib/Spinner.svelte';
|
||||
|
||||
const { t } = getLocalization();
|
||||
interface Props {
|
||||
results: Array<Answer>;
|
||||
game_data: QuizData;
|
||||
solution: Question;
|
||||
question_index: string;
|
||||
}
|
||||
|
||||
let {
|
||||
results,
|
||||
game_data,
|
||||
solution = $bindable(),
|
||||
question_index
|
||||
}: Props = $props();
|
||||
let data_store = $state({});
|
||||
|
||||
const question = solution.answers;
|
||||
for (let i = 0; i < question.length; i++) {
|
||||
data_store[question[i].answer] = 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < results.length; i++) {
|
||||
data_store[results[i].answer] += 1;
|
||||
}
|
||||
|
||||
let slider_values = $state([solution.answers.min_correct ?? 0, solution.answers.max_correct ?? 0]);
|
||||
console.log(slider_values, solution.answers);
|
||||
</script>
|
||||
|
||||
<!-- Show the results from the results object -->
|
||||
<!-- Language: SvelteHTML -->
|
||||
<!-- Path: frontend/src/lib/play/show_results.svelte -->
|
||||
|
||||
<div>
|
||||
<h2 class="text-center text-3xl mb-8">{$t('words.result', { count: 2 })}</h2>
|
||||
<div class="w-screen flex justify-center">
|
||||
{#if solution.type === QuizQuestionType.ABCD}
|
||||
<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">
|
||||
<thead class="bg-gray-50 dark:bg-gray-700">
|
||||
<tr>
|
||||
<th
|
||||
scope="col"
|
||||
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
||||
>
|
||||
{$t('words.answer')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
||||
>
|
||||
{$t('words.count')}
|
||||
</th>
|
||||
<th
|
||||
scope="col"
|
||||
class="py-3 px-6 text-xs font-medium tracking-wider text-left text-gray-700 uppercase dark:text-gray-400"
|
||||
>
|
||||
{$t('words.correct')}
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each solution.answers as answer}
|
||||
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
||||
<td
|
||||
class="py-4 px-6 text-sm font-medium text-gray-900 whitespace-nowrap dark:text-white"
|
||||
>
|
||||
{answer.answer}
|
||||
</td>
|
||||
<td
|
||||
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
|
||||
>
|
||||
{data_store[answer.answer]}
|
||||
</td>
|
||||
<td
|
||||
class="py-4 px-6 text-sm text-gray-500 whitespace-nowrap dark:text-gray-400"
|
||||
>
|
||||
{#if answer.right}
|
||||
✅
|
||||
{:else}
|
||||
❌
|
||||
{/if}
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{:else if solution.type === QuizQuestionType.RANGE}
|
||||
<!--<p class="text-center">
|
||||
Every number between {solution.answers
|
||||
.min_correct} and {solution.answers
|
||||
.max_correct} was correct.
|
||||
</p>-->
|
||||
{#await import('svelte-range-slider-pips')}
|
||||
<Spinner />
|
||||
{:then c}
|
||||
<div class="grayscale pointer-events-none w-full">
|
||||
<c.default
|
||||
bind:values={slider_values}
|
||||
bind:min={solution.answers.min}
|
||||
bind:max={solution.answers.max}
|
||||
pips
|
||||
float
|
||||
all="label"
|
||||
/>
|
||||
</div>
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
|
||||
import { fly } from 'svelte/transition';
|
||||
@@ -15,7 +13,11 @@ SPDX-License-Identifier: MPL-2.0
|
||||
const { t } = getLocalization();
|
||||
</script>
|
||||
|
||||
<div class="w-full px-6 lg:px-20 h-[80vh] absolute" in:fly|global={{ x: 100 }} out:fly|global={{ x: -100 }}>
|
||||
<div
|
||||
class="w-full px-6 lg:px-20 h-[80vh] absolute"
|
||||
in:fly|global={{ x: 100 }}
|
||||
out:fly|global={{ x: -100 }}
|
||||
>
|
||||
<div class="rounded-lg bg-white w-full h-full border-gray-500 dark:bg-gray-700">
|
||||
<div class="h-fit bg-gray-300 rounded-t-lg dark:bg-gray-500">
|
||||
<div class="flex align-middle p-4 gap-3">
|
||||
@@ -52,9 +54,10 @@ SPDX-License-Identifier: MPL-2.0
|
||||
src="/api/v1/storage/download/{data.cover_image}"
|
||||
alt="not available"
|
||||
class="max-h-72 h-auto w-auto"
|
||||
oncontextmenu={preventDefault(() => {
|
||||
oncontextmenu={(e) => {
|
||||
e.preventDefault();
|
||||
data.cover_image = '';
|
||||
})}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<!--
|
||||
SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
|
||||
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import type { Markdown } from '$lib/quiztivity/types';
|
||||
import { marked } from 'marked';
|
||||
import { browser } from '$app/environment';
|
||||
@@ -23,11 +21,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
};
|
||||
}
|
||||
|
||||
let rendered_html = $state('');
|
||||
|
||||
run(() => {
|
||||
rendered_html = browser ? marked.parse(data.markdown) : '';
|
||||
});
|
||||
let rendered_html = $derived(browser ? marked.parse(data.markdown) : '');
|
||||
</script>
|
||||
|
||||
<div class="w-full h-[70vh] flex flex-row p-4 gap-4">
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import type { Markdown } from '$lib/quiztivity/types';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { marked } from 'marked';
|
||||
@@ -18,11 +16,9 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
let { data }: Props = $props();
|
||||
|
||||
let rendered_html = $state('');
|
||||
|
||||
run(() => {
|
||||
rendered_html = browser ? DOMPurify.sanitize(marked.parse(data.markdown ?? '')) : '';
|
||||
});
|
||||
let rendered_html = $derived(
|
||||
browser ? DOMPurify.sanitize(marked.parse(data.markdown ?? '')) : ''
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="prose dark:prose-invert">
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import type { Data } from './types';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import BrownButton from '$lib/components/buttons/brown.svelte';
|
||||
@@ -48,7 +46,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
data.pages.push({ title: undefined, data: undefined, type, id });
|
||||
opened_slide = data.pages.length - 1;
|
||||
};
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
handle_slide_add(selected_type);
|
||||
});
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
|
||||
import BrownButton from '$lib/components/buttons/brown.svelte';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import Spinner from '$lib/Spinner.svelte';
|
||||
@@ -81,7 +79,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
});
|
||||
let never_expires_checked = $state(true);
|
||||
let selected_date = $state(undefined);
|
||||
const create_share = async () => {
|
||||
const create_share = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!selected_date && !never_expires_checked) {
|
||||
return;
|
||||
}
|
||||
@@ -145,7 +144,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<form
|
||||
class="flex justify-center p-2 border-b-2 border-l-2 border-r-2 border-[#B07156] flex-col gap-2"
|
||||
transition:fly={{ duration: 100, y: -10 }}
|
||||
onsubmit={preventDefault(create_share)}
|
||||
onsubmit={create_share}
|
||||
>
|
||||
<div class="grid grid-cols-2">
|
||||
<input
|
||||
|
||||
@@ -15,10 +15,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<div class="flex justify-center">
|
||||
<a href="/view/{quiz.id}" class="h-max w-fit">
|
||||
<div class="max-w-md py-4 px-8 bg-white shadow-lg rounded-lg my-20 dark:bg-slate-800">
|
||||
<!-- <div class='flex justify-center md:justify-end -mt-16'>
|
||||
<img class='w-20 h-20 object-cover rounded-full border-2 border-indigo-500'
|
||||
src='https://images.unsplash.com/photo-1499714608240-22fc6ad53fb2?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=334&q=80'>
|
||||
</div>-->
|
||||
<div>
|
||||
<div class="flex w-full items-center">
|
||||
<h2
|
||||
|
||||
@@ -9,8 +9,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
import Navbar from '$lib/navbar.svelte';
|
||||
import { pathname } from '$lib/stores';
|
||||
import { navbarVisible } from '$lib/stores.svelte';
|
||||
// import * as Sentry from '@sentry/browser';
|
||||
// import { BrowserTracing } from '@sentry/tracing';
|
||||
|
||||
import { initLocalizationContext } from '$lib/i18n';
|
||||
import { browser } from '$app/environment';
|
||||
import CommandPalette from '$lib/components/commandpalette.svelte';
|
||||
@@ -19,19 +18,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
|
||||
let { children }: Props = $props();
|
||||
// import Alert from '$lib/modals/alert.svelte';
|
||||
|
||||
/* afterNavigate(() => {
|
||||
if (browser) {
|
||||
if (latestPageVisitURl === window.location.href) {
|
||||
return;
|
||||
} else {
|
||||
latestPageVisitURl = window.location.href;
|
||||
plausible.trackPageview();
|
||||
}
|
||||
console.log('After nav');
|
||||
}
|
||||
});*/
|
||||
const plausible_data_url = import.meta.env.VITE_PLAUSIBLE_DATA_URL;
|
||||
|
||||
if (browser) {
|
||||
@@ -45,34 +31,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
|
||||
// Whenever the user explicitly chooses light mode
|
||||
// localStorage.theme = 'light';
|
||||
//
|
||||
// // Whenever the user explicitly chooses dark mode
|
||||
// localStorage.theme = 'dark';
|
||||
//
|
||||
// // Whenever the user explicitly chooses to respect the OS preference
|
||||
// localStorage.removeItem('theme');
|
||||
}
|
||||
let start_language = 'en';
|
||||
if (browser) {
|
||||
start_language = localStorage.getItem('language') ?? 'en';
|
||||
}
|
||||
initLocalizationContext(start_language);
|
||||
/*
|
||||
if (import.meta.env.VITE_SENTRY !== undefined && import.meta.env.PROD) {
|
||||
Sentry.init({
|
||||
dsn: String(import.meta.env.VITE_SENTRY),
|
||||
integrations: [new BrowserTracing()],
|
||||
|
||||
// Set tracesSampleRate to 1.0 to capture 100%
|
||||
// of transactions for performance monitoring.
|
||||
// We recommend adjusting this value in production
|
||||
tracesSampleRate: 0.5
|
||||
});
|
||||
}
|
||||
*/
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -92,40 +56,15 @@ SPDX-License-Identifier: MPL-2.0
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
<!-- {#if navbarVisible.visible = true.visible}
|
||||
<Navbar />
|
||||
<div class="pt-16 h-screen">
|
||||
<div class="z-40"></div>
|
||||
<slot />
|
||||
</div>
|
||||
{:else}
|
||||
<slot />
|
||||
{/if} -->
|
||||
{#if navbarVisible.visible}
|
||||
<Navbar />
|
||||
<div class="pt-16">
|
||||
<div class="z-40"></div>
|
||||
<!-- extra content above slot -->
|
||||
</div>
|
||||
{/if}
|
||||
{@render children?.()}
|
||||
<CommandPalette />
|
||||
|
||||
<!--{#if $alertModal.open ?? false}
|
||||
<div
|
||||
class="fixed inset-0 h-screen w-screen bg-black z-30 bg-opacity-60 items-center justify-center content-center"
|
||||
class:hidden={!$alertModal.open}
|
||||
class:flex={$alertModal.open}
|
||||
class:visible={$alertModal.open}
|
||||
>
|
||||
<Alert
|
||||
bind:title={$alertModal.title}
|
||||
bind:body={$alertModal.body}
|
||||
bind:open={$alertModal.open}
|
||||
/>
|
||||
</div>
|
||||
{/if}-->
|
||||
|
||||
<style lang="scss">
|
||||
:global(html:not(.dark)) {
|
||||
// height: 100%;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// import { redirect } from '@sveltejs/kit';
|
||||
import { signedIn } from '$lib/stores';
|
||||
import type { LayoutLoad } from './$types';
|
||||
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
export const load = async ({ parent }) => {
|
||||
export const load: PageServerLoad = async ({ parent }) => {
|
||||
const { email } = await parent();
|
||||
if (email) {
|
||||
redirect(302, '/dashboard');
|
||||
|
||||
@@ -13,8 +13,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
import Newsletter from '$lib/landing/newsletter.svelte';
|
||||
import { fly, fade } from 'svelte/transition';
|
||||
|
||||
/* import LandingPromo from '$lib/landing/landing-promo.svelte';*/
|
||||
|
||||
import FindScreenshot from '$lib/assets/landing_new/find.webp';
|
||||
import ImportScreenshot from '$lib/assets/landing_new/import.webp';
|
||||
import EditScreenshot from '$lib/assets/landing_new/edit.webp';
|
||||
@@ -27,16 +25,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
navbarVisible.visible = true;
|
||||
|
||||
/* interface StatsData {
|
||||
quiz_count: number;
|
||||
user_count: number;
|
||||
}*/
|
||||
|
||||
/* const getStats = async (): Promise<StatsData> => {
|
||||
const response = await fetch('/api/v1/stats/combined');
|
||||
return await response.json();
|
||||
};*/
|
||||
let newsletterModalOpen = $state();
|
||||
let newsletterModalOpen: boolean = $state();
|
||||
onMount(() => {
|
||||
const ls = localStorage.getItem('newsletter');
|
||||
newsletterModalOpen = ls === null;
|
||||
@@ -65,18 +54,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
let selected_create_thing = $state(SelectedCreateThing.Create);
|
||||
let selected_play_thing = $state(SelectedPlayThing.Select);
|
||||
|
||||
/* <li>No;
|
||||
Tracking < /li>
|
||||
< li > Self - hostable < /li>
|
||||
< li > German;
|
||||
Server < /li>
|
||||
< li > user - friendly < /li>
|
||||
< li > Completely;
|
||||
free < /li>
|
||||
< li > Quiz - results;
|
||||
are;
|
||||
downloadable < /li>;*/
|
||||
|
||||
const classquiz_reasons = [
|
||||
{
|
||||
headline: $t('index_page.no_player_limit'),
|
||||
@@ -154,57 +131,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
/>
|
||||
<meta name="twitter:image" content={WebPOpenGraph} />
|
||||
</svelte:head>
|
||||
|
||||
<!--<div class="min-h-screen flex flex-col">
|
||||
<section class="pb-40">
|
||||
<div class="pt-12 text-center">
|
||||
<h1 class="sm:text-8xl text-6xl mt-6 marck-script">ClassQuiz</h1>
|
||||
<p class="text-xl mt-4">{$t('index_page.slogan')}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="features" class="mt-8">
|
||||
<div class="text-center snap-y">
|
||||
<h1 class="sm:text-6xl text-4xl">{$t('words.features')}</h1>
|
||||
<p class="text-xl pt-4">
|
||||
{$t('index_page.features_description.1')}
|
||||
<br />
|
||||
{$t('index_page.features_description.2')}
|
||||
<br />
|
||||
{$t('index_page.features_description.3')}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
<section class="py-8">
|
||||
<h1 class="sm:text-6xl text-4xl text-center break-words">
|
||||
{$t('words.screenshot', { count: 2 })}
|
||||
</h1>
|
||||
<div>
|
||||
<LandingPromo />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h1 class="sm:text-6xl text-4xl text-center">Testimonials</h1>
|
||||
{#await import('$lib/landing/testimonials.svelte') then testimonials}
|
||||
<svelte:component this={testimonials.default} />
|
||||
{/await}
|
||||
</section>
|
||||
|
||||
<section id="stats">
|
||||
<div class="text-center pb-20 pt-10 snap-y">
|
||||
<h1 class="sm:text-6xl text-4xl">{$t('words.stats')}</h1>
|
||||
<p class="text-xl pt-4">
|
||||
{#await getStats() then stats}
|
||||
{$t('index_page.stats', {
|
||||
user_count: stats.user_count,
|
||||
quiz_count: stats.quiz_count
|
||||
})}
|
||||
{/await}
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
</div>-->
|
||||
<div class="min-h-screen flex flex-col">
|
||||
<section class="pb-40">
|
||||
<div class="pt-12 text-center">
|
||||
@@ -264,6 +190,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}}
|
||||
class:shadow-2xl={selected_create_thing === SelectedCreateThing.Create}
|
||||
class:opacity-70={selected_create_thing !== SelectedCreateThing.Create}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="rounded-lg w-fit p-1 bg-lime-500 hover:bg-lime-400 transition shadow-lg"
|
||||
@@ -295,6 +223,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
onkeyup={() => {
|
||||
selected_create_thing = SelectedCreateThing.Find;
|
||||
}}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
class:shadow-2xl={selected_create_thing === SelectedCreateThing.Find}
|
||||
class:opacity-70={selected_create_thing !== SelectedCreateThing.Find}
|
||||
>
|
||||
@@ -320,38 +250,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<h5 class="text-xl dark:text-black">{$t('words.find')}</h5>
|
||||
<p class="dark:text-black">{$t('index_page.find_or_explore')}</p>
|
||||
</div>
|
||||
<!--<div
|
||||
class="m-2 rounded-lg p-2 bg-opacity-40 bg-white transition-all cursor-pointer lg:h-full"
|
||||
onclick={() => {
|
||||
selected_create_thing = SelectedCreateThing.Import;
|
||||
}}
|
||||
class:shadow-2xl={selected_create_thing === SelectedCreateThing.Import}
|
||||
class:opacity-70={selected_create_thing !== SelectedCreateThing.Import}
|
||||
>
|
||||
<div
|
||||
class="rounded-lg w-fit p-1 bg-lime-500 hover:bg-lime-400 transition shadow-lg"
|
||||
>
|
||||
<svg
|
||||
class="w-8 h-8 text-black"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
aria-label="Cloud with arrow pointing down"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<h5 class="text-xl dark:text-black">{$t('words.import')}</h5>
|
||||
<p class="dark:text-black">
|
||||
{$t('index_page.import_quiz_from_kahoot_and_edit')}
|
||||
</p>
|
||||
</div>-->
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -405,11 +303,13 @@ SPDX-License-Identifier: MPL-2.0
|
||||
onkeyup={() => {
|
||||
selected_play_thing = SelectedPlayThing.Select;
|
||||
}}
|
||||
role="button"
|
||||
tabindex="0"
|
||||
class:shadow-2xl={selected_play_thing === SelectedPlayThing.Select}
|
||||
class:opacity-70={selected_play_thing !== SelectedPlayThing.Select}
|
||||
>
|
||||
<div
|
||||
class="rounded-lg bg-emerald-300 w-fit p-1 bg-lime-500 hover:bg-lime-400 transition shadow-lg"
|
||||
class="rounded-lg bg-emerald-300 w-fit p-1 hover:bg-lime-400 transition shadow-lg"
|
||||
>
|
||||
<svg
|
||||
aria-label="Mouse-Click icon"
|
||||
@@ -432,6 +332,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
<div
|
||||
class="m-2 rounded-lg p-2 bg-white/40 transition-all cursor-pointer lg:h-full"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onclick={() => {
|
||||
selected_play_thing = SelectedPlayThing.Results;
|
||||
}}
|
||||
@@ -442,7 +344,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
class:opacity-70={selected_play_thing !== SelectedPlayThing.Results}
|
||||
>
|
||||
<div
|
||||
class="rounded-lg bg-emerald-300 w-fit p-1 bg-lime-500 hover:bg-lime-400 transition shadow-lg"
|
||||
class="rounded-lg bg-emerald-300 w-fit p-1 hover:bg-lime-400 transition shadow-lg"
|
||||
>
|
||||
<svg
|
||||
aria-label="context-menu icon"
|
||||
@@ -465,6 +367,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
<div
|
||||
class="m-2 rounded-lg p-2 bg-white/40 transition-all cursor-pointer lg:h-full"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onclick={() => {
|
||||
selected_play_thing = SelectedPlayThing.Winners;
|
||||
}}
|
||||
@@ -475,7 +379,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
class:opacity-70={selected_play_thing !== SelectedPlayThing.Winners}
|
||||
>
|
||||
<div
|
||||
class="rounded-lg bg-emerald-300 w-fit p-1 bg-lime-500 hover:bg-lime-400 transition shadow-lg"
|
||||
class="rounded-lg bg-emerald-300 w-fit p-1 hover:bg-lime-400 transition shadow-lg"
|
||||
>
|
||||
<svg
|
||||
aria-label="sparkling stars-icon"
|
||||
@@ -523,6 +427,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
{#each classquiz_reasons as reason, index}
|
||||
<div
|
||||
class="m-2 rounded-lg p-2 bg-white/40 transition-all cursor-pointer lg:h-full"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
onclick={() => {
|
||||
selected_classquiz_reason = index;
|
||||
}}
|
||||
@@ -541,7 +447,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
{#if newsletterModalOpen}
|
||||
<div
|
||||
class="fixed bottom-8 right-5 bg-white rounded-lg h-fit w-11/12 ml-5 lg:w-2/12 z-50 p-2 bg-white dark:bg-gray-700"
|
||||
class="fixed bottom-8 right-5 bg-white rounded-lg h-fit w-11/12 ml-5 lg:w-2/12 z-50 p-2 dark:bg-gray-700"
|
||||
transition:fly|global
|
||||
>
|
||||
<Newsletter bind:open={newsletterModalOpen} />
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, preventDefault } from 'svelte/legacy';
|
||||
|
||||
import { goto } from '$app/navigation';
|
||||
import type { PageData } from './$types';
|
||||
|
||||
@@ -20,14 +18,11 @@ SPDX-License-Identifier: MPL-2.0
|
||||
name: ''
|
||||
});
|
||||
|
||||
let isValid = $state(false);
|
||||
let isValid = $derived(input_data.name.length !== 0 && input_data.player_name.length !== 0);
|
||||
let isSubmitting = false;
|
||||
|
||||
run(() => {
|
||||
isValid = input_data.name.length !== 0 && input_data.player_name.length !== 0;
|
||||
});
|
||||
|
||||
const submit = async () => {
|
||||
const submit = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
@@ -59,7 +54,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
Add a controller
|
||||
</h3>
|
||||
|
||||
<form onsubmit={preventDefault(submit)}>
|
||||
<form onsubmit={submit}>
|
||||
<div class="w-full mt-4">
|
||||
<div class="dark:bg-gray-800 bg-white p-4 rounded-lg">
|
||||
<div class="relative bg-inherit w-full">
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import { alertModal } from '$lib/stores';
|
||||
import { navbarVisible } from '$lib/stores.svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
@@ -23,7 +21,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
navbarVisible.visible = true;
|
||||
|
||||
let { data } = $props();
|
||||
const { verified }: boolean = data;
|
||||
const { verified: boolean } = data;
|
||||
|
||||
let session_data = $state({});
|
||||
let step = $state(0);
|
||||
@@ -38,7 +36,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
};
|
||||
let alertModalOpen = false;
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
redirect_back(done);
|
||||
});
|
||||
|
||||
@@ -51,8 +49,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
});
|
||||
|
||||
const check_auto = () => {
|
||||
if (step === 1) {
|
||||
const check_auto = (stp: number) => {
|
||||
if (stp === 1) {
|
||||
if (!browserSupportsWebAuthn()) {
|
||||
for (let i = 0; i < session_data.step_1.length; i++) {
|
||||
if (session_data.step_1[i] === 'PASSKEY') {
|
||||
@@ -65,7 +63,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
selected_method = session_data.step_1[0];
|
||||
}
|
||||
}
|
||||
if (step === 2) {
|
||||
if (stp === 2) {
|
||||
if (!browserSupportsWebAuthn()) {
|
||||
for (let i = 0; i < session_data.step_2.length; i++) {
|
||||
if (session_data.step_2[i] === 'PASSKEY') {
|
||||
@@ -79,10 +77,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
}
|
||||
};
|
||||
run(() => {
|
||||
check_auto();
|
||||
step;
|
||||
});
|
||||
$effect(() => check_auto(step));
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, preventDefault } from 'svelte/legacy';
|
||||
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
|
||||
const { t } = getLocalization();
|
||||
@@ -20,12 +18,10 @@ SPDX-License-Identifier: MPL-2.0
|
||||
let backup_code = $state('');
|
||||
let isSubmitting = $state(false);
|
||||
|
||||
let backup_code_valid = $state(false);
|
||||
run(() => {
|
||||
backup_code_valid = backup_code.length === 64;
|
||||
});
|
||||
let backup_code_valid = $derived(backup_code.length === 64);
|
||||
|
||||
const continue_in_login = async () => {
|
||||
const continue_in_login = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!backup_code_valid) {
|
||||
return;
|
||||
}
|
||||
@@ -50,7 +46,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<div class="px-6 py-4">
|
||||
<h2 class="text-3xl font-bold text-center text-gray-700 dark:text-white">ClassQuiz</h2>
|
||||
|
||||
<form onsubmit={preventDefault(continue_in_login)}>
|
||||
<form onsubmit={continue_in_login}>
|
||||
<div class="w-full mt-4">
|
||||
<div class="dark:bg-gray-800 bg-white p-4 rounded-lg">
|
||||
<div class="relative bg-inherit w-full">
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
|
||||
let {
|
||||
@@ -20,7 +18,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
let isSubmitting;
|
||||
let password = $state();
|
||||
|
||||
const continue_in_login = async () => {
|
||||
const continue_in_login = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!password) {
|
||||
return;
|
||||
}
|
||||
@@ -60,7 +59,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<div class="px-6 py-4">
|
||||
<h2 class="text-3xl font-bold text-center text-gray-700 dark:text-white">ClassQuiz</h2>
|
||||
|
||||
<form onsubmit={preventDefault(continue_in_login)}>
|
||||
<form onsubmit={continue_in_login}>
|
||||
<div class="w-full mt-4">
|
||||
<div class="dark:bg-gray-800 bg-white p-4 rounded-lg">
|
||||
<div class="relative bg-inherit w-full">
|
||||
|
||||
@@ -4,8 +4,6 @@ SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
let { session_data = {}, step, selected_method = $bindable() } = $props();
|
||||
|
||||
let available_methods = $state();
|
||||
@@ -18,7 +16,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
};
|
||||
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
set_available_methods(step);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, preventDefault } from 'svelte/legacy';
|
||||
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import OAuthBlock from './oauth_block.svelte';
|
||||
|
||||
@@ -14,14 +12,11 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
const { t } = getLocalization();
|
||||
let email = $state('');
|
||||
let emailEmpty = $state(true);
|
||||
let emailEmpty = $derived(email === '');
|
||||
let isSubmitting = $state(false);
|
||||
|
||||
run(() => {
|
||||
emailEmpty = email === '';
|
||||
});
|
||||
|
||||
const start_login = async (): Promise<void> => {
|
||||
const start_login = async (e: Event): Promise<void> => {
|
||||
e.preventDefault();
|
||||
if (emailEmpty) {
|
||||
return;
|
||||
}
|
||||
@@ -50,7 +45,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
{$t('login_page.login_or_create_account')}
|
||||
</p>
|
||||
|
||||
<form onsubmit={preventDefault(start_login)}>
|
||||
<form onsubmit={start_login}>
|
||||
<div class="w-full mt-4">
|
||||
<div class="dark:bg-gray-800 bg-white p-4 rounded-lg">
|
||||
<div class="relative bg-inherit w-full">
|
||||
|
||||
@@ -5,13 +5,9 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, preventDefault } from 'svelte/legacy';
|
||||
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
|
||||
|
||||
interface Props {
|
||||
// import { alertModal } from '$lib/stores';
|
||||
session_data: any;
|
||||
selected_method: any;
|
||||
done: any;
|
||||
@@ -26,16 +22,13 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}: Props = $props();
|
||||
|
||||
const { t } = getLocalization();
|
||||
let isSubmitting;
|
||||
let isSubmitting: boolean;
|
||||
let totp = $state('');
|
||||
|
||||
let totp_valid = $state(false);
|
||||
let totp_valid = $derived(totp.length === 6);
|
||||
|
||||
run(() => {
|
||||
totp_valid = totp.length === 6;
|
||||
});
|
||||
|
||||
const continue_in_login = async () => {
|
||||
const continue_in_login = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!totp_valid) {
|
||||
return;
|
||||
}
|
||||
@@ -84,7 +77,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<div class="px-6 py-4">
|
||||
<h2 class="text-3xl font-bold text-center text-gray-700 dark:text-white">ClassQuiz</h2>
|
||||
|
||||
<form onsubmit={preventDefault(continue_in_login)}>
|
||||
<form onsubmit={continue_in_login}>
|
||||
<div class="w-full mt-4">
|
||||
<div class="dark:bg-gray-800 bg-white p-4 rounded-lg">
|
||||
<div class="relative bg-inherit w-full">
|
||||
|
||||
@@ -5,13 +5,11 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, preventDefault } from 'svelte/legacy';
|
||||
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
|
||||
const { t } = getLocalization();
|
||||
let { data } = $props();
|
||||
let { token }: string = data;
|
||||
let { token: string } = data;
|
||||
let isSubmitting = $state(false);
|
||||
interface PasswordData {
|
||||
password1: string;
|
||||
@@ -21,15 +19,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
password1: '',
|
||||
password2: ''
|
||||
});
|
||||
let passwordsValid = $state(false);
|
||||
const checkIfPasswordsValid = (pwdata: PasswordData): void => {
|
||||
passwordsValid = pwdata.password1 === pwdata.password2 && pwdata.password1.length >= 8;
|
||||
};
|
||||
run(() => {
|
||||
checkIfPasswordsValid(passwordData);
|
||||
});
|
||||
let passwordsValid = $derived(
|
||||
passwordData.password1 === passwordData.password2 && passwordData.password1.length >= 8
|
||||
);
|
||||
|
||||
const submit = async () => {
|
||||
const submit = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!passwordsValid) {
|
||||
return;
|
||||
}
|
||||
@@ -77,7 +72,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
{$t('password_reset_page.reset_password')}
|
||||
</p>
|
||||
|
||||
<form onsubmit={preventDefault(submit)}>
|
||||
<form onsubmit={submit}>
|
||||
<div class="w-full mt-4">
|
||||
<div class="dark:bg-gray-800 bg-white p-4 rounded-lg">
|
||||
<div class="relative bg-inherit w-full">
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import { navbarVisible } from '$lib/stores.svelte.ts';
|
||||
|
||||
@@ -16,7 +14,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
let isSubmitting = $state(false);
|
||||
|
||||
const submit = async () => {
|
||||
const submit = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
isSubmitting = true;
|
||||
const res = await fetch('/api/v1/users/forgot-password', {
|
||||
method: 'POST',
|
||||
@@ -62,7 +61,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
{$t('password_reset_page.reset_password')}
|
||||
</p>
|
||||
|
||||
<form onsubmit={preventDefault(submit)}>
|
||||
<form onsubmit={submit}>
|
||||
<div class="w-full mt-4">
|
||||
<div class="dark:bg-gray-800 bg-white p-4 rounded-lg">
|
||||
<div class="relative bg-inherit w-full">
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, preventDefault } from 'svelte/legacy';
|
||||
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import { DateTime } from 'luxon';
|
||||
import { UAParser } from 'ua-parser-js';
|
||||
@@ -39,18 +37,15 @@ SPDX-License-Identifier: MPL-2.0
|
||||
let locationData;
|
||||
let this_session = $state();
|
||||
|
||||
let passwordChangeDataValid = $state(false);
|
||||
const checkPasswords = (data: ChangePasswordData): void => {
|
||||
passwordChangeDataValid =
|
||||
data.newPassword === data.newPasswordConfirm &&
|
||||
data.newPassword.length >= 8 &&
|
||||
data.oldPassword !== data.newPassword &&
|
||||
data.oldPassword !== '';
|
||||
};
|
||||
run(() => {
|
||||
checkPasswords(changePasswordData);
|
||||
});
|
||||
const changePassword = async () => {
|
||||
let passwordChangeDataValid = $derived(
|
||||
changePasswordData.newPassword === changePasswordData.newPasswordConfirm &&
|
||||
changePasswordData.newPassword.length >= 8 &&
|
||||
changePasswordData.oldPassword !== changePasswordData.newPassword &&
|
||||
changePasswordData.oldPassword !== ''
|
||||
);
|
||||
|
||||
const changePassword = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!passwordChangeDataValid) {
|
||||
return;
|
||||
}
|
||||
@@ -184,7 +179,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<form class="flex flex-col md:flex-row" onsubmit={preventDefault(changePassword)}>
|
||||
<form class="flex flex-col md:flex-row" onsubmit={changePassword}>
|
||||
<label
|
||||
>{$t('settings_page.old_password')}:<input
|
||||
type="password"
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import BrownButton from '$lib/components/buttons/brown.svelte';
|
||||
import { fade, fly } from 'svelte/transition';
|
||||
import { bounceOut } from 'svelte/easing';
|
||||
@@ -71,10 +69,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
let image_url = $derived(get_image_url(data));
|
||||
|
||||
run(() => {
|
||||
console.log('index', index);
|
||||
});
|
||||
|
||||
const save_avatar = async () => {
|
||||
save_finished = false;
|
||||
const res = await fetch(`/api/v1/avatar/save?${new URLSearchParams(data).toString()}`, {
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, preventDefault } from 'svelte/legacy';
|
||||
|
||||
import type { QuizData } from '$lib/quiz_types';
|
||||
|
||||
import { socket } from '$lib/socket';
|
||||
@@ -124,11 +122,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
};
|
||||
|
||||
const request_answer_export = async () => {
|
||||
await socket.emit('get_export_token');
|
||||
const request_answer_export = (e: Event) => {
|
||||
e.preventDefault();
|
||||
socket.emit('get_export_token');
|
||||
};
|
||||
const save_quiz = async () => {
|
||||
await socket.emit('save_quiz');
|
||||
const save_quiz = () => {
|
||||
socket.emit('save_quiz');
|
||||
};
|
||||
|
||||
let darkMode = false;
|
||||
@@ -143,10 +142,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
let bg_image = $derived(quiz_data ? quiz_data.background_image : undefined);
|
||||
let results_saved = $state(false);
|
||||
|
||||
let show_final_results = $state(false);
|
||||
run(() => {
|
||||
show_final_results = JSON.stringify(final_results) !== JSON.stringify([null]);
|
||||
});
|
||||
let show_final_results = $derived(JSON.stringify(final_results) !== JSON.stringify([null]));
|
||||
</script>
|
||||
|
||||
<svelte:window onbeforeunload={confirmUnload} />
|
||||
@@ -201,7 +197,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<FinalResults bind:data={player_scores} bind:show_final_results />
|
||||
<FinalResults bind:data={player_scores} {show_final_results} />
|
||||
{/if}
|
||||
{#if !success}
|
||||
{#if errorMessage !== ''}
|
||||
@@ -217,18 +213,16 @@ SPDX-License-Identifier: MPL-2.0
|
||||
{:else}
|
||||
<SomeAdminScreen
|
||||
bind:final_results
|
||||
{game_pin}
|
||||
bind:game_token
|
||||
{game_token}
|
||||
bind:quiz_data
|
||||
bind:game_mode
|
||||
bind:bg_color
|
||||
{bg_color}
|
||||
bind:player_scores
|
||||
bind:control_visible
|
||||
{control_visible}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
<a
|
||||
onclick={preventDefault(request_answer_export)}
|
||||
onclick={request_answer_export}
|
||||
href="#"
|
||||
target="_blank"
|
||||
bind:this={dataexport_download_a}
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import DownloadQuiz from '$lib/components/DownloadQuiz.svelte';
|
||||
import type { QuizData } from '$lib/quiz_types';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
@@ -50,23 +48,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
let id_to_position_map = {};
|
||||
|
||||
const getData = async (): Promise<{ items: Array<QuizData>; fuse: Fuse<any> }> => {
|
||||
/* items_to_show = [];
|
||||
for (let i = 0; i < data.quizzes.length; i++) {
|
||||
items_to_show.push({ ...data.quizzes[i], type: 'quiz' });
|
||||
}
|
||||
for (let i = 0; i < data.quiztivities.length; i++) {
|
||||
items_to_show.push({ ...data.quiztivities[i], type: 'quiztivity' });
|
||||
}
|
||||
fuse = new Fuse(items_to_show, {
|
||||
keys: ['title', 'description', 'questions.title'],
|
||||
findAllMatches: true
|
||||
});
|
||||
all_items = items_to_show;
|
||||
for (let i = 0; i < all_items.length; i++) {
|
||||
id_to_position_map[all_items[i].id] = i;
|
||||
}
|
||||
return all_items;
|
||||
*/
|
||||
const items: any[] = [];
|
||||
|
||||
for (const q of data.quizzes) items.push({ ...q, type: 'quiz' });
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
|
||||
import type { PageData } from './$types';
|
||||
import { fade } from 'svelte/transition';
|
||||
// import MediaComponent from '$lib/editor/MediaComponent.svelte';
|
||||
@@ -37,7 +35,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
};
|
||||
});
|
||||
|
||||
const save_image_metadata = async () => {
|
||||
const save_image_metadata = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
await fetch(`/api/v1/storage/meta/${edit_popup.id}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
@@ -131,7 +130,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
>
|
||||
<div class="w-auto h-auto m-auto rounded-sm bg-white dark:bg-gray-700 p-4">
|
||||
<h1 class="text-2xl text-center">{$t('file_dashboard.edit_the_image')}</h1>
|
||||
<form class="flex flex-col" onsubmit={preventDefault(save_image_metadata)}>
|
||||
<form class="flex flex-col" onsubmit={save_image_metadata}>
|
||||
<div class="flex flex-row">
|
||||
<div class="flex flex-col mr-4">
|
||||
<label for="name" class="m-auto">{$t('file_dashboard.filename_word')}</label
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import Spinner from '$lib/Spinner.svelte';
|
||||
|
||||
let uppyOpen = $state(false);
|
||||
@@ -14,7 +12,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
let selected_question = $state(undefined);
|
||||
let data = $state({ cover_image: undefined });
|
||||
|
||||
run(() => {
|
||||
$effect(() => {
|
||||
if (data.cover_image) {
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, preventDefault } from 'svelte/legacy';
|
||||
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import { navbarVisible } from '$lib/stores.svelte.ts';
|
||||
import { onMount } from 'svelte';
|
||||
@@ -17,16 +15,13 @@ SPDX-License-Identifier: MPL-2.0
|
||||
const { t } = getLocalization();
|
||||
let url_input = $state('');
|
||||
let file_input: File[] = $state();
|
||||
|
||||
let url_valid = $state(false);
|
||||
let kahoot_regex = /^https:\/\/create\.kahoot\.it\/details\/([a-zA-Z-\d]{36})\/?$/;
|
||||
|
||||
let url_valid = $derived(kahoot_regex.test(url_input));
|
||||
let is_loading = $state(false);
|
||||
|
||||
run(() => {
|
||||
url_valid = kahoot_regex.test(url_input);
|
||||
});
|
||||
|
||||
const submit = async () => {
|
||||
const submit = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
if (!url_valid) {
|
||||
return;
|
||||
}
|
||||
@@ -61,7 +56,8 @@ SPDX-License-Identifier: MPL-2.0
|
||||
is_loading = false;
|
||||
};
|
||||
|
||||
const file_submit = async () => {
|
||||
const file_submit = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
is_loading = true;
|
||||
const formdata = new FormData();
|
||||
formdata.append('file', file_input[0]);
|
||||
@@ -95,10 +91,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
is_loading = false;
|
||||
};
|
||||
|
||||
run(() => {
|
||||
console.log(file_input);
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
let url_from_path = page.url.searchParams.get('url');
|
||||
if (url_from_path === '') {
|
||||
@@ -112,23 +104,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<title>ClassQuiz - Import</title>
|
||||
</svelte:head>
|
||||
|
||||
<!--{#if is_loading}
|
||||
<svg class='h-8 w-8 animate-spin mx-auto my-20' viewBox='3 3 18 18'>
|
||||
<path
|
||||
class='fill-black'
|
||||
d='M12 5C8.13401 5 5 8.13401 5 12C5 15.866 8.13401 19 12 19C15.866 19 19 15.866 19 12C19 8.13401 15.866 5 12 5ZM3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 21C7.02944 21 3 16.9706 3 12Z'
|
||||
/>
|
||||
<path
|
||||
class='fill-blue-100'
|
||||
d='M16.9497 7.05015C14.2161 4.31648 9.78392 4.31648 7.05025 7.05015C6.65973 7.44067 6.02656 7.44067 5.63604 7.05015C5.24551 6.65962 5.24551 6.02646 5.63604 5.63593C9.15076 2.12121 14.8492 2.12121 18.364 5.63593C18.7545 6.02646 18.7545 6.65962 18.364 7.05015C17.9734 7.44067 17.3403 7.44067 16.9497 7.05015Z'
|
||||
/>
|
||||
</svg>
|
||||
{:else}-->
|
||||
|
||||
<!-- <form on:submit|preventDefault={submit}>
|
||||
<input type='text' class="text-black w-2/5" bind:value={url_input} />
|
||||
<button type='submit' disabled={!url_valid}>Submit</button>
|
||||
</form>-->
|
||||
<div class="flex items-center justify-center h-full px-4">
|
||||
<div>
|
||||
<span class="p-4"></span>
|
||||
@@ -149,7 +124,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
Login or create account
|
||||
</p>-->
|
||||
<div class="grid grid-cols-2">
|
||||
<form onsubmit={preventDefault(submit)}>
|
||||
<form onsubmit={submit}>
|
||||
<div class="w-full mt-4 h-full flex flex-col">
|
||||
<h2 class="text-center text-2xl">{$t('import_page.a_kahoot_quiz')}</h2>
|
||||
<div class="dark:bg-gray-800 bg-white p-4 rounded-lg">
|
||||
@@ -208,7 +183,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form onsubmit={preventDefault(file_submit)}>
|
||||
<form onsubmit={file_submit}>
|
||||
<div class="w-full mt-4 border-l-2 border-gray-600 h-full flex flex-col">
|
||||
<h2 class="text-center text-2xl">{$t('import_page.classquiz_quiz')}</h2>
|
||||
<div class="dark:bg-gray-800 bg-white p-4 rounded-lg">
|
||||
|
||||
@@ -11,8 +11,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
import type { Answer, Question as QuestionType } from '$lib/quiz_types';
|
||||
import ShowTitle from '$lib/play/title.svelte';
|
||||
import Question from '$lib/play/question.svelte';
|
||||
// import ShowResults from '$lib/play/show_results.svelte';
|
||||
import { navbarVisible} from '$lib/stores.svelte.ts';
|
||||
import { navbarVisible } from '$lib/stores.svelte.ts';
|
||||
import ShowEndScreen from '$lib/play/admin/final_results.svelte';
|
||||
import KahootResults from '$lib/play/results_kahoot.svelte';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
@@ -44,8 +43,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
// Variables init
|
||||
let question_index = $state('');
|
||||
let unique = $state({});
|
||||
navbarVisible.visible=false;
|
||||
let game_pin_valid: boolean;
|
||||
navbarVisible.visible = false;
|
||||
let answer_results: Array<Answer> = $state();
|
||||
let gameData = $state();
|
||||
let solution: QuestionType = $state();
|
||||
@@ -55,7 +53,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
started: false
|
||||
});
|
||||
|
||||
let question = $state();
|
||||
let question: Question = $state();
|
||||
|
||||
let preventReload = true;
|
||||
|
||||
@@ -64,7 +62,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
unique = {};
|
||||
}
|
||||
|
||||
const confirmUnload = () => {
|
||||
const confirmUnload = (event: Event) => {
|
||||
if (preventReload) {
|
||||
event.preventDefault();
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
@@ -76,9 +74,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
socket.on('time_sync', (data) => {
|
||||
socket.emit('echo_time_sync', data);
|
||||
});
|
||||
socket.on('session_id', (d) => {
|
||||
const session_id = d.session_id;
|
||||
});
|
||||
|
||||
socket.on('connect', async () => {
|
||||
console.log('Connected!');
|
||||
@@ -120,7 +115,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
window.location.reload();
|
||||
return;
|
||||
}
|
||||
game_pin_valid = false;
|
||||
});
|
||||
|
||||
socket.on('set_question_number', (data) => {
|
||||
@@ -169,13 +163,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<svelte:window onbeforeunload={confirmUnload} />
|
||||
<svelte:head>
|
||||
<title>ClassQuiz - Play</title>
|
||||
<!-- {#if gameData !== undefined && game_mode !== 'kahoot'}
|
||||
{#each gameData.questions as question}
|
||||
{#if question.image !== undefined}
|
||||
<link rel="preload" as="image" href={question.image} />
|
||||
{/if}
|
||||
{/each}
|
||||
{/if}-->
|
||||
</svelte:head>
|
||||
<div
|
||||
class="min-h-screen min-w-full"
|
||||
@@ -186,17 +173,17 @@ SPDX-License-Identifier: MPL-2.0
|
||||
{#if !gameMeta.started && gameData === undefined}
|
||||
<JoinGame bind:game_pin bind:game_mode bind:username />
|
||||
{:else if JSON.stringify(final_results) !== JSON.stringify([null])}
|
||||
<ShowEndScreen bind:data={scores} show_final_results={true} bind:username />
|
||||
<ShowEndScreen bind:data={scores} show_final_results={true} {username} />
|
||||
{:else if gameData !== undefined && question_index === ''}
|
||||
<ShowTitle
|
||||
bind:title={gameData.title}
|
||||
bind:description={gameData.description}
|
||||
bind:cover_image={gameData.cover_image}
|
||||
title={gameData.title}
|
||||
description={gameData.description}
|
||||
cover_image={gameData.cover_image}
|
||||
/>
|
||||
{:else if gameMeta.started && gameData !== undefined && question_index !== '' && answer_results === undefined}
|
||||
{#key unique}
|
||||
<div class="text-black dark:text-black">
|
||||
<Question bind:game_mode bind:question bind:question_index bind:solution />
|
||||
<Question bind:game_mode bind:question {question_index} {solution} />
|
||||
</div>
|
||||
{/key}
|
||||
{:else if gameMeta.started && answer_results !== undefined}
|
||||
@@ -209,11 +196,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<h2 class="text-center text-3xl mb-8">{$t('words.result', { count: 2 })}</h2>
|
||||
</div>
|
||||
{#key unique}
|
||||
<KahootResults
|
||||
bind:username
|
||||
bind:question_results={answer_results}
|
||||
bind:scores
|
||||
/>
|
||||
<KahootResults {username} question_results={answer_results} bind:scores />
|
||||
{/key}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run } from 'svelte/legacy';
|
||||
|
||||
import type { PageData } from './$types';
|
||||
import type { QuizTivityPage } from '$lib/quiztivity/types';
|
||||
import { QuizTivityTypes } from '$lib/quiztivity/types';
|
||||
@@ -25,10 +23,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
|
||||
let current_slide_index = $state(0);
|
||||
let current_slide: QuizTivityPage = $state(quiztivity.pages[current_slide_index]);
|
||||
run(() => {
|
||||
current_slide = quiztivity.pages[current_slide_index];
|
||||
});
|
||||
let current_slide: QuizTivityPage = $derived(quiztivity.pages[current_slide_index]);
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full flex flex-col overflow-scroll">
|
||||
|
||||
@@ -5,8 +5,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
|
||||
<script lang="ts">
|
||||
import { run, preventDefault } from 'svelte/legacy';
|
||||
|
||||
import { page } from '$app/state';
|
||||
import { socket } from '$lib/socket';
|
||||
import type { QuizData } from '$lib/quiz_types';
|
||||
@@ -24,19 +22,19 @@ SPDX-License-Identifier: MPL-2.0
|
||||
navbarVisible.visible = false;
|
||||
|
||||
const { t } = getLocalization();
|
||||
let timer_interval;
|
||||
let timer_interval: NodeJS.Timeout;
|
||||
let timer_res = $state(undefined);
|
||||
let selected_question = $state(-1);
|
||||
let game_started = $state(false);
|
||||
let question_results = $state();
|
||||
let final_results = $state([null]);
|
||||
let warnToLeave = true;
|
||||
let dataexport_download_a = $state();
|
||||
let dataexport_download_a: HTMLAnchorElement = $state();
|
||||
|
||||
let players: Array<{ sid: string; username: string }> = $state([]);
|
||||
|
||||
let game_data: QuizData = $state();
|
||||
let shown_question_now;
|
||||
let shown_question_now: number;
|
||||
let control_visible = $state(false);
|
||||
|
||||
if (!data.game_id || !data.game_pin) {
|
||||
@@ -90,12 +88,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
socket.emit('get_final_results', {});
|
||||
};
|
||||
|
||||
const request_answer_export = async () => {
|
||||
await socket.emit('get_export_token');
|
||||
const request_answer_export = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
socket.emit('get_export_token');
|
||||
};
|
||||
|
||||
const get_already_joined_players = async () => {
|
||||
console.log('GETTING PLAYERS');
|
||||
const res = await fetch(
|
||||
`/api/v1/live/players?game_pin=${data.game_pin}&game_id=${data.game_id}`
|
||||
);
|
||||
@@ -103,15 +101,16 @@ SPDX-License-Identifier: MPL-2.0
|
||||
players = await res.json();
|
||||
}
|
||||
};
|
||||
|
||||
let circular_progress = $state(0);
|
||||
run(() => {
|
||||
let circular_progress = $derived.by(() => {
|
||||
try {
|
||||
circular_progress =
|
||||
return (
|
||||
1 -
|
||||
((100 / game_data.questions[selected_question].time) * parseInt(timer_res)) / 100;
|
||||
((100 / parseInt(game_data.questions[selected_question].time)) *
|
||||
parseInt(timer_res)) /
|
||||
100
|
||||
);
|
||||
} catch {
|
||||
circular_progress = 0;
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -235,7 +234,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
{#await import('$lib/play/admin/slide.svelte')}
|
||||
<Spinner my_20={false} />
|
||||
{:then c}
|
||||
<c.default bind:question={game_data.questions[selected_question]} />
|
||||
<c.default question={game_data.questions[selected_question]} />
|
||||
{/await}
|
||||
{:else}
|
||||
<div class="flex flex-col justify-center w-screen h-1/6">
|
||||
@@ -244,11 +243,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</h1>
|
||||
<!-- <span class='text-center py-2 text-lg'>{$t('admin_page.time_left')}: {timer_res}</span>-->
|
||||
<div class="mx-auto my-2">
|
||||
<CircularTimer
|
||||
bind:text={timer_res}
|
||||
bind:progress={circular_progress}
|
||||
color="#ef4444"
|
||||
/>
|
||||
<CircularTimer text={timer_res} progress={circular_progress} color="#ef4444" />
|
||||
</div>
|
||||
{#if game_data.questions[selected_question].image !== null}
|
||||
<div>
|
||||
@@ -328,7 +323,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
|
||||
<a
|
||||
onclick={preventDefault(request_answer_export)}
|
||||
onclick={request_answer_export}
|
||||
href="#"
|
||||
bind:this={dataexport_download_a}
|
||||
class="absolute -top-3/4 -left-3/4 opacity-0 hidden">Download</a
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// import type { PageLoad } from './$types';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = async ({ fetch }) => {
|
||||
export const load: PageLoad = async ({ fetch }) => {
|
||||
const res = await fetch('/api/v1/results/list?include_quiz=true');
|
||||
let json;
|
||||
if (res.ok) {
|
||||
|
||||
@@ -79,8 +79,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
{#if selected_tab === SelectedTab.Overview}
|
||||
<div in:fade|global={{ duration: 150 }}>
|
||||
<GeneralOverview
|
||||
questions={data.results.questions}
|
||||
answers={data.results.answers}
|
||||
scores={data.results.player_scores}
|
||||
title={data.results.title}
|
||||
timestamp={data.results.timestamp}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
// SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
// import type { PageLoad } from './$types';
|
||||
|
||||
export const load = async ({ params, fetch }) => {
|
||||
export const load: PageLoad = async ({ params, fetch }) => {
|
||||
const res = await fetch(`/api/v1/results/${params.result_id}?include_quiz=true`);
|
||||
let json;
|
||||
if (res.ok) {
|
||||
@@ -15,4 +14,4 @@ export const load = async ({ params, fetch }) => {
|
||||
return {
|
||||
results: json
|
||||
};
|
||||
}; //satisfies PageLoad;
|
||||
};
|
||||
|
||||
@@ -11,14 +11,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
const { t } = getLocalization();
|
||||
|
||||
interface Props {
|
||||
questions: Question[];
|
||||
answers: {
|
||||
username: string;
|
||||
answer: string;
|
||||
right: boolean;
|
||||
tike_taken: number;
|
||||
score: number;
|
||||
}[][];
|
||||
scores: {
|
||||
[key: string]: string;
|
||||
};
|
||||
@@ -26,7 +18,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
timestamp: string;
|
||||
}
|
||||
|
||||
let { questions, answers, scores, title, timestamp }: Props = $props();
|
||||
let { scores, title, timestamp }: Props = $props();
|
||||
|
||||
const usernames = Object.keys(scores);
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
});
|
||||
});
|
||||
console.log(custom_field);
|
||||
</script>
|
||||
|
||||
<div class="w-full">
|
||||
|
||||
@@ -7,7 +7,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<script lang="ts">
|
||||
import type { Question } from '$lib/quiz_types';
|
||||
import { fly } from 'svelte/transition';
|
||||
import QuestionTab from './question_tab_thing.svelte';
|
||||
import QuestionTab from './question_tab_dropdown.svelte';
|
||||
import { QuizQuestionType } from '$lib/quiz_types';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
|
||||
@@ -16,12 +16,12 @@ SPDX-License-Identifier: MPL-2.0
|
||||
interface Props {
|
||||
questions: Question[];
|
||||
answers: {
|
||||
username: string;
|
||||
answer: string;
|
||||
right: boolean;
|
||||
tike_taken: number;
|
||||
score: number;
|
||||
}[][];
|
||||
username: string;
|
||||
answer: string;
|
||||
right: boolean;
|
||||
tike_taken: number;
|
||||
score: number;
|
||||
}[][];
|
||||
}
|
||||
|
||||
let { questions, answers }: Props = $props();
|
||||
@@ -57,7 +57,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</script>
|
||||
|
||||
<div class="w-full flex justify-center">
|
||||
<div class="w-11/12 flex flex-col w-full gap-4">
|
||||
<div class="flex flex-col w-full gap-4">
|
||||
{#each questions as question, i}
|
||||
<div class="transition-all">
|
||||
<div
|
||||
|
||||
-2
@@ -11,7 +11,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
const { t } = getLocalization();
|
||||
|
||||
|
||||
interface Answer {
|
||||
username: string;
|
||||
answer: string;
|
||||
@@ -26,7 +25,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
}
|
||||
|
||||
let { question, answers }: Props = $props();
|
||||
// console.log(question);
|
||||
|
||||
const get_answer_count_for_answer = (answer: string): number => {
|
||||
let count = 0;
|
||||
@@ -4,8 +4,6 @@ SPDX-FileCopyrightText: 2023 Marlon W (Mawoka)
|
||||
SPDX-License-Identifier: MPL-2.0
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { preventDefault } from 'svelte/legacy';
|
||||
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
const { t } = getLocalization();
|
||||
import SearchCard from '$lib/search-card.svelte';
|
||||
@@ -52,7 +50,10 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<div class="mb-3 xl:w-96">
|
||||
<form
|
||||
class="input-group relative flex items-stretch flex-row w-full mb-4"
|
||||
onsubmit={preventDefault(submit)}
|
||||
onsubmit={(e: Event) => {
|
||||
e.preventDefault();
|
||||
submit();
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="search"
|
||||
@@ -65,6 +66,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<button
|
||||
class="px-6 py-2.5 bg-blue-600 text-white font-medium text-xs leading-tight uppercase rounded-sm shadow-md hover:bg-blue-700 hover:shadow-lg focus:bg-blue-700 focus:shadow-lg focus:outline-hidden focus:ring-0 active:bg-blue-800 active:shadow-lg transition duration-150 ease-in-out flex items-center disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
id="button-addon2"
|
||||
aria-label="Search"
|
||||
disabled={search_term.length <= 2}
|
||||
type="submit"
|
||||
>
|
||||
|
||||
@@ -13,7 +13,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
import StartGamePopup from '$lib/dashboard/start_game.svelte';
|
||||
|
||||
const { t } = getLocalization();
|
||||
// import { DateTime } from 'luxon';
|
||||
let start_game = $state(null);
|
||||
|
||||
const tippy = createTippy({
|
||||
@@ -71,12 +70,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
class="rounded-lg border-2 border-black hover:outline transition-all outline-[#B07156] -outline-offset-2 outline-8"
|
||||
>
|
||||
<div class="grid grid-cols-6 h-[25vh]">
|
||||
<!-- <p
|
||||
style='writing-mode: vertical-lr'
|
||||
class='text-center h-full text-xl p-2'
|
||||
>
|
||||
{@html quiz.title}
|
||||
</p>-->
|
||||
<div class="col-start-2 col-end-6">
|
||||
<h3 class="text-center text-2xl">{@html quiz.title}</h3>
|
||||
<p class="text-center">
|
||||
@@ -95,15 +88,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- <div class='flex justify-end'>
|
||||
<p
|
||||
style='writing-mode: sideways-lr'
|
||||
class='text-center text-xl p-2'
|
||||
>
|
||||
{@html quiz.title}
|
||||
</p>
|
||||
</div>-->
|
||||
</div>
|
||||
<div class="flex justify-center">
|
||||
<a href="/view/{quiz.id}" class="action-button w-1/6"
|
||||
@@ -166,6 +150,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
>
|
||||
<button
|
||||
disabled
|
||||
aria-label="Download"
|
||||
class="action-button w-full flex justify-center"
|
||||
>
|
||||
<svg
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// import type { PageLoad } from './$types';
|
||||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load = async ({ params, fetch }) => {
|
||||
export const load: PageLoad = async ({ params, fetch }) => {
|
||||
const user_req = await fetch(`/api/v1/community/user/${params.user_id}`);
|
||||
const user = await user_req.json();
|
||||
if (!user) {
|
||||
@@ -24,4 +24,4 @@ export const load = async ({ params, fetch }) => {
|
||||
user,
|
||||
quizzes
|
||||
};
|
||||
}; // satisfies PageLoad;
|
||||
};
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
import { error } from '@sveltejs/kit';
|
||||
import type { PageServerLoad } from './$types';
|
||||
|
||||
export const load = async ({ params, parent }) => {
|
||||
export const load: PageServerLoad = async ({ params, parent }) => {
|
||||
const { quiz_id } = params;
|
||||
const res = await fetch(`${process.env.API_URL}/api/v1/quiz/get/public/${quiz_id}`);
|
||||
const { email } = await parent();
|
||||
|
||||
@@ -245,10 +245,6 @@ SPDX-License-Identifier: MPL-2.0
|
||||
<h3 class="text-3xl m-1 text-center">
|
||||
{index_question + 1}: {@html question.question}
|
||||
</h3>
|
||||
|
||||
<!-- <label class='m-1 flex flex-row gap-2 w-3/5'>-->
|
||||
|
||||
<!-- </label>-->
|
||||
{#if question.image}
|
||||
<span>
|
||||
<MediaComponent
|
||||
@@ -316,7 +312,7 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</ul>
|
||||
{:else if question.type === QuizQuestionType.VOTING || question.type === QuizQuestionType.TEXT}
|
||||
<div class="grid grid-cols-2 gap-4 m-4 p-6">
|
||||
{#each question.answers as answer, index_answer}
|
||||
{#each question.answers as _, index_answer}
|
||||
<div class="p-1 rounded-lg py-4 dark:bg-gray-500 bg-gray-300">
|
||||
<h4 class="text-center">
|
||||
{quiz.questions[index_question].answers[index_answer]
|
||||
@@ -327,10 +323,10 @@ SPDX-License-Identifier: MPL-2.0
|
||||
</div>
|
||||
{:else if question.type === QuizQuestionType.SLIDE}
|
||||
{#await import('$lib/play/admin/slide.svelte')}
|
||||
<Spinner my={false} />
|
||||
<Spinner my_20={false} />
|
||||
{:then c}
|
||||
<div class="max-h-[90%] max-w-[90%]">
|
||||
<c.default bind:question={questions[index_question]} />
|
||||
<c.default question={quiz.questions[index_question]} />
|
||||
</div>
|
||||
{/await}
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user