From f4d931131b274c9b88b081d3100f812525bf9401 Mon Sep 17 00:00:00 2001 From: Damien Elmes Date: Fri, 5 Mar 2021 14:54:28 +1000 Subject: [PATCH] undo support for note removing --- rslib/src/card.rs | 34 +++++++++++++-- rslib/src/collection/op.rs | 2 + rslib/src/notes.rs | 66 ++++++++++++++++++++++++++--- rslib/src/storage/graves/mod.rs | 30 ++++++++++--- rslib/src/storage/graves/remove.sql | 3 ++ 5 files changed, 119 insertions(+), 16 deletions(-) create mode 100644 rslib/src/storage/graves/remove.sql diff --git a/rslib/src/card.rs b/rslib/src/card.rs index 6d9f70259..b3ab66b1b 100644 --- a/rslib/src/card.rs +++ b/rslib/src/card.rs @@ -142,6 +142,24 @@ impl Undo for CardRemoved { } } +#[derive(Debug)] +pub(crate) struct CardGraveAdded(CardID, Usn); + +impl Undo for CardGraveAdded { + fn undo(self: Box, col: &mut crate::collection::Collection) -> Result<()> { + col.remove_card_grave_for_undo(self.0, self.1) + } +} + +#[derive(Debug)] +pub(crate) struct CardGraveRemoved(CardID, Usn); + +impl Undo for CardGraveRemoved { + fn undo(self: Box, col: &mut crate::collection::Collection) -> Result<()> { + col.add_card_grave(self.0, self.1) + } +} + #[derive(Debug)] pub(crate) struct CardUpdated(Card); @@ -222,10 +240,8 @@ impl Collection { let mut nids = HashSet::new(); for cid in cids { if let Some(card) = self.storage.get_card(*cid)? { - // fixme: undo nids.insert(card.note_id); - self.storage.remove_card(*cid)?; - self.storage.add_card_grave(*cid, usn)?; + self.remove_card_only(card, usn)?; } } for nid in nids { @@ -238,8 +254,8 @@ impl Collection { } pub(crate) fn remove_card_only(&mut self, card: Card, usn: Usn) -> Result<()> { + self.add_card_grave(card.id, usn)?; self.storage.remove_card(card.id)?; - self.storage.add_card_grave(card.id, usn)?; self.save_undo(Box::new(CardRemoved(card))); Ok(()) @@ -252,6 +268,16 @@ impl Collection { Ok(()) } + fn add_card_grave(&mut self, cid: CardID, usn: Usn) -> Result<()> { + self.save_undo(Box::new(CardGraveAdded(cid, usn))); + self.storage.add_card_grave(cid, usn) + } + + fn remove_card_grave_for_undo(&mut self, cid: CardID, usn: Usn) -> Result<()> { + self.save_undo(Box::new(CardGraveRemoved(cid, usn))); + self.storage.remove_card_grave(cid) + } + pub fn set_deck(&mut self, cards: &[CardID], deck_id: DeckID) -> Result<()> { let deck = self.get_deck(deck_id)?.ok_or(AnkiError::NotFound)?; if deck.is_filtered() { diff --git a/rslib/src/collection/op.rs b/rslib/src/collection/op.rs index 0d7790cce..ab61d3329 100644 --- a/rslib/src/collection/op.rs +++ b/rslib/src/collection/op.rs @@ -11,6 +11,7 @@ pub enum CollectionOp { Suspend, UnburyUnsuspend, AddNote, + RemoveNote, } impl Collection { @@ -22,6 +23,7 @@ impl Collection { CollectionOp::Suspend => TR::StudyingSuspend, CollectionOp::UnburyUnsuspend => TR::UndoUnburyUnsuspend, CollectionOp::AddNote => TR::UndoAddNote, + CollectionOp::RemoveNote => TR::StudyingDeleteNote, }; self.i18n.tr(key).to_string() diff --git a/rslib/src/notes.rs b/rslib/src/notes.rs index 907c11fb4..f0fd879f8 100644 --- a/rslib/src/notes.rs +++ b/rslib/src/notes.rs @@ -397,14 +397,24 @@ impl Collection { /// Remove a note. Cards must already have been deleted. pub(crate) fn remove_note_only(&mut self, nid: NoteID, usn: Usn) -> Result<()> { - if let Some(_note) = self.storage.get_note(nid)? { - // fixme: undo + if let Some(note) = self.storage.get_note(nid)? { + self.save_undo(Box::new(NoteRemoved(note))); self.storage.remove_note(nid)?; - self.storage.add_note_grave(nid, usn)?; + self.add_note_grave(nid, usn)?; } Ok(()) } + fn add_note_grave(&mut self, nid: NoteID, usn: Usn) -> Result<()> { + self.save_undo(Box::new(NoteGraveAdded(nid, usn))); + self.storage.add_note_grave(nid, usn) + } + + fn remove_note_grave_for_undo(&mut self, nid: NoteID, usn: Usn) -> Result<()> { + self.save_undo(Box::new(NoteGraveRemoved(nid, usn))); + self.storage.remove_note_grave(nid) + } + fn remove_note_for_undo(&mut self, note: Note) -> Result<()> { self.storage.remove_note(note.id)?; self.save_undo(Box::new(NoteRemoved(note))); @@ -414,11 +424,10 @@ impl Collection { /// Remove provided notes, and any cards that use them. pub(crate) fn remove_notes(&mut self, nids: &[NoteID]) -> Result<()> { let usn = self.usn()?; - self.transact(None, |col| { + self.transact(Some(CollectionOp::RemoveNote), |col| { for nid in nids { let nid = *nid; if let Some(_existing_note) = col.storage.get_note(nid)? { - // fixme: undo for card in col.storage.all_cards_of_note(nid)? { col.remove_card_only(card, usn)?; } @@ -591,6 +600,24 @@ impl Undo for NoteRemoved { } } +#[derive(Debug)] +pub(crate) struct NoteGraveAdded(NoteID, Usn); + +impl Undo for NoteGraveAdded { + fn undo(self: Box, col: &mut crate::collection::Collection) -> Result<()> { + col.remove_note_grave_for_undo(self.0, self.1) + } +} + +#[derive(Debug)] +pub(crate) struct NoteGraveRemoved(NoteID, Usn); + +impl Undo for NoteGraveRemoved { + fn undo(self: Box, col: &mut crate::collection::Collection) -> Result<()> { + col.add_note_grave(self.0, self.1) + } +} + #[derive(Debug)] pub(crate) struct NoteUpdated(Note); @@ -718,12 +745,20 @@ mod test { let assert_initial = |col: &mut Collection| -> Result<()> { assert_eq!(col.search_notes("")?.len(), 0); assert_eq!(col.search_cards("", SortMode::NoOrder)?.len(), 0); + assert_eq!( + col.storage.db_scalar::("select count() from graves")?, + 0 + ); Ok(()) }; let assert_after_add = |col: &mut Collection| -> Result<()> { assert_eq!(col.search_notes("")?.len(), 1); assert_eq!(col.search_cards("", SortMode::NoOrder)?.len(), 2); + assert_eq!( + col.storage.db_scalar::("select count() from graves")?, + 0 + ); Ok(()) }; @@ -743,6 +778,27 @@ mod test { col.undo()?; assert_initial(&mut col)?; + let assert_after_remove = |col: &mut Collection| -> Result<()> { + assert_eq!(col.search_notes("")?.len(), 0); + assert_eq!(col.search_cards("", SortMode::NoOrder)?.len(), 0); + // 1 note + 2 cards + assert_eq!( + col.storage.db_scalar::("select count() from graves")?, + 3 + ); + Ok(()) + }; + + col.redo()?; + assert_after_add(&mut col)?; + let nids = col.search_notes("")?; + col.remove_notes(&nids)?; + assert_after_remove(&mut col)?; + col.undo()?; + assert_after_add(&mut col)?; + col.redo()?; + assert_after_remove(&mut col)?; + Ok(()) } } diff --git a/rslib/src/storage/graves/mod.rs b/rslib/src/storage/graves/mod.rs index 5a18602bd..e9884126f 100644 --- a/rslib/src/storage/graves/mod.rs +++ b/rslib/src/storage/graves/mod.rs @@ -23,13 +23,6 @@ enum GraveKind { } impl SqliteStorage { - fn add_grave(&self, oid: i64, kind: GraveKind, usn: Usn) -> Result<()> { - self.db - .prepare_cached(include_str!("add.sql"))? - .execute(params![usn, oid, kind as u8])?; - Ok(()) - } - pub(crate) fn clear_all_graves(&self) -> Result<()> { self.db.execute("delete from graves", NO_PARAMS)?; Ok(()) @@ -47,6 +40,14 @@ impl SqliteStorage { self.add_grave(did.0, GraveKind::Deck, usn) } + pub(crate) fn remove_card_grave(&self, cid: CardID) -> Result<()> { + self.remove_grave(cid.0, GraveKind::Card) + } + + pub(crate) fn remove_note_grave(&self, nid: NoteID) -> Result<()> { + self.remove_grave(nid.0, GraveKind::Note) + } + pub(crate) fn pending_graves(&self, pending_usn: Usn) -> Result { let mut stmt = self.db.prepare(&format!( "select oid, type from graves where {}", @@ -74,4 +75,19 @@ impl SqliteStorage { .execute(&[new_usn])?; Ok(()) } + + fn add_grave(&self, oid: i64, kind: GraveKind, usn: Usn) -> Result<()> { + self.db + .prepare_cached(include_str!("add.sql"))? + .execute(params![usn, oid, kind as u8])?; + Ok(()) + } + + /// Only useful when undoing + fn remove_grave(&self, oid: i64, kind: GraveKind) -> Result<()> { + self.db + .prepare_cached(include_str!("remove.sql"))? + .execute(params![oid, kind as u8])?; + Ok(()) + } } diff --git a/rslib/src/storage/graves/remove.sql b/rslib/src/storage/graves/remove.sql new file mode 100644 index 000000000..a1430c405 --- /dev/null +++ b/rslib/src/storage/graves/remove.sql @@ -0,0 +1,3 @@ +DELETE FROM graves +WHERE oid = ? + AND type = ? \ No newline at end of file