mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
implement bury/suspend undo
This commit is contained in:
parent
b466f0ce90
commit
41779c1aad
9 changed files with 82 additions and 62 deletions
|
@ -1262,9 +1262,9 @@ where id in %s"""
|
||||||
self.editor.saveNow(self._onSuspend)
|
self.editor.saveNow(self._onSuspend)
|
||||||
|
|
||||||
def _onSuspend(self) -> None:
|
def _onSuspend(self) -> None:
|
||||||
sus = not self.isSuspended()
|
want_suspend = not self.isSuspended()
|
||||||
c = self.selectedCards()
|
c = self.selectedCards()
|
||||||
if sus:
|
if want_suspend:
|
||||||
self.col.sched.suspend_cards(c)
|
self.col.sched.suspend_cards(c)
|
||||||
else:
|
else:
|
||||||
self.col.sched.unsuspend_cards(c)
|
self.col.sched.unsuspend_cards(c)
|
||||||
|
|
|
@ -292,10 +292,10 @@ class Reviewer:
|
||||||
("Ctrl+3", lambda: self.setFlag(3)),
|
("Ctrl+3", lambda: self.setFlag(3)),
|
||||||
("Ctrl+4", lambda: self.setFlag(4)),
|
("Ctrl+4", lambda: self.setFlag(4)),
|
||||||
("*", self.onMark),
|
("*", self.onMark),
|
||||||
("=", self.onBuryNote),
|
("=", self.bury_current_note),
|
||||||
("-", self.onBuryCard),
|
("-", self.bury_current_card),
|
||||||
("!", self.onSuspend),
|
("!", self.suspend_current_note),
|
||||||
("@", self.onSuspendCard),
|
("@", self.suspend_current_card),
|
||||||
("Ctrl+Delete", self.onDelete),
|
("Ctrl+Delete", self.onDelete),
|
||||||
("Ctrl+Shift+D", self.on_set_due),
|
("Ctrl+Shift+D", self.on_set_due),
|
||||||
("v", self.onReplayRecorded),
|
("v", self.onReplayRecorded),
|
||||||
|
@ -727,11 +727,11 @@ time = %(time)d;
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[tr(TR.STUDYING_MARK_NOTE), "*", self.onMark],
|
[tr(TR.STUDYING_MARK_NOTE), "*", self.onMark],
|
||||||
[tr(TR.STUDYING_BURY_CARD), "-", self.onBuryCard],
|
[tr(TR.STUDYING_BURY_CARD), "-", self.bury_current_card],
|
||||||
[tr(TR.STUDYING_BURY_NOTE), "=", self.onBuryNote],
|
[tr(TR.STUDYING_BURY_NOTE), "=", self.bury_current_note],
|
||||||
[tr(TR.ACTIONS_SET_DUE_DATE), "Ctrl+Shift+D", self.on_set_due],
|
[tr(TR.ACTIONS_SET_DUE_DATE), "Ctrl+Shift+D", self.on_set_due],
|
||||||
[tr(TR.ACTIONS_SUSPEND_CARD), "@", self.onSuspendCard],
|
[tr(TR.ACTIONS_SUSPEND_CARD), "@", self.suspend_current_card],
|
||||||
[tr(TR.STUDYING_SUSPEND_NOTE), "!", self.onSuspend],
|
[tr(TR.STUDYING_SUSPEND_NOTE), "!", self.suspend_current_note],
|
||||||
[tr(TR.STUDYING_DELETE_NOTE), "Ctrl+Delete", self.onDelete],
|
[tr(TR.STUDYING_DELETE_NOTE), "Ctrl+Delete", self.onDelete],
|
||||||
[tr(TR.ACTIONS_OPTIONS), "O", self.onOptions],
|
[tr(TR.ACTIONS_OPTIONS), "O", self.onOptions],
|
||||||
None,
|
None,
|
||||||
|
@ -808,17 +808,25 @@ time = %(time)d;
|
||||||
on_done=self.mw.reset,
|
on_done=self.mw.reset,
|
||||||
)
|
)
|
||||||
|
|
||||||
def onSuspend(self) -> None:
|
def suspend_current_note(self) -> None:
|
||||||
self.mw.checkpoint(tr(TR.STUDYING_SUSPEND))
|
|
||||||
self.mw.col.sched.suspend_cards([c.id for c in self.card.note().cards()])
|
self.mw.col.sched.suspend_cards([c.id for c in self.card.note().cards()])
|
||||||
|
self.mw.reset()
|
||||||
tooltip(tr(TR.STUDYING_NOTE_SUSPENDED))
|
tooltip(tr(TR.STUDYING_NOTE_SUSPENDED))
|
||||||
self.mw.reset()
|
|
||||||
|
|
||||||
def onSuspendCard(self) -> None:
|
def suspend_current_card(self) -> None:
|
||||||
self.mw.checkpoint(tr(TR.STUDYING_SUSPEND))
|
|
||||||
self.mw.col.sched.suspend_cards([self.card.id])
|
self.mw.col.sched.suspend_cards([self.card.id])
|
||||||
tooltip(tr(TR.STUDYING_CARD_SUSPENDED))
|
|
||||||
self.mw.reset()
|
self.mw.reset()
|
||||||
|
tooltip(tr(TR.STUDYING_CARD_SUSPENDED))
|
||||||
|
|
||||||
|
def bury_current_card(self) -> None:
|
||||||
|
self.mw.col.sched.bury_cards([self.card.id])
|
||||||
|
self.mw.reset()
|
||||||
|
tooltip(tr(TR.STUDYING_CARD_BURIED))
|
||||||
|
|
||||||
|
def bury_current_note(self) -> None:
|
||||||
|
self.mw.col.sched.bury_note(self.card.note())
|
||||||
|
self.mw.reset()
|
||||||
|
tooltip(tr(TR.STUDYING_NOTE_BURIED))
|
||||||
|
|
||||||
def onDelete(self) -> None:
|
def onDelete(self) -> None:
|
||||||
# need to check state because the shortcut is global to the main
|
# need to check state because the shortcut is global to the main
|
||||||
|
@ -831,18 +839,6 @@ time = %(time)d;
|
||||||
self.mw.reset()
|
self.mw.reset()
|
||||||
tooltip(tr(TR.STUDYING_NOTE_AND_ITS_CARD_DELETED, count=cnt))
|
tooltip(tr(TR.STUDYING_NOTE_AND_ITS_CARD_DELETED, count=cnt))
|
||||||
|
|
||||||
def onBuryCard(self) -> None:
|
|
||||||
self.mw.checkpoint(tr(TR.STUDYING_BURY))
|
|
||||||
self.mw.col.sched.bury_cards([self.card.id])
|
|
||||||
self.mw.reset()
|
|
||||||
tooltip(tr(TR.STUDYING_CARD_BURIED))
|
|
||||||
|
|
||||||
def onBuryNote(self) -> None:
|
|
||||||
self.mw.checkpoint(tr(TR.STUDYING_BURY))
|
|
||||||
self.mw.col.sched.bury_note(self.card.note())
|
|
||||||
self.mw.reset()
|
|
||||||
tooltip(tr(TR.STUDYING_NOTE_BURIED))
|
|
||||||
|
|
||||||
def onRecordVoice(self) -> None:
|
def onRecordVoice(self) -> None:
|
||||||
def after_record(path: str) -> None:
|
def after_record(path: str) -> None:
|
||||||
self._recordedAudio = path
|
self._recordedAudio = path
|
||||||
|
@ -855,3 +851,10 @@ time = %(time)d;
|
||||||
tooltip(tr(TR.STUDYING_YOU_HAVENT_RECORDED_YOUR_VOICE_YET))
|
tooltip(tr(TR.STUDYING_YOU_HAVENT_RECORDED_YOUR_VOICE_YET))
|
||||||
return
|
return
|
||||||
av_player.play_file(self._recordedAudio)
|
av_player.play_file(self._recordedAudio)
|
||||||
|
|
||||||
|
# legacy
|
||||||
|
|
||||||
|
onBuryCard = bury_current_card
|
||||||
|
onBuryNote = bury_current_note
|
||||||
|
onSuspend = suspend_current_note
|
||||||
|
onSuspendCard = suspend_current_card
|
||||||
|
|
|
@ -707,7 +707,7 @@ impl BackendService for Backend {
|
||||||
|
|
||||||
fn clear_card_queues(&self, _input: pb::Empty) -> BackendResult<pb::Empty> {
|
fn clear_card_queues(&self, _input: pb::Empty) -> BackendResult<pb::Empty> {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
col.clear_queues();
|
col.clear_study_queues();
|
||||||
Ok(().into())
|
Ok(().into())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// Copyright: Ankitects Pty Ltd and contributors
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
mod op;
|
||||||
|
|
||||||
use crate::i18n::I18n;
|
use crate::i18n::I18n;
|
||||||
use crate::log::Logger;
|
use crate::log::Logger;
|
||||||
use crate::types::Usn;
|
use crate::types::Usn;
|
||||||
|
@ -11,6 +13,7 @@ use crate::{
|
||||||
undo::UndoManager,
|
undo::UndoManager,
|
||||||
};
|
};
|
||||||
use crate::{err::Result, scheduler::queue::CardQueues};
|
use crate::{err::Result, scheduler::queue::CardQueues};
|
||||||
|
pub use op::CollectionOp;
|
||||||
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
use std::{collections::HashMap, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
pub fn open_collection<P: Into<PathBuf>>(
|
pub fn open_collection<P: Into<PathBuf>>(
|
||||||
|
@ -78,12 +81,6 @@ pub struct Collection {
|
||||||
pub(crate) state: CollectionState,
|
pub(crate) state: CollectionState,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
pub enum CollectionOp {
|
|
||||||
UpdateCard,
|
|
||||||
AnswerCard,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Collection {
|
impl Collection {
|
||||||
/// Execute the provided closure in a transaction, rolling back if
|
/// Execute the provided closure in a transaction, rolling back if
|
||||||
/// an error is returned.
|
/// an error is returned.
|
25
rslib/src/collection/op.rs
Normal file
25
rslib/src/collection/op.rs
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
use crate::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub enum CollectionOp {
|
||||||
|
UpdateCard,
|
||||||
|
AnswerCard,
|
||||||
|
Bury,
|
||||||
|
Suspend,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collection {
|
||||||
|
pub fn describe_collection_op(&self, op: CollectionOp) -> String {
|
||||||
|
let key = match op {
|
||||||
|
CollectionOp::UpdateCard => todo!(),
|
||||||
|
CollectionOp::AnswerCard => TR::UndoAnswerCard,
|
||||||
|
CollectionOp::Bury => TR::StudyingBury,
|
||||||
|
CollectionOp::Suspend => TR::StudyingSuspend,
|
||||||
|
};
|
||||||
|
|
||||||
|
self.i18n.tr(key).to_string()
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,7 +66,7 @@ mod test {
|
||||||
col.storage.update_card(&card)?;
|
col.storage.update_card(&card)?;
|
||||||
|
|
||||||
// fail it, which should cause it to be marked as a leech
|
// fail it, which should cause it to be marked as a leech
|
||||||
col.clear_queues();
|
col.clear_study_queues();
|
||||||
let queued = col.next_card()?.unwrap();
|
let queued = col.next_card()?.unwrap();
|
||||||
dbg!(&queued);
|
dbg!(&queued);
|
||||||
col.answer_card(&CardAnswer {
|
col.answer_card(&CardAnswer {
|
||||||
|
|
|
@ -12,7 +12,10 @@ use crate::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::timing::SchedTimingToday;
|
use super::timing::SchedTimingToday;
|
||||||
use pb::unbury_cards_in_current_deck_in::Mode as UnburyDeckMode;
|
use pb::{
|
||||||
|
bury_or_suspend_cards_in::Mode as BuryOrSuspendMode,
|
||||||
|
unbury_cards_in_current_deck_in::Mode as UnburyDeckMode,
|
||||||
|
};
|
||||||
|
|
||||||
impl Card {
|
impl Card {
|
||||||
/// True if card was buried/suspended prior to the call.
|
/// True if card was buried/suspended prior to the call.
|
||||||
|
@ -86,20 +89,16 @@ impl Collection {
|
||||||
|
|
||||||
/// Bury/suspend cards in search table, and clear it.
|
/// Bury/suspend cards in search table, and clear it.
|
||||||
/// Marks the cards as modified.
|
/// Marks the cards as modified.
|
||||||
fn bury_or_suspend_searched_cards(
|
fn bury_or_suspend_searched_cards(&mut self, mode: BuryOrSuspendMode) -> Result<()> {
|
||||||
&mut self,
|
|
||||||
mode: pb::bury_or_suspend_cards_in::Mode,
|
|
||||||
) -> Result<()> {
|
|
||||||
use pb::bury_or_suspend_cards_in::Mode;
|
|
||||||
let usn = self.usn()?;
|
let usn = self.usn()?;
|
||||||
let sched = self.scheduler_version();
|
let sched = self.scheduler_version();
|
||||||
|
|
||||||
for original in self.storage.all_searched_cards()? {
|
for original in self.storage.all_searched_cards()? {
|
||||||
let mut card = original.clone();
|
let mut card = original.clone();
|
||||||
let desired_queue = match mode {
|
let desired_queue = match mode {
|
||||||
Mode::Suspend => CardQueue::Suspended,
|
BuryOrSuspendMode::Suspend => CardQueue::Suspended,
|
||||||
Mode::BurySched => CardQueue::SchedBuried,
|
BuryOrSuspendMode::BurySched => CardQueue::SchedBuried,
|
||||||
Mode::BuryUser => {
|
BuryOrSuspendMode::BuryUser => {
|
||||||
if sched == SchedulerVersion::V1 {
|
if sched == SchedulerVersion::V1 {
|
||||||
// v1 scheduler only had one bury type
|
// v1 scheduler only had one bury type
|
||||||
CardQueue::SchedBuried
|
CardQueue::SchedBuried
|
||||||
|
@ -124,9 +123,14 @@ impl Collection {
|
||||||
pub fn bury_or_suspend_cards(
|
pub fn bury_or_suspend_cards(
|
||||||
&mut self,
|
&mut self,
|
||||||
cids: &[CardID],
|
cids: &[CardID],
|
||||||
mode: pb::bury_or_suspend_cards_in::Mode,
|
mode: BuryOrSuspendMode,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.transact(None, |col| {
|
let op = match mode {
|
||||||
|
BuryOrSuspendMode::Suspend => CollectionOp::Suspend,
|
||||||
|
BuryOrSuspendMode::BurySched | BuryOrSuspendMode::BuryUser => CollectionOp::Bury,
|
||||||
|
};
|
||||||
|
self.transact(Some(op), |col| {
|
||||||
|
col.clear_study_queues();
|
||||||
col.storage.set_search_table_to_card_ids(cids, false)?;
|
col.storage.set_search_table_to_card_ids(cids, false)?;
|
||||||
col.bury_or_suspend_searched_cards(mode)
|
col.bury_or_suspend_searched_cards(mode)
|
||||||
})
|
})
|
||||||
|
@ -139,10 +143,9 @@ impl Collection {
|
||||||
include_new: bool,
|
include_new: bool,
|
||||||
include_reviews: bool,
|
include_reviews: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
use pb::bury_or_suspend_cards_in::Mode;
|
|
||||||
self.storage
|
self.storage
|
||||||
.search_siblings_for_bury(cid, nid, include_new, include_reviews)?;
|
.search_siblings_for_bury(cid, nid, include_new, include_reviews)?;
|
||||||
self.bury_or_suspend_searched_cards(Mode::BurySched)
|
self.bury_or_suspend_searched_cards(BuryOrSuspendMode::BurySched)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,11 +132,12 @@ impl Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clear_queues(&mut self) {
|
pub(crate) fn clear_study_queues(&mut self) {
|
||||||
// clearing the queue will remove any undone reviews from the undo queue,
|
|
||||||
// causing problems if we then try to redo them
|
|
||||||
self.state.undo.clear_redo();
|
|
||||||
self.state.card_queues = None;
|
self.state.card_queues = None;
|
||||||
|
// clearing the queue will remove any undone reviews from the undo queue,
|
||||||
|
// causing problems if we then try to redo them, so we need to clear the
|
||||||
|
// redo queue as well
|
||||||
|
self.state.undo.clear_redo();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn update_queues_after_answering_card(
|
pub(crate) fn update_queues_after_answering_card(
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
use crate::backend_proto as pb;
|
use crate::backend_proto as pb;
|
||||||
use crate::i18n::TR;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
collection::{Collection, CollectionOp},
|
collection::{Collection, CollectionOp},
|
||||||
err::Result,
|
err::Result,
|
||||||
|
@ -145,14 +144,6 @@ impl Collection {
|
||||||
self.state.undo.save(item)
|
self.state.undo.save(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn describe_collection_op(&self, op: CollectionOp) -> String {
|
|
||||||
match op {
|
|
||||||
CollectionOp::UpdateCard => todo!(),
|
|
||||||
CollectionOp::AnswerCard => self.i18n.tr(TR::UndoAnswerCard),
|
|
||||||
}
|
|
||||||
.to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn undo_status(&self) -> pb::UndoStatus {
|
pub fn undo_status(&self) -> pb::UndoStatus {
|
||||||
pb::UndoStatus {
|
pb::UndoStatus {
|
||||||
undo: self
|
undo: self
|
||||||
|
|
Loading…
Reference in a new issue