From 3f1d905a2ed66b7c2a7583e95f0de0c2cd9db17c Mon Sep 17 00:00:00 2001 From: Roderick Date: Sun, 12 May 2024 21:45:21 -0700 Subject: [PATCH] Allow user to select training letters --- src/App.tsx | 11 +---- src/Display.tsx | 111 ++++++++++++++++++++++++++++++----------------- src/Stations.tsx | 60 +++++++++++++++++-------- src/keyer.ts | 2 +- src/main.tsx | 22 +++++++--- 5 files changed, 133 insertions(+), 73 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 266c441..bea7d94 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import { Carousel } from '@mantine/carousel'; -import { Button, Center, Container, Flex, Switch } from '@mantine/core'; +import { Button, Center, Container, Flex } from '@mantine/core'; import { useCallback, useEffect, useState } from 'react'; import { useLocalStorage } from 'usehooks-ts'; @@ -29,16 +29,11 @@ export default function App(props: { audio: Audio; keyer: Keyer }) { const [mobileStart, setMobileStart] = useState( 'ontouchstart' in document.documentElement, ); - const [station, setStation] = useLocalStorage('station', 'test'); + const [station] = useLocalStorage('station', 'test'); const [currentGuess, setCurrentGuess] = useState(['', 0]); const [currentMessage, setCurrentMessage] = useState(''); const [keyType] = useLocalStorage('key-type', 'straight'); - const keyTest = () => { - setCurrentMessage('hello'); - props.keyer.keyPartnerMessage('hello'); - }; - const handleKeypress = useCallback( letter => { if (station === 'copy' || station === 'rxPractice') { @@ -186,8 +181,6 @@ export default function App(props: { audio: Audio; keyer: Keyer }) {
{key} -
- ); + const currentLetters = new Set(progress.training); + for (const letter of LCWO_LETTERS) { + if (!currentLetters.has(letter)) { + extraButton = ( + + ); + break; + } + } } notifications.show({ icon: PERSONAS.elmer.icon, @@ -171,10 +197,12 @@ export function Display(props: { radius: 'lg', title: PERSONAS.elmer.name, autoClose: false, - message: ( - You did it! {Math.round(100 * perCorrect)}% right. - { extraButton } - ), + message: ( + + You did it! {Math.round(100 * perCorrect)}% right. + {extraButton} + + ), }); } @@ -184,9 +212,8 @@ export function Display(props: { ); if (currentMessage) setCurrentMessage(currentMessage.slice(1)); - let color = 'grey'; if (chr !== ' ') { - let score = gradeGuess(chr); + const score = gradeGuess(chr); if (score === 1) color = 'blue'; if (score === -1) color = 'red'; } @@ -195,7 +222,7 @@ export function Display(props: { updateDisplay(chr, color); break; default: - updateDisplay(chr, primary ? 'black' : 'grey'); + updateDisplay(chr, color); } }); @@ -212,11 +239,17 @@ export function Display(props: { currentGuess, currentLesson, currentMessage, + gradeGuess, keyer, + progress, setCurrentGuess, setCurrentLesson, setCurrentMessage, + setProgress, + startTraining, station, + updateProgress, + updateDisplay, ]); return ( diff --git a/src/Stations.tsx b/src/Stations.tsx index 145f735..4e7cc55 100644 --- a/src/Stations.tsx +++ b/src/Stations.tsx @@ -1,26 +1,53 @@ -import { SegmentedControl, Slider } from '@mantine/core'; +import { Checkbox, Flex, SegmentedControl } from '@mantine/core'; import { useLocalStorage } from 'usehooks-ts'; - -const LCWO_LETTERS = 'kmuresnaptlwi.jz=foy,vg5/q92h38b?47c1d60x'; -const HOLECEK_DROID_LETTERS = - 'etimansorkdugwhpxbflvczjqy1234567890.,:?\'-/()"=+@'; -const FINLEY_LETTERS = 'kmrsuaptlowi.njef0y,vg5/q9zh38b?427c1d6x'; +import { LCWO_LETTERS } from './Display'; export function StationPane() { - let [station, setStation] = useLocalStorage('station', 'test'); + const [progress, setProgress] = useLocalStorage('learning-progress', { + training: 'kmur', + letters: {}, + daily: [], + }); + const [station, setStation] = useLocalStorage('station', 'test'); let description = ''; + let controls = <>; if (station === 'test') description = 'Practice sending with the keys.'; - if (station === 'copy') description = 'Practice receiving copy.'; + if (station === 'copy') { + description = 'Practice receiving copy.'; + const boxes = []; + for (const letter of LCWO_LETTERS) { + boxes.push( + -1} + onChange={(evt: React.ChangeEvent) => { + const loc = progress.training.indexOf(letter); + console.log(evt.currentTarget.checked, loc); + if (evt.currentTarget.checked && loc === -1) { + progress.training += letter; + } else if (!evt.currentTarget.checked) { + progress.training = + progress.training.slice(0, loc) + + progress.training.slice(loc + 1); + } + setProgress(progress); + }} + />, + ); + } + controls = ( + + {' '} + {boxes}{' '} + + ); + } if (station === 'listen') description = 'Listen to text.'; if (station === 'txPractice') description = 'Initiate a QSO.'; if (station === 'rxPractice') description = 'Respond to a QSO.'; - const marks = LCWO_LETTERS.split('').map((letter, ix) => ({ - value: ix, - label: letter, - })); - return ( <>
{description} - marks.find(mark => mark.value === val)!.label} - marks={marks} - /> + {controls} ); } diff --git a/src/keyer.ts b/src/keyer.ts index d2e37c8..20a8250 100644 --- a/src/keyer.ts +++ b/src/keyer.ts @@ -210,7 +210,7 @@ export class Keyer { keyLetter(letter: string, primary: boolean = true) { if (this.timeoutGap) clearTimeout(this.timeoutGap); - let code = MORSE_MAP[letter.toLowerCase()] + let code = MORSE_MAP[letter.toLowerCase()]; if (letter === ' ') code = ' '; const delay = this.audio.key( code, diff --git a/src/main.tsx b/src/main.tsx index 84ac0b5..2aaba3d 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -4,7 +4,12 @@ import '@mantine/notifications/styles.css'; import React from 'react'; import ReactDOM from 'react-dom/client'; -import { localStorageColorSchemeManager, Button, Group, MantineProvider } from '@mantine/core'; +import { + localStorageColorSchemeManager, + Button, + Group, + MantineProvider, +} from '@mantine/core'; import { Notifications } from '@mantine/notifications'; import { notifications } from '@mantine/notifications'; @@ -17,6 +22,9 @@ const volume = parseFloat(localStorage.getItem('volume') || '1'); const wpm = parseInt(localStorage.getItem('wpm') || '25'); const farnsworth = parseInt(localStorage.getItem('farnsworth') || '5'); +if (new URLSearchParams(document.location.search).has('clearLocalStorage')) + window.localStorage.clear(); + const audio = new Audio(volume); const keyer = new Keyer(audio, wpm, farnsworth); window.keyer = keyer; @@ -32,10 +40,14 @@ notifications.show({ radius: 'lg', title: PERSONAS.elmer.name, autoClose: false, - message: ( - Welcome! I'm here to help you learn morse code. - - ), + message: ( + + Welcome! I'm here to help you learn morse code. + + + ), }); ReactDOM.createRoot(document.getElementById('root')).render(