diff --git a/qt/aqt/editor.py b/qt/aqt/editor.py index a0c931970..e723cfc35 100644 --- a/qt/aqt/editor.py +++ b/qt/aqt/editor.py @@ -35,10 +35,9 @@ from anki.consts import MODEL_CLOZE from anki.hooks import runFilter from anki.httpclient import HttpClient from anki.models import NotetypeDict, NotetypeId, StockNotetype -from anki.notes import Note, NoteFieldsCheckResult, NoteId +from anki.notes import Note, NoteId from anki.utils import checksum, is_lin, is_mac, is_win, namedtmp from aqt import AnkiQt, gui_hooks -from aqt.operations import QueryOp from aqt.operations.note import update_note from aqt.operations.notetype import update_notetype_legacy from aqt.qt import * @@ -407,11 +406,8 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too self.mw.progress.timer( 100, self.loadNoteKeepingFocus, False, parent=self.widget ) - else: - self._check_and_update_duplicate_display_async() else: gui_hooks.editor_did_fire_typing_timer(self.note) - self._check_and_update_duplicate_display_async() # focused into field? elif cmd.startswith("focus"): @@ -511,7 +507,7 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too return self.widget.show() - note_fields_status = self.note.fields_check() + # note_fields_status = self.note.fields_check() def oncallback(arg: Any) -> None: if not self.note: @@ -519,7 +515,6 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too self.setupForegroundButton() # we currently do this synchronously to ensure we load before the # sidebar on browser startup - self._update_duplicate_display(note_fields_status) if focusTo is not None: self.web.setFocus() gui_hooks.editor_did_load_note(self) @@ -552,42 +547,6 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too saveNow = call_after_note_saved - def _check_and_update_duplicate_display_async(self) -> None: - note = self.note - if not note: - return - - def on_done(result: NoteFieldsCheckResult.V) -> None: - if self.note != note: - return - self._update_duplicate_display(result) - - QueryOp( - parent=self.parentWindow, - op=lambda _: note.fields_check(), - success=on_done, - ).run_in_background() - - checkValid = _check_and_update_duplicate_display_async - - def _update_duplicate_display(self, result: NoteFieldsCheckResult.V) -> None: - assert self.note is not None - cols = [""] * len(self.note.fields) - cloze_hint = "" - if result == NoteFieldsCheckResult.DUPLICATE: - cols[0] = "dupe" - elif result == NoteFieldsCheckResult.NOTETYPE_NOT_CLOZE: - cloze_hint = tr.adding_cloze_outside_cloze_notetype() - elif result == NoteFieldsCheckResult.FIELD_NOT_CLOZE: - cloze_hint = tr.adding_cloze_outside_cloze_field() - - self.web.eval( - 'require("anki/ui").loaded.then(() => {' - f"setBackgrounds({json.dumps(cols)});\n" - f"setClozeHint({json.dumps(cloze_hint)});\n" - "}); " - ) - def fieldsAreBlank(self, previousNote: Note | None = None) -> bool: if not self.note: return True diff --git a/qt/aqt/mediasrv.py b/qt/aqt/mediasrv.py index 3fd1996af..45eac407c 100644 --- a/qt/aqt/mediasrv.py +++ b/qt/aqt/mediasrv.py @@ -703,6 +703,7 @@ exposed_backend_list = [ "get_field_names", "get_note", "new_note", + "note_fields_check", # NotetypesService "get_notetype", "get_notetype_names", diff --git a/ts/routes/editor/NoteEditor.svelte b/ts/routes/editor/NoteEditor.svelte index ec83fe969..08fa4c2d5 100644 --- a/ts/routes/editor/NoteEditor.svelte +++ b/ts/routes/editor/NoteEditor.svelte @@ -325,7 +325,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html fieldSave.schedule(async () => { bridgeCommand(`key:${index}`); note!.fields[index] = await transformContentBeforeSave(content); - updateCurrentNote(); + await updateCurrentNote(); + await updateDuplicateDisplay(); }, 600); } @@ -347,6 +348,23 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } } + async function updateDuplicateDisplay(): Promise { + if (!note) { + return; + } + const result = await noteFieldsCheck(note); + const cols = new Array(note.fields.length).fill(""); + if (result.state === NoteFieldsCheckResponse_State.DUPLICATE) { + cols[0] = "dupe"; + } else if (result.state === NoteFieldsCheckResponse_State.NOTETYPE_NOT_CLOZE) { + hint = tr.addingClozeOutsideClozeNotetype(); + } else if (result.state === NoteFieldsCheckResponse_State.FIELD_NOT_CLOZE) { + hint = tr.addingClozeOutsideClozeField(); + } + setBackgrounds(cols); + setClozeHint(hint); + } + export function focusIfField(x: number, y: number): boolean { const elements = document.elementsFromPoint(x, y); const first = elements[0].closest(".field-container"); @@ -446,6 +464,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html newNote, editorUpdateNote, decodeIriPaths, + noteFieldsCheck, } from "@generated/backend"; import { wrapInternal } from "@tslib/wrap"; import { getProfileConfig, getMeta, setMeta, getColConfig } from "@tslib/profile"; @@ -466,7 +485,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import StickyBadge from "./StickyBadge.svelte"; import ButtonGroupItem from "$lib/components/ButtonGroupItem.svelte"; import PreviewButton from "./PreviewButton.svelte"; - import type { Note } from "@generated/anki/notes_pb"; + import { NoteFieldsCheckResponse_State, type Note } from "@generated/anki/notes_pb"; $: isIOImageLoaded = false; $: ioImageLoadedStore.set(isIOImageLoaded); @@ -673,6 +692,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html }); } } + await updateDuplicateDisplay(); triggerChanges(); } @@ -841,7 +861,8 @@ components and functionality for general note editing. note!.fields[index] = await transformContentBeforeSave( get(content), ); - updateCurrentNote(); + await updateCurrentNote(); + await updateDuplicateDisplay(); }} on:mouseenter={() => { $hoveredField = fields[index];