Timeouts instead of promises, wpm progress, misc progress

This commit is contained in:
Gene Mecija 2020-01-24 23:32:08 -08:00
parent 90c9318bb4
commit 1d331a1d21
20 changed files with 690 additions and 127 deletions

20
package-lock.json generated
View file

@ -4538,6 +4538,15 @@
"utila": "~0.4"
}
},
"dom-helpers": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.1.3.tgz",
"integrity": "sha512-nZD1OtwfWGRBWlpANxacBEZrEuLa16o1nh7YopFWeoF68Zt8GGEmzHu6Xv4F3XaFIC+YXtTLrzgqKxFgLEe4jw==",
"requires": {
"@babel/runtime": "^7.6.3",
"csstype": "^2.6.7"
}
},
"dom-serializer": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
@ -11073,6 +11082,17 @@
"workbox-webpack-plugin": "4.3.1"
}
},
"react-transition-group": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.3.0.tgz",
"integrity": "sha512-1qRV1ZuVSdxPlPf4O8t7inxUGpdyO5zG9IoNfJxSO0ImU2A1YWkEQvFPuIPZmMLkg5hYs7vv5mMOyfgSkvAwvw==",
"requires": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
}
},
"read-pkg": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz",

View file

@ -8,7 +8,8 @@
"@testing-library/user-event": "^7.2.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-scripts": "3.3.0"
"react-scripts": "3.3.0",
"react-transition-group": "^4.3.0"
},
"scripts": {
"start": "react-scripts start",

View file

@ -12,12 +12,13 @@ import KeyTypePicker from './components/KeyTypePicker'
import WordListPicker from './components/WordListPicker';
import PracticeMode from './app-modes/PracticeMode';
import TimedMode from './app-modes/TimedMode'
import TrainingMode from './app-modes/TrainingMode'
import ChallengeMode from './app-modes/ChallengeMode'
import Header from './components/Header';
import Legend from './components/Legend';
// import GameClock from "./components/GameClock"
import WordsPerMinute from "./components/WordsPerMinute"
import MorseButtons from './components/MorseButtons'
import MorseBufferDisplay from './components/MorseBufferDisplay'
import MorseHistory from './components/MorseHistory'
@ -25,6 +26,7 @@ import MorseHistory from './components/MorseHistory'
import StraightKey from './components/StraightKey';
import ElectronicKey from './components/ElectronicKey';
import Footer from './components/Footer';
import { WPMContextProvider } from './contexts/wpmContext';
export default React.memo(function App() {
@ -32,39 +34,52 @@ export default React.memo(function App() {
const {gameMode} = useContext(GameModeContext)
const {keyType} = useContext(KeyTypeContext)
console.log('gameMode', gameMode);
console.log('keyType', keyType);
return (
<>
<Header />
<div id='main-content'>
<Legend />
<MorseBufferContextProvider>
<ModePicker />
<KeyTypePicker />
{gameMode === 'practice' &&
<>
{keyType === "straight" ?
<StraightKey gameMode='practice' /> : <ElectronicKey gameMode='practice' />}
<PracticeMode /><br/>
<MorseBufferDisplay /><br/>
<MorseHistory /><br/>
</>
}
{/* {gameMode === 'timed' && <TimedMode />} */}
{gameMode === 'challenge' &&
<>
<WordListPickerContextProvider>
<WordFeederContextProvider>
<WordListPicker />
{keyType === "straight" ?
<StraightKey gameMode='challenge' /> : <ElectronicKey gameMode='challenge' />}
<ChallengeMode />
</WordFeederContextProvider>
</WordListPickerContextProvider>
</>
}
<MorseButtons />
</MorseBufferContextProvider>
<WPMContextProvider>
<Legend />
<WordsPerMinute />
<MorseBufferContextProvider>
<ModePicker />
<KeyTypePicker />
{gameMode === 'practice' &&
<>
{keyType === "straight" ?
<StraightKey gameMode='practice' /> : <ElectronicKey gameMode='practice' />}
<PracticeMode /><br/>
<MorseBufferDisplay /><br/>
<MorseHistory /><br/>
</>
}
{gameMode === 'timed' &&
<>
{keyType === "straight" ?
<StraightKey gameMode='training' /> : <ElectronicKey gameMode='training' />}
<TrainingMode /><br/>
<MorseBufferDisplay /><br/>
<MorseHistory /><br/>
</>
}
{gameMode === 'challenge' &&
<>
<WordListPickerContextProvider>
<WordFeederContextProvider>
<WordListPicker />
{keyType === "straight" ?
<StraightKey gameMode='challenge' /> : <ElectronicKey gameMode='challenge' />}
<ChallengeMode />
</WordFeederContextProvider>
</WordListPickerContextProvider>
</>
}
<MorseButtons />
</MorseBufferContextProvider>
</WPMContextProvider>
</div>
<Footer />
</>

View file

@ -5,11 +5,11 @@ import GameClock from '../components/GameClock'
import MorseBufferDisplay from '../components/MorseBufferDisplay'
import MorseHistory from '../components/MorseHistory'
function TimedMode() {
function TrainingMode() {
const {morseCharBuffer, morseWords} = useStraightKey('timed')
console.log('TimedMode.js rendered')
console.log('TrainingMode.js rendered')
return (
<>
@ -21,4 +21,4 @@ function TimedMode() {
}
export default React.memo(TimedMode);
export default React.memo(TrainingMode);

View file

@ -2,9 +2,7 @@ import React from "react"
function DitDahDisplay(props) {
return (
<div className={`ditDah ${props.className}`}>
{props.dd}
</div>
<div className='ditDah'>{props.dd}</div>
)
}

View file

@ -13,7 +13,7 @@ function Legend() {
let newWord = convertWordToMorse(word)
console.log(newWord);
playMorseWord(newWord)
playMorseWord(newWord)
}
function convertWordToMorse(word) {
@ -37,6 +37,7 @@ function Legend() {
{legend}
<button id="test" onClick={handleClick}>Anya</button>
<button id="test" onClick={handleClick}>Alexandra</button>
<button id="test" onClick={handleClick}>Paris</button>
</div>
)
}

View file

@ -18,8 +18,8 @@ function ModePicker() {
<button id="practice" onClick={handleClick}>
Practice
</button>
<button id="timed" onClick={handleClick}>
Timed Mode
<button id="training" onClick={handleClick}>
Training Mode
</button>
<button id="challenge" onClick={handleClick}>
Challenge Mode

View file

@ -1,4 +1,4 @@
import React, { useContext } from "react"
import React, { useContext, useEffect } from "react"
import DitDahDisplay from "./DitDahDisplay"
import morseCode from '../data/morse-reverse.json'
import {MorseBufferContext} from "../contexts/morseBufferContext"

View file

@ -0,0 +1,44 @@
import React, {useContext, useState} from "react"
// import MorseCard from './MorseCard'
import morseCode from '../data/morse-reverse.json'
import {MorseBufferContext} from "../contexts/morseBufferContext"
export default (function MorseHistory() {
const {morseWords} = useContext(MorseBufferContext)
let text = ''
console.log('morseWords', morseWords);
morseWords.forEach((word) => {
if (word.includes(' ')) {
let newWord = ''
word.split(' ').forEach(letter => {
if (morseCode[letter] === undefined) {
newWord += '[?]'
console.log('undefined', letter);
} else {
console.log('here1');
newWord += morseCode[letter].toUpperCase()
}
})
text = newWord + ' ' + text
}
else if (morseCode[word] === undefined) {
text = '[?]' + text
console.log('undefined', word);
} else {
console.log('here2');
text = morseCode[word].toUpperCase() + ' ' + text
}
})
// try {
// document.getElementById('morseHistory').innerText = text
// } catch {}
return (
<div id="morseHistory">{text}</div>
)
})

View file

@ -0,0 +1,16 @@
import React, {useContext} from "react"
import { WPMContext } from "../contexts/wpmContext";
export default React.memo(function WordsPerMinute(props) {
console.log('WordsPerMinute rendered');
const {wpm, setWPM} = useContext(WPMContext)
function handleChange(e) {
setWPM(e.target.value)
}
return (
<input id='wpm-input' type='text' value={wpm} onChange={handleChange} />
)
})

View file

@ -8,5 +8,5 @@
"normal": 24,
"fast": 17
},
"historySize": 5
"historySize": 10
}

View file

@ -4,7 +4,7 @@ const KeyTypeContext = React.createContext()
function KeyTypeContextProvider(props) {
const [keyType, setKeyType] = useState('')
const [keyType, setKeyType] = useState('straight')
return (
<KeyTypeContext.Provider value={{

View file

@ -0,0 +1,14 @@
import React, {useState} from "react"
const WPMContext = React.createContext()
function WPMContextProvider(props) {
const [wpm, setWPM] = useState(15)
return (
<WPMContext.Provider value={{wpm: wpm, setWPM: setWPM}}>
{props.children}
</WPMContext.Provider>
)
}
export {WPMContextProvider, WPMContext}

View file

@ -8,7 +8,7 @@
html, body {
height: 100%;
width: 100%;
background: #444;
background: #FFF;
}
#root {
@ -41,6 +41,9 @@ header {
flex-direction: column;
width: 95vw;
border: 1px solid red;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
}
.mode-picker {
@ -58,7 +61,7 @@ header {
-webkit-box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2);
box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2);
border: 0px;
border-radius: 0px;
border-radius: 5px;
}
.mode-picker .selected {
@ -74,9 +77,9 @@ header {
-webkit-box-pack: space-evenly;
-ms-flex-pack: space-evenly;
justify-content: space-evenly;
position: absolute;
-ms-flex-item-align: left;
align-self: left;
position: fixed;
top: 50px;
left: 50px;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
}
@ -201,7 +204,123 @@ header {
background: rgba(0, 200, 0, 0.7);
}
#morseBufferDisplay, #challengeBufferDisplay {
#morseBufferDisplay {
border: 1px solid green;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-orient: vertical;
-webkit-box-direction: reverse;
-ms-flex-direction: column-reverse;
flex-direction: column-reverse;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
width: 100%;
height: 150px;
margin-bottom: 20px;
}
#morseBufferDisplay #alphanumeric-container {
background: #ccc;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
padding-left: 5px;
height: 50px;
min-width: 50px;
margin-bottom: 10px;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
border-radius: 5px;
-webkit-box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2);
box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2);
}
#morseBufferDisplay #alphanumeric-container #alphanumeric {
font-size: 40px;
font-family: 'Courier';
font-weight: bold;
background-color: transparent;
}
#morseBufferDisplay #alphanumeric-container #alphanumeric:first-child {
padding-left: 5px;
padding-right: 5px;
}
#morseBufferDisplay #alphanumeric-container #alphanumeric span {
padding: 4px;
-webkit-transition: background 300ms ease-in-out;
transition: background 300ms ease-in-out;
}
#morseBufferDisplay #ditDahs {
width: 50%;
height: 50px;
padding-right: 5px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
font-size: 25px;
font-family: 'Courier';
font-weight: bold;
}
#morseBufferDisplay #ditDahs span {
padding: 4px;
-webkit-transition: background 100ms ease-in-out;
transition: background 100ms ease-in-out;
}
#morseBufferDisplay #ditDahs .ditDah {
background: #DDD;
height: 40px;
width: 30px !important;
margin-left: 3px;
border-radius: 5px;
-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;
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;
opacity: 50%;
-webkit-transition: all 100ms ease-in-out;
transition: all 100ms ease-in-out;
}
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
-webkit-transition: opacity .5s ease-in;
transition: opacity .5s ease-in;
}
#challengeBufferDisplay {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
@ -217,10 +336,11 @@ header {
-ms-flex-align: center;
align-items: center;
width: 100%;
height: 150px;
margin-bottom: 20px;
}
#morseBufferDisplay #alphanumeric-container, #challengeBufferDisplay #alphanumeric-container {
#challengeBufferDisplay #alphanumeric-container {
padding-left: 5px;
width: 50%;
display: -webkit-box;
@ -231,7 +351,7 @@ header {
justify-content: center;
}
#morseBufferDisplay #alphanumeric-container #alphanumeric, #challengeBufferDisplay #alphanumeric-container #alphanumeric {
#challengeBufferDisplay #alphanumeric-container #alphanumeric {
font-size: 40px;
font-family: 'Courier';
font-weight: bold;
@ -239,18 +359,18 @@ header {
margin-bottom: 10px;
}
#morseBufferDisplay #alphanumeric-container #alphanumeric:first-child, #challengeBufferDisplay #alphanumeric-container #alphanumeric:first-child {
#challengeBufferDisplay #alphanumeric-container #alphanumeric:first-child {
padding-left: 5px;
padding-right: 5px;
}
#morseBufferDisplay #alphanumeric-container #alphanumeric span, #challengeBufferDisplay #alphanumeric-container #alphanumeric span {
#challengeBufferDisplay #alphanumeric-container #alphanumeric span {
padding: 4px;
-webkit-transition: background 300ms ease-in-out;
transition: background 300ms ease-in-out;
}
#morseBufferDisplay #ditDahs, #challengeBufferDisplay #ditDahs {
#challengeBufferDisplay #ditDahs {
width: 50%;
padding-right: 5px;
display: -webkit-box;
@ -268,18 +388,17 @@ header {
font-weight: bold;
}
#morseBufferDisplay #ditDahs span, #challengeBufferDisplay #ditDahs span {
#challengeBufferDisplay #ditDahs span {
padding: 4px;
-webkit-transition: background 100ms ease-in-out;
transition: background 100ms ease-in-out;
}
#morseBufferDisplay #ditDahs .ditDah, #challengeBufferDisplay #ditDahs .ditDah {
background: #DDD;
#challengeBufferDisplay #ditDahs .ditDah {
height: 40px;
width: 30px !important;
margin-left: 3px;
border-radius: 0px;
border-radius: 5px;
-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;
@ -313,8 +432,7 @@ header {
border-radius: 5px;
}
#morseDisplay {
/* border: 1px solid blue; */
#morseHistory {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
@ -324,23 +442,43 @@ header {
flex-direction: column;
}
#morseDisplay .morseCard:nth-child(1) {
#morseHistory .morseCard:nth-child(1) {
opacity: 100%;
}
#morseDisplay .morseCard:nth-child(2) {
#morseHistory .morseCard:nth-child(2) {
opacity: 90%;
}
#morseHistory .morseCard:nth-child(3) {
opacity: 80%;
}
#morseHistory .morseCard:nth-child(4) {
opacity: 70%;
}
#morseDisplay .morseCard:nth-child(3) {
#morseHistory .morseCard:nth-child(5) {
opacity: 60%;
}
#morseHistory .morseCard:nth-child(6) {
opacity: 50%;
}
#morseDisplay .morseCard:nth-child(4) {
#morseHistory .morseCard:nth-child(7) {
opacity: 40%;
}
#morseHistory .morseCard:nth-child(8) {
opacity: 30%;
}
#morseDisplay .morseCard:nth-child(5) {
#morseHistory .morseCard:nth-child(9) {
opacity: 20%;
}
#morseHistory .morseCard:nth-child(10) {
opacity: 10%;
}
@ -357,9 +495,8 @@ header {
justify-content: center;
/* border: 1px solid orange; */
margin-bottom: 2px;
font-size: 2.5rem;
font-size: 1.5rem;
font-family: 'Courier';
font-weight: bold;
/* width: 100%; */
}
@ -369,10 +506,10 @@ header {
margin: 3px;
background: #EEE;
white-space: nowrap;
border-radius: 0px;
border-radius: 5px;
-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;
line-height: 1rem;
}
.morseCard .ditDahs-container, .morseCard .alphanumeric-container {
@ -380,6 +517,7 @@ header {
display: -ms-flexbox;
display: flex;
width: 50%;
font-weight: bold;
}
.morseCard .ditDahs-container {

File diff suppressed because one or more lines are too long

View file

@ -1,17 +1,19 @@
import {useEffect, useContext} from 'react'
import {MorseBufferContext} from '../contexts/morseBufferContext'
import config from '../config.json'
import { WPMContext } from '../contexts/wpmContext'
// ELECTRONIC KEY TELEGRAPH - Iambic A
function useElectronicKey(gameMode) {
const {morseCharBuffer, setMorseCharBuffer, morseWords, setMorseWords} = useContext(MorseBufferContext)
const {wpm} = useContext(WPMContext)
const timingUnit = config.timingUnit
let ratio = .2
const ditMaxTime = 70 // ditMaxTime * 0.365 to get ms, e.g. 85 * 0.365 ~= 31ms
const ditMaxTime = 1200/wpm // ditMaxTime * 0.365 to get ms, e.g. 85 * 0.365 ~= 31ms
const letterGapMinTime = ditMaxTime*ratio*3 //config.practiceSpeed.normal*3
const wordGapMaxTime = ditMaxTime*ratio*7 // config.practiceSpeed.normal*7
@ -31,6 +33,8 @@ function useElectronicKey(gameMode) {
// let gapTimerRunning = false
let paddlesReleasedSimultaneously = false
let insideBufferDisplay = false
// function consoleLogVars() {
// // Log variables (Debug tool)
// console.log('<VARS>');
@ -226,13 +230,30 @@ function useElectronicKey(gameMode) {
}
}
const bufferDisplay = ['morseBufferDisplay', 'challengeBufferDisplay', 'ditDahs', 'alphanumeric-container']
function handleInputStart(event) {
event.preventDefault()
// if (event.keyCode === 71) {
// queue = ['.',' ','.',' ','.',' ','.',' ','-','.','.','.','.','-']
// executeQueue()
// }
console.log(event.target);
if (event.type === 'mousedown' && event.target.className !== 'paddle') {
if (bufferDisplay.includes(event.target.id)) {
document.addEventListener('keydown', handleInputStart)
document.addEventListener('keyup', handleInputEnd)
insideBufferDisplay = true
console.log('insideBufferDisplay', insideBufferDisplay);
} else {
document.removeEventListener('keydown', handleInputStart)
document.removeEventListener('keyup', handleInputEnd)
insideBufferDisplay = false
console.log('insideBufferDisplay', insideBufferDisplay);
if (event.target.id === 'wpm-input') {
event.target.focus()
} else {
document.activeElement.blur()
}
}
}
paddlesReleasedSimultaneously = false
@ -261,6 +282,8 @@ function useElectronicKey(gameMode) {
function handleInputEnd(event) {
event.preventDefault()
if (!insideBufferDisplay) {return}
if (event.keyCode === 188 || event.target.id === "left") {
leftIsPressed = false
@ -320,6 +343,7 @@ function useElectronicKey(gameMode) {
useEffect(() => {
document.addEventListener('keydown', handleInputStart)
document.addEventListener('keyup', handleInputEnd)
document.addEventListener('mousedown', handleInputStart)
const paddles = document.querySelectorAll('.paddle')
paddles.forEach(paddle => {
@ -333,6 +357,7 @@ function useElectronicKey(gameMode) {
return function cleanup() {
document.removeEventListener('keydown', handleInputStart)
document.removeEventListener('keyup', handleInputEnd)
document.removeEventListener('mousedown', handleInputStart)
const paddles = document.querySelectorAll('.paddle')
paddles.forEach(paddle => {

View file

@ -0,0 +1,84 @@
import config from '../config.json'
function useMorsePlayer() {
const ditMaxTime = 85 //config.ditMaxTime
// Tone Setup
let AudioContext = window.AudioContext || window.webkitAudioContext
window.AudioContext = window.AudioContext || window.webkitAudioContext;
let context
if (AudioContext) {
context = new AudioContext()
} else {
context = null
}
let frequency = config.frequency
function play(ditDah) {
let length = ((ditDah === '.') ? ditMaxTime : ditMaxTime*3)
// length = 1
return new Promise((resolve, reject) => {
if (context.state === 'interrupted') {
context.resume()
}
let o
o = context.createOscillator()
o.frequency.value = frequency
o.type = "sine"
o.onended = () => {
resolve()
}
let startTime = context.currentTime;
let g = context.createGain()
g.gain.exponentialRampToValueAtTime(config.mainVolume, startTime)
g.gain.setValueAtTime(config.mainVolume, startTime)
o.connect(g)
g.connect(context.destination)
o.start(startTime)
// g.gain.setTargetAtTime(0.0001, startTime + length/1000, 0.001)
// o.stop(startTime + length/1000 + 0.05)
setTimeout(() => {
g.gain.setTargetAtTime(0.0001, context.currentTime, 0.009)
o.stop(context.currentTime + 0.05)
}, length)
})
}
function playMorseWord(morse) {
let chars = Array.from(morse)
let currentPromise = Promise.resolve();
for (let i = 0; i < chars.length; i++) {
currentPromise = currentPromise.then(() => {
return playChar(chars[i]);
});
}
function playChar(char) {
let delay = (char === '.') ? ditMaxTime + ditMaxTime : ditMaxTime*3 + ditMaxTime
return new Promise(function(resolve) {
if (char === '.' || char === '-') {
play(char)
.then(setTimeout(() => {
resolve();
}, delay))
} else {
setTimeout(() => {
resolve();
}, delay)
}
});
}
}
return { playMorseWord }
}
export default useMorsePlayer

View file

@ -1,8 +1,12 @@
import config from '../config.json'
import { WPMContext } from '../contexts/wpmContext.js';
import { useContext } from 'react';
function useMorsePlayer() {
const ditMaxTime = 85 //config.ditMaxTime
const {wpm} = useContext(WPMContext)
// const ditMaxTime = 85 //config.ditMaxTime
const ditMaxTime = 1200/wpm
// Tone Setup
let AudioContext = window.AudioContext || window.webkitAudioContext
@ -20,44 +24,87 @@ function useMorsePlayer() {
let length = ((ditDah === '.') ? ditMaxTime : ditMaxTime*3)
// length = 1
return new Promise((resolve, reject) => {
if (context.state === 'interrupted') {
context.resume()
}
let o
o = context.createOscillator()
o.frequency.value = frequency
o.type = "sine"
o.onended = () => {
resolve()
}
let startTime = context.currentTime;
let g = context.createGain()
g.gain.exponentialRampToValueAtTime(config.mainVolume, startTime)
g.gain.setValueAtTime(config.mainVolume, startTime)
o.connect(g)
g.connect(context.destination)
o.start(startTime)
// g.gain.setTargetAtTime(0.0001, startTime + length/1000, 0.001)
// o.stop(startTime + length/1000 + 0.05)
setTimeout(() => {
g.gain.setTargetAtTime(0.0001, context.currentTime, 0.009)
o.stop(context.currentTime + 0.05)
}, length)
})
// return new Promise((resolve, reject) => {
if (context.state === 'interrupted') {
context.resume()
}
let o
o = context.createOscillator()
o.frequency.value = frequency
o.type = "sine"
// o.onended = () => {
// resolve()
// }
let startTime = context.currentTime;
let g = context.createGain()
g.gain.exponentialRampToValueAtTime(config.mainVolume, startTime)
g.gain.setValueAtTime(config.mainVolume, startTime)
o.connect(g)
g.connect(context.destination)
o.start(startTime)
// g.gain.setTargetAtTime(0.0001, startTime + length/1000, 0.001)
// o.stop(startTime + length/1000 + 0.05)
setTimeout(() => {
g.gain.setTargetAtTime(0.0001, context.currentTime, 0.009)
o.stop(context.currentTime + 0.05)
}, length)
// })
}
function playMorseWord(morse) {
let chars = Array.from(morse)
let currentPromise = Promise.resolve();
// let currentPromise = Promise.resolve();
let soundQueue = []
let delay = 0
let firstWord = true
for (let i = 0; i < chars.length; i++) {
currentPromise = currentPromise.then(() => {
return playChar(chars[i]);
});
// currentPromise = currentPromise.then(() => {
// return playChar(chars[i]);
// });
let char = chars[i]
if (char === '.') {
if (firstWord) {
firstWord = false
// soundQueue.push(
setTimeout(() => {
play(char)
}, 0)
// )
} else {
// soundQueue.push(
setTimeout(() => {
play(char)
}, delay)
// )
}
delay += ditMaxTime*2
} else if (char === '-') {
if (firstWord) {
firstWord = false
// soundQueue.push(
setTimeout(() => {
play(char)
}, 0)
// )
} else {
// soundQueue.push(
setTimeout(() => {
play(char)
}, delay)
// )
}
delay += ditMaxTime*4
} else if (char === ' ') {
setTimeout(() => {
}, delay)
delay += ditMaxTime*3
}
}
function playChar(char) {

View file

@ -1,11 +1,13 @@
import {useEffect, useContext} from 'react'
import {MorseBufferContext} from '../contexts/morseBufferContext'
import config from '../config.json'
import { WPMContext } from '../contexts/wpmContext'
// STRAIGHT KEY TELEGRAPH
function useStraightKey(gameMode) {
const {morseCharBuffer, setMorseCharBuffer, morseWords, setMorseWords} = useContext(MorseBufferContext)
const {wpm} = useContext(WPMContext)
let charTimer = 0
let charTime = 0
@ -14,10 +16,12 @@ function useStraightKey(gameMode) {
const timingUnit = config.timingUnit
const ditMaxTime = config.practiceSpeed.normal
const ditMaxTime = 1200/wpm
const letterGapMinTime = ditMaxTime*3
const wordGapMaxTime = ditMaxTime*7
const morseHistorySize = config.historySize
let insideBufferDisplay = false
// Tone Setup
let AudioContext = window.AudioContext || window.webkitAudioContext || false
@ -39,12 +43,36 @@ function useStraightKey(gameMode) {
setMorseWords([])
}
const bufferDisplay = ['morseBufferDisplay', 'challengeBufferDisplay', 'ditDahs', 'alphanumeric-container']
function handleInputStart(event) {
event.preventDefault()
console.log(event.target);
if (event.type === 'mousedown' && event.target.className !== 'paddle') {
if (bufferDisplay.includes(event.target.id)) {
document.addEventListener('keydown', handleInputStart)
document.addEventListener('keyup', handleInputEnd)
insideBufferDisplay = true
console.log('insideBufferDisplay', insideBufferDisplay);
} else {
document.removeEventListener('keydown', handleInputStart)
document.removeEventListener('keyup', handleInputEnd)
insideBufferDisplay = false
console.log('insideBufferDisplay', insideBufferDisplay);
if (event.target.id === 'wpm-input') {
event.target.focus()
} else {
document.activeElement.blur()
}
}
}
if (isRunning) {
console.log('insideBufferDisplay', insideBufferDisplay);
return
} else {
} else if (insideBufferDisplay === true) {
if ((event.keyCode !== 32 &&
event.target.id !== "morseButton" &&
event.target.className !== "paddle") ||
@ -72,6 +100,8 @@ function useStraightKey(gameMode) {
clearInterval(gapTimer)
startCharTimer()
} else {
return
}
}
@ -86,6 +116,12 @@ function useStraightKey(gameMode) {
function handleInputEnd(event) {
event.preventDefault()
// if (event.target.id !== 'morseBufferDisplay') {
// insideBufferDisplay = true
// console.log('insideBufferDisplay', insideBufferDisplay);
// }
if (!insideBufferDisplay) {return}
if (isRunning) {
if ((event.keyCode !== 32 &&
event.target.id !== "morseButton" &&
@ -153,9 +189,15 @@ function useStraightKey(gameMode) {
}
}
useEffect(() => {
// const buffer = document.getElementById('morseBufferDisplay')
// document.addEventListener('mousedown', morseOn)
// document.addEventListener('mousedown', morseOff)
document.addEventListener('keydown', handleInputStart)
document.addEventListener('keyup', handleInputEnd)
document.addEventListener('mousedown', handleInputStart)
const paddles = document.querySelectorAll('.paddle')
paddles.forEach(paddle => {
@ -167,8 +209,13 @@ function useStraightKey(gameMode) {
})
return function cleanup() {
// const buffer = document.getElementById('morseBufferDisplay')
// document.removeEventListener('mousedown', morseOn)
// document.removeEventListener('mousedown', morseOff)
document.removeEventListener('keydown', handleInputStart)
document.removeEventListener('keyup', handleInputEnd)
document.removeEventListener('mouseUp', handleInputStart)
const paddles = document.querySelectorAll('.paddle')
paddles.forEach(paddle => {

View file

@ -1,6 +1,6 @@
$main-bg-color: #FFF;
$main-box-shadow: 0px 3px 3px rgba(0, 0, 0, 0.2);
$main-border-radius: 0px;
$main-border-radius: 5px;
$border-radius-neumorphic: 0px -6px 10px rgba(255, 255, 255, 1), 0px 4px 15px rgba(0, 0, 0, 0.15);
$border-radius-neumorphic-active: 0 15px 20px rgba(0, 0, 0, 0.015), inset 0px -2px 5px rgb(255, 255, 255), inset 0px 2px 5px rgba(0, 0, 0, 0.15);
* {
@ -11,8 +11,8 @@ $border-radius-neumorphic-active: 0 15px 20px rgba(0, 0, 0, 0.015), inset 0px -2
html, body {
height: 100%;
width: 100%;
// background: $main-bg-color;
background: #444;
background: $main-bg-color;
// background: #444;
}
#root {
height: 100%;
@ -31,6 +31,7 @@ header {
flex-direction: column;
width: 95vw;
border: 1px solid red;
align-items: center;
}
footer {
@ -56,8 +57,9 @@ footer {
width: 450px;
display: flex;
justify-content: space-evenly;
position: absolute;
align-self: left;
position: fixed;
top: 50px;
left: 50px;
flex-wrap: wrap;
div {
@ -158,14 +160,103 @@ footer {
#morseBufferDisplay, #challengeBufferDisplay {
#morseBufferDisplay {
border: 1px solid green;
display: flex;
justify-content: center;
flex-direction: column-reverse;
align-items: center;
width: 100%;
height: 150px;
margin-bottom: 20px;
#alphanumeric-container {
// border-left: 2px solid #000;
background: #ccc;
display: flex;
padding-left: 5px;
height: 50px;
min-width: 50px;
margin-bottom: 10px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 5px;
box-shadow: $main-box-shadow;
#alphanumeric {
font-size: 40px;
font-family: 'Courier';
font-weight: bold;
background-color: transparent;
&:first-child {
padding-left: 5px;
padding-right: 5px;
// box-shadow: $main-box-shadow;
}
span {
padding: 4px;
transition: background 300ms ease-in-out;
}
}
}
#ditDahs {
width: 50%;
height: 50px;
padding-right: 5px;
// border-right: 2px solid #000;
display: flex;
flex-direction: row;
justify-content: center;
font-size: 25px;
font-family: 'Courier';
font-weight: bold;
span {
padding: 4px;
transition: background 100ms ease-in-out;
}
.ditDah {
background: #DDD;
height: 40px;
width: 30px !important;
margin-left: 3px;
// box-shadow: 0px 1px 0px #000;
border-radius: $main-border-radius;
box-shadow: $main-box-shadow;
display: flex;
justify-content: center;
align-items: center;
opacity: 50%;
transition: all 100ms ease-in-out;
}
}
}
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
#challengeBufferDisplay {
display: flex;
justify-content: center;
border: 1px solid green;
flex-direction: column;
align-items: center;
width: 100%;
// height: 40px;
height: 150px;
margin-bottom: 20px;
#alphanumeric-container {
@ -210,9 +301,9 @@ footer {
padding: 4px;
transition: background 100ms ease-in-out;
}
.ditDah {
background: #DDD;
// background: #DDD;
height: 40px;
width: 30px !important;
margin-left: 3px;
@ -222,7 +313,6 @@ footer {
display: flex;
justify-content: center;
align-items: center;
}
}
}
@ -246,8 +336,16 @@ footer {
border-radius: 5px;
}
#morseDisplay {
/* border: 1px solid blue; */
// #morseHistory {
// border: 1px solid purple;
// padding: 5px;
// width: 650px;
// font-family: 'Verdana';
// font-size: 1.5em;
// }
#morseHistory {
// /* border: 1px solid blue; */
display: flex;
flex-direction: column;
@ -255,15 +353,30 @@ footer {
opacity: 100%;
}
.morseCard:nth-child(2){
opacity: 70%;
opacity: 90%;
}
.morseCard:nth-child(3){
opacity: 50%;
opacity: 80%;
}
.morseCard:nth-child(4){
opacity: 30%;
opacity: 70%;
}
.morseCard:nth-child(5){
opacity: 60%;
}
.morseCard:nth-child(6){
opacity: 50%;
}
.morseCard:nth-child(7){
opacity: 40%;
}
.morseCard:nth-child(8){
opacity: 30%;
}
.morseCard:nth-child(9){
opacity: 20%;
}
.morseCard:nth-child(10){
opacity: 10%;
}
}
@ -274,9 +387,8 @@ footer {
justify-content: center;
/* border: 1px solid orange; */
margin-bottom: 2px;
font-size: 2.5rem;
font-size: 1.5rem;
font-family: 'Courier';
font-weight: bold;
/* width: 100%; */
@ -290,11 +402,12 @@ footer {
white-space: nowrap;
border-radius: $main-border-radius;
box-shadow: $main-box-shadow;
line-height: 2rem;
line-height: 1rem;
}
.ditDahs-container, .alphanumeric-container {
display: flex;
width: 50%;
font-weight: bold;
}
.ditDahs-container {
justify-content: flex-end;