Move some JS calls in loadNote()

This commit is contained in:
Abdo 2025-05-25 14:54:01 +03:00
parent 0ace032dc6
commit 053bd4380f
4 changed files with 104 additions and 72 deletions

View file

@ -553,22 +553,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
if not self.note: if not self.note:
return return
data = [
(fld, self.mw.col.media.escape_media_filenames(val))
for fld, val in self.note.items()
]
note_type = self.note_type()
flds = note_type["flds"]
collapsed = [fld["collapsed"] for fld in flds]
cloze_fields_ords = self.mw.col.models.cloze_fields(self.note.mid)
cloze_fields = [ord in cloze_fields_ords for ord in range(len(flds))]
plain_texts = [fld.get("plainText", False) for fld in flds]
descriptions = [fld.get("description", "") for fld in flds]
notetype_meta = {"id": self.note.mid, "modTime": note_type["mod"]}
self.widget.show() self.widget.show()
note_fields_status = self.note.fields_check() note_fields_status = self.note.fields_check()
def oncallback(arg: Any) -> None: def oncallback(arg: Any) -> None:
@ -583,29 +568,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
gui_hooks.editor_did_load_note(self) gui_hooks.editor_did_load_note(self)
assert self.mw.pm.profile is not None assert self.mw.pm.profile is not None
text_color = self.mw.pm.profile.get("lastTextColor", "#0000ff") js = f"loadNote({self.note.id}, {self.note.mid});"
highlight_color = self.mw.pm.profile.get("lastHighlightColor", "#0000ff")
js = f"""
saveSession();
setFields({json.dumps(data)});
setIsImageOcclusion({json.dumps(self.current_notetype_is_image_occlusion())});
setNotetypeMeta({json.dumps(notetype_meta)});
setCollapsed({json.dumps(collapsed)});
setClozeFields({json.dumps(cloze_fields)});
setPlainTexts({json.dumps(plain_texts)});
setDescriptions({json.dumps(descriptions)});
setFonts({json.dumps(self.fonts())});
focusField({json.dumps(focusTo)});
setNoteId({json.dumps(self.note.id)});
setColorButtons({json.dumps([text_color, highlight_color])});
setTags({json.dumps(self.note.tags)});
setTagsCollapsed({json.dumps(self.mw.pm.tags_collapsed(self.editorMode))});
setMathjaxEnabled({json.dumps(self.mw.col.get_config("renderMathjax", True))});
setShrinkImages({json.dumps(self.mw.col.get_config("shrinkEditorImages", True))});
setCloseHTMLTags({json.dumps(self.mw.col.get_config("closeHTMLTags", True))});
triggerChanges();
"""
if self.addMode: if self.addMode:
sticky = [field["sticky"] for field in self.note_type()["flds"]] sticky = [field["sticky"] for field in self.note_type()["flds"]]

View file

@ -648,8 +648,10 @@ exposed_backend_list = [
"get_field_names", "get_field_names",
"get_note", "get_note",
# NotetypesService # NotetypesService
"get_notetype",
"get_notetype_names", "get_notetype_names",
"get_change_notetype_info", "get_change_notetype_info",
"get_cloze_field_ords",
# StatsService # StatsService
"card_stats", "card_stats",
"get_review_logs", "get_review_logs",

View file

@ -92,7 +92,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const sessionOptions: SessionOptions = {}; const sessionOptions: SessionOptions = {};
export function saveSession(): void { export function saveSession(): void {
if (notetypeMeta) { if (notetypeMeta) {
sessionOptions[notetypeMeta.id] = { sessionOptions[notetypeMeta.id.toString()] = {
fieldsCollapsed, fieldsCollapsed,
fieldStates: { fieldStates: {
richTextsHidden, richTextsHidden,
@ -106,19 +106,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const fieldStores: Writable<string>[] = []; const fieldStores: Writable<string>[] = [];
let fieldNames: string[] = []; let fieldNames: string[] = [];
export function setFields(fs: [string, string][]): void { export function setFields(newFieldNames: string[], fieldValues: string[]): void {
// this is a bit of a mess -- when moving to Rust calls, we should make fieldNames = newFieldNames;
// sure to have two backend endpoints for:
// * the note, which can be set through this view
// * the fieldname, font, etc., which cannot be set
const newFieldNames: string[] = []; for (let i = fieldStores.length; i < fieldValues.length; i++) {
for (const [index, [fieldName]] of fs.entries()) {
newFieldNames[index] = fieldName;
}
for (let i = fieldStores.length; i < newFieldNames.length; i++) {
const newStore = writable(""); const newStore = writable("");
fieldStores[i] = newStore; fieldStores[i] = newStore;
newStore.subscribe((value) => updateField(i, value)); newStore.subscribe((value) => updateField(i, value));
@ -126,23 +117,22 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
for ( for (
let i = fieldStores.length; let i = fieldStores.length;
i > newFieldNames.length; i > fieldValues.length;
i = fieldStores.length i = fieldStores.length
) { ) {
fieldStores.pop(); fieldStores.pop();
} }
for (const [index, [, fieldContent]] of fs.entries()) { for (const [index, value] of fieldValues.entries()) {
fieldStores[index].set(fieldContent); fieldStores[index].set(value);
} }
fieldNames = newFieldNames;
} }
let fieldsCollapsed: boolean[] = []; let fieldsCollapsed: boolean[] = [];
export function setCollapsed(defaultCollapsed: boolean[]): void { export function setCollapsed(defaultCollapsed: boolean[]): void {
fieldsCollapsed = fieldsCollapsed =
sessionOptions[notetypeMeta?.id]?.fieldsCollapsed ?? defaultCollapsed; sessionOptions[notetypeMeta?.id?.toString()]?.fieldsCollapsed ??
defaultCollapsed;
} }
let clozeFields: boolean[] = []; let clozeFields: boolean[] = [];
export function setClozeFields(defaultClozeFields: boolean[]): void { export function setClozeFields(defaultClozeFields: boolean[]): void {
@ -154,7 +144,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let plainTextDefaults: boolean[] = []; let plainTextDefaults: boolean[] = [];
export function setPlainTexts(defaultPlainTexts: boolean[]): void { export function setPlainTexts(defaultPlainTexts: boolean[]): void {
const states = sessionOptions[notetypeMeta?.id]?.fieldStates; const states = sessionOptions[notetypeMeta?.id?.toString()]?.fieldStates;
if (states) { if (states) {
richTextsHidden = states.richTextsHidden; richTextsHidden = states.richTextsHidden;
plainTextsHidden = states.plainTextsHidden; plainTextsHidden = states.plainTextsHidden;
@ -227,8 +217,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
bridgeCommand(`setTagsCollapsed:${$tagsCollapsed}`); bridgeCommand(`setTagsCollapsed:${$tagsCollapsed}`);
} }
let noteId: number | null = null; let noteId: bigint | null = null;
export function setNoteId(ntid: number): void { export function setNoteId(ntid: bigint | null): void {
// TODO this is a hack, because it requires the NoteEditor to know implementation details of the PlainTextInput. // TODO this is a hack, because it requires the NoteEditor to know implementation details of the PlainTextInput.
// It should be refactored once we work on our own Undo stack // It should be refactored once we work on our own Undo stack
for (const pi of plainTextInputs) { for (const pi of plainTextInputs) {
@ -238,21 +228,24 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
let notetypeMeta: NotetypeIdAndModTime; let notetypeMeta: NotetypeIdAndModTime;
function setNotetypeMeta({ id, modTime }: NotetypeIdAndModTime): void { function setNotetypeMeta(notetype: Notetype): void {
notetypeMeta = { id, modTime }; notetypeMeta = { id: notetype.id, modTime: notetype.mtimeSecs };
// Discard the saved state of the fields if the notetype has been modified. // Discard the saved state of the fields if the notetype has been modified.
if (sessionOptions[id]?.modTimeOfNotetype !== modTime) { if (
delete sessionOptions[id]; sessionOptions[notetype.id.toString()]?.modTimeOfNotetype !==
notetype.mtimeSecs
) {
delete sessionOptions[notetype.id.toString()];
} }
if (isImageOcclusion) { if (isImageOcclusion) {
getImageOcclusionFields({ getImageOcclusionFields({
notetypeId: BigInt(notetypeMeta.id), notetypeId: BigInt(notetype.id),
}).then((r) => (ioFields = r.fields!)); }).then((r) => (ioFields = r.fields!));
} }
} }
function getNoteId(): number | null { function getNoteId(): string | null {
return noteId; return noteId?.toString() ?? null;
} }
let isImageOcclusion = false; let isImageOcclusion = false;
@ -412,7 +405,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
import { ImageOcclusionFieldIndexes } from "@generated/anki/image_occlusion_pb"; import { ImageOcclusionFieldIndexes } from "@generated/anki/image_occlusion_pb";
import { getImageOcclusionFields } from "@generated/backend";
import { StockNotetype_OriginalStockKind } from "@generated/anki/notetypes_pb";
import type { Notetype } from "@generated/anki/notetypes_pb";
import {
getFieldNames,
getClozeFieldOrds,
getImageOcclusionFields,
getNote,
getNotetype,
encodeIriPaths,
} from "@generated/backend";
import { wrapInternal } from "@tslib/wrap"; import { wrapInternal } from "@tslib/wrap";
import Shortcut from "$lib/components/Shortcut.svelte"; import Shortcut from "$lib/components/Shortcut.svelte";
@ -549,6 +552,69 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}); });
} }
async function loadNote(nid: bigint | null, notetypeId: bigint, focusTo: number) {
const notetype = await getNotetype({
ntid: notetypeId,
});
let fieldValues: string[] = notetype.fields.map(() => "");
const fieldNames = (
await getFieldNames({
ntid: notetype.id,
})
).vals;
const clozeFieldOrds = (await getClozeFieldOrds({ ntid: notetype.id })).ords;
const clozeFields = fieldNames.map((name, index) =>
clozeFieldOrds.includes(index),
);
let tags: string[] = [];
if (nid) {
const note = await getNote({
nid,
});
fieldValues = (
await Promise.all(
note.fields.map((field) => encodeIriPaths({ val: field })),
)
).map((field) => field.val);
tags = note.tags;
}
saveSession();
setFields(fieldNames, fieldValues);
setIsImageOcclusion(
notetype.config?.originalStockKind ===
StockNotetype_OriginalStockKind.IMAGE_OCCLUSION,
);
setNotetypeMeta(notetype);
setCollapsed(notetype.fields.map((field) => field.config?.collapsed ?? false));
setClozeFields(clozeFields);
setPlainTexts(notetype.fields.map((field) => field.config?.plainText ?? false));
setDescriptions(
notetype.fields.map((field) => field.config?.description ?? ""),
);
// TODO: gui_hooks.editor_will_use_font_for_field
setFonts(
notetype.fields.map((field) => [
field.config?.fontName ?? "",
field.config?.fontSize ?? 16,
field.config?.rtl ?? false,
]),
);
focusField(focusTo);
setNoteId(nid);
// TODO: lastTextColor/lastHighlightColor profile config
// setColorButtons(["#0000ff", "#0000ff"]);
setTags(tags);
// TODO: mw.pm.tags_collapsed()
setTagsCollapsed(false);
// TODO: renderMathjax col config
setMathjaxEnabled(true);
// TODO: shrinkEditorImages col config
setShrinkImages(true);
// TODO: closeHTMLTags col config
setCloseHTMLTags(true);
triggerChanges();
}
$: signalEditorState(editorState); $: signalEditorState(editorState);
$: editorState = getEditorState( $: editorState = getEditorState(
@ -575,6 +641,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
Object.assign(globalThis, { Object.assign(globalThis, {
loadNote,
saveSession, saveSession,
setFields, setFields,
setCollapsed, setCollapsed,

View file

@ -8,16 +8,16 @@ export type EditorOptions = {
plainTextsHidden: boolean[]; plainTextsHidden: boolean[];
plainTextDefaults: boolean[]; plainTextDefaults: boolean[];
}; };
modTimeOfNotetype: number; modTimeOfNotetype: bigint;
}; };
export type SessionOptions = { export type SessionOptions = {
[key: number]: EditorOptions; [key: string]: EditorOptions;
}; };
export type NotetypeIdAndModTime = { export type NotetypeIdAndModTime = {
id: number; id: bigint;
modTime: number; modTime: bigint;
}; };
export enum EditorState { export enum EditorState {