diff --git a/pylib/anki/collection.py b/pylib/anki/collection.py index a400c9988..933f5b6a7 100644 --- a/pylib/anki/collection.py +++ b/pylib/anki/collection.py @@ -39,7 +39,7 @@ from anki.cards import Card, CardId from anki.config import Config, ConfigManager from anki.consts import * from anki.dbproxy import DBProxy -from anki.decks import DeckId, DeckManager +from anki.decks import Deck, DeckId, DeckManager from anki.errors import AbortSchemaModification, DBError from anki.lang import FormatTimeSpan from anki.media import MediaManager, media_paths_from_col_path @@ -331,6 +331,10 @@ class Collection: note=note._to_backend_note(), skip_undo_entry=False ) + def get_deck(self, id: DeckId) -> Deck: + "Get a new-style deck object. Currently read-only." + return self._backend.get_deck(id) + getCard = get_card getNote = get_note diff --git a/pylib/anki/decks.py b/pylib/anki/decks.py index 0b593d68e..7e758a4eb 100644 --- a/pylib/anki/decks.py +++ b/pylib/anki/decks.py @@ -30,6 +30,9 @@ defaultDynamicDeck = 1 DeckDict = Dict[str, Any] DeckConfigDict = Dict[str, Any] +# currently only supports read-only access +Deck = _pb.Deck + DeckId = NewType("DeckId", int) DeckConfId = NewType("DeckConfId", int) diff --git a/qt/aqt/deckbrowser.py b/qt/aqt/deckbrowser.py index eb759c3fc..6e459a520 100644 --- a/qt/aqt/deckbrowser.py +++ b/qt/aqt/deckbrowser.py @@ -9,7 +9,7 @@ from typing import Any import aqt from anki.collection import OpChanges -from anki.decks import DeckId, DeckTreeNode +from anki.decks import Deck, DeckId, DeckTreeNode from anki.utils import intTime from aqt import AnkiQt, gui_hooks from aqt.operations.deck import ( @@ -270,13 +270,14 @@ class DeckBrowser: self.mw.onExport(did=did) def _rename(self, did: DeckId) -> None: - deck = self.mw.col.decks.get(did) - current_name = deck["name"] - new_name = getOnlyText(tr.decks_new_deck_name(), default=current_name) - if not new_name or new_name == current_name: - return + def prompt(deck: Deck) -> None: + new_name = getOnlyText(tr.decks_new_deck_name(), default=deck.name) + if not new_name or new_name == deck.name: + return + else: + rename_deck(mw=self.mw, deck_id=did, new_name=new_name) - rename_deck(mw=self.mw, deck_id=did, new_name=new_name) + self.mw.query_op(lambda: self.mw.col.get_deck(did), success=prompt) def _options(self, did: DeckId) -> None: # select the deck first, because the dyn deck conf assumes the deck diff --git a/qt/aqt/sidebar.py b/qt/aqt/sidebar.py index 6cfa9a196..037ff48da 100644 --- a/qt/aqt/sidebar.py +++ b/qt/aqt/sidebar.py @@ -8,7 +8,7 @@ from typing import Dict, Iterable, List, Optional, Tuple, cast import aqt from anki.collection import Config, OpChanges, SearchJoiner, SearchNode -from anki.decks import DeckId, DeckTreeNode +from anki.decks import Deck, DeckId, DeckTreeNode from anki.models import NotetypeId from anki.notes import Note from anki.tags import TagTreeNode @@ -1148,22 +1148,26 @@ class SidebarTreeView(QTreeView): ########################### def rename_deck(self, item: SidebarItem, new_name: str) -> None: - deck = self.mw.col.decks.get(DeckId(item.id)) if not new_name: return new_name = item.name_prefix + new_name - if new_name == deck["name"]: - return + deck_id = DeckId(item.id) - rename_deck( - mw=self.mw, - deck_id=DeckId(item.id), - new_name=new_name, - after_rename=lambda: self.refresh( - lambda other: other.item_type == SidebarItemType.DECK - and other.id == item.id - ), - ) + def after_fetch(deck: Deck) -> None: + if new_name == deck.name: + return + + rename_deck( + mw=self.mw, + deck_id=deck_id, + new_name=new_name, + after_rename=lambda: self.refresh( + lambda other: other.item_type == SidebarItemType.DECK + and other.id == item.id + ), + ) + + self.mw.query_op(lambda: self.mw.col.get_deck(deck_id), success=after_fetch) def delete_decks(self, _item: SidebarItem) -> None: remove_decks(mw=self.mw, parent=self.browser, deck_ids=self._selected_decks()) diff --git a/rslib/backend.proto b/rslib/backend.proto index 5227d4d41..331e1407f 100644 --- a/rslib/backend.proto +++ b/rslib/backend.proto @@ -142,6 +142,7 @@ service DecksService { rpc DeckTreeLegacy(Empty) returns (Json); rpc GetAllDecksLegacy(Empty) returns (Json); rpc GetDeckIdByName(String) returns (DeckId); + rpc GetDeck(DeckId) returns (Deck); rpc GetDeckLegacy(DeckId) returns (Json); rpc GetDeckNames(GetDeckNamesIn) returns (DeckNames); rpc NewDeckLegacy(Bool) returns (Json); diff --git a/rslib/src/backend/decks.rs b/rslib/src/backend/decks.rs index 1a4b34fbd..99a41eb3a 100644 --- a/rslib/src/backend/decks.rs +++ b/rslib/src/backend/decks.rs @@ -78,6 +78,17 @@ impl DecksService for Backend { }) } + fn get_deck(&self, input: pb::DeckId) -> Result { + self.with_col(|col| { + Ok(col + .storage + .get_deck(input.into())? + .ok_or(AnkiError::NotFound)? + .with_human_name() + .into()) + }) + } + fn get_deck_legacy(&self, input: pb::DeckId) -> Result { self.with_col(|col| { let deck: DeckSchema11 = col diff --git a/rslib/src/decks/mod.rs b/rslib/src/decks/mod.rs index edbec1e4f..44ed7fb10 100644 --- a/rslib/src/decks/mod.rs +++ b/rslib/src/decks/mod.rs @@ -153,6 +153,12 @@ impl Deck { String::new() } } + + // Mutate name to human representation for sharing. + pub fn with_human_name(mut self) -> Self { + self.name = self.human_name(); + self + } } fn invalid_char_for_deck_component(c: char) -> bool {