Skip to content
Extraits de code Groupes Projets
Valider 490c2e47 rédigé par Brieuc Dubois's avatar Brieuc Dubois
Parcourir les fichiers

[frontend] typing test InputLog like

parent 55f05acc
Aucune branche associée trouvée
Aucune étiquette associée trouvée
Aucune requête de fusion associée trouvée
<script lang="ts">
import { requireLogin } from '$lib/utils/login';
import { onMount } from 'svelte';
import { _ } from '$lib/services/i18n';
import { createTestTypingAPI } from '$lib/api/users';
import JWTSession from '$lib/stores/JWTSession';
import { toastAlert } from '$lib/utils/toasts';
import config from '$lib/config';
import { init, t } from 'svelte-i18n';
import { split } from 'postcss/lib/list';
export let initialDuration: number;
export let exerciceId: number;
export let explications: string;
export let text: string;
export let data: {
exerciceId: number;
position: number;
downtime: number;
uptime: number;
keyCode: number;
keyValue: string;
}[];
let duration = initialDuration >= 0 ? initialDuration : 0;
export let inProgress = false;
let lastInput = '';
let input = '';
let textArea: HTMLTextAreaElement;
let startTime = new Date().getTime();
onMount(async () => {
textArea.focus();
});
function start() {
inProgress = true;
startTime = new Date().getTime();
const interval = setInterval(() => {
duration += initialDuration >= 0 ? -1 : 1;
if ((duration <= 0 && initialDuration > 0) || !inProgress) {
clearInterval(interval);
inProgress = false;
}
}, 1000);
}
</script>
<div class=" w-full max-w-5xl m-auto mt-8">
<div>{explications}</div>
<div class="flex justify-between my-2 p-2">
<button
class="button"
on:click={() => {
input = '';
duration = initialDuration >= 0 ? initialDuration : 0;
start();
}}
disabled={inProgress}
>
{$_('button.start')}
</button>
<div class="text-2xl font-bold">
{duration}s
</div>
</div>
<ul class="h-10 flex justify-around border-t-2 rounded-t-lg border-x-2 divide-x-2 text-center">
{#each config.SPECIAL_CHARS as char (char)}
<button
class="flex-grow"
on:click={() => {
input += char;
}}
on:mousedown={(e) => e.preventDefault()}
>
{char}
</button>
{/each}
</ul>
<div class="relative border-2 rounded-b-lg text-xl select-none">
<div class="font-mono p-4 break-words">
<span class="text-inherit p-0 m-0 whitespace-pre-wrap break-words"
><!--
-->{#each text.slice(0, input.length) as char, i}
<span
class="text-gray-800 p-0 m-0"
class:text-red-500={char !== input[i]}
class:bg-red-100={char !== input[i]}>{input[i]}</span
><!--
-->{/each}<!--
--></span
><span
class="text-gray-400 p-0 m-0 underline decoration-2 underline-offset-6 decoration-black whitespace-pre-wrap"
>{text[input.length] ?? ''}</span
><span class="text-gray-400 p-0 m-0 whitespace-pre-wrap"
>{text.slice(input.length + 1) ?? ''}</span
>
</div>
<textarea
bind:value={input}
bind:this={textArea}
spellcheck="false"
disabled={!inProgress &&
duration <= 0 &&
initialDuration >= 0 &&
duration >= 0 &&
initialDuration < 0}
on:keyup={(e) => {
if (inProgress) {
data[data.length - 1].uptime = new Date().getTime() - startTime;
}
}}
on:keydown={(e) => {
if (
!inProgress &&
((duration > 0 && initialDuration > 0) || (duration >= 0 && initialDuration < 0))
) {
start();
}
if (inProgress) {
lastInput = input;
data.push({
exerciceId,
position: input.length,
downtime: new Date().getTime() - startTime,
uptime: 0,
keyCode: e.keyCode,
keyValue: e.key
});
if (input === text || input.split('\n').length >= text.split('\n').length + 1) {
inProgress = false;
}
} else {
input = lastInput;
}
}}
class="absolute top-0 resize-none font-mono p-4 w-full h-full bg-transparent select-none text-transparent"
/>
</div>
</div>
......@@ -3,152 +3,74 @@
import { requireLogin } from '$lib/utils/login';
import { onMount } from 'svelte';
import { _ } from '$lib/services/i18n';
import { createTestTypingAPI } from '$lib/api/users';
import JWTSession from '$lib/stores/JWTSession';
import { toastAlert } from '$lib/utils/toasts';
import config from '$lib/config';
import Typingbox from '$lib/components/tests/typingbox.svelte';
import { get } from 'svelte/store';
const initialDuration = 60;
let data: {
exerciceId: number;
position: number;
downtime: number;
uptime: number;
keyCode: number;
keyValue: string;
}[] = [];
let text =
"Bonjour, je suis un long texte en français, pour tester la vitesse de frappe. Napoleon Bonaparte, né le 15 aout 1769 à Ajaccio et mort le 5 mai 1821 sur l'ile Sainte-Hélène, est un militaire et homme d'État français, premier empereur des Français, du 18 mai 1804 au 6 avril 1814 et du 20 mars 1815 au 22 juin 1815. Il est l'une des figures les plus célèbres de l'histoire de France et une des plus étudiées au monde. Il est considéré comme lun des plus grands chefs militaires de l'histoire, avec une carrière longue et brillante, mais aussi comme lun des plus grands stratèges militaires de l'histoire. Charles de Gaulle, né le 22 novembre 1890 à Lille et mort le 9 novembre 1970 à Colombey-les-Deux-Églises, est un militaire, écrivain et homme dÉtat français. Il est le chef de la France libre pendant la Seconde Guerre mondiale, le président du Gouvernement provisoire de la République française de 1944 à 1946, puis le président de la République française du 8 janvier 1959 au 28 avril 1969. Il est le fondateur de la Cinquième République française en 1958 et le seul président de la République française à avoir démissionné de ses fonctions. Il est considéré comme lun des plus grands chefs militaires de l'histoire, avec une carrière longue et brillante, mais aussi comme lun des plus grands stratèges militaires de l'histoire.";
$: currentExercice = 0;
let duration = initialDuration;
let inProgress = false;
let lastInput = '';
let input = '';
let sent = false;
let textArea: HTMLTextAreaElement;
let exercices = [
{
duration: 15,
explications: `Repetez les lettres "dk" autant de fois que possible en 15 secondes. Le chronomètre démarre dès que vous appuyez sur une touche ou sur le boutton ${get(_)('button.start')}. Une vois que vous aurez terminé, appuyez sur le bouton ${get(_)('button.next')} pour passer à l'exercice suivant.`,
text: 'dk'.repeat(150) + '...'
},
{
duration: 30,
explications: 'Repetez la phrase suivante autant de fois que possible en 30 secondes.',
text: 'Le chat est sur le toit.\n'.repeat(20) + '...'
},
{
duration: -1,
explications: 'Repetez 7 fois la phrase suivante le plus rapidement possible.',
text: 'Six animaux mangent\n'.repeat(6) + 'Six animaux mangent'
}
];
onMount(async () => {
if (!requireLogin()) return;
textArea.focus();
});
function start() {
inProgress = true;
const interval = setInterval(() => {
duration--;
if (duration <= 0) {
clearInterval(interval);
inProgress = false;
}
}, 1000);
}
function wpm(str: string, duration: number) {
const n_words = str.length / 6;
const n_minutes = duration / 60;
return Math.round(n_words / n_minutes);
}
function getErrors() {
let errors = 0;
for (let i = 0; i < input.length; i++) {
if (input[i] !== text[i]) {
errors++;
}
}
return errors;
}
async function sendResults() {
const user = JWTSession.user();
if (!user) {
toastAlert('Failed to get your user data.');
return;
}
const id = await createTestTypingAPI(user.id, input.length, initialDuration, getErrors());
if (!id) return;
sent = true;
}
</script>
<Header />
<div class=" w-full max-w-5xl m-auto mt-8">
<div class="flex justify-between mb-2 p-2">
{#each exercices as exercice, i (i)}
{#if i === currentExercice}
<Typingbox
exerciceId={i}
initialDuration={exercices[i].duration}
explications={exercices[i].explications}
text={exercices[i].text}
bind:data
bind:inProgress
/>
{/if}
{/each}
<div class="flex items-center mt-8">
{#if currentExercice < exercices.length - 1}
<button
class="button"
class="button m-auto"
on:click={() => {
input = '';
duration = initialDuration;
start();
currentExercice++;
inProgress = false;
console.log(data);
}}
disabled={inProgress}>Start</button
disabled={inProgress}
>
<div class="text-2xl font-bold">
{duration}s
</div>
</div>
<ul class="h-10 flex justify-around border-t-2 rounded-t-lg border-x-2 divide-x-2 text-center">
{#each config.SPECIAL_CHARS as char (char)}
<button
class="flex-grow"
on:click={() => {
input += char;
}}
on:mousedown={(e) => e.preventDefault()}
>
{char}
</button>
{/each}
</ul>
<div class="relative border-2 rounded-b-lg text-xl select-none">
<div class="font-mono p-4">
<span class="text-inherit p-0 m-0 whitespace-pre-wrap"
><!--
-->{#each text.slice(0, input.length) as char, i}
<span
class="text-gray-800 p-0 m-0"
class:text-red-500={char !== input[i]}
class:bg-red-100={char !== input[i]}>{input[i]}</span
><!--
-->{/each}<!--
--></span
><span
class="text-gray-400 p-0 m-0 underline decoration-2 underline-offset-6 decoration-black"
>{text[input.length]}</span
><span class="text-gray-400 p-0 m-0">{text.slice(input.length + 1)}</span>
</div>
<textarea
bind:value={input}
bind:this={textArea}
spellcheck="false"
disabled={!inProgress && duration <= 0}
on:input={(e) => {
if (!inProgress && duration > 0) {
start();
}
if (inProgress) {
// && text.toLowerCase().startsWith(input.toLowerCase())) {
lastInput = input;
} else {
input = lastInput;
}
}}
class="absolute top-0 resize-none font-mono p-4 w-full h-full bg-transparent select-none text-transparent"
/>
</div>
{#if !inProgress && duration <= 0}
<div class="flex flex-col items-center">
<div class="text-2xl font-bold text-center mt-4">
{input.length} characters in {initialDuration} seconds, resulting in {wpm(
input,
initialDuration
)} WPM
</div>
<button class="button mt-4" on:click={sendResults} disabled={sent}>
{#if sent}
{$_('tests.sendResultsDone')}
{:else}
{$_('tests.sendResults')}
{/if}
</button>
</div>
{$_('button.next')}
</button>
{:else}
<button class="button m-auto" disabled={inProgress}>{$_('button.submit')}</button>
{/if}
</div>
......@@ -85,7 +85,9 @@
"button": {
"create": "Créer",
"submit": "Envoyer",
"sent": "Envoyé !"
"sent": "Envoyé !",
"next": "Suivant",
"start": "Commencer"
},
"utils": {
"month": {
......
0% Chargement en cours ou .
You are about to add 0 people to the discussion. Proceed with caution.
Terminez d'abord l'édition de ce message.
Veuillez vous inscrire ou vous pour commenter