Anki/ts/routes/reviewer/reviewer.ts
2025-10-04 18:32:40 +01:00

67 lines
2 KiB
TypeScript

// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import {
CardAnswer,
type NextCardDataResponse_NextCardData,
} from "@generated/anki/scheduler_pb";
import { nextCardData } from "@generated/backend";
import { writable } from "svelte/store";
export class ReviewerState {
answerHtml: string = ""
cardData: NextCardDataResponse_NextCardData | undefined = undefined;
beginAnsweringMs = Date.now();
readonly cardClass = writable("");
iframe: HTMLIFrameElement | undefined = undefined;
onReady() {
this.iframe?.contentWindow?.postMessage({ type: "nightMode", value: true }, "*");
this.showQuestion(null);
}
public registerIFrame(iframe: HTMLIFrameElement) {
this.iframe = iframe;
iframe.addEventListener("load", this.onReady.bind(this));
}
updateHtml(htmlString: string) {
this.iframe?.contentWindow?.postMessage({ type: "html", value: htmlString }, "*");
}
async showQuestion(answer: CardAnswer | null) {
const resp = await nextCardData({
answer: answer || undefined,
});
// TODO: "Congratulation screen" logic
this.cardData = resp.nextCard
const question = resp.nextCard?.front || "";
this.updateHtml(question);
}
public showAnswer() {
this.updateHtml(this.cardData?.back || "");
}
public easeButtonPressed(rating: number) {
const states = this.cardData!.states!;
let newState = ({
[1]: states.again!,
[2]: states.hard!,
[3]: states.good!,
[4]: states.easy!,
})[rating]!;
this.showQuestion(
new CardAnswer({
rating: rating,
currentState: states!.current!,
newState,
cardId: this.cardData!.cardId,
answeredAtMillis: BigInt(Date.now()),
millisecondsTaken: Date.now() - this.beginAnsweringMs,
}),
);
}
}