This repository has been archived on 2024-03-28. You can view files and clone it, but cannot push or open issues or pull requests.
atsebay.t/ui/src/routes/surveys/[sid]/live/+page.svelte

303 lines
10 KiB
Svelte
Raw Normal View History

2022-02-28 18:00:30 +00:00
<script>
2022-09-02 10:25:32 +00:00
import { onDestroy } from 'svelte';
2022-11-18 14:38:50 +00:00
import { user } from '$lib/stores/user';
import { ToastsStore } from '$lib/stores/toasts';
import CorrectionPieChart from '$lib/components/CorrectionPieChart.svelte';
2022-11-18 14:38:50 +00:00
import QuestionForm from '$lib/components/QuestionForm.svelte';
import SurveyBadge from '$lib/components/SurveyBadge.svelte';
2022-11-18 14:38:50 +00:00
import { getQuestion } from '$lib/questions';
2022-02-28 18:00:30 +00:00
2022-12-12 09:24:30 +00:00
export let data;
2022-02-28 18:00:30 +00:00
let survey;
2022-12-12 09:24:30 +00:00
$: survey = data.survey;
2022-02-28 18:00:30 +00:00
let ws_up = false;
let show_question = null;
let value;
2022-03-01 12:03:16 +00:00
let req_question;
let nosend = false;
2022-03-01 15:38:52 +00:00
let timer_init = null;
let timer_end = null;
let timer = 0;
let timer_cancel = null;
2022-03-01 12:03:16 +00:00
function afterQUpdate(q) {
value = undefined;
if (q) {
q.getMyResponse().then((response) => {
if (response && response.value)
value = response.value;
})
}
}
$: {
if (show_question) {
req_question = getQuestion(show_question);
req_question.then(afterQUpdate);
}
}
2022-03-01 15:38:52 +00:00
function updTimer() {
const now = new Date().getTime();
if (now > timer_end) {
timer = 100;
clearInterval(timer_cancel);
timer_cancel = null;
} else {
const dist1 = timer_end - timer_init;
const dist2 = timer_end - now;
timer = Math.ceil(100-dist2*100/dist1);
}
}
2022-09-02 10:25:32 +00:00
let ws = null;
let autoreconnect = true;
onDestroy(() => {
autoreconnect = false;
console.log("destroy", ws)
if (ws) {
ws.close();
}
});
2022-02-28 18:00:30 +00:00
function wsconnect() {
2022-12-12 09:24:30 +00:00
ws = new WebSocket((window.location.protocol == 'https:'?'wss://':'ws://') + window.location.host + `/api/surveys/${data.sid}/ws`);
2022-02-28 18:00:30 +00:00
ws.addEventListener("open", () => {
ws_up = true;
});
ws.addEventListener("close", (e) => {
ws_up = false;
show_question = false;
2022-09-02 10:25:32 +00:00
console.log('Socket is closed. Reconnect will be attempted in 1 second.', e.reason, e);
if (autoreconnect && e.reason != "end")
setTimeout(function() {
wsconnect();
}, 1500);
2022-02-28 18:00:30 +00:00
});
ws.addEventListener("error", (err) => {
ws_up = false;
console.log('Socket closed due to error.', err.message);
});
ws.addEventListener("message", (message) => {
const data = JSON.parse(message.data);
if (data.action && data.action == "new_question") {
show_question = data.question;
survey.corrected = data.corrected;
if (data.stats) {
stats = data.stats;
} else {
stats = null;
}
if(data.corrected) {
2022-09-02 09:54:58 +00:00
corrections = data.corrections;
} else {
corrections = null;
}
2022-03-01 15:38:52 +00:00
if (timer_cancel) {
clearInterval(timer_cancel);
timer_cancel = null;
}
if (data.timer) {
timer_init = new Date().getTime();;
timer_end = timer_init + data.timer;
updTimer();
timer_cancel = setInterval(updTimer, 150);
} else {
timer_init = null;
}
} else if (data.action && data.action == "where_are_you") {
ws.send('{"action":"myscroll", "value": "' + (window.scrollY/window.scrollMaxY) +'", "question": '+show_question+', "corrected": '+(survey.corrected==true)+'}')
2022-02-28 18:00:30 +00:00
} else {
show_question = null;
2022-03-01 15:38:52 +00:00
if (timer_cancel) {
clearInterval(timer_cancel);
timer_cancel = null;
}
timer_init = null;
2022-02-28 18:00:30 +00:00
}
});
}
wsconnect();
let displaySendInProgress = false;
2022-02-28 18:00:30 +00:00
function sendValue() {
2022-03-01 12:03:16 +00:00
if (show_question && value && !nosend) {
displaySendInProgress = true;
2022-02-28 18:00:30 +00:00
survey.submitAnswers([{"id_question": show_question, "value": value}], $user.id_user).then((response) => {
setTimeout(() => displaySendInProgress = false, 150);
2022-02-28 18:00:30 +00:00
console.log("Vos réponses ont bien étés sauvegardées.");
}, (error) => {
displaySendInProgress = false;
2022-02-28 18:00:30 +00:00
value = null;
2022-03-01 12:16:20 +00:00
ToastsStore.addErrorToast({
msg: "Une erreur s'est produite durant l'envoi de vos réponses : " + error + "\nVeuillez réessayer dans quelques instants.",
});
2022-02-28 18:00:30 +00:00
});
}
}
2022-03-01 14:26:30 +00:00
let myQuestion = "";
let submitQuestionInProgress = false;
function askQuestion() {
if (!myQuestion) {
ToastsStore.addErrorToast({
msg: "Quel est ta question ?",
});
return;
}
submitQuestionInProgress = true;
fetch(`api/surveys/${survey.id}/ask`, {
method: 'POST',
headers: {'Accept': 'application/json'},
body: JSON.stringify({"content": myQuestion}),
}).then((r) => {
submitQuestionInProgress = false;
myQuestion = "";
ToastsStore.addToast({
msg: "Ta question a bien été envoyée.",
title: survey.title,
color: "success",
});
}, (error) => {
ToastsStore.addErrorToast({
msg: "Un problème est survenu : " + error.errmsg,
});
});
}
2022-09-01 20:09:14 +00:00
2022-09-02 09:54:58 +00:00
let corrections = null;
let stats = null;
2022-02-28 18:00:30 +00:00
</script>
2022-12-12 09:24:30 +00:00
<div
style={"transition: opacity 150ms ease-out; opacity: " + (displaySendInProgress?1:0)}
class="ms-2 float-end"
>
<div style="position: relative; left: 25%; top: 4px">
<div style="position: absolute">
<i class="bi bi-save"></i>
</div>
</div>
2022-12-12 09:24:30 +00:00
<div class="spinner-border text-primary" role="status"></div>
</div>
{#if $user && $user.is_admin}
<a href="surveys/{survey.id}/admin" class="btn btn-primary ms-1 float-end" title="Aller à l'interface d'administration"><i class="bi bi-pencil"></i></a>
<a href="surveys/{survey.id}/responses" class="btn btn-success ms-1 float-end" title="Voir les réponses"><i class="bi bi-files"></i></a>
{/if}
<div class="d-flex align-items-center mb-3 mb-md-4 mb-lg-5">
<h2>
<a href="surveys/" class="text-muted" style="text-decoration: none">&lt;</a>
{survey.title}
</h2>
<div
class="badge rounded-pill ms-2"
class:bg-success={ws_up}
class:bg-danger={!ws_up}
>
{#if ws_up}Connecté{:else}Déconnecté{/if}
2022-02-28 18:00:30 +00:00
</div>
2022-12-12 09:24:30 +00:00
</div>
2022-02-28 18:00:30 +00:00
2022-12-12 09:24:30 +00:00
<form on:submit|preventDefault={sendValue}>
{#if show_question}
{#await req_question}
<div class="text-center">
<div class="spinner-border text-primary mx-3" role="status"></div>
<span>Chargement d'une nouvelle question &hellip;</span>
</div>
{:then question}
{#if stats != null}
<CorrectionPieChart
{question}
proposals={true}
data={stats}
/>
{/if}
2022-12-12 09:24:30 +00:00
<QuestionForm
{survey}
{question}
readonly={timer >= 100 || survey.corrected}
{corrections}
bind:value={value}
on:change={sendValue}
>
{#if timer_init}
<div class="progress" style="border-radius: 0; height: 4px">
<div class="progress-bar" class:bg-warning={timer > 85 && timer < 100} class:bg-danger={timer >= 100} role="progressbar" style="width: {timer}%"></div>
</div>
{/if}
</QuestionForm>
{#if !survey.corrected && question.kind != 'mcq' && question.kind != 'ucq' && question.kind != 'none'}
2022-12-12 09:24:30 +00:00
<button
class="btn btn-primary"
2022-02-28 18:00:30 +00:00
>
2022-12-12 09:24:30 +00:00
Soumettre cette réponse
</button>
{/if}
{/await}
{:else if ws_up}
<h2 class="text-center mb-4">
Pas de question actuellement.
</h2>
<form on:submit|preventDefault={askQuestion}>
<div class="row">
<div class="d-none d-sm-block col-sm">
<hr>
</div>
<h3 class="col-sm-auto text-center text-muted mb-3"><label for="askquestion">Vous avez une question&nbsp;?</label></h3>
<div class="d-none d-sm-block col-sm">
<hr>
</div>
</div>
<div class="row">
<div class="offset-md-1 col-md-10 offset-lg-2 col-lg-8 offset-xl-3 col-xl-6 mb-4">
<div class="input-group">
2022-12-15 14:15:38 +00:00
<!-- svelte-ignore a11y-autofocus -->
2022-12-12 09:24:30 +00:00
<textarea
id="askquestion"
class="form-control"
bind:value={myQuestion}
autofocus
placeholder="Remarques, soucis, choses pas claires? Levez la main ou écrivez ici!"
></textarea>
2022-03-01 12:03:16 +00:00
<button
2022-12-12 09:24:30 +00:00
class="d-sm-none btn btn-primary"
disabled={!myQuestion || submitQuestionInProgress}
2022-03-01 12:03:16 +00:00
>
2022-12-12 09:24:30 +00:00
{#if submitQuestionInProgress}
<div class="spinner-border spinner-border-sm me-1" role="status"></div>
{/if}
Poser cette question
2022-03-01 12:03:16 +00:00
</button>
2022-03-01 15:54:04 +00:00
</div>
</div>
2022-12-12 09:24:30 +00:00
</div>
{#if myQuestion}
<div class="d-none d-sm-block text-center mb-4">
<button
class="btn btn-primary"
disabled={submitQuestionInProgress}
>
{#if submitQuestionInProgress}
<div class="spinner-border spinner-border-sm me-1" role="status"></div>
{/if}
Poser cette question
</button>
2022-03-01 14:26:30 +00:00
</div>
2022-12-12 09:24:30 +00:00
{/if}
</form>
{:else}
<h2 class="text-center">
La session est terminée. <small class="text-muted">On se retrouve une prochaine fois&hellip;</small>
</h2>
{/if}
</form>