undo support for note removing

This commit is contained in:
Damien Elmes 2021-03-05 14:54:28 +10:00
parent 9445e2ee22
commit f4d931131b
5 changed files with 119 additions and 16 deletions

View file

@ -142,6 +142,24 @@ impl Undo for CardRemoved {
}
}
#[derive(Debug)]
pub(crate) struct CardGraveAdded(CardID, Usn);
impl Undo for CardGraveAdded {
fn undo(self: Box<Self>, 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<Self>, 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() {

View file

@ -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()

View file

@ -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<Self>, 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<Self>, 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::<u32>("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::<u32>("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::<u32>("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(())
}
}

View file

@ -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<Graves> {
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(())
}
}

View file

@ -0,0 +1,3 @@
DELETE FROM graves
WHERE oid = ?
AND type = ?