Skip to content

Commit 14cf7b0

Browse files
authored
refactor(hints): allow joinOverlappingHints to run on joined hints (@nadalaba) (#6716)
1 parent 0c5cb1b commit 14cf7b0

File tree

2 files changed

+89
-82
lines changed

2 files changed

+89
-82
lines changed

frontend/src/ts/controllers/input-controller.ts

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -454,32 +454,32 @@ function isCharCorrect(char: string, charIndex: number): boolean {
454454
return false;
455455
}
456456

457-
function handleChar(
457+
async function handleChar(
458458
char: string,
459459
charIndex: number,
460460
realInputValue?: string
461-
): void {
461+
): Promise<void> {
462462
if (TestUI.resultCalculating || TestUI.resultVisible) {
463463
return;
464464
}
465465

466466
if (char === "…" && TestWords.words.getCurrent()[charIndex] !== "…") {
467467
for (let i = 0; i < 3; i++) {
468-
handleChar(".", charIndex + i);
468+
await handleChar(".", charIndex + i);
469469
}
470470

471471
return;
472472
}
473473

474474
if (char === "œ" && TestWords.words.getCurrent()[charIndex] !== "œ") {
475-
handleChar("o", charIndex);
476-
handleChar("e", charIndex + 1);
475+
await handleChar("o", charIndex);
476+
await handleChar("e", charIndex + 1);
477477
return;
478478
}
479479

480480
if (char === "æ" && TestWords.words.getCurrent()[charIndex] !== "æ") {
481-
handleChar("a", charIndex);
482-
handleChar("e", charIndex + 1);
481+
await handleChar("a", charIndex);
482+
await handleChar("e", charIndex + 1);
483483
return;
484484
}
485485

@@ -725,7 +725,7 @@ function handleChar(
725725
}
726726

727727
const activeWordTopBeforeJump = activeWord?.offsetTop;
728-
void TestUI.updateActiveWordLetters();
728+
await TestUI.updateActiveWordLetters();
729729

730730
const newActiveTop = activeWord?.offsetTop;
731731
//stop the word jump by slicing off the last character, update word again
@@ -740,7 +740,7 @@ function handleChar(
740740
if (!Config.showAllLines) void TestUI.lineJump(activeWordTopBeforeJump);
741741
} else {
742742
TestInput.input.current = TestInput.input.current.slice(0, -1);
743-
void TestUI.updateActiveWordLetters();
743+
await TestUI.updateActiveWordLetters();
744744
}
745745
}
746746

@@ -777,7 +777,10 @@ function handleChar(
777777
}
778778
}
779779

780-
function handleTab(event: JQuery.KeyDownEvent, popupVisible: boolean): void {
780+
async function handleTab(
781+
event: JQuery.KeyDownEvent,
782+
popupVisible: boolean
783+
): Promise<void> {
781784
if (TestUI.resultCalculating) {
782785
event.preventDefault();
783786
return;
@@ -805,7 +808,7 @@ function handleTab(event: JQuery.KeyDownEvent, popupVisible: boolean): void {
805808
event.preventDefault();
806809
// insert tab character if needed (only during the test)
807810
if (!TestUI.resultVisible && shouldInsertTabCharacter) {
808-
handleChar("\t", TestInput.input.current.length);
811+
await handleChar("\t", TestInput.input.current.length);
809812
setWordsInput(" " + TestInput.input.current);
810813
return;
811814
}
@@ -832,7 +835,7 @@ function handleTab(event: JQuery.KeyDownEvent, popupVisible: boolean): void {
832835
// insert tab character if needed (only during the test)
833836
if (!TestUI.resultVisible && shouldInsertTabCharacter) {
834837
event.preventDefault();
835-
handleChar("\t", TestInput.input.current.length);
838+
await handleChar("\t", TestInput.input.current.length);
836839
setWordsInput(" " + TestInput.input.current);
837840
return;
838841
}
@@ -851,7 +854,7 @@ function handleTab(event: JQuery.KeyDownEvent, popupVisible: boolean): void {
851854
// insert tab character if needed
852855
if (shouldInsertTabCharacter) {
853856
event.preventDefault();
854-
handleChar("\t", TestInput.input.current.length);
857+
await handleChar("\t", TestInput.input.current.length);
855858
setWordsInput(" " + TestInput.input.current);
856859
return;
857860
}
@@ -934,7 +937,7 @@ $(document).on("keydown", async (event) => {
934937

935938
//tab
936939
if (event.key === "Tab") {
937-
handleTab(event, popupVisible);
940+
await handleTab(event, popupVisible);
938941
}
939942

940943
//esc
@@ -1103,7 +1106,7 @@ $(document).on("keydown", async (event) => {
11031106
}
11041107
}
11051108
} else {
1106-
handleChar("\n", TestInput.input.current.length);
1109+
await handleChar("\n", TestInput.input.current.length);
11071110
setWordsInput(" " + TestInput.input.current);
11081111
updateUI();
11091112
}
@@ -1169,7 +1172,7 @@ $(document).on("keydown", async (event) => {
11691172
)
11701173
) {
11711174
event.preventDefault();
1172-
handleChar(event.key, TestInput.input.current.length);
1175+
await handleChar(event.key, TestInput.input.current.length);
11731176
updateUI();
11741177
setWordsInput(" " + TestInput.input.current);
11751178
}
@@ -1185,7 +1188,7 @@ $(document).on("keydown", async (event) => {
11851188
const char: string | null = await LayoutEmulator.getCharFromEvent(event);
11861189
if (char !== null) {
11871190
event.preventDefault();
1188-
handleChar(char, TestInput.input.current.length);
1191+
await handleChar(char, TestInput.input.current.length);
11891192
updateUI();
11901193
setWordsInput(" " + TestInput.input.current);
11911194
}
@@ -1273,7 +1276,7 @@ $("#wordsInput").on("beforeinput", (event) => {
12731276
}
12741277
});
12751278

1276-
$("#wordsInput").on("input", (event) => {
1279+
$("#wordsInput").on("input", async (event) => {
12771280
if (!event.originalEvent?.isTrusted || TestUI.testRestarting) {
12781281
(event.target as HTMLInputElement).value = " ";
12791282
return;
@@ -1363,7 +1366,11 @@ $("#wordsInput").on("input", (event) => {
13631366
iOffset = inputValue.indexOf(" ") + 1;
13641367
}
13651368
for (let i = diffStart; i < inputValue.length; i++) {
1366-
handleChar(inputValue[i] as string, i - iOffset, realInputValue);
1369+
await handleChar(
1370+
inputValue[i] as string,
1371+
i - iOffset,
1372+
realInputValue
1373+
);
13671374
}
13681375
}
13691376
} else if (containsKorean) {
@@ -1400,7 +1407,7 @@ $("#wordsInput").on("input", (event) => {
14001407
}
14011408
for (let i = diffStart; i < inputValue.length; i++) {
14021409
// passing realInput to allow for correct Korean character compilation
1403-
handleChar(inputValue[i] as string, i - iOffset, realInputValue);
1410+
await handleChar(inputValue[i] as string, i - iOffset, realInputValue);
14041411
}
14051412
}
14061413

frontend/src/ts/test/test-ui.ts

Lines changed: 62 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -234,69 +234,69 @@ async function joinOverlappingHints(
234234
const currentLanguage = await JSONData.getCurrentLanguage(Config.language);
235235
const isLanguageRTL = currentLanguage.rightToLeft;
236236

237-
let firstHintInSeq = 0;
238-
for (const adjacentLettersSequence of incorrectLettersIndices) {
239-
const lastHintInSeq = firstHintInSeq + adjacentLettersSequence.length - 1;
240-
joinHintsOfAdjacentLetters(firstHintInSeq, lastHintInSeq);
241-
firstHintInSeq += adjacentLettersSequence.length;
242-
}
243-
244-
function joinHintsOfAdjacentLetters(
245-
firstHintInSequence: number,
246-
lastHintInSequence: number
247-
): void {
248-
let currentHint = firstHintInSequence;
249-
250-
while (currentHint < lastHintInSequence) {
251-
const block1El = hintElements[currentHint] as HTMLElement;
252-
const block2El = hintElements[currentHint + 1] as HTMLElement;
253-
254-
const block1Indices = block1El.dataset["charsIndex"]?.split(",") ?? [];
255-
const block2Indices = block2El.dataset["charsIndex"]?.split(",") ?? [];
256-
257-
const block1Letter1Indx = parseInt(block1Indices[0] ?? "0");
258-
const block2Letter1Indx = parseInt(block2Indices[0] ?? "0");
259-
260-
const block1Letter1 = activeWordLetters[block1Letter1Indx] as HTMLElement;
261-
const block2Letter1 = activeWordLetters[block2Letter1Indx] as HTMLElement;
262-
263-
const leftBlock = isLanguageRTL ? block2El : block1El;
264-
const rightBlock = isLanguageRTL ? block1El : block2El;
265-
266-
// block edge is offset half its width because of transform: translate(-50%)
267-
const leftBlockEnds = leftBlock.offsetLeft + leftBlock.offsetWidth / 2;
268-
const rightBlockStarts =
269-
rightBlock.offsetLeft - rightBlock.offsetWidth / 2;
270-
271-
const sameTop = block1Letter1.offsetTop === block2Letter1.offsetTop;
272-
273-
if (sameTop && leftBlockEnds > rightBlockStarts) {
274-
// join hint blocks
275-
block1El.dataset["charsIndex"] = [
276-
...block1Indices,
277-
...block2Indices,
278-
].join(",");
279-
280-
const block1Letter1Pos =
281-
block1Letter1.offsetLeft +
282-
(isLanguageRTL ? block1Letter1.offsetWidth : 0);
283-
const bothBlocksLettersWidthHalved =
284-
block2El.offsetLeft - block1El.offsetLeft;
285-
block1El.style.left =
286-
block1Letter1Pos + bothBlocksLettersWidthHalved + "px";
287-
288-
block1El.insertAdjacentHTML("beforeend", block2El.innerHTML);
289-
block2El.remove();
290-
291-
// after joining blocks, the sequence is shorter
292-
lastHintInSequence--;
293-
// check if the newly formed block overlaps with the previous one
294-
currentHint--;
295-
if (currentHint < firstHintInSeq) currentHint = firstHintInSeq;
296-
} else {
297-
currentHint++;
298-
}
237+
let previousBlocksAdjacent = false;
238+
let currentHintBlock = 0;
239+
let HintBlocksCount = hintElements.length;
240+
while (currentHintBlock < HintBlocksCount - 1) {
241+
const hintBlock1 = hintElements[currentHintBlock] as HTMLElement;
242+
const hintBlock2 = hintElements[currentHintBlock + 1] as HTMLElement;
243+
244+
const block1Indices = hintBlock1.dataset["charsIndex"]?.split(",") ?? [];
245+
const block2Indices = hintBlock2.dataset["charsIndex"]?.split(",") ?? [];
246+
247+
const block1Letter1Indx = parseInt(block1Indices[0] ?? "0");
248+
const block2Letter1Indx = parseInt(block2Indices[0] ?? "0");
249+
250+
const currentBlocksAdjacent = incorrectLettersIndices.some(
251+
(adjacentLettersSequence) =>
252+
adjacentLettersSequence.includes(block1Letter1Indx) &&
253+
adjacentLettersSequence.includes(block2Letter1Indx)
254+
);
255+
256+
if (!currentBlocksAdjacent) {
257+
currentHintBlock++;
258+
previousBlocksAdjacent = false;
259+
continue;
260+
}
261+
262+
const block1Letter1 = activeWordLetters[block1Letter1Indx] as HTMLElement;
263+
const block2Letter1 = activeWordLetters[block2Letter1Indx] as HTMLElement;
264+
265+
const sameTop = block1Letter1.offsetTop === block2Letter1.offsetTop;
266+
267+
const leftBlock = isLanguageRTL ? hintBlock2 : hintBlock1;
268+
const rightBlock = isLanguageRTL ? hintBlock1 : hintBlock2;
269+
270+
// block edge is offset half its width because of transform: translate(-50%)
271+
const leftBlockEnds = leftBlock.offsetLeft + leftBlock.offsetWidth / 2;
272+
const rightBlockStarts = rightBlock.offsetLeft - rightBlock.offsetWidth / 2;
273+
274+
if (sameTop && leftBlockEnds > rightBlockStarts) {
275+
// join hint blocks
276+
hintBlock1.dataset["charsIndex"] = [
277+
...block1Indices,
278+
...block2Indices,
279+
].join(",");
280+
281+
const block1Letter1Pos =
282+
block1Letter1.offsetLeft +
283+
(isLanguageRTL ? block1Letter1.offsetWidth : 0);
284+
const bothBlocksLettersWidthHalved =
285+
hintBlock2.offsetLeft - hintBlock1.offsetLeft;
286+
hintBlock1.style.left =
287+
block1Letter1Pos + bothBlocksLettersWidthHalved + "px";
288+
289+
hintBlock1.insertAdjacentHTML("beforeend", hintBlock2.innerHTML);
290+
hintBlock2.remove();
291+
292+
// after joining blocks, the sequence is shorter
293+
HintBlocksCount--;
294+
// check if the newly formed block overlaps with the previous one
295+
if (previousBlocksAdjacent && currentHintBlock > 0) currentHintBlock--;
296+
} else {
297+
currentHintBlock++;
299298
}
299+
previousBlocksAdjacent = true;
300300
}
301301
}
302302

0 commit comments

Comments
 (0)