diff --git a/src/App.js b/src/App.js index af132d3..a76de12 100644 --- a/src/App.js +++ b/src/App.js @@ -1,172 +1,33 @@ -import React, { useEffect, useState } from 'react'; +import React, {useContext, useEffect} from 'react'; import './css/App.css'; -import MorseDisplay from './components/MorseDisplay' -import MorseBufferDisplay from './components/MorseBufferDisplay' import MorseButton from './components/MorseButton' import ModePicker from './components/ModePicker' -// import LettersDisplay from './components/LettersDisplay' +// import MorseDisplay from './components/MorseDisplay' +// import MorseBufferDisplay from './components/MorseBufferDisplay' +// import GameClock from "./components/GameClock" +// import ChallengeWord from "./components/ChallengeWord" +import {GameModeContext} from "./gameContext" +import PracticeMode from './app-modes/PracticeMode'; +import TimedMode from './app-modes/TimedMode' +import ChallengeMode from './app-modes/ChallengeMode' function App() { - const [morseCharBuffer, setMorseCharBuffer] = useState('') // e.g. '-..' - const [morseWords, setMorseWords] = useState([]) // e.g. [['-..','.','-,'], ['...','---','...']] - - let charTimer = 0 - let charTime = 0 - let gapTimer = 0 - let gapTime = 0 - - const timingUnit = 15 // default: 25 - - const ditMaxTime = 5 // default: 3 - const letterGapMinTime = ditMaxTime*3 - const wordGapMaxTime = ditMaxTime*7 - const morseHistorySize = 5 - - // Tone Setup - let AudioContext = window.AudioContext || window.webkitAudioContext || false - let context - window.AudioContext = window.AudioContext || window.webkitAudioContext; - if (AudioContext) { - context = new AudioContext() - } else { - context = null - } - - let o - let frequency = 550.0 - - - function handleInputStart(event) { - event.preventDefault() - if ((event.keyCode !== 32 && event.target.id !== "morseButton") || - (event.repeat)) { - return - } - if (context.state === 'interrupted') { - context.resume() - } - - o = context.createOscillator() - o.frequency.value = frequency - o.type = "sine" - - let g = context.createGain() - g.gain.exponentialRampToValueAtTime(0.08, context.currentTime) - o.connect(g) - g.connect(context.destination) - o.start() - - // if (gapTimer===0) { setMorseLettersBuffer('') } - checkGapBetweenInputs() - clearInterval(gapTimer) - - - startCharTimer() - } - function startCharTimer() { - // Reset character time - charTime = 0 - // Start Character Timer - charTimer = setInterval(() => { - charTime += 1 - }, timingUnit); - } - - function handleInputEnd(event) { - event.preventDefault() - if ((event.keyCode !== 32 && event.target.id !== "morseButton") || - (event.repeat)) { - return - } - - if (charTime <= ditMaxTime) { - setMorseCharBuffer(prev => prev + '.') - } else { - setMorseCharBuffer(prev => prev + '-') - } - - stopCharTimer() - startGapTimer() - o.stop() - } - - function stopCharTimer() { - clearInterval(charTimer) - charTimer = 0 - } - - function startGapTimer() { - gapTime = 0 - gapTimer = setInterval(() => { - gapTime += 1 - - // Gap between words - if (gapTime >= wordGapMaxTime) { - setMorseCharBuffer(prev => prev + '/') - clearInterval(gapTimer) - gapTimer = 0 - gapTime = 0 - } - }, timingUnit); - } - - function checkGapBetweenInputs() { - // Check Gap between letters - if ((gapTime >= letterGapMinTime) && (gapTime < wordGapMaxTime)) { - // setMorseLettersBuffer(prev => [...prev, morseCharBuffer]) - setMorseCharBuffer(prev => prev + ' ') - // morseLettersBuffer = [...morseLettersBuffer, morseCharBuffer] - // setMorseCharBuffer('') - - clearInterval(gapTimer) - gapTimer = 0 - } - } - - useEffect(() => { - document.addEventListener('keydown', handleInputStart) - document.addEventListener('keyup', handleInputEnd) - document.getElementById('morseButton').addEventListener('mousedown', handleInputStart) - document.getElementById('morseButton').addEventListener('touchstart', handleInputStart) - document.getElementById('morseButton').addEventListener('mouseup', handleInputEnd) - document.getElementById('morseButton').addEventListener('touchend', handleInputEnd) - // eslint-disable-next-line - }, []) - - useEffect(() => { - if (morseCharBuffer.slice(-1) === ' ') { - // setMorseLettersBuffer([morseCharBuffer]) - // setMorseCharBuffer('') - } - if (morseCharBuffer.slice(-1) === '/') { - // Remove forward slash - let val = morseCharBuffer.slice(0,morseCharBuffer.length-1) - console.log('val: ', val); - - setMorseWords(prev => [val, ...prev]) - - if (morseWords.length >= morseHistorySize) { - setMorseWords(prev => prev.slice(0,prev.length-1)) - } - - setMorseCharBuffer('') - } - - // setMorseLettersBuffer(prev => [...prev, morseCharBuffer]) - // eslint-disable-next-line - }, [morseCharBuffer]) console.log('App.js rendered') + const {gameMode} = useContext(GameModeContext) return (
- morseCharBuffer:
-
- + + {gameMode === 'practice' && } + {gameMode === 'timed' && } + {gameMode === 'challenge' && } +
); + } export default React.memo(App); diff --git a/src/app-modes/ChallengeMode.js b/src/app-modes/ChallengeMode.js new file mode 100644 index 0000000..b3e3871 --- /dev/null +++ b/src/app-modes/ChallengeMode.js @@ -0,0 +1,22 @@ +import React from 'react'; +import '../css/App.css'; +import useTelegraph from '../hooks/useTelegraph'; +import ChallengeWord from '../components/ChallengeWord' +import MorseBufferDisplay from '../components/MorseBufferDisplay' + +function ChallengeMode() { + + const {morseCharBuffer} = useTelegraph('challenge') + + console.log('ChallengeMode.js rendered') + + return ( + <> + +
+ + ); + +} + +export default React.memo(ChallengeMode); diff --git a/src/app-modes/PracticeMode.js b/src/app-modes/PracticeMode.js new file mode 100644 index 0000000..fbd46a1 --- /dev/null +++ b/src/app-modes/PracticeMode.js @@ -0,0 +1,23 @@ +import React, {useEffect} from 'react'; +import '../css/App.css'; +import useTelegraph from '../hooks/useTelegraph'; +import MorseBufferDisplay from '../components/MorseBufferDisplay' +import MorseDisplay from '../components/MorseDisplay' + +function PracticeMode() { + + const {morseCharBuffer, morseWords, clearHistory} = useTelegraph() + + console.log('PracticeMode.js rendered') + + return ( + <> +
+
+
+ + ); + +} + +export default React.memo(PracticeMode); diff --git a/src/app-modes/TimedMode.js b/src/app-modes/TimedMode.js new file mode 100644 index 0000000..3e568cc --- /dev/null +++ b/src/app-modes/TimedMode.js @@ -0,0 +1,24 @@ +import React from 'react'; +import '../css/App.css'; +import useTelegraph from '../hooks/useTelegraph'; +import GameClock from '../components/GameClock' +import MorseBufferDisplay from '../components/MorseBufferDisplay' +import MorseDisplay from '../components/MorseDisplay' + +function TimedMode() { + + const {morseCharBuffer, morseWords} = useTelegraph() + + console.log('TimedMode.js rendered') + + return ( + <> + +
+ + + ); + +} + +export default React.memo(TimedMode); diff --git a/src/app-modes/practice.js b/src/app-modes/practice.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/components/ChallengeWord.js b/src/components/ChallengeWord.js new file mode 100644 index 0000000..19647bf --- /dev/null +++ b/src/components/ChallengeWord.js @@ -0,0 +1,13 @@ +import React from "react" + +function ChallengeWord(props) { + console.log('ChallengeWord rendered'); + + let spannedWord = props.word.split('').map((letter,index) => {letter}) + + return ( +
{spannedWord}
+ ) +} + +export default React.memo(ChallengeWord) \ No newline at end of file diff --git a/src/components/GameClock.js b/src/components/GameClock.js new file mode 100644 index 0000000..f52ae76 --- /dev/null +++ b/src/components/GameClock.js @@ -0,0 +1,15 @@ +import React, {useState} from "react" + +function GameClock(props) { + console.log('GameClock rendered'); + + // const [clockTime, setClockTime] = useState(30) + + // setClockTime(props.time) + + return ( +
{props.time}
+ ) +} + +export default GameClock \ No newline at end of file diff --git a/src/components/ModePicker.js b/src/components/ModePicker.js index c1ff0f6..9339520 100644 --- a/src/components/ModePicker.js +++ b/src/components/ModePicker.js @@ -1,18 +1,27 @@ -import React from "react" +import React, {useContext} from "react" +import {GameModeContext} from "../gameContext" function ModePicker() { + + const {switchGameModeTo} = useContext(GameModeContext) + + function handleClick(e) { + switchGameModeTo(e.target.id) + console.log("Switched to " + e.target.id + " mode."); + } + return ( -
-
- Practice +
+ + +
-
- Timed Mode -
-
- Challenge Mode -
-
) } diff --git a/src/components/MorseButton.js b/src/components/MorseButton.js index 66aaadd..1cba5f6 100644 --- a/src/components/MorseButton.js +++ b/src/components/MorseButton.js @@ -7,4 +7,4 @@ function MorseButton() { ) } -export default MorseButton \ No newline at end of file +export default React.memo(MorseButton) \ No newline at end of file diff --git a/src/css/App.css b/src/css/App.css index 4e2edcc..9bcbe52 100644 --- a/src/css/App.css +++ b/src/css/App.css @@ -26,15 +26,18 @@ html, body { display: -webkit-box; display: -ms-flexbox; display: flex; + -ms-flex-item-align: center; + align-self: center; } -#mode-picker div { +#mode-picker button { background: #fcfcfc; margin: 15px; padding: 5px; -webkit-box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2); box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2); - border-radius: 5px; + border: 0px; + border-radius: 0px; } #main-content { @@ -60,6 +63,33 @@ html, body { align-self: center; } +#challengeWord { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + height: 50px; + margin-bottom: 20px; + padding-left: 10px; + padding-right: 10px; + width: -webkit-fit-content; + width: -moz-fit-content; + width: fit-content; + display: flex; + font-size: 40px; + font-family: 'Courier'; + font-weight: bold; + background: #EEE; + text-transform: uppercase; + -ms-flex-item-align: center; + align-self: center; +} + #morseBufferDisplay { display: -webkit-box; display: -ms-flexbox; @@ -93,7 +123,7 @@ html, body { height: 40px; width: 30px !important; margin-left: 3px; - border-radius: 5px; + border-radius: 0px; -webkit-box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2); box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2); display: -webkit-box; @@ -197,7 +227,7 @@ html, body { margin: 3px; background: #EEE; white-space: nowrap; - border-radius: 5px; + border-radius: 0px; -webkit-box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2); box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2); line-height: 2rem; diff --git a/src/css/App.css.map b/src/css/App.css.map index d2d75c2..a4c3262 100644 --- a/src/css/App.css.map +++ b/src/css/App.css.map @@ -1,6 +1,6 @@ { "version": 3, - "mappings": "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", + "mappings": "AAKA,AAAA,IAAI,EAAE,IAAI,CAAC;EACP,MAAM,EAAE,GAAG;EACX,OAAO,EAAE,GAAG;EACZ,MAAM,EAAE,IAAI;EACZ,KAAK,EAAE,IAAI;EACX,UAAU,EAAE,UAAU;EACtB,UAAU,EAXE,IAAI;CAYnB;;AACD,AAAA,KAAK,CAAC;EACF,MAAM,EAAE,IAAI;EACZ,OAAO,EAAE,IAAI;EACb,eAAe,EAAE,MAAM;EACvB,WAAW,EAAE,MAAM;EACnB,KAAK,EAAE,IAAI;CACd;;AACD,AAAA,YAAY,CAAC;EACT,OAAO,EAAE,IAAI;EACb,UAAU,EAAE,MAAM;CASrB;;AAXD,AAGI,YAHQ,CAGR,MAAM,CAAC;EACH,UAAU,EAAE,OAAO;EACnB,MAAM,EAAE,IAAI;EACZ,OAAO,EAAE,GAAG;EACZ,UAAU,EA1BA,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,kBAAkB;EA2BxC,MAAM,EAAE,GAAG;EACX,aAAa,EA3BA,GAAG;CA4BnB;;AAEL,AAAA,aAAa,CAAC;EACV,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,aAAa;EACrB,OAAO,EAAE,IAAI;EACb,cAAc,EAAE,MAAM;CACzB;;AAED,AAAA,YAAY,CAAC;EACT,KAAK,EAAE,gBAAgB;EACvB,MAAM,EAAE,KAAK;EACb,MAAM,EAAE,IAAI;EACZ,UAAU,EAAE,SAAS;EACrB,aAAa,EAAE,GAAG;EAClB,UAAU,EAAE,MAAM;CACrB;;AACD,AAAA,cAAc,CAAC;EACX,OAAO,EAAE,IAAI;EACb,eAAe,EAAE,MAAM;EACvB,WAAW,EAAE,MAAM;EAEnB,MAAM,EAAE,IAAI;EACZ,aAAa,EAAE,IAAI;EACnB,YAAY,EAAE,IAAI;EAClB,aAAa,EAAE,IAAI;EACnB,KAAK,EAAE,WAAW;EAClB,OAAO,EAAE,IAAI;EACb,SAAS,EAAE,IAAI;EACf,WAAW,EAAE,SAAS;EACtB,WAAW,EAAE,IAAI;EACjB,UAAU,EAAE,IAAI;EAChB,cAAc,EAAE,SAAS;EACzB,UAAU,EAAE,MAAM;CACrB;;AACD,AAAA,mBAAmB,CAAC;EAChB,OAAO,EAAE,IAAI;EACb,eAAe,EAAE,MAAM;EAEvB,KAAK,EAAE,IAAI;EACX,MAAM,EAAE,IAAI;EACZ,aAAa,EAAE,IAAI;CA8CtB;;AApDD,AAQI,mBARe,CAQf,QAAQ,CAAC;EACL,KAAK,EAAE,GAAG;EACV,aAAa,EAAE,GAAG;EAClB,YAAY,EAAE,cAAc;EAC5B,OAAO,EAAE,IAAI;EACb,cAAc,EAAE,GAAG;EACnB,eAAe,EAAE,QAAQ;CAiB5B;;AA/BL,AAgBQ,mBAhBW,CAQf,QAAQ,CAQJ,OAAO,CAAC;EACJ,UAAU,EAAE,IAAI;EAChB,MAAM,EAAE,IAAI;EACZ,KAAK,EAAE,eAAe;EACtB,WAAW,EAAE,GAAG;EAEhB,aAAa,EArFJ,GAAG;EAsFZ,UAAU,EAvFJ,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,kBAAkB;EAwFpC,OAAO,EAAE,IAAI;EACb,eAAe,EAAE,MAAM;EACvB,WAAW,EAAE,MAAM;EACnB,SAAS,EAAE,IAAI;EACf,WAAW,EAAE,SAAS;EACtB,WAAW,EAAE,IAAI;CACpB;;AA9BT,AAiCI,mBAjCe,CAiCf,uBAAuB,CAAC;EACpB,WAAW,EAAE,cAAc;EAC3B,YAAY,EAAE,GAAG;EACjB,KAAK,EAAE,GAAG;EACV,OAAO,EAAE,IAAI;CAchB;;AAnDL,AAuCQ,mBAvCW,CAiCf,uBAAuB,CAMnB,aAAa,CAAC;EACV,SAAS,EAAE,IAAI;EACf,WAAW,EAAE,SAAS;EACtB,WAAW,EAAE,IAAI;EACjB,gBAAgB,EAAE,WAAW;CAOhC;;AAlDT,AA6CY,mBA7CO,CAiCf,uBAAuB,CAMnB,aAAa,AAMR,YAAY,CAAC;EACV,YAAY,EAAE,GAAG;EACjB,aAAa,EAAE,GAAG;CAErB;;AAKb,AAAA,MAAM,CAAC;EACH,SAAS,EAAE,IAAI;CAClB;;AACD,AAAA,WAAW,CAAC;EACR,UAAU,EAAE,oBAAiB;EAC7B,KAAK,EAAE,OAAc;EACrB,aAAa,EAAE,GAAG;CACrB;;AAED,AAAA,aAAa,CAAC;EACV,6BAA6B;EAC7B,OAAO,EAAE,IAAI;EACb,cAAc,EAAE,MAAM;CAiBzB;;AApBD,AAKI,aALS,CAKT,UAAU,AAAA,UAAW,CAAA,CAAC,EAAC;EACnB,OAAO,EAAE,IAAI;CAChB;;AAPL,AAQI,aARS,CAQT,UAAU,AAAA,UAAW,CAAA,CAAC,EAAC;EACnB,OAAO,EAAE,GAAG;CACf;;AAVL,AAWI,aAXS,CAWT,UAAU,AAAA,UAAW,CAAA,CAAC,EAAC;EACnB,OAAO,EAAE,GAAG;CACf;;AAbL,AAcI,aAdS,CAcT,UAAU,AAAA,UAAW,CAAA,CAAC,EAAC;EACnB,OAAO,EAAE,GAAG;CACf;;AAhBL,AAiBI,aAjBS,CAiBT,UAAU,AAAA,UAAW,CAAA,CAAC,EAAC;EACnB,OAAO,EAAE,GAAG;CACf;;AAGL,AAAA,UAAU,CAAC;EACP,OAAO,EAAE,IAAI;EACb,cAAc,EAAE,cAAc;EAC9B,eAAe,EAAE,MAAM;EACvB,+BAA+B;EAC/B,aAAa,EAAE,GAAG;EAClB,SAAS,EAAE,MAAM;EACjB,WAAW,EAAE,SAAS;EACtB,WAAW,EAAE,IAAI;EACjB,kBAAkB;CA6BrB;;AAtCD,AAYI,UAZM,CAYN,GAAG,CAAC,GAAG,CAAC;EAEJ,MAAM,EAAE,OAAO;EACf,OAAO,EAAE,GAAG;EACZ,MAAM,EAAE,GAAG;EACX,UAAU,EAAE,IAAI;EAEhB,WAAW,EAAE,MAAM;EACnB,aAAa,EAxKA,GAAG;EAyKhB,UAAU,EA1KA,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,kBAAkB;EA2KxC,WAAW,EAAE,IAAI;CACpB;;AAvBL,AAwBI,UAxBM,CAwBN,kBAAkB,EAxBtB,UAAU,CAwBc,uBAAuB,CAAC;EACxC,OAAO,EAAE,IAAI;EACb,KAAK,EAAE,GAAG;CACb;;AA3BL,AA4BI,UA5BM,CA4BN,kBAAkB,CAAC;EACf,eAAe,EAAE,QAAQ;CAC5B;;AA9BL,AA+BI,UA/BM,CA+BN,QAAQ,CAAC;EAEL,OAAO,EAAE,IAAI;EACb,eAAe,EAAE,QAAQ;EACzB,WAAW,EAAE,MAAM;EACnB,aAAa,EAAE,UAAU;CAC5B", "sources": [ "../scss/App.scss" ], diff --git a/src/gameContext.js b/src/gameContext.js new file mode 100644 index 0000000..890527e --- /dev/null +++ b/src/gameContext.js @@ -0,0 +1,23 @@ +import React, {Component} from "react" +const GameModeContext = React.createContext() + +class GameModeContextProvider extends Component { + state = { + gameMode: "practice" + } + + switchGameModeTo = (mode = "practice") => { + this.setState({gameMode: mode}) + } + + render() { + return ( + + {this.props.children} + + ) + } + +} + +export {GameModeContextProvider, GameModeContext} diff --git a/src/hooks/usePracticeMode.js b/src/hooks/usePracticeMode.js new file mode 100644 index 0000000..2c3d965 --- /dev/null +++ b/src/hooks/usePracticeMode.js @@ -0,0 +1,30 @@ +import {useEffect} from "react" +import useTelegraph from './hooks/useTelegraph' + + +function usePracticeMode() { + const {morseCharBuffer, setMorseWords, morseWords, setMorseCharBuffer} = useTelegraph() + + const morseHistorySize = 5 + + useEffect(() => { + if (morseCharBuffer.slice(-1) === '/') { + // Remove forward slash + let val = morseCharBuffer.slice(0,morseCharBuffer.length-1) + console.log('val: ', val); + + setMorseWords(prev => [val, ...prev]) + + if (morseWords.length >= morseHistorySize) { + setMorseWords(prev => prev.slice(0,prev.length-1)) + } + + setMorseCharBuffer('') + } + + // setMorseLettersBuffer(prev => [...prev, morseCharBuffer]) + // eslint-disable-next-line + }, [morseCharBuffer]) +} + +export default usePracticeMode \ No newline at end of file diff --git a/src/hooks/useTelegraph.js b/src/hooks/useTelegraph.js new file mode 100644 index 0000000..d56fae7 --- /dev/null +++ b/src/hooks/useTelegraph.js @@ -0,0 +1,157 @@ +import {useState, useEffect} from 'react' + +function useTelegraph(mode = 'practice') { + + const [morseCharBuffer, setMorseCharBuffer] = useState('') // e.g. '-..' + const [morseWords, setMorseWords] = useState([]) // e.g. [['-..','.','-,'], ['...','---','...']] + + let charTimer = 0 + let charTime = 0 + let gapTimer = 0 + let gapTime = 0 + + const timingUnit = 15 // default: 25 + + const ditMaxTime = 5 // default: 3 + const letterGapMinTime = ditMaxTime*3 + const wordGapMaxTime = ditMaxTime*7 + const morseHistorySize = 5 + + // Tone Setup + let AudioContext = window.AudioContext || window.webkitAudioContext || false + let context + window.AudioContext = window.AudioContext || window.webkitAudioContext; + if (AudioContext) { + context = new AudioContext() + } else { + context = null + } + + let o + let frequency = 550.0 + + function clearHistory() { + setMorseWords([]) + } + + function handleInputStart(event) { + + event.preventDefault() + if ((event.keyCode !== 32 && event.target.id !== "morseButton") || + (event.repeat)) { + return + } + if (context.state === 'interrupted') { + context.resume() + } + + o = context.createOscillator() + o.frequency.value = frequency + o.type = "sine" + + let g = context.createGain() + g.gain.exponentialRampToValueAtTime(0.08, context.currentTime) + o.connect(g) + g.connect(context.destination) + o.start() + + checkGapBetweenInputs() + clearInterval(gapTimer) + + startCharTimer() + } + function startCharTimer() { + // Reset character time + charTime = 0 + // Start Character Timer + charTimer = setInterval(() => { + charTime += 1 + }, timingUnit); + } + + function handleInputEnd(event) { + event.preventDefault() + if ((event.keyCode !== 32 && event.target.id !== "morseButton") || + (event.repeat)) { + return + } + + if (charTime <= ditMaxTime) { + setMorseCharBuffer(prev => prev + '.') + } else { + setMorseCharBuffer(prev => prev + '-') + } + + stopCharTimer() + startGapTimer() + o.stop() + } + + function stopCharTimer() { + clearInterval(charTimer) + charTimer = 0 + } + + function startGapTimer() { + gapTime = 0 + gapTimer = setInterval(() => { + gapTime += 1 + + // Gap between words + if (gapTime >= wordGapMaxTime && mode === 'practice') { + setMorseCharBuffer(prev => prev + '/') + clearInterval(gapTimer) + gapTimer = 0 + gapTime = 0 + } + }, timingUnit); + } + + function checkGapBetweenInputs() { + // Check Gap between letters + if ((gapTime >= letterGapMinTime) && (gapTime < wordGapMaxTime)) { + setMorseCharBuffer(prev => prev + ' ') + clearInterval(gapTimer) + gapTimer = 0 + } + } + + useEffect(() => { + document.addEventListener('keydown', handleInputStart) + document.addEventListener('keyup', handleInputEnd) + + const morseButton = document.getElementById('morseButton') + morseButton.addEventListener('mousedown', handleInputStart) + morseButton.addEventListener('touchstart', handleInputStart) + morseButton.addEventListener('mouseup', handleInputEnd) + morseButton.addEventListener('touchend', handleInputEnd) + + return function cleanup() { + document.removeEventListener('keydown', handleInputStart) + document.removeEventListener('keyup', handleInputEnd) + } + // eslint-disable-next-line + }, []) + + useEffect(() => { + if (morseCharBuffer.slice(-1) === '/') { + // Remove forward slash + let val = morseCharBuffer.slice(0,morseCharBuffer.length-1) + console.log('val: ', val); + + setMorseWords(prev => [val, ...prev]) + + if (morseWords.length >= morseHistorySize) { + setMorseWords(prev => prev.slice(0,prev.length-1)) + } + + setMorseCharBuffer('') + } + + // eslint-disable-next-line + }, [morseCharBuffer]) + + return {morseCharBuffer, morseWords, clearHistory, setMorseCharBuffer, setMorseWords} +} + +export default useTelegraph \ No newline at end of file diff --git a/src/index.js b/src/index.js index 87d1be5..959578c 100644 --- a/src/index.js +++ b/src/index.js @@ -3,8 +3,13 @@ import ReactDOM from 'react-dom'; import './index.css'; import App from './App'; import * as serviceWorker from './serviceWorker'; +import {GameModeContextProvider} from "./gameContext" -ReactDOM.render(, document.getElementById('root')); +ReactDOM.render( + + + + , document.getElementById('root')); // If you want your app to work offline and load faster, you can change // unregister() to register() below. Note this comes with some pitfalls. diff --git a/src/scss/App.scss b/src/scss/App.scss index 8590253..acf4f19 100644 --- a/src/scss/App.scss +++ b/src/scss/App.scss @@ -1,6 +1,6 @@ $main-bg-color: #FFF; $main-box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2); -$main-border-radius: 5px; +$main-border-radius: 0px; // $border-radius-neumorphic: 0px -6px 10px rgba(255, 255, 255, 1), 0px 4px 15px rgba(0, 0, 0, 0.15); html, body { @@ -20,11 +20,13 @@ html, body { } #mode-picker { display: flex; - div { + align-self: center; + button { background: #fcfcfc; margin: 15px; padding: 5px; box-shadow: $main-box-shadow; + border: 0px; border-radius: $main-border-radius; } } @@ -43,7 +45,24 @@ html, body { border-radius: 50%; align-self: center; } - +#challengeWord { + display: flex; + justify-content: center; + align-items: center; + // border: 1px solid green; + height: 50px; + margin-bottom: 20px; + padding-left: 10px; + padding-right: 10px; + width: fit-content; + display: flex; + font-size: 40px; + font-family: 'Courier'; + font-weight: bold; + background: #EEE; + text-transform: uppercase; + align-self: center; +} #morseBufferDisplay { display: flex; justify-content: center;