Fix "Create copy" for IO notes (#3730)

* expose get_image_occlusion_fields

* fix create copy for io

* revert current impl

* passthru original note id when creating copy

* add IOCloningMode

* fix create copy for io
This commit is contained in:
llama 2025-01-17 13:03:00 +08:00 committed by GitHub
parent 3acca96ed8
commit 9c0911891d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 49 additions and 21 deletions

View file

@ -78,7 +78,7 @@ class AddCards(QMainWindow):
new_note.fields = note.fields[:] new_note.fields = note.fields[:]
new_note.tags = note.tags[:] new_note.tags = note.tags[:]
self.setAndFocusNote(new_note) self.setAndFocusNote(new_note, orig_note_id=note.id)
def setupEditor(self) -> None: def setupEditor(self) -> None:
self.editor = aqt.editor.Editor( self.editor = aqt.editor.Editor(
@ -143,8 +143,8 @@ class AddCards(QMainWindow):
b.setEnabled(False) b.setEnabled(False)
self.historyButton = b self.historyButton = b
def setAndFocusNote(self, note: Note) -> None: def setAndFocusNote(self, note: Note, orig_note_id: NoteId | None = None) -> None:
self.editor.set_note(note, focusTo=0) self.editor.set_note(note, focusTo=0, orig_note_id=orig_note_id)
def show_notetype_selector(self) -> None: def show_notetype_selector(self) -> None:
self.editor.call_after_note_saved(self.notetype_chooser.choose_notetype) self.editor.call_after_note_saved(self.notetype_chooser.choose_notetype)

View file

@ -524,20 +524,26 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
###################################################################### ######################################################################
def set_note( def set_note(
self, note: Note | None, hide: bool = True, focusTo: int | None = None self,
note: Note | None,
hide: bool = True,
focusTo: int | None = None,
orig_note_id: NoteId | None = None,
) -> None: ) -> None:
"Make NOTE the current note." "Make NOTE the current note."
self.note = note self.note = note
self.currentField = None self.currentField = None
if self.note: if self.note:
self.loadNote(focusTo=focusTo) self.loadNote(focusTo=focusTo, orig_note_id=orig_note_id)
elif hide: elif hide:
self.widget.hide() self.widget.hide()
def loadNoteKeepingFocus(self) -> None: def loadNoteKeepingFocus(self) -> None:
self.loadNote(self.currentField) self.loadNote(self.currentField)
def loadNote(self, focusTo: int | None = None) -> None: def loadNote(
self, focusTo: int | None = None, orig_note_id: NoteId | None = None
) -> None:
if not self.note: if not self.note:
return return
@ -596,12 +602,13 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
sticky = [field["sticky"] for field in self.note_type()["flds"]] sticky = [field["sticky"] for field in self.note_type()["flds"]]
js += " setSticky(%s);" % json.dumps(sticky) js += " setSticky(%s);" % json.dumps(sticky)
if ( if self.current_notetype_is_image_occlusion():
self.editorMode != EditorMode.ADD_CARDS if self.editorMode is not EditorMode.ADD_CARDS:
and self.current_notetype_is_image_occlusion() io_options = self._create_edit_io_options(note_id=self.note.id)
): js += " setupMaskEditor(%s);" % json.dumps(io_options)
io_options = self._create_edit_io_options(note_id=self.note.id) elif orig_note_id:
js += " setupMaskEditor(%s);" % json.dumps(io_options) io_options = self._create_clone_io_options(cloned_note_id=orig_note_id)
js += " setupMaskEditor(%s);" % json.dumps(io_options)
js = gui_hooks.editor_will_load_note(js, self.note, self) js = gui_hooks.editor_will_load_note(js, self.note, self)
self.web.evalWithCallback( self.web.evalWithCallback(
@ -1161,6 +1168,12 @@ require("anki/ui").loaded.then(() => require("anki/NoteEditor").instances[0].too
"html": image_field_html, "html": image_field_html,
} }
@staticmethod
def _create_clone_io_options(cloned_note_id: NoteId) -> dict:
return {
"mode": {"kind": "add", "clonedNoteId": cloned_note_id},
}
@staticmethod @staticmethod
def _create_edit_io_options(note_id: NoteId) -> dict: def _create_edit_io_options(note_id: NoteId) -> dict:
return {"mode": {"kind": "edit", "noteId": note_id}} return {"mode": {"kind": "edit", "noteId": note_id}}

View file

@ -431,7 +431,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
imageOcclusionMode = undefined; imageOcclusionMode = undefined;
await tick(); await tick();
imageOcclusionMode = options.mode; imageOcclusionMode = options.mode;
if (options.mode.kind === "add") { if (options.mode.kind === "add" && !("clonedNoteId" in options.mode)) {
fieldStores[ioFields.image].set(options.html); fieldStores[ioFields.image].set(options.html);
// the image field is set programmatically and does not need debouncing // the image field is set programmatically and does not need debouncing
// commit immediately to avoid a race condition with the occlusions field // commit immediately to avoid a race condition with the occlusions field

View file

@ -44,10 +44,19 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
function init(_node: HTMLDivElement) { function init(_node: HTMLDivElement) {
if (mode.kind == "add") { if (mode.kind == "add") {
// Editing occlusions on a new note through the "Add" window if ("clonedNoteId" in mode) {
setupMaskEditor(mode.imagePath, onImageLoaded).then((canvas1) => { // Editing occlusions on a new note cloned from an existing note via "Create copy"
canvas = canvas1; setupMaskEditorForEdit(mode.clonedNoteId, onImageLoaded).then(
}); (canvas1) => {
canvas = canvas1;
},
);
} else {
// Editing occlusions on a new note through the "Add" window
setupMaskEditor(mode.imagePath, onImageLoaded).then((canvas1) => {
canvas = canvas1;
});
}
} else { } else {
// Editing occlusions on an existing note through the "Browser" window // Editing occlusions on an existing note through the "Browser" window
setupMaskEditorForEdit(mode.noteId, onImageLoaded).then((canvas1) => { setupMaskEditorForEdit(mode.noteId, onImageLoaded).then((canvas1) => {

View file

@ -7,7 +7,7 @@ import * as tr from "@generated/ftl";
import { get } from "svelte/store"; import { get } from "svelte/store";
import { mount } from "svelte"; import { mount } from "svelte";
import type { IOMode } from "./lib"; import type { IOAddingMode, IOMode } from "./lib";
import { exportShapesToClozeDeletions } from "./shapes/to-cloze"; import { exportShapesToClozeDeletions } from "./shapes/to-cloze";
import { notesDataStore, tagsWritable } from "./store"; import { notesDataStore, tagsWritable } from "./store";
import Toast from "./Toast.svelte"; import Toast from "./Toast.svelte";
@ -40,8 +40,9 @@ export const addOrUpdateNote = async function(
showResult(mode.noteId, result, noteCount); showResult(mode.noteId, result, noteCount);
} else { } else {
const result = await addImageOcclusionNote({ const result = await addImageOcclusionNote({
notetypeId: BigInt(mode.notetypeId), // IOCloningMode is not used on mobile
imagePath: mode.imagePath, notetypeId: BigInt((<IOAddingMode> mode).notetypeId),
imagePath: (<IOAddingMode> mode).imagePath,
occlusions: occlusionCloze, occlusions: occlusionCloze,
header, header,
backExtra, backExtra,

View file

@ -7,9 +7,14 @@ export interface IOAddingMode {
imagePath: string; imagePath: string;
} }
export interface IOCloningMode {
kind: "add";
clonedNoteId: number;
}
export interface IOEditingMode { export interface IOEditingMode {
kind: "edit"; kind: "edit";
noteId: number; noteId: number;
} }
export type IOMode = IOAddingMode | IOEditingMode; export type IOMode = IOAddingMode | IOEditingMode | IOCloningMode;