Challenge mode functionality updates

This commit is contained in:
Gene Mecija 2020-02-10 00:26:04 -08:00
parent e683a4a824
commit dd077e6d12
16 changed files with 217 additions and 132 deletions

View file

@ -9,6 +9,7 @@ import { GameClockContextProvider } from './contexts/gameClockContext';
import { WPMContextProvider } from './contexts/wpmContext';
import { FrequencyContextProvider } from './contexts/frequencyContext';
import { KeyTypeContextProvider } from './contexts/keyTypeContext';
import { ChallengeContextProvider } from './contexts/challengeContext';
import PracticeMode from './app-modes/PracticeMode';
import ChallengeMode from './app-modes/ChallengeMode'
@ -40,6 +41,7 @@ export default React.memo(function App() {
<WordListPickerContextProvider>
<WordFeederContextProvider>
<GameClockContextProvider>
<ChallengeContextProvider>
<SidebarLeft />
<div id="main-interface">
<div id="mainOptions">
@ -80,6 +82,7 @@ export default React.memo(function App() {
<div id="settings-icon" onClick={toggleRight}><i class="ri-settings-3-line"></i></div>
</div> */}
</ChallengeContextProvider>
</GameClockContextProvider>
</WordFeederContextProvider>
</WordListPickerContextProvider>

View file

@ -1,60 +1,45 @@
import React, {useContext, useEffect, useState} from 'react';
import React, {useContext} from 'react';
import '../css/App.css';
import morseCode from '../data/morse-reverse.json'
import ChallengeWord from '../components/ChallengeWord'
import ChallengeBufferDisplay from '../components/ChallengeBufferDisplay';
import { MorseBufferContext } from '../contexts/morseBufferContext';
import { WordFeederContext } from '../contexts/wordFeederContext';
import { WordListPickerContext } from '../contexts/wordListPickerContext';
import GameClock from '../components/GameClock';
import { GameClockContext } from '../contexts/gameClockContext';
import { KeyTypeContext } from '../contexts/keyTypeContext';
import StraightKey from '../components/StraightKey';
import ElectronicKey from '../components/ElectronicKey';
import ChallengeControls from '../components/ChallengeControls';
import { ChallengeContext } from '../contexts/challengeContext';
export default React.memo(function ChallengeMode(props) {
const {stopGameClock, clockIsRunning, intervals} = useContext(GameClockContext)
const {word, getNextWord, resetFeeder} = useContext(WordFeederContext)
console.log('CHALLENGE MODE');
const {word, getNextWord} = useContext(WordFeederContext)
const {clockIsRunning} = useContext(GameClockContext)
const {morseCharBuffer, setMorseCharBuffer} = useContext(MorseBufferContext)
const {keyType} = useContext(KeyTypeContext)
const {completeChallenge, cancelChallenge} = useContext(ChallengeContext)
const [challengeStarted, setChallengeStarted] = useState(false)
let morseArray = morseCharBuffer.split('_').filter(l => l !== '')
let challengeWordClass = ''
let correctCharIndexes = [] // Indexes of correct letters in Challenge Word
// let incorrectCharIndex = null
let incorrectMorseIndexes = [] // Indexes of incorrect morse characters in morse character buffer
let offset = 0
let challengeLetters
if (!word) {
console.log('FINISHED ALL WORDS!')
stopChallenge()
// Show a card showing challenge completion stats and option to restart word list
try { alert(`Time: ${document.getElementById('gameClock').innerText}`) }
catch { return }
return
// If no more words in wordlist, feeder returns first word in an array
if (typeof word === 'object') {
completeChallenge()
challengeLetters = word[0].split('')
}
function stopChallenge() {
stopGameClock()
for (let i = 0; i < intervals.length; i++) {
clearInterval(intervals[i]);
}
resetFeeder()
else {
challengeLetters = word.split('')
}
let challengeLetters = word.split('')
// Iterate through the morse character buffer and compare with each letter of challenge word
morseArray.forEach((item, index) => {
@ -103,6 +88,7 @@ export default React.memo(function ChallengeMode(props) {
setMorseCharBuffer('')
morseArray = []
incorrectMorseIndexes = []
correctCharIndexes = []
offset = 0
}, 800)
setTimeout(() => {
@ -121,8 +107,8 @@ export default React.memo(function ChallengeMode(props) {
{clockIsRunning ? (keyType === "straight" ?
<StraightKey /> : <ElectronicKey />) : <></>
}
<ChallengeControls stopChallenge={stopChallenge} />
<GameClock stopChallenge={stopChallenge}/>
<ChallengeControls cancelChallenge={cancelChallenge} />
<GameClock />
<ChallengeWord className={challengeWordClass} word={word} />
<ChallengeBufferDisplay morseArray={morseArray} incorrectMorseIndexes={incorrectMorseIndexes} />
</>

View file

@ -0,0 +1,25 @@
import React, { useContext } from "react"
import { GameClockContext } from "../contexts/gameClockContext"
import { ChallengeContext } from "../contexts/challengeContext"
export default (function ChallengeComplete(props) {
const {gameClockTime} = useContext(GameClockContext)
const {cancelChallenge} = useContext(ChallengeContext)
function _continue() {
// setGameClockTime(0)
// props.setChallengeState('ready')
cancelChallenge()
}
return (
<div id="challengeComplete" className="notify">
<h2>Challenge Complete</h2>
Challenge completed in {gameClockTime} seconds!
<button id="continue" onClick={_continue}>Continue</button>
</div>
)
})

View file

@ -4,7 +4,7 @@ export default (function ChallengeControls(props) {
return (
<div id="challengeControls">
<button onClick={props.stopChallenge}>Exit</button>
<button onClick={props.cancelChallenge}>Exit</button>
</div>
)
})

View file

@ -1,43 +1,16 @@
import React, { useContext, useEffect } from "react"
import { GameClockContext } from "../contexts/gameClockContext"
import WordListPicker from "./WordListPicker"
// import { GameModeContext } from "../contexts/gameModeContext"
import React, { useContext } from "react"
import ChallengeReady from "./ChallengeReady"
import { ChallengeContext } from "../contexts/challengeContext"
import ChallengeComplete from "./ChallengeComplete"
export default (function ChallengeOverlay() {
const {startGameClock} = useContext(GameClockContext)
// const {gameMode} = useContext(GameModeContext)
function startChallenge(e) {
let countdown
let count = 3
document.getElementById('challengeReady').classList.add('starting')
document.getElementById('challengeReady').innerHTML = `<span id="message">Challenge starting in</span><span id="count">${count}</span>`
countdown = setInterval(() => {
count--
if (count === 0) {
// Do this when countdown hits 0
document.getElementById('challenge-overlay').classList.add('fade')
clearInterval(countdown)
setTimeout(() => {
document.getElementById('challenge-overlay').classList.add('hide')
startGameClock()
}, 900);
}
document.getElementById('challengeReady').innerHTML = `<span id="message">Challenge starting in</span><span id="count">${count}</span>`
}, 1000)
}
const {challengeState, setChallengeState} = useContext(ChallengeContext)
return (
<div id="challenge-overlay">
<div id="challengeReady" className="notify">
<h2>Challenge Options</h2>
<WordListPicker />
<button id="startChallenge" onClick={startChallenge}>Start Challenge</button>
</div>
{challengeState === 'ready' && <ChallengeReady />}
{challengeState === 'completed' && <ChallengeComplete setChallengeState={setChallengeState} />}
</div>
)
})

View file

@ -0,0 +1,17 @@
import React, { useContext } from "react"
import WordListPicker from "./WordListPicker"
import { ChallengeContext } from "../contexts/challengeContext"
export default (function ChallengeReady() {
const {startChallenge} = useContext(ChallengeContext)
return (
<div id="challengeReady" className="notify">
<h2>Challenge Options</h2>
<WordListPicker />
<button id="startChallenge" onClick={startChallenge}>Start Challenge</button>
</div>
)
})

View file

@ -6,7 +6,13 @@ export default React.memo(function ChallengeWord(props) {
let challengeWordClass= props.className
const {word} = useContext(WordFeederContext)
let challengeLetters = word.split('')
let challengeLetters
if (typeof word === 'object') {
challengeLetters = word[0].split('')
}
else {
challengeLetters = word.split('')
}
let spannedWord = challengeLetters.map((letter,index) => {
return (

View file

@ -1,33 +1,10 @@
import React, {useState, useContext, useEffect} from "react"
import React, {useContext} from "react"
import { GameClockContext } from "../contexts/gameClockContext";
function GameClock(props) {
console.log('GameClock rendered');
// console.log('props.gameClockTime', props.gameClockTime);
// const [clockTime, setClockTime] = useState(30)
// setClockTime(props.time)
// let gameClock
// let clockRunning = false
// function startClock() {
// if (!clockRunning) {
// clockRunning = true
// gameClock = setInterval(() => {
// document.getElementById('gameClock').innerText = Number(document.getElementById('gameClock').innerText) + 1
// }, 1000)
// }
// }
// function stopClock() {
// if (clockRunning) {
// clearInterval(gameClock)
// clockRunning = false
// }
// }
const {gameClockTime} = useContext(GameClockContext)
return (
<div id="gameClock">{gameClockTime}</div>
)

View file

@ -3,21 +3,27 @@ import {GameModeContext} from "../contexts/gameModeContext"
import { MorseBufferContext } from "../contexts/morseBufferContext"
import { WordFeederContext } from "../contexts/wordFeederContext"
import { GameClockContext } from "../contexts/gameClockContext"
import { ChallengeContext } from "../contexts/challengeContext"
function ModePicker() {
const {setGameMode} = useContext(GameModeContext)
const {setMorseCharBuffer} = useContext(MorseBufferContext)
const {resetFeeder} = useContext(WordFeederContext)
const {stopGameClock, clockIsRunning} = useContext(GameClockContext)
const {stopGameClock, setGameClockTime, clockIsRunning} = useContext(GameClockContext)
const {setChallengeState} = useContext(ChallengeContext)
function handleClick(e) {
setMorseCharBuffer('')
resetFeeder()
setChallengeState('ready')
setGameMode(e.target.id)
if (clockIsRunning) { stopGameClock() }
if (clockIsRunning) {
stopGameClock()
setGameClockTime(0)
}
let buttons = document.querySelector(".mode-picker#gameMode #buttons").childNodes
buttons.forEach(button => {

View file

@ -0,0 +1,76 @@
import React, {useState, useContext} from "react"
import { GameClockContext } from "./gameClockContext"
import { WordFeederContext } from "./wordFeederContext"
// import { KeyTypeContext } from "./keyTypeContext"
const ChallengeContext = React.createContext()
function ChallengeContextProvider(props) {
const [challengeState, setChallengeState] = useState('ready')
const {startGameClock, stopGameClock, setGameClockTime, intervals} = useContext(GameClockContext)
const {resetFeeder} = useContext(WordFeederContext)
function startChallenge() {
setGameClockTime(0)
let countdown
let count = 3
document.getElementById('challengeReady').classList.add('starting')
document.getElementById('challengeReady').innerHTML = `<span id="message">Challenge starting in</span><span id="count">${count}</span>`
countdown = setInterval(() => {
count--
if (count === 0) {
// Do this when countdown hits 0
document.getElementById('challenge-overlay').classList.add('fade')
clearInterval(countdown)
setTimeout(() => {
document.getElementById('challenge-overlay').classList.add('hide')
startGameClock()
setChallengeState('started')
}, 900);
}
document.getElementById('challengeReady').innerHTML = `<span id="message">Challenge starting in</span><span id="count">${count}</span>`
}, 1000)
}
function completeChallenge() {
stopGameClock()
setChallengeState('completed')
for (let i = 0; i < intervals.length; i++) {
clearInterval(intervals[i]);
}
resetFeeder()
const challengeOverlay = document.getElementById('challenge-overlay')
challengeOverlay.classList.remove('fade')
challengeOverlay.classList.remove('hide')
}
function cancelChallenge() {
stopGameClock()
for (let i = 0; i < intervals.length; i++) {
clearInterval(intervals[i]);
}
setChallengeState('ready')
resetFeeder()
setGameClockTime(0)
const challengeOverlay = document.getElementById('challenge-overlay')
challengeOverlay.classList.remove('fade')
challengeOverlay.classList.remove('hide')
}
return (
<ChallengeContext.Provider value={{
challengeState: challengeState,
setChallengeState: setChallengeState,
startChallenge: startChallenge,
completeChallenge: completeChallenge,
cancelChallenge: cancelChallenge
}}>
{props.children}
</ChallengeContext.Provider>
)
}
export {ChallengeContextProvider, ChallengeContext}

View file

@ -20,7 +20,8 @@ function GameClockContextProvider(props) {
stopGameClock()
return
}
document.getElementById('gameClock').innerText = Number(document.getElementById('gameClock').innerText) + 1
setGameClockTime(prev => prev + 1)
// document.getElementById('gameClock').innerText = Number(gameClockTime) //Number(document.getElementById('gameClock').innerText) + 1
}, 1000))
])
}

View file

@ -4,17 +4,29 @@ const WordFeederContext = React.createContext()
function WordFeederContextProvider(props) {
// let wordList = ['hi', 'morse', 'code', 'hello', 'gene']
const {wordList, wordListShuffled, wordListCategory} = useContext(WordListPickerContext)
const {wordList, wordListShuffled} = useContext(WordListPickerContext)
const [wordIndex, setWordIndex] = useState(0)
const [order, setOrder] = useState('sequential')
let word
let word
if (order === 'sequential') {
word = wordList[wordIndex]
// word = wordList[wordIndex]
if (wordList[wordIndex] === undefined) {
word = [wordList[0]]
}
else {
word = wordList[wordIndex]
}
}
else if (order === 'random') {
word = wordListShuffled[wordIndex]
// word = wordListShuffled[wordIndex]
if (wordListShuffled[wordIndex] === undefined) {
word = [wordListShuffled[0]]
}
else {
word = wordListShuffled[wordIndex]
}
}
function resetFeeder() {
@ -22,14 +34,20 @@ function WordFeederContextProvider(props) {
}
function getNextWord() {
if (order === 'sequential') {
setWordIndex(prev => prev + 1)
word = wordList[wordIndex] || null
} else if (order === 'random') {
setWordIndex(prev => prev + 1)
word = wordListShuffled[wordIndex] || null
}
setWordIndex(prev => prev + 1)
// if (order === 'sequential') {
// } else if (order === 'random') {
// setWordIndex(prev => prev + 1)
// if (wordListShuffled[wordIndex] === undefined) {
// alert('NULL2')
// word = '!end'
// }
// else {
// word = wordListShuffled[wordIndex]
// }
// }
}
return (
<WordFeederContext.Provider value={{word: word, getNextWord: getNextWord, resetFeeder: resetFeeder, setOrder: setOrder}}>

View file

@ -46,7 +46,6 @@ function WordListPickerContextProvider(props) {
return array;
}
return (
<WordListPickerContext.Provider value={{wordList: wordList, wordListShuffled: randomize(wordList), wordListCategory: wordListCategory, setWordListCategory: setWordListCategory}}>
{props.children}

View file

@ -766,7 +766,7 @@ i[class*="ri-"] {
z-index: -100;
}
#challenge-overlay #challengeReady {
#challenge-overlay #challengeReady, #challenge-overlay #challengeComplete {
position: relative;
display: -webkit-box;
display: -ms-flexbox;
@ -790,23 +790,23 @@ i[class*="ri-"] {
box-shadow: 0px 5px 15px rgba(0, 0, 0, 0.5);
}
#challenge-overlay #challengeReady #message {
#challenge-overlay #challengeReady #message, #challenge-overlay #challengeComplete #message {
font-size: 2em;
font-weight: bold;
}
#challenge-overlay #challengeReady #count {
#challenge-overlay #challengeReady #count, #challenge-overlay #challengeComplete #count {
font-size: 4.5em;
font-weight: bold;
}
#challenge-overlay #challengeReady.starting {
#challenge-overlay #challengeReady.starting, #challenge-overlay #challengeComplete.starting {
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
}
#challenge-overlay #challengeReady button {
#challenge-overlay #challengeReady button, #challenge-overlay #challengeComplete button {
background: #eee;
-webkit-box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.35), 0px -1px 1px white;
box-shadow: 0px 2px 2px rgba(0, 0, 0, 0.35), 0px -1px 1px white;
@ -820,18 +820,18 @@ i[class*="ri-"] {
max-width: 250px;
}
#challenge-overlay #challengeReady button#startChallenge {
#challenge-overlay #challengeReady button#startChallenge, #challenge-overlay #challengeReady button#continue, #challenge-overlay #challengeComplete button#startChallenge, #challenge-overlay #challengeComplete button#continue {
font-size: 1.2em;
font-weight: bold;
padding: 0.3em;
}
#challenge-overlay #challengeReady button.selected {
#challenge-overlay #challengeReady button.selected, #challenge-overlay #challengeComplete button.selected {
-webkit-box-shadow: inset 0px 2px 2px rgba(0, 0, 0, 0.3), inset 0px -1px 1px white;
box-shadow: inset 0px 2px 2px rgba(0, 0, 0, 0.3), inset 0px -1px 1px white;
}
#challenge-overlay #challengeReady #challengeOptions {
#challenge-overlay #challengeReady #challengeOptions, #challenge-overlay #challengeComplete #challengeOptions {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
@ -847,7 +847,7 @@ i[class*="ri-"] {
align-items: flex-start;
}
#challenge-overlay #challengeReady #challengeOptions .mode-picker {
#challenge-overlay #challengeReady #challengeOptions .mode-picker, #challenge-overlay #challengeComplete #challengeOptions .mode-picker {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
@ -858,12 +858,12 @@ i[class*="ri-"] {
justify-content: flex-start;
}
#challenge-overlay #challengeReady #challengeOptions .mode-picker div {
#challenge-overlay #challengeReady #challengeOptions .mode-picker div, #challenge-overlay #challengeComplete #challengeOptions .mode-picker div {
padding: 5px;
height: 2.4em;
}
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#title {
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#title, #challenge-overlay #challengeComplete #challengeOptions .mode-picker div#title {
font-weight: bold;
font-size: 1.08em;
height: 100%;
@ -879,7 +879,7 @@ i[class*="ri-"] {
align-items: center;
}
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#buttons {
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#buttons, #challenge-overlay #challengeComplete #challengeOptions .mode-picker div#buttons {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
@ -891,7 +891,7 @@ i[class*="ri-"] {
align-items: center;
}
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#input {
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#input, #challenge-overlay #challengeComplete #challengeOptions .mode-picker div#input {
margin-left: 10px;
display: -webkit-box;
display: -ms-flexbox;
@ -901,7 +901,7 @@ i[class*="ri-"] {
align-items: center;
}
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#input input {
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#input input, #challenge-overlay #challengeComplete #challengeOptions .mode-picker div#input input {
width: 50px;
-webkit-appearance: textfield;
-moz-appearance: textfield;
@ -913,17 +913,17 @@ i[class*="ri-"] {
font-size: 0.75em;
}
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#input select {
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#input select, #challenge-overlay #challengeComplete #challengeOptions .mode-picker div#input select {
height: 1.4rem;
}
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#input button {
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#input button, #challenge-overlay #challengeComplete #challengeOptions .mode-picker div#input button {
width: 20px;
height: 20px;
border-radius: 3px;
}
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#input button i {
#challenge-overlay #challengeReady #challengeOptions .mode-picker div#input button i, #challenge-overlay #challengeComplete #challengeOptions .mode-picker div#input button i {
position: relative;
left: -1px;
font-size: 1.1em;
@ -994,7 +994,6 @@ i[class*="ri-"] {
}
#morseBufferDisplay {
background: #eee;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
@ -1016,8 +1015,8 @@ i[class*="ri-"] {
}
#morseBufferDisplay #overlay {
-webkit-box-shadow: inset 20px 0px 20px -5px #eee;
box-shadow: inset 20px 0px 20px -5px #eee;
-webkit-box-shadow: inset 20px 0px 20px -5px #fff;
box-shadow: inset 20px 0px 20px -5px #fff;
position: absolute;
display: inline-block;
top: 0;

File diff suppressed because one or more lines are too long

View file

@ -878,7 +878,7 @@ $button-radius: 50px;
z-index: -100;
}
#challengeReady {
#challengeReady, #challengeComplete {
position: relative;
// width: 400px;
display: flex;
@ -906,7 +906,6 @@ $button-radius: 50px;
&.starting {
justify-content: center;
}
button {
background: $main-bg-color-light;
box-shadow: $main-box-shadow-light;
@ -919,7 +918,7 @@ $button-radius: 50px;
font-size: 0.75em;
color: $main-font-color-light;
max-width: 250px;
&#startChallenge {
&#startChallenge, &#continue {
font-size: 1.2em;
font-weight: bold;
padding: 0.3em;
@ -1054,7 +1053,7 @@ $button-radius: 50px;
#morseBufferDisplay {
// border: 1px solid green;
background: #eee;
// background: #eee;
display: flex;
justify-content: center;
flex-direction: column-reverse;
@ -1069,7 +1068,7 @@ $button-radius: 50px;
#overlay {
// background: blue;
box-shadow: inset 20px 0px 20px -5px #eee;
box-shadow: inset 20px 0px 20px -5px #fff;
position: absolute;
display: inline-block;
top:0;