Skip to content

Commit c50d776

Browse files
committed
Fix countdown bug, considerable refactor
1 parent 5c22a4a commit c50d776

File tree

80 files changed

+763
-782
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

80 files changed

+763
-782
lines changed

src/components/Practice/PlayArea/Input/PracticePlayAreaInput.css renamed to src/components/Practice/PlayArea/InputBuffer/PracticePlayAreaInputBuffer.css

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#practiceInput {
1+
#practiceInputBuffer {
22
--shadow-color: transparent;
33
background-color: rgb(var(--rgb-h2));
44
border: none;
@@ -12,9 +12,9 @@
1212
padding: 0.75rem;
1313
resize: none;
1414
}
15-
#practiceInput:focus {
15+
#practiceInputBuffer:focus {
1616
--shadow-color: var(--color-border-low);
1717
}
18-
#practiceInput[data-has-mistakes='true'] {
18+
#practiceInputBuffer[data-has-mistakes='true'] {
1919
background-color: rgba(var(--rgb-red), 0.5);
2020
}

src/components/Practice/PlayArea/Input/PracticePlayAreaInput.tsx renamed to src/components/Practice/PlayArea/InputBuffer/PracticePlayAreaInputBuffer.tsx

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React, { useEffect, useRef, useState } from 'react';
2-
import roundTextGetFirstUncompletedItemIndex from '../../../../core/roundTextGetFirstUncompletedItemIndex';
2+
import firstUncompletedRoundTextWordIdx from '../../../../core/firstUncompletedRoundTextWordIdx';
33

44
import { useAppDispatch, useAppSelector } from '../../../../redux/hooks';
55
import {
@@ -9,21 +9,21 @@ import {
99
} from '../../../../redux/roundActions';
1010
import { PracticeStatus } from '../../../../redux/types';
1111
import replaceControlCharsWithVisibleChars from '../../../../utility/functions/replaceControlCharsWithVisibleChars';
12-
import './PracticePlayAreaInput.css';
12+
import './PracticePlayAreaInputBuffer.css';
1313

1414
type HtmlElementType = HTMLTextAreaElement;
1515

16-
export default function PracticePlayAreaInput() {
16+
export default function PracticePlayAreaInputBuffer() {
1717
const [value, setValue] = useState('');
1818
const [isFocused, setIsFocused] = useState(false);
1919
const roundStatus = useAppSelector(
2020
(state) => state.practice.playArea.roundStatus,
2121
);
22-
const textItems = useAppSelector(
23-
(state) => state.practice.playArea.roundText?.items,
22+
const words = useAppSelector(
23+
(state) => state.practice.playArea.roundText?.words,
2424
);
25-
const textItemsCompletedCount = useAppSelector(
26-
(state) => state.practice.playArea.roundText?.itemsCompletedCount,
25+
const wordsCompletedCount = useAppSelector(
26+
(state) => state.practice.playArea.roundText?.numWordsCompleted,
2727
);
2828
const element = useRef<HtmlElementType>(null);
2929
const dispatch = useAppDispatch();
@@ -39,7 +39,7 @@ export default function PracticePlayAreaInput() {
3939
setValue('');
4040
}, [roundStatus]);
4141

42-
useEffect(() => setValue(''), [textItemsCompletedCount]);
42+
useEffect(() => setValue(''), [wordsCompletedCount]);
4343

4444
// Cleanup
4545
useEffect(
@@ -105,16 +105,16 @@ export default function PracticePlayAreaInput() {
105105
}
106106

107107
function areThereUncorrectedMistakesInBuffer() {
108-
if (textItems === undefined || textItems.length === 0) {
108+
if (words === undefined || words.length === 0) {
109109
return false;
110110
}
111-
const currentItemIdx = roundTextGetFirstUncompletedItemIndex(textItems);
112-
if (currentItemIdx >= textItems.length) {
113-
// all items are completed and correct
111+
const currentWordIdx = firstUncompletedRoundTextWordIdx(words);
112+
if (currentWordIdx >= words.length) {
113+
// all words are completed and correct
114114
return false;
115115
}
116-
const currentItem = textItems[currentItemIdx];
117-
for (const char of currentItem.chars) {
116+
const currentWord = words[currentWordIdx];
117+
for (const char of currentWord.chars) {
118118
if (char.input === null) {
119119
// reached end of input
120120
return false;
@@ -133,7 +133,7 @@ export default function PracticePlayAreaInput() {
133133
autoFocus={true}
134134
className="text-norm py-1 rounded w-100"
135135
data-has-mistakes={areThereUncorrectedMistakesInBuffer()}
136-
id="practiceInput"
136+
id="practiceInputBuffer"
137137
onBlur={() => setIsFocused(false)}
138138
onChange={handleChange}
139139
onFocus={() => setIsFocused(true)}

src/components/Practice/PlayArea/PracticePlayArea.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import React, { useState } from 'react';
2-
import type IRoundTextItemCharMistake from '../../../core/types/IRoundTextItemCharMistake';
2+
import type { IRoundTextCharMistake } from '../../../core/types';
33
import PracticePlayAreaAccuracyMeter from './AccuracyMeter/PracticePlayAreaAccuracyMeter';
44
import PracticePlayAreaActionButton from './ActionButton/PracticePlayAreaActionButton';
55
import PracticePlayAreaCountdownTimer from './CountdownTimer/PracticePlayAreaCountdownTimer';
6-
import PracticePlayAreaInput from './Input/PracticePlayAreaInput';
6+
import PracticePlayAreaInputBuffer from './InputBuffer/PracticePlayAreaInputBuffer';
77
import PracticePlayAreaMistakeAnalysis from './MistakeAnalysis/PracticePlayAreaMistakeAnalysis';
88
import PracticePlayAreaStopwatch from './Stopwatch/PracticePlayAreaStopwatch';
99
import PracticePlayAreaText from './Text/PracticePlayAreaText';
1010
import PracticePlayAreaWpmCounter from './WpmCounter/PracticePlayAreaWpmCounter';
1111

1212
export interface ISelectedChar {
1313
actual: string;
14-
mistakes: IRoundTextItemCharMistake[];
14+
mistakes: IRoundTextCharMistake[];
1515
position: number;
1616
}
1717

@@ -33,7 +33,7 @@ export default function PracticePlayArea() {
3333
setSelectedChar={setSelectedChar}
3434
/>
3535
<div className="d-flex gap-2">
36-
<PracticePlayAreaInput />
36+
<PracticePlayAreaInputBuffer />
3737
<PracticePlayAreaActionButton />
3838
</div>
3939
<PracticePlayAreaMistakeAnalysis selectedChar={selectedChar} />

src/components/Practice/PlayArea/Text/Char/Analyzed/PracticePlayAreaTextCharAnalyzed.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import React, { useState } from 'react';
2-
import type IRoundTextItemCharMistake from '../../../../../../core/types/IRoundTextItemCharMistake';
2+
import type { IRoundTextCharMistake } from '../../../../../../core/types';
33
import { SPACE } from '../../../../../../utility/constants';
44
import replaceControlCharsWithVisibleChars from '../../../../../../utility/functions/replaceControlCharsWithVisibleChars';
55
import type { ISelectedChar } from '../../../PracticePlayArea';
66
import './PracticePlayAreaTextCharAnalyzed.css';
77

88
interface IProps {
99
actual: string;
10-
mistakes: IRoundTextItemCharMistake[];
10+
mistakes: IRoundTextCharMistake[];
1111
position: number;
1212
selectedChar: ISelectedChar | null;
1313
setSelectedChar: React.Dispatch<React.SetStateAction<ISelectedChar | null>>;

src/components/Practice/PlayArea/Text/PracticePlayAreaText.tsx

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ export default function PracticePlayAreaText(props: IProps) {
3030
const isRoundCompleted =
3131
roundStatus === PracticeStatus.running &&
3232
roundText !== null &&
33-
roundText.items.length > 0 &&
34-
roundText.itemsCompletedCount === roundText.items.length;
33+
roundText.words.length > 0 &&
34+
roundText.numWordsCompleted === roundText.words.length;
3535

3636
if (isRoundCompleted) {
3737
dispatch(actionCreatorPracticeRoundEnd());
3838
}
39-
}, [roundText?.items.length, roundText?.itemsCompletedCount, roundStatus]);
39+
}, [roundText?.words.length, roundText?.numWordsCompleted, roundStatus]);
4040

4141
useEffect(() => {
4242
if (roundStatus !== PracticeStatus.idle) {
@@ -51,7 +51,7 @@ export default function PracticePlayAreaText(props: IProps) {
5151
if (roundText === null) {
5252
return <p className="text-danger m-0">Internal error</p>;
5353
}
54-
if (roundText.items.length === 0) {
54+
if (roundText.words.length === 0) {
5555
return (
5656
<p className="m-0">
5757
Use the button or the keyboard shortcuts <Key text="Enter" /> to
@@ -60,7 +60,7 @@ export default function PracticePlayAreaText(props: IProps) {
6060
);
6161
}
6262
if (
63-
roundText.items.length > 0 &&
63+
roundText.words.length > 0 &&
6464
roundStatus !== PracticeStatus.running &&
6565
roundResult !== null
6666
) {
@@ -77,8 +77,8 @@ export default function PracticePlayAreaText(props: IProps) {
7777
throw new TypeError('roundText === null');
7878
}
7979

80-
for (const item of roundText.items) {
81-
for (const char of item.chars) {
80+
for (const word of roundText.words) {
81+
for (const char of word.chars) {
8282
chars.push(
8383
<PracticePlayAreaTextChar
8484
actual={char.actual}
@@ -105,8 +105,8 @@ export default function PracticePlayAreaText(props: IProps) {
105105
throw new TypeError('roundText === null');
106106
}
107107

108-
for (const item of roundText.items) {
109-
for (const char of item.chars) {
108+
for (const word of roundText.words) {
109+
for (const char of word.chars) {
110110
chars.push(
111111
<PracticePlayAreaTextCharAnalyzed
112112
actual={char.actual}

src/components/Practice/PlayArea/WpmCounter/PracticePlayAreaWpmCounter.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import React, { useEffect, useRef, useState } from 'react';
2-
import { CHARACTERS_PER_WORD } from '../../../../core/constants';
3-
import roundResultCalcNetWordsPerMinute from '../../../../core/roundResultCalcNetWordsPerMinute';
4-
import roundTextCalcCorrectCharsCount from '../../../../core/roundTextCalcCorrectCharsCount';
2+
import { CHARS_PER_WORD } from '../../../../core/constants';
3+
import calcNetWPM from '../../../../core/calcNetWPM';
4+
import countCorrectRoundTextChars from '../../../../core/countCorrectRoundTextChars';
55
import { useAppSelector } from '../../../../redux/hooks';
66
import store from '../../../../redux/store';
77
import { PracticeStatus } from '../../../../redux/types';
8-
import getMinutesElapsed from '../../../../utility/functions/getMinutesElapsed';
8+
import minutesElapsed from '../../../../utility/functions/minutesElapsed';
99
import './PracticePlayAreaWpmCounter.css';
1010

1111
const UPDATE_INTERVAL_MS = 250;
@@ -54,10 +54,10 @@ export default function PracticePlayAreaWpmCounter() {
5454
}
5555

5656
setWpm(
57-
roundResultCalcNetWordsPerMinute(
58-
roundTextCalcCorrectCharsCount(text.items),
59-
CHARACTERS_PER_WORD,
60-
getMinutesElapsed(startTime, Date.now()),
57+
calcNetWPM(
58+
countCorrectRoundTextChars(text.words),
59+
CHARS_PER_WORD,
60+
minutesElapsed(startTime, Date.now()),
6161
),
6262
);
6363
}

src/components/Practice/Settings/Advanced/Items/MedleyCollectionsCustom/Collection/PracticeSettingsAdvancedItemMedleyCollectionCustom.tsx

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,22 +15,22 @@ import BootstrapButton from '../../../../../../Bootstrap/Button/BootstrapButton'
1515
import './PracticeSettingsAdvancedItemMedleyCollectionCustom.css';
1616

1717
const MAX_LENGTH_NAME = 32;
18-
const MAX_LENGTH_ITEMS = 1000;
18+
const MAX_LENGTH_WORDS_BUFFER = 1000;
1919

2020
export default function PracticeSettingsAdvancedItemMedleyCollectionCustom(
2121
props: IPracticeMedleyCollection,
2222
) {
2323
const [name, setName] = useState(props.name);
24-
const [items, setItems] = useState(props.items.join(SPACE));
24+
const [words, setWords] = useState(props.words.join(SPACE));
2525
const medleyCollectionsActive = useAppSelector(
2626
(state) => state.practice.settings.current.medleyCollectionsActive,
2727
);
2828
const medleyCollectionsCustom = useAppSelector(
2929
(state) => state.practice.settings.current.medleyCollectionsCustom,
3030
);
31-
const prevItems = useRef(items);
31+
const prevWords = useRef(words);
3232
const nameLength = useRef(name.length);
33-
const itemCount = useRef(items.length);
33+
const wordsBufferLen = useRef(words.length);
3434
const dispatch = useAppDispatch();
3535

3636
return (
@@ -83,36 +83,36 @@ export default function PracticeSettingsAdvancedItemMedleyCollectionCustom(
8383
<textarea
8484
className="items p-2 rounded w-100"
8585
onBlur={(event) =>
86-
handleCollectionItemsInputBlur(
86+
handleCollectionWordsInputBlur(
8787
event.target.value,
88-
prevItems,
88+
prevWords,
8989
props,
9090
medleyCollectionsCustom,
9191
dispatch,
9292
)
9393
}
9494
onChange={(event) => {
9595
handleChange(
96-
ChangeTarget.Items,
96+
ChangeTarget.Words,
9797
event.target.value,
98-
MAX_LENGTH_ITEMS,
99-
itemCount,
100-
setItems,
98+
MAX_LENGTH_WORDS_BUFFER,
99+
wordsBufferLen,
100+
setWords,
101101
{ forceLowerCase: false },
102102
);
103103
}}
104104
placeholder="Items (letters/numbers/symbols separated by spaces)"
105105
spellCheck="false"
106-
value={items}
106+
value={words}
107107
/>
108-
<div className="small text-low">{`${itemCount.current}/${MAX_LENGTH_ITEMS} characters`}</div>
108+
<div className="small text-low">{`${wordsBufferLen.current}/${MAX_LENGTH_WORDS_BUFFER} characters`}</div>
109109
</div>
110110
);
111111
}
112112

113113
enum ChangeTarget {
114114
Name,
115-
Items,
115+
Words,
116116
}
117117

118118
function handleChange(
@@ -272,15 +272,15 @@ function handleCollectionDeleteButtonClick(
272272
);
273273
}
274274

275-
function handleCollectionItemsInputBlur(
275+
function handleCollectionWordsInputBlur(
276276
eventTargetValue: string,
277-
prevItems: React.MutableRefObject<string>,
277+
prevWords: React.MutableRefObject<string>,
278278
collectionToUpdate: IPracticeMedleyCollection,
279279
currentCollections: IPracticeMedleyCollection[],
280280
dispatch: Dispatch<any>,
281281
) {
282-
const isNewValueSameAsCurrent = eventTargetValue === prevItems.current;
283-
prevItems.current = eventTargetValue;
282+
const isNewValueSameAsCurrent = eventTargetValue === prevWords.current;
283+
prevWords.current = eventTargetValue;
284284

285285
if (isNewValueSameAsCurrent) {
286286
return;
@@ -295,11 +295,11 @@ function handleCollectionItemsInputBlur(
295295
}
296296

297297
const valueTrimmed = eventTargetValue.trim();
298-
let newItems: string[];
298+
let newWords: string[];
299299
if (valueTrimmed.length === 0) {
300-
newItems = [];
300+
newWords = [];
301301
} else {
302-
newItems = valueTrimmed.split(/ +/);
302+
newWords = valueTrimmed.split(/ +/);
303303
}
304304

305305
const updatedCollections = createDeepCopy(
@@ -308,7 +308,7 @@ function handleCollectionItemsInputBlur(
308308

309309
updatedCollections[collectionToUpdateIndex] = {
310310
name: collectionToUpdate.name,
311-
items: newItems,
311+
words: newWords,
312312
};
313313

314314
dispatch(

src/components/Practice/Settings/Advanced/Items/MedleyCollectionsCustom/PracticeSettingsAdvancedItemMedleyCollectionsCustomContainer.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ export default function PracticeSettingsAdvancedItemMedleyCollectionsCustomConta
3535
<div className="text-norm d-flex flex-column gap-3 mb-2">
3636
{medleyCollectionsCustom.map((collection) => (
3737
<PracticeSettingsAdvancedItemMedleyCollectionCustom
38-
items={collection.items}
38+
words={collection.words}
3939
key={v4()}
4040
name={collection.name}
4141
/>
@@ -71,7 +71,7 @@ function AddCollectionButton() {
7171
...medleyCollectionsCustom,
7272
{
7373
name: getInitialCollectionName(medleyCollectionsCustom),
74-
items: [],
74+
words: [],
7575
},
7676
],
7777
}),

0 commit comments

Comments
 (0)