mirror of
https://github.com/ankitects/anki.git
synced 2025-09-24 16:56:36 -04:00
undo support for note removing
This commit is contained in:
parent
9445e2ee22
commit
f4d931131b
5 changed files with 119 additions and 16 deletions
|
@ -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() {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
||||
|
|
3
rslib/src/storage/graves/remove.sql
Normal file
3
rslib/src/storage/graves/remove.sql
Normal file
|
@ -0,0 +1,3 @@
|
|||
DELETE FROM graves
|
||||
WHERE oid = ?
|
||||
AND type = ?
|
Loading…
Reference in a new issue