refactor: rework keypress tracking

removes the need for a setTimeout which causes issues during lag
This commit is contained in:
Miodec 2025-11-09 09:41:34 +01:00
parent 52b7d963b9
commit df06b62636
2 changed files with 70 additions and 28 deletions

View file

@ -1182,11 +1182,9 @@ $("#wordsInput").on("keydown", (event) => {
}
const now = performance.now();
setTimeout(() => {
const eventCode =
event.code === "" || event.key === "Unidentified" ? "NoCode" : event.code;
TestInput.recordKeydownTime(now, eventCode);
}, 0);
const eventCode =
event.code === "" || event.key === "Unidentified" ? "NoCode" : event.code;
TestInput.recordKeydownTime(now, eventCode);
});
$("#wordsInput").on("keyup", (event) => {
@ -1213,11 +1211,9 @@ $("#wordsInput").on("keyup", (event) => {
}
const now = performance.now();
setTimeout(() => {
const eventCode =
event.code === "" || event.key === "Unidentified" ? "NoCode" : event.code;
TestInput.recordKeyupTime(now, eventCode);
}, 0);
const eventCode =
event.code === "" || event.key === "Unidentified" ? "NoCode" : event.code;
TestInput.recordKeyupTime(now, eventCode);
});
$("#wordsInput").on("keyup", (event) => {

View file

@ -262,16 +262,37 @@ export function forceKeyup(now: number): void {
//using mean here because for words mode, the last keypress ends the test.
//if we then force keyup on that last keypress, it will record a duration of 0
//skewing the average and standard deviation
const avg = roundTo2(mean(keypressTimings.duration.array));
const keysOrder = Object.entries(keyDownData);
keysOrder.sort((a, b) => a[1].timestamp - b[1].timestamp);
for (const keyOrder of keysOrder) {
recordKeyupTime(now, keyOrder[0]);
const indexesToRemove = new Set(
Object.values(keyDownData).map((data) => data.index)
);
const keypressDurations = keypressTimings.duration.array.filter(
(_, index) => !indexesToRemove.has(index)
);
if (keypressDurations.length === 0) {
// this means the test ended while all keys were still held - probably safe to ignore
// since this will result in a "too short" test anyway
return;
}
const last = lastElementFromArray(keysOrder)?.[0] as string;
const index = keyDownData[last]?.index;
if (last !== undefined && index !== undefined) {
const avg = roundTo2(mean(keypressDurations));
const orderedKeys = Object.entries(keyDownData).sort(
(a, b) => a[1].timestamp - b[1].timestamp
);
for (const [key, { index }] of orderedKeys) {
keypressTimings.duration.array[index] = avg;
if (key === "NoCode") {
noCodeIndex--;
}
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete keyDownData[key];
updateOverlap(now);
}
}
@ -350,6 +371,21 @@ function updateOverlap(now: number): void {
}
export function resetKeypressTimings(): void {
//because keydown triggers before input, we need to grab the first keypress data here and carry it over
//take the key with the largest index
const lastKey = Object.keys(keyDownData).reduce((a, b) => {
const aIndex = keyDownData[a]?.index;
const bIndex = keyDownData[b]?.index;
if (aIndex === undefined) return b;
if (bIndex === undefined) return a;
return aIndex > bIndex ? a : b;
});
//get the data
const lastKeyData = keyDownData[lastKey];
//reset
keypressTimings = {
spacing: {
first: -1,
@ -366,6 +402,26 @@ export function resetKeypressTimings(): void {
};
keyDownData = {};
noCodeIndex = 0;
//carry over
if (lastKeyData !== undefined) {
keypressTimings = {
spacing: {
first: lastKeyData.timestamp,
last: lastKeyData.timestamp,
array: [],
},
duration: {
array: [0],
},
};
keyDownData[lastKey] = {
timestamp: lastKeyData.timestamp,
// make sure to set it to the first index
index: 0,
};
}
console.debug("Keypress timings reset");
}
@ -413,14 +469,4 @@ export function restart(): void {
correct: 0,
incorrect: 0,
};
keypressTimings = {
spacing: {
first: -1,
last: -1,
array: [],
},
duration: {
array: [],
},
};
}