add a separate 'rename deck' method

This commit is contained in:
Damien Elmes 2021-03-11 18:54:30 +10:00
parent 4bd120cc4b
commit 984e2c2666
7 changed files with 84 additions and 43 deletions

View file

@ -250,11 +250,13 @@ class DeckManager:
deck=to_json_bytes(g), preserve_usn_and_mtime=preserve_usn deck=to_json_bytes(g), preserve_usn_and_mtime=preserve_usn
) )
def rename(self, g: Deck, newName: str) -> None: def rename(self, deck: Union[Deck, int], new_name: str) -> None:
"Rename deck prefix to NAME if not exists. Updates children." "Rename deck prefix to NAME if not exists. Updates children."
g["name"] = newName if isinstance(deck, int):
self.update(g, preserve_usn=False) deck_id = deck
return else:
deck_id = deck["id"]
self.col._backend.rename_deck(deck_id=deck_id, new_name=new_name)
# Drag/drop # Drag/drop
############################################################# #############################################################

View file

@ -132,6 +132,7 @@ service DecksService {
rpc NewDeckLegacy(Bool) returns (Json); rpc NewDeckLegacy(Bool) returns (Json);
rpc RemoveDeck(DeckID) returns (Empty); rpc RemoveDeck(DeckID) returns (Empty);
rpc DragDropDecks(DragDropDecksIn) returns (Empty); rpc DragDropDecks(DragDropDecksIn) returns (Empty);
rpc RenameDeck(RenameDeckIn) returns (Empty);
} }
service NotesService { service NotesService {
@ -1448,3 +1449,8 @@ message DeckAndNotetype {
int64 deck_id = 1; int64 deck_id = 1;
int64 notetype_id = 2; int64 notetype_id = 2;
} }
message RenameDeckIn {
int64 deck_id = 1;
string new_name = 2;
}

View file

@ -124,4 +124,21 @@ impl DecksService for Backend {
self.with_col(|col| col.drag_drop_decks(&source_dids, target_did)) self.with_col(|col| col.drag_drop_decks(&source_dids, target_did))
.map(Into::into) .map(Into::into)
} }
fn rename_deck(&self, input: pb::RenameDeckIn) -> Result<pb::Empty> {
self.with_col(|col| col.rename_deck(input.deck_id.into(), &input.new_name))
.map(Into::into)
}
}
impl From<pb::DeckId> for DeckID {
fn from(did: pb::DeckId) -> Self {
DeckID(did.did)
}
}
impl From<DeckID> for pb::DeckId {
fn from(did: DeckID) -> Self {
pb::DeckId { did: did.0 }
}
} }

View file

@ -63,18 +63,6 @@ impl From<pb::NoteTypeId> for NoteTypeID {
} }
} }
impl From<pb::DeckId> for DeckID {
fn from(did: pb::DeckId) -> Self {
DeckID(did.did)
}
}
impl From<DeckID> for pb::DeckId {
fn from(did: DeckID) -> Self {
pb::DeckId { did: did.0 }
}
}
impl From<pb::DeckConfigId> for DeckConfID { impl From<pb::DeckConfigId> for DeckConfID {
fn from(dcid: pb::DeckConfigId) -> Self { fn from(dcid: pb::DeckConfigId) -> Self {
DeckConfID(dcid.dcid) DeckConfID(dcid.dcid)

View file

@ -292,30 +292,39 @@ impl Collection {
}) })
} }
pub(crate) fn update_deck(&mut self, deck: &mut Deck) -> Result<()> { pub fn update_deck(&mut self, deck: &mut Deck) -> Result<()> {
self.transact(Some(UndoableOpKind::UpdateDeck), |col| { self.transact(Some(UndoableOpKind::UpdateDeck), |col| {
let usn = col.usn()?; let existing_deck = col.storage.get_deck(deck.id)?.ok_or(AnkiError::NotFound)?;
col.prepare_deck_for_update(deck, usn)?; col.update_deck_inner(deck, existing_deck, col.usn()?)
})
}
pub fn rename_deck(&mut self, did: DeckID, new_human_name: &str) -> Result<()> {
self.transact(Some(UndoableOpKind::RenameDeck), |col| {
let existing_deck = col.storage.get_deck(did)?.ok_or(AnkiError::NotFound)?;
let mut deck = existing_deck.clone();
deck.name = human_deck_name_to_native(new_human_name);
col.update_deck_inner(&mut deck, existing_deck, col.usn()?)
})
}
fn update_deck_inner(&mut self, deck: &mut Deck, original: Deck, usn: Usn) -> Result<()> {
self.prepare_deck_for_update(deck, usn)?;
deck.set_modified(usn); deck.set_modified(usn);
if let Some(existing_deck) = col.storage.get_deck(deck.id)? { let name_changed = original.name != deck.name;
let name_changed = existing_deck.name != deck.name;
if name_changed { if name_changed {
// match closest parent name // match closest parent name
col.match_or_create_parents(deck, usn)?; self.match_or_create_parents(deck, usn)?;
// rename children // rename children
col.rename_child_decks(&existing_deck, &deck.name, usn)?; self.rename_child_decks(&original, &deck.name, usn)?;
} }
col.update_single_deck_undoable(deck, &existing_deck)?; self.update_single_deck_undoable(deck, original)?;
if name_changed { if name_changed {
// after updating, we need to ensure all grandparents exist, which may not be the case // after updating, we need to ensure all grandparents exist, which may not be the case
// in the parent->child case // in the parent->child case
col.create_missing_parents(&deck.name, usn)?; self.create_missing_parents(&deck.name, usn)?;
} }
Ok(()) Ok(())
} else {
Err(AnkiError::invalid_input("updating non-existent deck"))
}
})
} }
/// Add/update a single deck when syncing/importing. Ensures name is unique /// Add/update a single deck when syncing/importing. Ensures name is unique
@ -377,7 +386,7 @@ impl Collection {
let new_name = format!("{}\x1f{}", new_name, child_only.join("\x1f")); let new_name = format!("{}\x1f{}", new_name, child_only.join("\x1f"));
child.name = new_name; child.name = new_name;
child.set_modified(usn); child.set_modified(usn);
self.update_single_deck_undoable(&mut child, &original)?; self.update_single_deck_undoable(&mut child, original)?;
} }
Ok(()) Ok(())
@ -600,7 +609,7 @@ impl Collection {
deck.reset_stats_if_day_changed(today); deck.reset_stats_if_day_changed(today);
mutator(&mut deck.common); mutator(&mut deck.common);
deck.set_modified(usn); deck.set_modified(usn);
self.update_single_deck_undoable(deck, &original) self.update_single_deck_undoable(deck, original)
} }
pub fn drag_drop_decks( pub fn drag_drop_decks(
@ -761,6 +770,23 @@ mod test {
col.add_or_update_deck(&mut middle)?; col.add_or_update_deck(&mut middle)?;
assert_eq!(middle.name, "other+"); assert_eq!(middle.name, "other+");
// public function takes human name
col.rename_deck(middle.id, "one::two")?;
assert_eq!(
sorted_names(&col),
vec![
"Default",
"one",
"one::two",
"one::two::baz",
"one::two::baz2",
"other",
"quux",
"quux::foo",
"quux::foo::baz",
]
);
Ok(()) Ok(())
} }

View file

@ -22,7 +22,7 @@ impl Collection {
.storage .storage
.get_deck(deck.id)? .get_deck(deck.id)?
.ok_or_else(|| AnkiError::invalid_input("deck disappeared"))?; .ok_or_else(|| AnkiError::invalid_input("deck disappeared"))?;
self.update_single_deck_undoable(&mut *deck, &current) self.update_single_deck_undoable(&mut *deck, current)
} }
UndoableDeckChange::Removed(deck) => self.restore_deleted_deck(*deck), UndoableDeckChange::Removed(deck) => self.restore_deleted_deck(*deck),
UndoableDeckChange::GraveAdded(e) => self.remove_deck_grave(e.0, e.1), UndoableDeckChange::GraveAdded(e) => self.remove_deck_grave(e.0, e.1),
@ -52,10 +52,10 @@ impl Collection {
pub(super) fn update_single_deck_undoable( pub(super) fn update_single_deck_undoable(
&mut self, &mut self,
deck: &mut Deck, deck: &mut Deck,
original: &Deck, original: Deck,
) -> Result<()> { ) -> Result<()> {
self.state.deck_cache.clear(); self.state.deck_cache.clear();
self.save_undo(UndoableDeckChange::Updated(Box::new(original.clone()))); self.save_undo(UndoableDeckChange::Updated(Box::new(original)));
self.storage.update_deck(deck) self.storage.update_deck(deck)
} }

View file

@ -11,6 +11,7 @@ pub enum UndoableOpKind {
Bury, Bury,
RemoveDeck, RemoveDeck,
RemoveNote, RemoveNote,
RenameDeck,
Suspend, Suspend,
UnburyUnsuspend, UnburyUnsuspend,
UpdateCard, UpdateCard,
@ -35,6 +36,7 @@ impl Collection {
UndoableOpKind::Bury => TR::StudyingBury, UndoableOpKind::Bury => TR::StudyingBury,
UndoableOpKind::RemoveDeck => TR::DecksDeleteDeck, UndoableOpKind::RemoveDeck => TR::DecksDeleteDeck,
UndoableOpKind::RemoveNote => TR::StudyingDeleteNote, UndoableOpKind::RemoveNote => TR::StudyingDeleteNote,
UndoableOpKind::RenameDeck => TR::ActionsRenameDeck,
UndoableOpKind::Suspend => TR::StudyingSuspend, UndoableOpKind::Suspend => TR::StudyingSuspend,
UndoableOpKind::UnburyUnsuspend => TR::UndoUnburyUnsuspend, UndoableOpKind::UnburyUnsuspend => TR::UndoUnburyUnsuspend,
UndoableOpKind::UpdateCard => TR::UndoUpdateCard, UndoableOpKind::UpdateCard => TR::UndoUpdateCard,