Add a temporary workaround for addcards

This commit is contained in:
Abdo 2025-06-02 06:04:17 +03:00
parent b3aa8e93f3
commit 05360e2d19
4 changed files with 97 additions and 90 deletions

View file

@ -172,63 +172,13 @@ class AddCards(QMainWindow):
if deck_id := self.col.default_deck_for_notetype(notetype_id):
self.deck_chooser.selected_deck_id = deck_id
# only used for detecting changed sticky fields on close
self._last_added_note = None
# copy fields into new note with the new notetype
old_note = self.editor.note
new_note = self._new_note()
if old_note:
old_field_names = list(old_note.keys())
new_field_names = list(new_note.keys())
copied_field_names = set()
for f in new_note.note_type()["flds"]:
field_name = f["name"]
# copy identical non-empty fields
if field_name in old_field_names and old_note[field_name]:
new_note[field_name] = old_note[field_name]
copied_field_names.add(field_name)
new_idx = 0
for old_idx, old_field_value in enumerate(old_field_names):
# skip previously copied identical fields in new note
while (
new_idx < len(new_field_names)
and new_field_names[new_idx] in copied_field_names
):
new_idx += 1
if new_idx >= len(new_field_names):
break
# copy non-empty old fields
if (
old_field_value not in copied_field_names
and old_note.fields[old_idx]
):
new_note.fields[new_idx] = old_note.fields[old_idx]
new_idx += 1
new_note.tags = old_note.tags
# and update editor state
self.editor.note = new_note
self.editor.loadNote(
focusTo=min(self.editor.last_field_index or 0, len(new_note.fields) - 1)
)
gui_hooks.addcards_did_change_note_type(
self, old_note.note_type(), new_note.note_type()
)
if notetype_id:
self.editor.set_nid(None, mid=notetype_id, focus_to=0)
def _load_new_note(self, sticky_fields_from: Note | None = None) -> None:
note = self._new_note()
if old_note := sticky_fields_from:
flds = note.note_type()["flds"]
# copy fields from old note
if old_note:
for n in range(min(len(note.fields), len(old_note.fields))):
if flds[n]["sticky"]:
note.fields[n] = old_note.fields[n]
# and tags
note.tags = old_note.tags
self.setAndFocusNote(note)
self.editor.set_nid(
None, mid=self.notetype_chooser.selected_notetype_id, focus_to=0
)
def on_operation_did_execute(
self, changes: OpChanges, handler: object | None
@ -279,12 +229,7 @@ class AddCards(QMainWindow):
aqt.dialogs.open("Browser", self.mw, search=(SearchNode(nid=nid),))
def add_current_note(self) -> None:
if self.editor.current_notetype_is_image_occlusion():
self.editor.update_occlusions_field()
self.editor.call_after_note_saved(self._add_current_note)
self.editor.reset_image_occlusion()
else:
self.editor.call_after_note_saved(self._add_current_note)
self.editor.web.eval(f"addCurrentNote({self.deck_chooser.selected_deck_id})")
def _add_current_note(self) -> None:
note = self.editor.note

View file

@ -28,6 +28,7 @@ import aqt
import aqt.forms
import aqt.operations
import aqt.sound
from anki._legacy import deprecated
from anki.cards import Card
from anki.collection import Config
from anki.consts import MODEL_CLOZE
@ -37,7 +38,6 @@ from anki.models import NotetypeDict, NotetypeId, StockNotetype
from anki.notes import Note, NoteId
from anki.utils import checksum, is_mac, is_win, namedtmp
from aqt import AnkiQt, gui_hooks
from aqt.operations.note import update_note
from aqt.operations.notetype import update_notetype_legacy
from aqt.qt import *
from aqt.sound import av_player
@ -129,7 +129,7 @@ class Editor:
self.mw = mw
self.widget = widget
self.parentWindow = parentWindow
self.note: Note | None = None
self.nid: NoteId | None = None
# legacy argument provided?
if addMode is not None:
editor_mode = EditorMode.ADD_CARDS if addMode else EditorMode.EDIT_CURRENT
@ -380,10 +380,6 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
######################################################################
def onBridgeCmd(self, cmd: str) -> Any:
if not self.note:
# shutdown
return
# focus lost or key/button pressed?
if cmd.startswith("blur") or cmd.startswith("key"):
(type, ord_str) = cmd.split(":", 1)
@ -475,6 +471,18 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
# Setting/unsetting the current note
######################################################################
def set_nid(
self,
nid: NoteId | None,
mid: int,
focus_to: int | None = None,
) -> None:
"Make note with ID `nid` the current note."
self.nid = nid
self.currentField = None
self.load_note(mid, focus_to=focus_to)
@deprecated(replaced_by=set_nid)
def set_note(
self,
note: Note | None,
@ -482,10 +490,10 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
focusTo: int | None = None,
) -> None:
"Make NOTE the current note."
self.note = note
self.currentField = None
if self.note:
self.loadNote(focusTo=focusTo)
if note:
self.nid = note.id
self.load_note(mid=note.mid, focus_to=focusTo)
elif hide:
self.widget.hide()
@ -493,43 +501,35 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
self.loadNote(self.currentField)
@on_editor_ready
def loadNote(self, focusTo: int | None = None) -> None:
if not self.note:
return
def load_note(self, mid: int, focus_to: int | None = None) -> None:
self.widget.show()
# note_fields_status = self.note.fields_check()
def oncallback(arg: Any) -> None:
if not self.note:
if not self.nid:
return
# we currently do this synchronously to ensure we load before the
# sidebar on browser startup
if focusTo is not None:
if focus_to is not None:
self.web.setFocus()
gui_hooks.editor_did_load_note(self)
assert self.mw.pm.profile is not None
js = f"loadNote({self.note.id}, {self.note.mid}, {json.dumps(focusTo)}, {json.dumps(self.orig_note_id)});"
js = f"loadNote({json.dumps(self.nid)}, {mid}, {json.dumps(focus_to)}, {json.dumps(self.orig_note_id)});"
js = gui_hooks.editor_will_load_note(js, self.note, self)
self.web.evalWithCallback(
f'require("anki/ui").loaded.then(() => {{ {js} }})', oncallback
)
def _save_current_note(self) -> None:
"Call after note is updated with data from webview."
if not self.note:
return
update_note(parent=self.widget, note=self.note).run_in_background(
initiator=self
)
@deprecated(replaced_by=load_note)
def loadNote(self, focusTo: int | None = None) -> None:
self.load_note(self.note.mid, focus_to=focusTo)
def call_after_note_saved(
self, callback: Callable, keepFocus: bool = False
) -> None:
"Save unsaved edits then call callback()."
if not self.note:
if not self.nid:
# calling code may not expect the callback to fire immediately
self.mw.progress.single_shot(10, callback)
return
@ -1007,6 +1007,12 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
addImageForOcclusionFromClipboard=Editor.select_image_from_clipboard_and_occlude,
)
@property
def note(self) -> Note | None:
if self.nid is None:
return None
return self.mw.col.get_note(self.nid)
def note_type(self) -> NotetypeDict:
assert self.note is not None
note_type = self.note.note_type()

View file

@ -704,6 +704,7 @@ exposed_backend_list = [
"get_note",
"new_note",
"note_fields_check",
"add_note",
# NotetypesService
"get_notetype",
"get_notetype_names",

View file

@ -365,6 +365,57 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
setClozeHint(hint);
}
async function loadNewNote() {
await loadNote(0n, notetypeMeta.id, 0, null);
}
async function noteCanBeAdded(): Promise<boolean> {
let problem: string | null = null;
const result = await noteFieldsCheck(note!);
if(result.state === NoteFieldsCheckResponse_State.EMPTY) {
if(isImageOcclusion) {
problem = tr.notetypesNoOcclusionCreated2();
} else {
problem = tr.addingTheFirstFieldIsEmpty();
}
}
if(result.state === NoteFieldsCheckResponse_State.MISSING_CLOZE) {
// TODO: askUser(tr.addingYouHaveAClozeDeletionNote())
return false;
}
if(result.state === NoteFieldsCheckResponse_State.NOTETYPE_NOT_CLOZE) {
problem = tr.addingClozeOutsideClozeNotetype();
}
if(result.state === NoteFieldsCheckResponse_State.FIELD_NOT_CLOZE) {
problem = tr.addingClozeOutsideClozeField();
}
return problem ? false : true;
}
async function addCurrentNoteInner(deckId: bigint) {
if(!await noteCanBeAdded()) {
return;
}
await addNote({
note: note!,
deckId,
});
await loadNewNote();
}
export async function addCurrentNote(deckId: bigint) {
if(mode !== "add") {
return;
}
if(isImageOcclusion) {
saveOcclusions();
await addCurrentNoteInner(deckId);
resetIOImageLoaded();
} else {
await addCurrentNoteInner(deckId);
}
}
export function focusIfField(x: number, y: number): boolean {
const elements = document.elementsFromPoint(x, y);
const first = elements[0].closest(".field-container");
@ -465,6 +516,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
editorUpdateNote,
decodeIriPaths,
noteFieldsCheck,
addNote,
} from "@generated/backend";
import { wrapInternal } from "@tslib/wrap";
import { getProfileConfig, getMeta, setMeta, getColConfig } from "@tslib/profile";
@ -605,11 +657,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
async function loadNote(
nid: bigint,
nid: bigint | null,
notetypeId: bigint,
focusTo: number,
originalNoteId: bigint | null,
) {
): Promise<bigint> {
const notetype = await getNotetype({
ntid: notetypeId,
});
@ -627,7 +679,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} else {
setNote(
await getNote({
nid,
nid!,
}),
);
}
@ -679,7 +731,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
html: imageField,
mode: {
kind: "edit",
noteId: nid,
noteId: nid!,
},
});
} else if (originalNoteId) {
@ -694,6 +746,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
}
await updateDuplicateDisplay();
triggerChanges();
return note!.id;
}
$: signalEditorState(editorState);
@ -749,6 +803,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
resetIOImageLoaded,
saveOcclusions,
setSticky,
addCurrentNote,
...oldEditorAdapter,
});