✨ Finished Slides
This commit is contained in:
@@ -198,7 +198,6 @@ async def finish_edit(edit_id: str, quiz_input: QuizInput):
|
||||
quiz.description = quiz_input.description
|
||||
quiz.updated_at = datetime.now()
|
||||
quiz.questions = quiz_input.dict()["questions"]
|
||||
print(quiz.questions)
|
||||
quiz.cover_image = quiz_input.cover_image
|
||||
quiz.background_color = quiz_input.background_color
|
||||
for image in images_to_delete:
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"luxon": "^3.1.1",
|
||||
"mapbox-gl": "^2.11.0",
|
||||
"minisearch": "^5.1.0",
|
||||
"pikaso": "^2.7.4",
|
||||
"plausible-tracker": "^0.3.8",
|
||||
"postcss": "^8.4.19",
|
||||
"postcss-import": "^14.1.0",
|
||||
|
||||
Generated
+20
@@ -56,6 +56,7 @@ specifiers:
|
||||
luxon: ^3.1.1
|
||||
mapbox-gl: ^2.11.0
|
||||
minisearch: ^5.1.0
|
||||
pikaso: ^2.7.4
|
||||
plausible-tracker: ^0.3.8
|
||||
postcss: ^8.4.19
|
||||
postcss-import: ^14.1.0
|
||||
@@ -138,6 +139,7 @@ devDependencies:
|
||||
luxon: 3.1.1
|
||||
mapbox-gl: 2.11.1
|
||||
minisearch: 5.1.0
|
||||
pikaso: 2.7.4
|
||||
plausible-tracker: 0.3.8
|
||||
postcss: 8.4.20
|
||||
postcss-import: 14.1.0_postcss@8.4.20
|
||||
@@ -3911,6 +3913,13 @@ packages:
|
||||
engines: { node: '>=6' }
|
||||
dev: true
|
||||
|
||||
/konva/8.3.14:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-6I/TZppgY3Frs//AvZ87YVQLFxLywitb8wLS3qMM+Ih9e4QcB5Yy8br6eq7DdUzxPdbsYTz1FQBHzNxs08M1Tw==
|
||||
}
|
||||
dev: true
|
||||
|
||||
/levn/0.3.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -4646,6 +4655,17 @@ packages:
|
||||
engines: { node: '>=0.10.0' }
|
||||
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:
|
||||
resolution:
|
||||
{
|
||||
|
||||
@@ -27,16 +27,21 @@
|
||||
let timer_res: string;
|
||||
let shown_question_now: number;
|
||||
let final_results_clicked = false;
|
||||
let timer_interval;
|
||||
|
||||
export let player_scores;
|
||||
|
||||
export const set_question_number = (q_number: number) => {
|
||||
timer_res = '0';
|
||||
clearInterval(timer_interval);
|
||||
// setTimeout(() => {
|
||||
question_results = null;
|
||||
socket.emit('set_question_number', q_number.toString());
|
||||
shown_question_now = q_number;
|
||||
timer_res = quiz_data.questions[q_number].time;
|
||||
selected_question = selected_question + 1;
|
||||
timer(timer_res);
|
||||
// }, 1000);
|
||||
};
|
||||
const get_question_results = () => {
|
||||
socket.emit('get_question_results', {
|
||||
@@ -53,6 +58,7 @@
|
||||
const get_final_results = () => {
|
||||
socket.emit('get_final_results', {});
|
||||
final_results_clicked = true;
|
||||
timer_res = '0';
|
||||
};
|
||||
|
||||
socket.on('final_results', (data) => {
|
||||
@@ -75,7 +81,7 @@
|
||||
|
||||
const timer = (time: string) => {
|
||||
let seconds = Number(time);
|
||||
let timer_interval = setInterval(() => {
|
||||
timer_interval = setInterval(() => {
|
||||
if (timer_res === '0') {
|
||||
clearInterval(timer_interval);
|
||||
// socket.emit('show_solutions', {});
|
||||
@@ -109,7 +115,7 @@
|
||||
/{quiz_data.questions.length}
|
||||
</p>
|
||||
<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])}
|
||||
<button on:click={get_final_results} class="admin-button"
|
||||
>Get final results
|
||||
@@ -126,12 +132,34 @@
|
||||
</button>
|
||||
{/if}
|
||||
{#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}
|
||||
{:else if selected_question !== -1}
|
||||
<button on:click={show_solutions} class="admin-button"
|
||||
>Stop time and show solutions
|
||||
</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={show_solutions} class="admin-button"
|
||||
>Stop time and show solutions
|
||||
</button>
|
||||
{/if}
|
||||
{:else}
|
||||
<!-- <button
|
||||
on:click={() => {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import StartGamePopup from './start_game.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import viewport from './useViewportAction.js';
|
||||
import Spinner from '$lib/Spinner.svelte';
|
||||
|
||||
let copy_toast_open = false;
|
||||
let start_game = null;
|
||||
@@ -350,6 +351,28 @@
|
||||
</div>
|
||||
{/each}
|
||||
</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}
|
||||
</div>
|
||||
</SwiperSlide>
|
||||
|
||||
@@ -23,26 +23,11 @@
|
||||
let selected_element = undefined;
|
||||
let canvas_el: HTMLDivElement | undefined;
|
||||
let canvas: Pikaso;
|
||||
let elements = [];
|
||||
let selected_el: null | ShapeModel<Konva.Shape | Konva.Group, Konva.ShapeConfig> = null;
|
||||
|
||||
let elements_binds: Array<HTMLElement> | undefined = [];
|
||||
let main_el: undefined | HTMLElement;
|
||||
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) => {
|
||||
for (const i of e) {
|
||||
@@ -116,10 +101,6 @@
|
||||
assign_resize_handlers();
|
||||
}
|
||||
|
||||
const set_correct_size = (e: UIEvent) => {
|
||||
console.log(e, 'resized');
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
/* setTimeout(() => {
|
||||
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>
|
||||
|
||||
<div class="flex h-full relative w-full" bind:this={main_el}>
|
||||
|
||||
@@ -5,38 +5,36 @@
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { Question } from '$lib/quiz_types';
|
||||
import TextElement from '$lib/editor/slides/types/text.svelte';
|
||||
import HeadlineElement from '$lib/editor/slides/types/headline.svelte';
|
||||
import { ElementTypes } from '$lib/quiz_types.js';
|
||||
import { draggable } from '@neodrag/svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import Pikaso from 'pikaso';
|
||||
|
||||
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>
|
||||
|
||||
<div class="w-full min-h-[calc(100vh-8rem)]" bind:this={main_el}>
|
||||
{#if main_el}
|
||||
{#each question.answers as el, i}
|
||||
<div
|
||||
use:draggable={{
|
||||
bounds: 'parent',
|
||||
handle: '.drag',
|
||||
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 class="w-full h-full">
|
||||
<div class="hidden">
|
||||
<div bind:this={canvas_el} class="w-full h-full block" />
|
||||
</div>
|
||||
<div class="w-full h-full flex justify-center">
|
||||
<img src={img_src} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -150,5 +150,13 @@
|
||||
{#if timer_res === '0'}
|
||||
<p>No correct answers, since this is a poll-question</p>
|
||||
{/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}
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
*/
|
||||
|
||||
import * as yup from 'yup';
|
||||
import { ElementTypes } from '$lib/quiz_types';
|
||||
|
||||
export const ABCDQuestionSchema = yup
|
||||
.array()
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import { QuizQuestionType } from '$lib/quiz_types.js';
|
||||
import StartGamePopup from '$lib/dashboard/start_game.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
import Spinner from '$lib/Spinner.svelte';
|
||||
|
||||
const tippy = createTippy({
|
||||
arrow: true,
|
||||
@@ -228,6 +229,14 @@
|
||||
</div>
|
||||
{/each}
|
||||
</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}
|
||||
</div>
|
||||
</CollapsSection>
|
||||
|
||||
Reference in New Issue
Block a user