diff --git a/proto/anki/notes.proto b/proto/anki/notes.proto index 3c5fc7759..ced401e76 100644 --- a/proto/anki/notes.proto +++ b/proto/anki/notes.proto @@ -15,7 +15,7 @@ service NotesService { rpc AddNote(AddNoteRequest) returns (AddNoteResponse); rpc DefaultsForAdding(DefaultsForAddingRequest) returns (DeckAndNotetype); rpc DefaultDeckForNotetype(notetypes.NotetypeId) returns (decks.DeckId); - rpc UpdateNote(UpdateNoteRequest) returns (collection.OpChanges); + rpc UpdateNotes(UpdateNotesRequest) returns (collection.OpChanges); rpc GetNote(NoteId) returns (Note); rpc RemoveNotes(RemoveNotesRequest) returns (collection.OpChangesWithCount); rpc ClozeNumbersInNote(Note) returns (ClozeNumbersInNoteResponse); @@ -56,8 +56,8 @@ message AddNoteResponse { collection.OpChanges changes = 2; } -message UpdateNoteRequest { - Note note = 1; +message UpdateNotesRequest { + repeated Note notes = 1; bool skip_undo_entry = 2; } diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index ca250c77f..adc61d0f0 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -329,12 +329,17 @@ class Collection(DeprecatedNamesMixin): def get_note(self, id: NoteId) -> Note: return Note(self, id=id) + def update_notes(self, notes: Sequence[Note]) -> OpChanges: + """Save note changes to database, and add an undo entry. + Unlike note.flush(), this will invalidate any current checkpoint.""" + return self._backend.update_notes( + notes=[n._to_backend_note() for n in notes], skip_undo_entry=False + ) + def update_note(self, note: Note) -> OpChanges: """Save note changes to database, and add an undo entry. Unlike note.flush(), this will invalidate any current checkpoint.""" - return self._backend.update_note( - note=note._to_backend_note(), skip_undo_entry=False - ) + return self.update_notes([note]) getCard = get_card getNote = get_note diff --git a/pylib/anki/notes.py b/pylib/anki/notes.py index f63da1083..2c357d6b7 100644 --- a/pylib/anki/notes.py +++ b/pylib/anki/notes.py @@ -79,8 +79,8 @@ class Note(DeprecatedNamesMixin): """This preserves any current checkpoint. For an undo entry, use col.update_note() instead.""" assert self.id != 0 - self.col._backend.update_note( - note=self._to_backend_note(), skip_undo_entry=True + self.col._backend.update_notes( + notes=[self._to_backend_note()], skip_undo_entry=True ) def joined_fields(self) -> str: diff --git a/rslib/src/backend/notes.rs b/rslib/src/backend/notes.rs index e54123910..360a69a47 100644 --- a/rslib/src/backend/notes.rs +++ b/rslib/src/backend/notes.rs @@ -49,10 +49,14 @@ impl NotesService for Backend { }) } - fn update_note(&self, input: pb::UpdateNoteRequest) -> Result { + fn update_notes(&self, input: pb::UpdateNotesRequest) -> Result { self.with_col(|col| { - let mut note: Note = input.note.ok_or(AnkiError::NotFound)?.into(); - col.update_note_maybe_undoable(&mut note, !input.skip_undo_entry) + let notes = input + .notes + .into_iter() + .map(Into::into) + .collect::>(); + col.update_notes_maybe_undoable(notes, !input.skip_undo_entry) }) .map(Into::into) } diff --git a/rslib/src/notes/mod.rs b/rslib/src/notes/mod.rs index 9a9152e99..f78466e74 100644 --- a/rslib/src/notes/mod.rs +++ b/rslib/src/notes/mod.rs @@ -348,19 +348,26 @@ impl Collection { #[cfg(test)] pub(crate) fn update_note(&mut self, note: &mut Note) -> Result> { - self.update_note_maybe_undoable(note, true) + self.transact(Op::UpdateNote, |col| col.update_note_inner(note)) } - pub(crate) fn update_note_maybe_undoable( + pub(crate) fn update_notes_maybe_undoable( &mut self, - note: &mut Note, + notes: Vec, undoable: bool, ) -> Result> { if undoable { - self.transact(Op::UpdateNote, |col| col.update_note_inner(note)) + self.transact(Op::UpdateNote, |col| { + for mut note in notes { + col.update_note_inner(&mut note)?; + } + Ok(()) + }) } else { self.transact_no_undo(|col| { - col.update_note_inner(note)?; + for mut note in notes { + col.update_note_inner(&mut note)?; + } Ok(OpOutput { output: (), changes: OpChanges {