support updating multiple cards in one transaction/undo op

This commit is contained in:
Damien Elmes 2021-08-02 16:47:05 +10:00
parent cc65196b0a
commit 88a3fd8d7b
5 changed files with 32 additions and 15 deletions

View file

@ -10,7 +10,7 @@ import "anki/collection.proto";
service CardsService { service CardsService {
rpc GetCard(CardId) returns (Card); rpc GetCard(CardId) returns (Card);
rpc UpdateCard(UpdateCardRequest) returns (collection.OpChanges); rpc UpdateCards(UpdateCardsRequest) returns (collection.OpChanges);
rpc RemoveCards(RemoveCardsRequest) returns (generic.Empty); rpc RemoveCards(RemoveCardsRequest) returns (generic.Empty);
rpc SetDeck(SetDeckRequest) returns (collection.OpChangesWithCount); rpc SetDeck(SetDeckRequest) returns (collection.OpChangesWithCount);
rpc SetFlag(SetFlagRequest) returns (collection.OpChangesWithCount); rpc SetFlag(SetFlagRequest) returns (collection.OpChangesWithCount);
@ -44,8 +44,8 @@ message Card {
string data = 18; string data = 18;
} }
message UpdateCardRequest { message UpdateCardsRequest {
Card card = 1; repeated Card cards = 1;
bool skip_undo_entry = 2; bool skip_undo_entry = 2;
} }

View file

@ -114,8 +114,8 @@ class Card(DeprecatedNamesMixin):
def flush(self) -> None: def flush(self) -> None:
hooks.card_will_flush(self) hooks.card_will_flush(self)
if self.id != 0: if self.id != 0:
self.col._backend.update_card( self.col._backend.update_cards(
card=self._to_backend_card(), skip_undo_entry=True cards=[self._to_backend_card()], skip_undo_entry=True
) )
else: else:
raise Exception("card.flush() expects an existing card") raise Exception("card.flush() expects an existing card")

View file

@ -314,10 +314,17 @@ class Collection(DeprecatedNamesMixin):
def get_card(self, id: CardId) -> Card: def get_card(self, id: CardId) -> Card:
return Card(self, id) return Card(self, id)
def update_card(self, card: Card) -> None: def update_cards(self, cards: Sequence[Card]) -> OpChanges:
"""Save card changes to database, and add an undo entry. """Save card changes to database, and add an undo entry.
Unlike card.flush(), this will invalidate any current checkpoint.""" Unlike card.flush(), this will invalidate any current checkpoint."""
self._backend.update_card(card=card._to_backend_card(), skip_undo_entry=False) return self._backend.update_cards(
cards=[c._to_backend_card() for c in cards], skip_undo_entry=False
)
def update_card(self, card: Card) -> OpChanges:
"""Save card changes to database, and add an undo entry.
Unlike card.flush(), this will invalidate any current checkpoint."""
return self.update_cards([card])
def get_note(self, id: NoteId) -> Note: def get_note(self, id: NoteId) -> Note:
return Note(self, id=id) return Note(self, id=id)

View file

@ -21,10 +21,14 @@ impl CardsService for Backend {
}) })
} }
fn update_card(&self, input: pb::UpdateCardRequest) -> Result<pb::OpChanges> { fn update_cards(&self, input: pb::UpdateCardsRequest) -> Result<pb::OpChanges> {
self.with_col(|col| { self.with_col(|col| {
let mut card: Card = input.card.ok_or(AnkiError::NotFound)?.try_into()?; let cards = input
col.update_card_maybe_undoable(&mut card, !input.skip_undo_entry) .cards
.into_iter()
.map(TryInto::try_into)
.collect::<Result<Vec<Card>, AnkiError>>()?;
col.update_cards_maybe_undoable(cards, !input.skip_undo_entry)
}) })
.map(Into::into) .map(Into::into)
} }

View file

@ -159,19 +159,25 @@ impl Card {
} }
impl Collection { impl Collection {
pub(crate) fn update_card_maybe_undoable( pub(crate) fn update_cards_maybe_undoable(
&mut self, &mut self,
card: &mut Card, cards: Vec<Card>,
undoable: bool, undoable: bool,
) -> Result<OpOutput<()>> { ) -> Result<OpOutput<()>> {
let existing = self.storage.get_card(card.id)?.ok_or(AnkiError::NotFound)?;
if undoable { if undoable {
self.transact(Op::UpdateCard, |col| { self.transact(Op::UpdateCard, |col| {
col.update_card_inner(card, existing, col.usn()?) for mut card in cards {
let existing = col.storage.get_card(card.id)?.ok_or(AnkiError::NotFound)?;
col.update_card_inner(&mut card, existing, col.usn()?)?
}
Ok(())
}) })
} else { } else {
self.transact_no_undo(|col| { self.transact_no_undo(|col| {
col.update_card_inner(card, existing, col.usn()?)?; for mut card in cards {
let existing = col.storage.get_card(card.id)?.ok_or(AnkiError::NotFound)?;
col.update_card_inner(&mut card, existing, col.usn()?)?;
}
Ok(OpOutput { Ok(OpOutput {
output: (), output: (),
changes: OpChanges { changes: OpChanges {