✨ Basic Quiztivity working
This commit is contained in:
@@ -42,3 +42,9 @@ async def delete_quiztivity(uuid: UUID):
|
||||
if quiztivity is None:
|
||||
raise HTTPException(status_code=404, detail="QuizTivity not found")
|
||||
await quiztivity.delete()
|
||||
|
||||
|
||||
@router.get("/")
|
||||
async def get_all_quiztivities(user: User = Depends(get_current_user)) -> list[QuizTivity]:
|
||||
quiztivities = await QuizTivity.objects.filter(user=user).order_by(QuizTivity.created_at.desc()).all()
|
||||
return quiztivities
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
"@tailwindcss/typography": "^0.5.9",
|
||||
"@types/canvas-confetti": "^1.6.0",
|
||||
"@types/cookie": "^0.5.1",
|
||||
"@types/dompurify": "^3.0.2",
|
||||
"@types/js-cookie": "^3.0.3",
|
||||
"@types/luxon": "^3.3.0",
|
||||
"@types/marked": "^4.3.0",
|
||||
@@ -56,6 +57,7 @@
|
||||
"cookie": "^0.5.0",
|
||||
"crypto-js": "^4.1.1",
|
||||
"cssnano": "^6.0.0",
|
||||
"dompurify": "^3.0.3",
|
||||
"eslint": "^8.38.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-svelte3": "^4.0.0",
|
||||
|
||||
Generated
+27
@@ -20,6 +20,7 @@ specifiers:
|
||||
'@tailwindcss/typography': ^0.5.9
|
||||
'@types/canvas-confetti': ^1.6.0
|
||||
'@types/cookie': ^0.5.1
|
||||
'@types/dompurify': ^3.0.2
|
||||
'@types/js-cookie': ^3.0.3
|
||||
'@types/luxon': ^3.3.0
|
||||
'@types/marked': ^4.3.0
|
||||
@@ -43,6 +44,7 @@ specifiers:
|
||||
cookie: ^0.5.0
|
||||
crypto-js: ^4.1.1
|
||||
cssnano: ^6.0.0
|
||||
dompurify: ^3.0.3
|
||||
eslint: ^8.38.0
|
||||
eslint-config-prettier: ^8.8.0
|
||||
eslint-plugin-svelte3: ^4.0.0
|
||||
@@ -106,6 +108,7 @@ devDependencies:
|
||||
'@tailwindcss/typography': 0.5.9_tailwindcss@3.3.1
|
||||
'@types/canvas-confetti': 1.6.0
|
||||
'@types/cookie': 0.5.1
|
||||
'@types/dompurify': 3.0.2
|
||||
'@types/js-cookie': 3.0.3
|
||||
'@types/luxon': 3.3.0
|
||||
'@types/marked': 4.3.0
|
||||
@@ -129,6 +132,7 @@ devDependencies:
|
||||
cookie: 0.5.0
|
||||
crypto-js: 4.1.1
|
||||
cssnano: 6.0.0_postcss@8.4.22
|
||||
dompurify: 3.0.3
|
||||
eslint: 8.38.0
|
||||
eslint-config-prettier: 8.8.0_eslint@8.38.0
|
||||
eslint-plugin-svelte3: 4.0.0_a4ckjr7darsrglbip3svgxgxc4
|
||||
@@ -1438,6 +1442,15 @@ packages:
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@types/dompurify/3.0.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-YBL4ziFebbbfQfH5mlC+QTJsvh0oJUrWbmxKMyEdL7emlHJqGR2Qb34TEFKj+VCayBvjKy3xczMFNhugThUsfQ==
|
||||
}
|
||||
dependencies:
|
||||
'@types/trusted-types': 2.0.3
|
||||
dev: true
|
||||
|
||||
/@types/estree/1.0.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -1517,6 +1530,13 @@ packages:
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@types/trusted-types/2.0.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==
|
||||
}
|
||||
dev: true
|
||||
|
||||
/@types/ua-parser-js/0.7.36:
|
||||
resolution:
|
||||
{
|
||||
@@ -2620,6 +2640,13 @@ packages:
|
||||
domelementtype: 2.3.0
|
||||
dev: true
|
||||
|
||||
/dompurify/3.0.3:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-axQ9zieHLnAnHh0sfAamKYiqXMJAVwu+LM/alQ7WDagoWessyWvMSFyW65CqF3owufNu8HBcE4cM2Vflu7YWcQ==
|
||||
}
|
||||
dev: true
|
||||
|
||||
/domutils/3.0.1:
|
||||
resolution:
|
||||
{
|
||||
|
||||
@@ -5,13 +5,9 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Markdown } from '../../types';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import BrownButton from '$lib/components/buttons/brown.svelte';
|
||||
import { marked } from 'marked';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
const { t } = getLocalization();
|
||||
|
||||
export let data: Markdown | undefined;
|
||||
|
||||
if (!data) {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<!--
|
||||
- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Markdown } from '$lib/quiztivity/types';
|
||||
import DOMPurify from 'dompurify';
|
||||
import { marked } from 'marked';
|
||||
import { browser } from '$app/environment';
|
||||
|
||||
export let data: Markdown | undefined;
|
||||
|
||||
let rendered_html = '';
|
||||
|
||||
$: rendered_html = browser ? DOMPurify.sanitize(marked.parse(data.markdown ?? '')) : '';
|
||||
</script>
|
||||
|
||||
<div class="prose dark:prose-invert">
|
||||
{@html rendered_html}
|
||||
</div>
|
||||
@@ -7,6 +7,7 @@
|
||||
import type { Memory } from '../../types';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
import BrownButton from '$lib/components/buttons/brown.svelte';
|
||||
import { flip } from 'svelte/animate';
|
||||
|
||||
const { t } = getLocalization();
|
||||
|
||||
@@ -21,6 +22,20 @@
|
||||
cards: []
|
||||
};
|
||||
}
|
||||
const arraymove = (arr: any[], fromI: number, toI: number) => {
|
||||
const el = arr[fromI];
|
||||
arr.splice(fromI, 1);
|
||||
arr.splice(toI, 0, el);
|
||||
};
|
||||
const move_card_left = (selected_slide: number) => {
|
||||
arraymove(data.cards, selected_slide, selected_slide - 1);
|
||||
data.cards = data.cards;
|
||||
};
|
||||
|
||||
const move_card_right = (selected_slide: number) => {
|
||||
arraymove(data.cards, selected_slide, selected_slide + 1);
|
||||
data.cards = data.cards;
|
||||
};
|
||||
|
||||
const add_card = () => {
|
||||
if (!new_pair_data.text_1 || !new_pair_data.text_2) {
|
||||
@@ -81,8 +96,11 @@
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{#each data.cards as card_pair}
|
||||
<div class="border-[#B07156] border-2 rounded">
|
||||
{#each data.cards as card_pair, i (card_pair[0].id)}
|
||||
<div
|
||||
class="border-[#B07156] border-2 rounded group flex flex-col"
|
||||
animate:flip={{ duration: 200 }}
|
||||
>
|
||||
<div class="grid grid-cols-2 py-2 h-full">
|
||||
{#each card_pair as card}
|
||||
<div class="px-2 flex h-full">
|
||||
@@ -90,6 +108,21 @@
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
<div class="grid grid-cols-2 p-2 gap-2">
|
||||
<BrownButton
|
||||
on:click={() => {
|
||||
move_card_left(i);
|
||||
}}
|
||||
disabled={i === 0}>{$t('quiztivity.editor.move_left')}</BrownButton
|
||||
>
|
||||
<BrownButton
|
||||
on:click={() => {
|
||||
move_card_right(i);
|
||||
}}
|
||||
disabled={i + 1 === data.cards.length}
|
||||
>{$t('quiztivity.editor.move_right')}</BrownButton
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
<!--
|
||||
- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Memory, MemoryCard } from '$lib/quiztivity/types';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
|
||||
const { t } = getLocalization();
|
||||
export let data: Memory | undefined;
|
||||
|
||||
const shuffle = <Type>(a: Array<Type>): Array<Type> => {
|
||||
for (let i = a.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[a[i], a[j]] = [a[j], a[i]];
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
let card_opened = {};
|
||||
|
||||
const get_all_cards_as_single_array = (): MemoryCard[] => {
|
||||
console.log('data', data.cards);
|
||||
let final_arr: MemoryCard[] = [];
|
||||
for (let i = 0; i < data.cards.length; i++) {
|
||||
const pair = data.cards[i];
|
||||
console.log('pair', pair);
|
||||
for (let c = 0; c < pair.length; c++) {
|
||||
final_arr.push({ ...pair[c], id: `${i}:${c}` });
|
||||
card_opened[`${i}:${c}`] = false;
|
||||
}
|
||||
}
|
||||
console.log(final_arr);
|
||||
return final_arr;
|
||||
};
|
||||
|
||||
const all_cards_array: MemoryCard[] = get_all_cards_as_single_array();
|
||||
|
||||
const random_card_order = shuffle(all_cards_array);
|
||||
console.log(random_card_order);
|
||||
|
||||
const found_cards: string[] = [];
|
||||
let opened_active_cards: string[] = [];
|
||||
let try_count = 0;
|
||||
let game_finished = false;
|
||||
const flip_card = (id: string) => {
|
||||
if (found_cards.includes(id) || game_finished) {
|
||||
return;
|
||||
}
|
||||
card_opened[id] = true;
|
||||
opened_active_cards.push(id);
|
||||
|
||||
if (opened_active_cards.length === 2) {
|
||||
const [pair_1_id, card_1_id] = opened_active_cards[0].split(':');
|
||||
if (!pair_1_id || !card_1_id) {
|
||||
throw "Mustn't happen";
|
||||
}
|
||||
const [pair_2_id, card_2_id] = opened_active_cards[1].split(':');
|
||||
if (!pair_2_id || !card_2_id) {
|
||||
throw "Mustn't happen";
|
||||
}
|
||||
if (pair_2_id === pair_1_id) {
|
||||
found_cards.push(opened_active_cards[0]);
|
||||
found_cards.push(opened_active_cards[1]);
|
||||
opened_active_cards = [];
|
||||
}
|
||||
}
|
||||
if (opened_active_cards.length === 3) {
|
||||
card_opened[opened_active_cards[0]] = false;
|
||||
card_opened[opened_active_cards[1]] = false;
|
||||
opened_active_cards.splice(0, 2);
|
||||
try_count += 1;
|
||||
}
|
||||
game_finished = random_card_order.length === found_cards.length;
|
||||
};
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<p class="text-center">{$t('quiztivity.play.memory.try_count', { try_count })}</p>
|
||||
<div class="grid lg:grid-cols-6 grid-cols-2 gap-2 m-4">
|
||||
{#each random_card_order as card}
|
||||
<button
|
||||
class="aspect-square flex border-[#B07156] border-2 rounded"
|
||||
on:click={() => {
|
||||
flip_card(card.id);
|
||||
}}
|
||||
>
|
||||
{#if card_opened[card.id]}
|
||||
<p class="m-auto transition-all">{card.text}</p>
|
||||
{:else}
|
||||
<img
|
||||
src="/android-chrome-512x512.png"
|
||||
alt="ClassQuiz logo"
|
||||
class="m-4 opacity-50 hover:opacity-80 transition-all"
|
||||
/>
|
||||
{/if}
|
||||
</button>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
@@ -19,7 +19,7 @@ export interface Pdf {
|
||||
url: string;
|
||||
}
|
||||
|
||||
interface MemoryCard {
|
||||
export interface MemoryCard {
|
||||
image?: string;
|
||||
text?: string;
|
||||
id: string;
|
||||
|
||||
@@ -4,9 +4,8 @@
|
||||
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Data } from '../../../lib/quiztivity/types';
|
||||
import Editor from '../../../lib/quiztivity/editor.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import type { Data } from '$lib/quiztivity/types';
|
||||
import Editor from '$lib/quiztivity/editor.svelte';
|
||||
|
||||
let data: Data = { pages: [], id: undefined, title: '' };
|
||||
let saving = false;
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import type { Data } from '../../../lib/quiztivity/types';
|
||||
import Editor from '../../../lib/quiztivity/editor.svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import type { PageLoad } from './$types';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import type { Data } from '../../../lib/quiztivity/types';
|
||||
import type { Data } from '$lib/quiztivity/types';
|
||||
|
||||
export const load = (async ({ url, fetch }) => {
|
||||
const id = url.searchParams.get('id');
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
<!--
|
||||
- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import type { QuizTivityPage } from '$lib/quiztivity/types';
|
||||
import { QuizTivityTypes } from '$lib/quiztivity/types';
|
||||
import NavigationBar from './navigation_bar.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
let quiztivity = data.quiztivity;
|
||||
for (let i = 0; i < quiztivity.pages.length; i++) {
|
||||
const id = (Math.random() + 1).toString(36).substring(7);
|
||||
const type: QuizTivityTypes = quiztivity.pages[i].type as QuizTivityTypes;
|
||||
quiztivity.pages[i] = { ...quiztivity.pages[i], id, type };
|
||||
}
|
||||
|
||||
let current_slide_index = 0;
|
||||
let current_slide: QuizTivityPage = quiztivity.pages[current_slide_index];
|
||||
$: current_slide = quiztivity.pages[current_slide_index];
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full flex flex-col overflow-scroll">
|
||||
<div class="w-full h-full">
|
||||
{#if current_slide.type === QuizTivityTypes.MARKDOWN}
|
||||
{#await import('$lib/quiztivity/components/markdown/play.svelte') then c}
|
||||
<svelte:component this={c.default} data={current_slide.data} />
|
||||
{/await}
|
||||
{:else if current_slide.type === QuizTivityTypes.MEMORY}
|
||||
{#await import('$lib/quiztivity/components/memory/play.svelte') then c}
|
||||
<svelte:component this={c.default} data={current_slide.data} />
|
||||
{/await}
|
||||
{/if}
|
||||
</div>
|
||||
<NavigationBar bind:current_slide_index question_count={quiztivity.pages.length} />
|
||||
</div>
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
import type { PageLoad } from './$types';
|
||||
import { error } from '@sveltejs/kit';
|
||||
import type { Data } from '$lib/quiztivity/types';
|
||||
|
||||
export const load = (async ({ url, fetch }) => {
|
||||
const id = url.searchParams.get('id');
|
||||
if (!id) {
|
||||
throw error(400, 'id missing');
|
||||
}
|
||||
const resp = await fetch(`/api/v1/quiztivity/${id}`);
|
||||
if (!resp.ok) {
|
||||
throw error(404, 'quiztivity not found');
|
||||
}
|
||||
const data: Data = await resp.json();
|
||||
return {
|
||||
quiztivity: data
|
||||
};
|
||||
}) satisfies PageLoad;
|
||||
@@ -0,0 +1,36 @@
|
||||
<!--
|
||||
- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import BrownButton from '$lib/components/buttons/brown.svelte';
|
||||
import { getLocalization } from '$lib/i18n';
|
||||
|
||||
export let current_slide_index: number;
|
||||
export let question_count: number;
|
||||
|
||||
const { t } = getLocalization();
|
||||
</script>
|
||||
|
||||
<div class="mt-auto h-[7vh] bg-opacity-50 bg-black shadow-2xl flex justify-between">
|
||||
<div class="my-auto justify-start ml-4">
|
||||
<BrownButton
|
||||
disabled={current_slide_index < 1}
|
||||
on:click={() => {
|
||||
current_slide_index -= 1;
|
||||
}}>{$t('words.back')}</BrownButton
|
||||
>
|
||||
</div>
|
||||
<div class="my-auto">
|
||||
{current_slide_index + 1}/{question_count}
|
||||
</div>
|
||||
<div class="my-auto justify-end mr-4">
|
||||
<BrownButton
|
||||
disabled={current_slide_index === 1}
|
||||
on:click={() => {
|
||||
current_slide_index += 1;
|
||||
}}>{$t('words.next')}</BrownButton
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user