diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index aab4e58c0..232f5b383 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -723,6 +723,8 @@ exposed_backend_list = [ # CollectionService "latest_progress", "get_custom_colours", + "set_config_json", + "get_config_json", # DeckService "get_deck_names", # I18nService diff --git a/ts/routes/reviewer-inner/index.ts b/ts/routes/reviewer-inner/index.ts index c5b230943..143afaae9 100644 --- a/ts/routes/reviewer-inner/index.ts +++ b/ts/routes/reviewer-inner/index.ts @@ -17,12 +17,17 @@ function postParentMessage(message: ReviewerRequest) { declare const MathJax: any; const urlParams = new URLSearchParams(location.search); - +const decoder = new TextDecoder(); const style = document.createElement("style"); document.head.appendChild(style); addEventListener("message", async (e: MessageEvent) => { switch (e.data.type) { + case "setstorage": { + const json = JSON.parse(decoder.decode(e.data.json_buffer)); + Object.assign(storageObj, json); + break; + } case "html": { document.body.innerHTML = e.data.value; if (e.data.css) { @@ -56,7 +61,7 @@ addEventListener("message", async (e: MessageEvent) => { break; } default: { - console.warn(`Unknown message type: ${e.data.type}`); + // console.warn(`Unknown message type: ${e.data.type}`); break; } } @@ -99,3 +104,61 @@ function _typeAnsPress() { ); } globalThis._typeAnsPress = _typeAnsPress; + +const storageObj = {}; +const encoder = new TextEncoder(); + +function updateParentStorage() { + postParentMessage({ type: "setstorage", json_buffer: encoder.encode(JSON.stringify(storageObj)) }); +} + +function createStorageProxy() { + return new Proxy({}, { + get(_target, prop) { + switch (prop) { + case "getItem": + return (key) => key in storageObj ? storageObj[key] : null; + case "setItem": + return (key, value) => { + storageObj[key] = String(value); + updateParentStorage(); + }; + case "removeItem": + return (key) => { + delete storageObj[key]; + updateParentStorage(); + }; + case "clear": + return () => { + Object.keys(storageObj).forEach(key => delete storageObj[key]); + updateParentStorage(); + }; + case "key": + return (index) => Object.keys(storageObj)[index] ?? null; + case "length": + return Object.keys(storageObj).length; + default: + return storageObj[prop]; + } + }, + set(_target, prop, value) { + storageObj[prop] = String(value); + return true; + }, + ownKeys() { + return Object.keys(storageObj); + }, + getOwnPropertyDescriptor(_target, _prop) { + return { enumerable: true, configurable: true }; + }, + }); +} + +const ankiStorage = createStorageProxy(); + +Object.defineProperty(window, "localStorage", { + value: ankiStorage, + writable: false, + configurable: true, + enumerable: true, +}); diff --git a/ts/routes/reviewer-inner/innerReviewerRequest.ts b/ts/routes/reviewer-inner/innerReviewerRequest.ts index 16afcbb88..aa9de1867 100644 --- a/ts/routes/reviewer-inner/innerReviewerRequest.ts +++ b/ts/routes/reviewer-inner/innerReviewerRequest.ts @@ -7,4 +7,9 @@ interface HtmlMessage { bodyclass?: string; } -export type InnerReviewerRequest = HtmlMessage; +interface StorageUpdateMessage { + type: "setstorage"; + json_buffer: Uint8Array; +} + +export type InnerReviewerRequest = HtmlMessage | StorageUpdateMessage; diff --git a/ts/routes/reviewer/reviewer.ts b/ts/routes/reviewer/reviewer.ts index 36917d7b5..06b86b442 100644 --- a/ts/routes/reviewer/reviewer.ts +++ b/ts/routes/reviewer/reviewer.ts @@ -1,7 +1,7 @@ // 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 { compareAnswer, nextCardData, playAvtags } from "@generated/backend"; +import { compareAnswer, getConfigJson, nextCardData, playAvtags, setConfigJson } from "@generated/backend"; import { derived, get, writable } from "svelte/store"; import type { InnerReviewerRequest } from "../reviewer-inner/innerReviewerRequest"; import type { ReviewerRequest } from "./reviewerRequest"; @@ -39,8 +39,10 @@ export class ReviewerState { iframe: HTMLIFrameElement | undefined = undefined; - onReady() { + async onReady() { this.iframe!.style.visibility = "visible"; + const { json } = await getConfigJson({ val: "reviewer_storage" }); + this.sendInnerRequest({ type: "setstorage", json_buffer: json }); this.showQuestion(null); addEventListener("message", this.onMessage.bind(this)); } @@ -60,6 +62,13 @@ export class ReviewerState { this.handleKeyPress(e.data.key); break; } + case "setstorage": { + setConfigJson({ + key: "reviewer_storage", + valueJson: e.data.json_buffer, + undoable: false, + }); + } } } diff --git a/ts/routes/reviewer/reviewerRequest.ts b/ts/routes/reviewer/reviewerRequest.ts index bcd0f7fde..8a3f04c9d 100644 --- a/ts/routes/reviewer/reviewerRequest.ts +++ b/ts/routes/reviewer/reviewerRequest.ts @@ -16,4 +16,9 @@ interface KeyPressMessage { key: string; } -export type ReviewerRequest = AudioMessage | UpdateTypedAnswerMessage | KeyPressMessage; +interface SetStorageMessage { + type: "setstorage"; + json_buffer: Uint8Array; +} + +export type ReviewerRequest = AudioMessage | UpdateTypedAnswerMessage | KeyPressMessage | SetStorageMessage;