Skip to content

Commit df8ccb8

Browse files
committed
feat: replay the typing speed result
1 parent 923c6e4 commit df8ccb8

File tree

9 files changed

+2950
-19
lines changed

9 files changed

+2950
-19
lines changed

package-lock.json

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/keybr-textinput-ui/lib/AnimatedText.tsx

+15-19
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {
33
type TextDisplaySettings,
44
TextInput,
55
} from "@keybr/textinput";
6-
import { type ReactNode, useEffect, useState } from "react";
6+
import { type ReactNode, useEffect, useMemo, useState } from "react";
77
import { StaticText } from "./StaticText.tsx";
88
import { type TextLineSize } from "./TextLines.tsx";
99

@@ -31,33 +31,29 @@ export function AnimatedText({
3131
}
3232

3333
function useAnimatedTextState(text: string): readonly Char[] {
34-
const [{ textInput, chars }, setState] = useState(() => makeState(text));
35-
if (textInput.text !== text) {
36-
setState(makeState(text));
37-
}
34+
const textInput = useMemo(
35+
() =>
36+
new TextInput(text, {
37+
stopOnError: false,
38+
forgiveErrors: false,
39+
spaceSkipsWords: false,
40+
}),
41+
[text],
42+
);
43+
const [chars, setChars] = useState<readonly Char[]>([]);
3844
useEffect(() => {
39-
const id = setInterval(() => {
45+
setChars(textInput.chars);
46+
const id = window.setInterval(() => {
4047
if (textInput.completed) {
4148
textInput.reset();
4249
} else {
4350
textInput.appendChar(0, textInput.at(textInput.pos).codePoint, 0);
4451
}
45-
const { chars } = textInput;
46-
setState({ textInput, chars });
52+
setChars(textInput.chars);
4753
}, 500);
4854
return () => {
49-
clearInterval(id);
55+
window.clearInterval(id);
5056
};
5157
}, [textInput]);
5258
return chars;
5359
}
54-
55-
function makeState(text: string) {
56-
const textInput = new TextInput(text, {
57-
stopOnError: false,
58-
forgiveErrors: false,
59-
spaceSkipsWords: false,
60-
});
61-
const { chars } = textInput;
62-
return { textInput, chars };
63-
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { type KeyId, useKeyboard } from "@keybr/keyboard";
2+
import { KeyLayer, VirtualKeyboard } from "@keybr/keyboard-ui";
3+
import { type AnyEvent } from "@keybr/textinput-events";
4+
import { type ReactNode, useEffect, useMemo, useState } from "react";
5+
import { ReplayState } from "../session/index.ts";
6+
7+
export function Replay({
8+
events,
9+
}: {
10+
readonly events: readonly AnyEvent[];
11+
}): ReactNode {
12+
const keyboard = useKeyboard();
13+
const depressedKeys = useReplayState(events);
14+
return (
15+
<VirtualKeyboard keyboard={keyboard} height="16rem">
16+
<KeyLayer depressedKeys={depressedKeys} />
17+
</VirtualKeyboard>
18+
);
19+
}
20+
21+
function useReplayState(events: readonly AnyEvent[]) {
22+
const stepper = useMemo(() => new ReplayState(events), [events]);
23+
const [depressedKeys, setDepressedKeys] = useState<KeyId[]>([]);
24+
useEffect(() => {
25+
let id = 0;
26+
const step = () => {
27+
stepper.step();
28+
setDepressedKeys(stepper.depressedKeys);
29+
id = window.setTimeout(step, stepper.delay);
30+
};
31+
setDepressedKeys(stepper.depressedKeys);
32+
id = window.setTimeout(step, stepper.delay);
33+
return () => {
34+
window.clearTimeout(id);
35+
};
36+
}, [stepper]);
37+
return depressedKeys;
38+
}

packages/page-typing-test/lib/components/Report.tsx

+3
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
import { mdiSkipNext } from "@mdi/js";
2626
import { memo, type ReactNode } from "react";
2727
import { type TestResult } from "../session/index.ts";
28+
import { Replay } from "./Replay.tsx";
2829
import * as styles from "./Report.module.less";
2930

3031
export const Report = memo(function Report({
@@ -110,6 +111,8 @@ export const Report = memo(function Report({
110111
</Name>
111112
</Para>
112113

114+
<Replay events={result.events} />
115+
113116
<Spacer size={3} />
114117

115118
<FieldList>

0 commit comments

Comments
 (0)