mirror of
https://github.com/hazemKrimi/touch-programming.git
synced 2026-05-01 18:20:26 +00:00
Score wip
This commit is contained in:
@@ -1,7 +1,15 @@
|
||||
.container {
|
||||
margin: auto;
|
||||
padding: 1rem;
|
||||
letter-spacing: 0.12rem;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
column-gap: 1rem;
|
||||
}
|
||||
|
||||
.code {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.score {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.pending, .space {
|
||||
|
||||
@@ -8,14 +8,18 @@ function Code() {
|
||||
// TODO: Create a function that combines a sequence of spaces into tabs.
|
||||
const [code, setCode] = useState<string>('');
|
||||
const [loaded, setLoaded] = useState<boolean>(false);
|
||||
const [startedTyping, setStartedTyping] = useState<boolean>(false);
|
||||
const [characters, setCharacters] = useState<Array<boolean | 'space'>>([]);
|
||||
const [timer, setTimer] = useState<number>(0);
|
||||
const [score, setScore] = useState<number>(0);
|
||||
const [accuracy, setAccuracy] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
(async function () {
|
||||
setCode('');
|
||||
|
||||
const response = await fetch(
|
||||
`${import.meta.env.VITE_API_URL}/generate?lang=typescript`,
|
||||
`${import.meta.env.VITE_API_URL}/generate?lang=lisp`,
|
||||
);
|
||||
|
||||
if (!response.ok || !response.body) return;
|
||||
@@ -38,14 +42,36 @@ function Code() {
|
||||
})();
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
let interval = null;
|
||||
|
||||
if (!startedTyping) {
|
||||
if (interval) clearInterval(interval);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
interval = setInterval(() => {
|
||||
setTimer(prev => prev + 1);
|
||||
}, 1000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, [startedTyping]);
|
||||
|
||||
useEffect(() => {
|
||||
function handleKeyPress(event: KeyboardEvent) {
|
||||
event.preventDefault();
|
||||
|
||||
if (!loaded) return;
|
||||
if (characters.length === code.length) return;
|
||||
if (characters.length === code.length) {
|
||||
setStartedTyping(false);
|
||||
|
||||
return;
|
||||
}
|
||||
if (KEYS_TO_DISABLE.includes(event.key)) return;
|
||||
|
||||
if (!startedTyping) setStartedTyping(true);
|
||||
|
||||
const char = code[characters.length];
|
||||
|
||||
if (/^\n$/.test(char)) {
|
||||
@@ -71,6 +97,17 @@ function Code() {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loaded, characters]);
|
||||
|
||||
useEffect(() => {
|
||||
const typed = characters.filter(char => char !== 'space').length;
|
||||
const correctlyTyped = characters.filter(char => char && char !== 'space').length;
|
||||
const incorrectlyTyped = characters.filter(char => !char).length;
|
||||
|
||||
if (timer > 0) {
|
||||
setScore((typed / 5 - incorrectlyTyped) / (timer / 60));
|
||||
setAccuracy(correctlyTyped / typed * 100);
|
||||
}
|
||||
}, [timer, characters]);
|
||||
|
||||
function renderCharacterClassName(index: number) {
|
||||
const typed = characters[index];
|
||||
|
||||
@@ -115,7 +152,14 @@ function Code() {
|
||||
|
||||
return (
|
||||
<div className='container'>
|
||||
{code.split('').map((char, index) => renderCharacter(char, index))}
|
||||
<div className='code'>
|
||||
{code.split('').map((char, index) => renderCharacter(char, index))}
|
||||
</div>
|
||||
<div className='score'>
|
||||
<p>Time: {timer}</p>
|
||||
<p>WPM: {Math.round(score)}</p>
|
||||
<p>Accuracy: {Math.round(accuracy)}%</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { useState } from "react";
|
||||
|
||||
function Score() {
|
||||
const [timer] = useState<number>(0);
|
||||
const [score] = useState<number>(0);
|
||||
const [accuracy] = useState<number>(0);
|
||||
|
||||
return (
|
||||
<div className='score'>
|
||||
<p>Time: {timer}</p>
|
||||
<p>WPM: {Math.round(score)}</p>
|
||||
<p>Accuracy: {Math.round(accuracy)}%</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Score;
|
||||
@@ -0,0 +1,58 @@
|
||||
import { createContext, useContext, useState } from "react";
|
||||
|
||||
export type CodeContextValues = {
|
||||
startedTyping: boolean;
|
||||
characters: Array<boolean | 'space'>;
|
||||
score: number;
|
||||
accuracy: number;
|
||||
|
||||
setStartedTyping: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
setCharacters: React.Dispatch<React.SetStateAction<Array<boolean | 'space'>>>;
|
||||
setScore: React.Dispatch<React.SetStateAction<number>>;
|
||||
setAccuracy: React.Dispatch<React.SetStateAction<number>>;
|
||||
}
|
||||
|
||||
const CodeContext = createContext<CodeContextValues>({
|
||||
startedTyping: false,
|
||||
characters: [],
|
||||
score: 0,
|
||||
accuracy: 0,
|
||||
|
||||
setStartedTyping: () => { },
|
||||
setCharacters: () => { },
|
||||
setScore: () => { },
|
||||
setAccuracy: () => { },
|
||||
});
|
||||
|
||||
export function useCodeContext() {
|
||||
return useContext(CodeContext);
|
||||
}
|
||||
|
||||
type CodeContextProviderProps = {
|
||||
children: React.ReactNode,
|
||||
}
|
||||
|
||||
function CodeContextProvider({ children }: CodeContextProviderProps) {
|
||||
const [startedTyping, setStartedTyping] = useState<boolean>(false);
|
||||
const [characters, setCharacters] = useState<Array<boolean | 'space'>>([]);
|
||||
const [score, setScore] = useState<number>(0);
|
||||
const [accuracy, setAccuracy] = useState<number>(0);
|
||||
|
||||
return (
|
||||
<CodeContext.Provider value={{
|
||||
startedTyping,
|
||||
characters,
|
||||
score,
|
||||
accuracy,
|
||||
|
||||
setStartedTyping,
|
||||
setCharacters,
|
||||
setScore,
|
||||
setAccuracy,
|
||||
}}>
|
||||
{children}
|
||||
</CodeContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default CodeContextProvider;
|
||||
Reference in New Issue
Block a user