Finished Slides

This commit is contained in:
Mawoka
2022-12-26 11:04:26 +01:00
parent 809a9024b6
commit d9f174f40b
10 changed files with 122 additions and 64 deletions
-1
View File
@@ -198,7 +198,6 @@ async def finish_edit(edit_id: str, quiz_input: QuizInput):
quiz.description = quiz_input.description quiz.description = quiz_input.description
quiz.updated_at = datetime.now() quiz.updated_at = datetime.now()
quiz.questions = quiz_input.dict()["questions"] quiz.questions = quiz_input.dict()["questions"]
print(quiz.questions)
quiz.cover_image = quiz_input.cover_image quiz.cover_image = quiz_input.cover_image
quiz.background_color = quiz_input.background_color quiz.background_color = quiz_input.background_color
for image in images_to_delete: for image in images_to_delete:
+1
View File
@@ -68,6 +68,7 @@
"luxon": "^3.1.1", "luxon": "^3.1.1",
"mapbox-gl": "^2.11.0", "mapbox-gl": "^2.11.0",
"minisearch": "^5.1.0", "minisearch": "^5.1.0",
"pikaso": "^2.7.4",
"plausible-tracker": "^0.3.8", "plausible-tracker": "^0.3.8",
"postcss": "^8.4.19", "postcss": "^8.4.19",
"postcss-import": "^14.1.0", "postcss-import": "^14.1.0",
+20
View File
@@ -56,6 +56,7 @@ specifiers:
luxon: ^3.1.1 luxon: ^3.1.1
mapbox-gl: ^2.11.0 mapbox-gl: ^2.11.0
minisearch: ^5.1.0 minisearch: ^5.1.0
pikaso: ^2.7.4
plausible-tracker: ^0.3.8 plausible-tracker: ^0.3.8
postcss: ^8.4.19 postcss: ^8.4.19
postcss-import: ^14.1.0 postcss-import: ^14.1.0
@@ -138,6 +139,7 @@ devDependencies:
luxon: 3.1.1 luxon: 3.1.1
mapbox-gl: 2.11.1 mapbox-gl: 2.11.1
minisearch: 5.1.0 minisearch: 5.1.0
pikaso: 2.7.4
plausible-tracker: 0.3.8 plausible-tracker: 0.3.8
postcss: 8.4.20 postcss: 8.4.20
postcss-import: 14.1.0_postcss@8.4.20 postcss-import: 14.1.0_postcss@8.4.20
@@ -3911,6 +3913,13 @@ packages:
engines: { node: '>=6' } engines: { node: '>=6' }
dev: true dev: true
/konva/8.3.14:
resolution:
{
integrity: sha512-6I/TZppgY3Frs//AvZ87YVQLFxLywitb8wLS3qMM+Ih9e4QcB5Yy8br6eq7DdUzxPdbsYTz1FQBHzNxs08M1Tw==
}
dev: true
/levn/0.3.0: /levn/0.3.0:
resolution: resolution:
{ {
@@ -4646,6 +4655,17 @@ packages:
engines: { node: '>=0.10.0' } engines: { node: '>=0.10.0' }
dev: true dev: true
/pikaso/2.7.4:
resolution:
{
integrity: sha512-30nqF5+xBUIBKiHHfDfjA3oZChjirN3AmhfWb456iUvTpMY2D51PKjGHfgF2SoQP5Jad7i4Zv+wrQAp7BhcNmQ==
}
dependencies:
deepmerge: 4.2.2
konva: 8.3.14
typescript: 4.7.4
dev: true
/plausible-tracker/0.3.8: /plausible-tracker/0.3.8:
resolution: resolution:
{ {
+34 -6
View File
@@ -27,16 +27,21 @@
let timer_res: string; let timer_res: string;
let shown_question_now: number; let shown_question_now: number;
let final_results_clicked = false; let final_results_clicked = false;
let timer_interval;
export let player_scores; export let player_scores;
export const set_question_number = (q_number: number) => { export const set_question_number = (q_number: number) => {
timer_res = '0';
clearInterval(timer_interval);
// setTimeout(() => {
question_results = null; question_results = null;
socket.emit('set_question_number', q_number.toString()); socket.emit('set_question_number', q_number.toString());
shown_question_now = q_number; shown_question_now = q_number;
timer_res = quiz_data.questions[q_number].time; timer_res = quiz_data.questions[q_number].time;
selected_question = selected_question + 1; selected_question = selected_question + 1;
timer(timer_res); timer(timer_res);
// }, 1000);
}; };
const get_question_results = () => { const get_question_results = () => {
socket.emit('get_question_results', { socket.emit('get_question_results', {
@@ -53,6 +58,7 @@
const get_final_results = () => { const get_final_results = () => {
socket.emit('get_final_results', {}); socket.emit('get_final_results', {});
final_results_clicked = true; final_results_clicked = true;
timer_res = '0';
}; };
socket.on('final_results', (data) => { socket.on('final_results', (data) => {
@@ -75,7 +81,7 @@
const timer = (time: string) => { const timer = (time: string) => {
let seconds = Number(time); let seconds = Number(time);
let timer_interval = setInterval(() => { timer_interval = setInterval(() => {
if (timer_res === '0') { if (timer_res === '0') {
clearInterval(timer_interval); clearInterval(timer_interval);
// socket.emit('show_solutions', {}); // socket.emit('show_solutions', {});
@@ -109,7 +115,7 @@
/{quiz_data.questions.length} /{quiz_data.questions.length}
</p> </p>
<div class="justify-self-end ml-auto mr-0 col-start-3 col-end-3"> <div class="justify-self-end ml-auto mr-0 col-start-3 col-end-3">
{#if selected_question + 1 === quiz_data.questions.length && timer_res === '0' && question_results !== null} {#if selected_question + 1 === quiz_data.questions.length && ((timer_res === '0' && question_results !== null) || quiz_data?.questions?.[selected_question]?.type === QuizQuestionType.SLIDE)}
{#if JSON.stringify(final_results) === JSON.stringify([null])} {#if JSON.stringify(final_results) === JSON.stringify([null])}
<button on:click={get_final_results} class="admin-button" <button on:click={get_final_results} class="admin-button"
>Get final results >Get final results
@@ -126,12 +132,34 @@
</button> </button>
{/if} {/if}
{#if question_results === null && selected_question !== -1} {#if question_results === null && selected_question !== -1}
<button on:click={get_question_results} class="admin-button">Show results</button> {#if quiz_data.questions[selected_question].type === QuizQuestionType.SLIDE}
<button
on:click={() => {
set_question_number(selected_question + 1);
}}
class="admin-button"
>Next Question ({selected_question + 2})
</button>
{:else}
<button on:click={get_question_results} class="admin-button"
>Show results</button
>
{/if}
{/if} {/if}
{:else if selected_question !== -1} {:else if selected_question !== -1}
<button on:click={show_solutions} class="admin-button" {#if quiz_data.questions[selected_question].type === QuizQuestionType.SLIDE}
>Stop time and show solutions <button
</button> on:click={() => {
set_question_number(selected_question + 1);
}}
class="admin-button"
>Next Question ({selected_question + 2})
</button>
{:else}
<button on:click={show_solutions} class="admin-button"
>Stop time and show solutions
</button>
{/if}
{:else} {:else}
<!-- <button <!-- <button
on:click={() => { on:click={() => {
@@ -15,6 +15,7 @@
import StartGamePopup from './start_game.svelte'; import StartGamePopup from './start_game.svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import viewport from './useViewportAction.js'; import viewport from './useViewportAction.js';
import Spinner from '$lib/Spinner.svelte';
let copy_toast_open = false; let copy_toast_open = false;
let start_game = null; let start_game = null;
@@ -350,6 +351,28 @@
</div> </div>
{/each} {/each}
</div> </div>
{:else if question.type === QuizQuestionType.SLIDE}
<div
use:viewport
on:enterViewport={() => {
visibleImages[i][q] = true;
}}
on:exitViewport={() => {
visibleImages[i][q] = false;
}}
class="flex justify-center align-middle items-center"
>
{#await import('$lib/play/admin/slide.svelte')}
<Spinner my={false} />
{:then c}
<div class="max-h-[50%] max-w-[60%]">
<svelte:component
this={c.default}
bind:question
/>
</div>
{/await}
</div>
{/if} {/if}
</div> </div>
</SwiperSlide> </SwiperSlide>
-27
View File
@@ -23,26 +23,11 @@
let selected_element = undefined; let selected_element = undefined;
let canvas_el: HTMLDivElement | undefined; let canvas_el: HTMLDivElement | undefined;
let canvas: Pikaso; let canvas: Pikaso;
let elements = [];
let selected_el: null | ShapeModel<Konva.Shape | Konva.Group, Konva.ShapeConfig> = null; let selected_el: null | ShapeModel<Konva.Shape | Konva.Group, Konva.ShapeConfig> = null;
let elements_binds: Array<HTMLElement> | undefined = []; let elements_binds: Array<HTMLElement> | undefined = [];
let main_el: undefined | HTMLElement; let main_el: undefined | HTMLElement;
let settings_menu_open = false; let settings_menu_open = false;
const get_location = (id: number): { row: number; col: number } => {
const numColumns = 12;
const row = Math.floor(id / numColumns);
const col = id % numColumns;
return { row, col };
};
const set_correct_position = (
e: CustomEvent<{ offsetX: number; offsetY: number; domRect: DOMRect }>
) => {
const id = parseInt(e.target.getAttribute('el_id'));
data.answers[id].x = e.detail.offsetX / main_el.offsetWidth;
data.answers[id].y = e.detail.offsetY / main_el.offsetHeight;
};
const set_correct_height = new ResizeObserver((e) => { const set_correct_height = new ResizeObserver((e) => {
for (const i of e) { for (const i of e) {
@@ -116,10 +101,6 @@
assign_resize_handlers(); assign_resize_handlers();
} }
const set_correct_size = (e: UIEvent) => {
console.log(e, 'resized');
};
onMount(() => { onMount(() => {
/* setTimeout(() => { /* setTimeout(() => {
for (let i = 0; i < data.answers.length; i++) { for (let i = 0; i < data.answers.length; i++) {
@@ -156,14 +137,6 @@
} }
}); });
}); });
const get_shape_from_id = (id: number) => {
for (let i = 0; i < canvas.board.shapes.length; i++) {
if (canvas.board.shapes[i].node._id === id) {
return canvas.board.shapes[i].node;
}
}
};
</script> </script>
<div class="flex h-full relative w-full" bind:this={main_el}> <div class="flex h-full relative w-full" bind:this={main_el}>
+27 -29
View File
@@ -5,38 +5,36 @@
--> -->
<script lang="ts"> <script lang="ts">
import type { Question } from '$lib/quiz_types'; import type { Question } from '$lib/quiz_types';
import TextElement from '$lib/editor/slides/types/text.svelte'; import { onMount } from 'svelte';
import HeadlineElement from '$lib/editor/slides/types/headline.svelte'; import Pikaso from 'pikaso';
import { ElementTypes } from '$lib/quiz_types.js';
import { draggable } from '@neodrag/svelte';
export let question: Question; export let question: Question;
let main_el: HTMLElement | undefined; let canvas_el: HTMLDivElement | undefined;
let canvas: Pikaso;
let img_src = '';
onMount(() => {
canvas = new Pikaso({
container: canvas_el,
snapToGrid: {},
selection: {
interactive: false
}
});
if (typeof question.answers === 'string') {
const data = JSON.parse(question.answers);
canvas.import.json(data);
img_src = canvas.export.toImage();
}
});
</script> </script>
<div class="w-full min-h-[calc(100vh-8rem)]" bind:this={main_el}> <div class="w-full h-full">
{#if main_el} <div class="hidden">
{#each question.answers as el, i} <div bind:this={canvas_el} class="w-full h-full block" />
<div </div>
use:draggable={{ <div class="w-full h-full flex justify-center">
bounds: 'parent', <img src={img_src} />
handle: '.drag', </div>
disabled: true,
defaultPosition: {
x: el.x * main_el.offsetWidth,
y: el.y * main_el.offsetHeight
}
}}
style="height: {el.height * main_el.offsetHeight}px; width: {el.width *
main_el.offsetWidth}px"
>
{#if el.type === ElementTypes.Text}
<TextElement bind:data={el.data} editor={false} />
{:else if el.type === ElementTypes.Headline}
<HeadlineElement bind:data={el.data} editor={false} />
{/if}
</div>
{/each}
{/if}
</div> </div>
@@ -150,5 +150,13 @@
{#if timer_res === '0'} {#if timer_res === '0'}
<p>No correct answers, since this is a poll-question</p> <p>No correct answers, since this is a poll-question</p>
{/if} {/if}
{:else if question.type === QuizQuestionType.SLIDE}
{#await import('$lib/play/admin/slide.svelte')}
<Spinner my={false} />
{:then c}
<div class="max-h-[90%] max-w-[90%]">
<svelte:component this={c.default} bind:question />
</div>
{/await}
{/if} {/if}
</div> </div>
-1
View File
@@ -5,7 +5,6 @@
*/ */
import * as yup from 'yup'; import * as yup from 'yup';
import { ElementTypes } from '$lib/quiz_types';
export const ABCDQuestionSchema = yup export const ABCDQuestionSchema = yup
.array() .array()
@@ -11,6 +11,7 @@
import { QuizQuestionType } from '$lib/quiz_types.js'; import { QuizQuestionType } from '$lib/quiz_types.js';
import StartGamePopup from '$lib/dashboard/start_game.svelte'; import StartGamePopup from '$lib/dashboard/start_game.svelte';
import { onMount } from 'svelte'; import { onMount } from 'svelte';
import Spinner from '$lib/Spinner.svelte';
const tippy = createTippy({ const tippy = createTippy({
arrow: true, arrow: true,
@@ -228,6 +229,14 @@
</div> </div>
{/each} {/each}
</div> </div>
{:else if question.type === QuizQuestionType.SLIDE}
{#await import('$lib/play/admin/slide.svelte')}
<Spinner my={false} />
{:then c}
<div class="max-h-[90%] max-w-[90%]">
<svelte:component this={c.default} bind:question />
</div>
{/await}
{/if} {/if}
</div> </div>
</CollapsSection> </CollapsSection>