mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 22:12:21 -04:00
current deck change is now undoable
- make sure we set flag in changes when config var changed - move current deck get/set into backend - set_config() now returns a bool indicating whether a change was made, so other operations can be gated off it - active decks generation is deferred until sched.reset()
This commit is contained in:
parent
5676ad5101
commit
6e954e82a5
23 changed files with 155 additions and 63 deletions
|
@ -447,30 +447,31 @@ class DeckManager:
|
||||||
# Deck selection
|
# Deck selection
|
||||||
#############################################################
|
#############################################################
|
||||||
|
|
||||||
def active(self) -> List[DeckId]:
|
def get_current(self) -> Deck:
|
||||||
"The currrently active dids."
|
return self.col._backend.get_current_deck()
|
||||||
return self.col.get_config("activeDecks", [1])
|
|
||||||
|
|
||||||
def selected(self) -> DeckId:
|
def set_current(self, deck: DeckId) -> OpChanges:
|
||||||
"The currently selected did."
|
return self.col._backend.set_current_deck(deck)
|
||||||
return DeckId(int(self.col.conf["curDeck"]))
|
|
||||||
|
def get_current_id(self) -> DeckId:
|
||||||
|
"The currently selected deck ID."
|
||||||
|
return DeckId(self.get_current().id)
|
||||||
|
|
||||||
|
# legacy
|
||||||
|
|
||||||
def current(self) -> DeckDict:
|
def current(self) -> DeckDict:
|
||||||
return self.get(self.selected())
|
return self.get(self.selected())
|
||||||
|
|
||||||
def select(self, did: DeckId) -> None:
|
def select(self, did: DeckId) -> None:
|
||||||
"Select a new branch."
|
|
||||||
# make sure arg is an int; legacy callers may be passing in a string
|
# make sure arg is an int; legacy callers may be passing in a string
|
||||||
did = DeckId(did)
|
did = DeckId(did)
|
||||||
current = self.selected()
|
self.set_current(did)
|
||||||
active = self.deck_and_child_ids(did)
|
self.col.reset()
|
||||||
if current != did or active != self.active():
|
|
||||||
self.col.conf["curDeck"] = did
|
|
||||||
self.col.conf["activeDecks"] = active
|
|
||||||
|
|
||||||
# don't use this, it will likely go away
|
def active(self) -> List[DeckId]:
|
||||||
def update_active(self) -> None:
|
return self.col.sched.active_decks
|
||||||
self.select(self.current()["id"])
|
|
||||||
|
selected = get_current_id
|
||||||
|
|
||||||
# Parents/children
|
# Parents/children
|
||||||
#############################################################
|
#############################################################
|
||||||
|
@ -518,7 +519,7 @@ class DeckManager:
|
||||||
)
|
)
|
||||||
|
|
||||||
def deck_and_child_ids(self, deck_id: DeckId) -> List[DeckId]:
|
def deck_and_child_ids(self, deck_id: DeckId) -> List[DeckId]:
|
||||||
parent_name = self.get_legacy(deck_id)["name"]
|
parent_name = self.col.get_deck(deck_id).name
|
||||||
out = [deck_id]
|
out = [deck_id]
|
||||||
out.extend(self.child_ids(parent_name))
|
out.extend(self.child_ids(parent_name))
|
||||||
return out
|
return out
|
||||||
|
|
|
@ -85,8 +85,9 @@ select id from cards where did in %s and queue = {QUEUE_TYPE_REV} and due <= ? l
|
||||||
# fixme: only used by totalRevForCurrentDeck and old deck stats;
|
# fixme: only used by totalRevForCurrentDeck and old deck stats;
|
||||||
# schedv2 defines separate version
|
# schedv2 defines separate version
|
||||||
def _deckLimit(self) -> str:
|
def _deckLimit(self) -> str:
|
||||||
self.col.decks.update_active()
|
return ids2str(
|
||||||
return ids2str(self.col.decks.active())
|
self.col.decks.deck_and_child_ids(self.col.decks.get_current_id())
|
||||||
|
)
|
||||||
|
|
||||||
# Filtered deck handling
|
# Filtered deck handling
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
|
@ -33,7 +33,7 @@ class Scheduler(V2):
|
||||||
def __init__( # pylint: disable=super-init-not-called
|
def __init__( # pylint: disable=super-init-not-called
|
||||||
self, col: anki.collection.Collection
|
self, col: anki.collection.Collection
|
||||||
) -> None:
|
) -> None:
|
||||||
self.col = col.weakref()
|
super().__init__(col)
|
||||||
self.queueLimit = 50
|
self.queueLimit = 50
|
||||||
self.reportLimit = 1000
|
self.reportLimit = 1000
|
||||||
self.dynReportLimit = 99999
|
self.dynReportLimit = 99999
|
||||||
|
@ -42,7 +42,6 @@ class Scheduler(V2):
|
||||||
self.revCount = 0
|
self.revCount = 0
|
||||||
self.newCount = 0
|
self.newCount = 0
|
||||||
self._haveQueues = False
|
self._haveQueues = False
|
||||||
self._updateCutoff()
|
|
||||||
|
|
||||||
def answerCard(self, card: Card, ease: int) -> None:
|
def answerCard(self, card: Card, ease: int) -> None:
|
||||||
self.col.log()
|
self.col.log()
|
||||||
|
|
|
@ -48,7 +48,16 @@ class Scheduler(SchedulerBaseWithLegacy):
|
||||||
self.reps = 0
|
self.reps = 0
|
||||||
self._haveQueues = False
|
self._haveQueues = False
|
||||||
self._lrnCutoff = 0
|
self._lrnCutoff = 0
|
||||||
self._updateCutoff()
|
self._active_decks: List[DeckId] = []
|
||||||
|
self._current_deck_id = DeckId(1)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def active_decks(self) -> List[DeckId]:
|
||||||
|
"Caller must make sure to make a copy."
|
||||||
|
return self._active_decks
|
||||||
|
|
||||||
|
def _update_active_decks(self) -> None:
|
||||||
|
self._active_decks = self.col.decks.deck_and_child_ids(self._current_deck_id)
|
||||||
|
|
||||||
# Daily cutoff
|
# Daily cutoff
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
@ -65,8 +74,8 @@ class Scheduler(SchedulerBaseWithLegacy):
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
||||||
def reset(self) -> None:
|
def reset(self) -> None:
|
||||||
self.col.decks.update_active()
|
self._current_deck_id = self.col.decks.selected()
|
||||||
self._updateCutoff()
|
self._update_active_decks()
|
||||||
self._reset_counts()
|
self._reset_counts()
|
||||||
self._resetLrn()
|
self._resetLrn()
|
||||||
self._resetRev()
|
self._resetRev()
|
||||||
|
@ -74,10 +83,8 @@ class Scheduler(SchedulerBaseWithLegacy):
|
||||||
self._haveQueues = True
|
self._haveQueues = True
|
||||||
|
|
||||||
def _reset_counts(self) -> None:
|
def _reset_counts(self) -> None:
|
||||||
tree = self.deck_due_tree(self.col.decks.selected())
|
tree = self.deck_due_tree(self._current_deck_id)
|
||||||
node = self.col.decks.find_deck_in_tree(
|
node = self.col.decks.find_deck_in_tree(tree, self._current_deck_id)
|
||||||
tree, DeckId(int(self.col.conf["curDeck"]))
|
|
||||||
)
|
|
||||||
if not node:
|
if not node:
|
||||||
# current deck points to a missing deck
|
# current deck points to a missing deck
|
||||||
self.newCount = 0
|
self.newCount = 0
|
||||||
|
|
|
@ -18,6 +18,7 @@ def test_basic():
|
||||||
assert col.decks.id("new deck") == parentId
|
assert col.decks.id("new deck") == parentId
|
||||||
# we start with the default col selected
|
# we start with the default col selected
|
||||||
assert col.decks.selected() == 1
|
assert col.decks.selected() == 1
|
||||||
|
col.reset()
|
||||||
assert col.decks.active() == [1]
|
assert col.decks.active() == [1]
|
||||||
# we can select a different col
|
# we can select a different col
|
||||||
col.decks.select(parentId)
|
col.decks.select(parentId)
|
||||||
|
|
|
@ -17,6 +17,7 @@ from aqt.operations.deck import (
|
||||||
remove_decks,
|
remove_decks,
|
||||||
rename_deck,
|
rename_deck,
|
||||||
reparent_decks,
|
reparent_decks,
|
||||||
|
set_current_deck,
|
||||||
set_deck_collapsed,
|
set_deck_collapsed,
|
||||||
)
|
)
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
|
@ -79,7 +80,7 @@ class DeckBrowser:
|
||||||
def op_executed(
|
def op_executed(
|
||||||
self, changes: OpChanges, handler: Optional[object], focused: bool
|
self, changes: OpChanges, handler: Optional[object], focused: bool
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if changes.study_queues:
|
if changes.study_queues and handler is not self:
|
||||||
self._refresh_needed = True
|
self._refresh_needed = True
|
||||||
|
|
||||||
if focused:
|
if focused:
|
||||||
|
@ -96,7 +97,7 @@ class DeckBrowser:
|
||||||
else:
|
else:
|
||||||
cmd = url
|
cmd = url
|
||||||
if cmd == "open":
|
if cmd == "open":
|
||||||
self._selDeck(arg)
|
self.set_current_deck(DeckId(int(arg)))
|
||||||
elif cmd == "opts":
|
elif cmd == "opts":
|
||||||
self._showOptions(arg)
|
self._showOptions(arg)
|
||||||
elif cmd == "shared":
|
elif cmd == "shared":
|
||||||
|
@ -119,9 +120,10 @@ class DeckBrowser:
|
||||||
self.refresh()
|
self.refresh()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _selDeck(self, did: str) -> None:
|
def set_current_deck(self, deck_id: DeckId) -> None:
|
||||||
self.mw.col.decks.select(DeckId(int(did)))
|
set_current_deck(parent=self.mw, deck_id=deck_id).success(
|
||||||
self.mw.onOverview()
|
lambda _: self.mw.onOverview()
|
||||||
|
).run_in_background(initiator=self)
|
||||||
|
|
||||||
# HTML generation
|
# HTML generation
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
|
@ -53,6 +53,7 @@ from aqt.legacy import install_pylib_legacy
|
||||||
from aqt.mediacheck import check_media_db
|
from aqt.mediacheck import check_media_db
|
||||||
from aqt.mediasync import MediaSyncer
|
from aqt.mediasync import MediaSyncer
|
||||||
from aqt.operations.collection import undo
|
from aqt.operations.collection import undo
|
||||||
|
from aqt.operations.deck import set_current_deck
|
||||||
from aqt.profiles import ProfileManager as ProfileManagerType
|
from aqt.profiles import ProfileManager as ProfileManagerType
|
||||||
from aqt.qt import *
|
from aqt.qt import *
|
||||||
from aqt.qt import sip
|
from aqt.qt import sip
|
||||||
|
@ -1425,8 +1426,11 @@ title="%s" %s>%s</button>""" % (
|
||||||
|
|
||||||
ret = StudyDeck(self, dyn=True, current=self.col.decks.current()["name"])
|
ret = StudyDeck(self, dyn=True, current=self.col.decks.current()["name"])
|
||||||
if ret.name:
|
if ret.name:
|
||||||
self.col.decks.select(self.col.decks.id(ret.name))
|
# fixme: this is silly, it should be returning an ID
|
||||||
self.moveToState("overview")
|
deck_id = self.col.decks.id(ret.name)
|
||||||
|
set_current_deck(parent=self, deck_id=deck_id).success(
|
||||||
|
lambda out: self.moveToState("overview")
|
||||||
|
).run_in_background()
|
||||||
|
|
||||||
def onEmptyCards(self) -> None:
|
def onEmptyCards(self) -> None:
|
||||||
show_empty_cards(self)
|
show_empty_cards(self)
|
||||||
|
|
|
@ -76,3 +76,7 @@ def set_deck_collapsed(
|
||||||
deck_id=deck_id, collapsed=collapsed, scope=scope
|
deck_id=deck_id, collapsed=collapsed, scope=scope
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def set_current_deck(*, parent: QWidget, deck_id: DeckId) -> CollectionOp[OpChanges]:
|
||||||
|
return CollectionOp(parent, lambda col: col.decks.set_current(deck_id))
|
||||||
|
|
|
@ -154,6 +154,8 @@ service DecksService {
|
||||||
rpc GetOrCreateFilteredDeck(DeckId) returns (FilteredDeckForUpdate);
|
rpc GetOrCreateFilteredDeck(DeckId) returns (FilteredDeckForUpdate);
|
||||||
rpc AddOrUpdateFilteredDeck(FilteredDeckForUpdate) returns (OpChangesWithId);
|
rpc AddOrUpdateFilteredDeck(FilteredDeckForUpdate) returns (OpChangesWithId);
|
||||||
rpc FilteredDeckOrderLabels(Empty) returns (StringList);
|
rpc FilteredDeckOrderLabels(Empty) returns (StringList);
|
||||||
|
rpc SetCurrentDeck(DeckId) returns (OpChanges);
|
||||||
|
rpc GetCurrentDeck(Empty) returns (Deck);
|
||||||
}
|
}
|
||||||
|
|
||||||
service NotesService {
|
service NotesService {
|
||||||
|
@ -1504,7 +1506,7 @@ message OpChanges {
|
||||||
bool deck = 3;
|
bool deck = 3;
|
||||||
bool tag = 4;
|
bool tag = 4;
|
||||||
bool notetype = 5;
|
bool notetype = 5;
|
||||||
bool preference = 6;
|
bool config = 6;
|
||||||
|
|
||||||
bool browser_table = 7;
|
bool browser_table = 7;
|
||||||
bool browser_sidebar = 8;
|
bool browser_sidebar = 8;
|
||||||
|
|
|
@ -67,7 +67,7 @@ impl ConfigService for Backend {
|
||||||
col.transact_no_undo(|col| {
|
col.transact_no_undo(|col| {
|
||||||
// ensure it's a well-formed object
|
// ensure it's a well-formed object
|
||||||
let val: Value = serde_json::from_slice(&input.value_json)?;
|
let val: Value = serde_json::from_slice(&input.value_json)?;
|
||||||
col.set_config(input.key.as_str(), &val)
|
col.set_config(input.key.as_str(), &val).map(|_| ())
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
|
@ -98,7 +98,7 @@ impl ConfigService for Backend {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
col.transact_no_undo(|col| col.set_bool(input.key().into(), input.value))
|
col.transact_no_undo(|col| col.set_bool(input.key().into(), input.value))
|
||||||
})
|
})
|
||||||
.map(Into::into)
|
.map(|_| ().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_config_string(&self, input: pb::config::String) -> Result<pb::String> {
|
fn get_config_string(&self, input: pb::config::String) -> Result<pb::String> {
|
||||||
|
@ -113,7 +113,7 @@ impl ConfigService for Backend {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
col.transact_no_undo(|col| col.set_string(input.key().into(), &input.value))
|
col.transact_no_undo(|col| col.set_string(input.key().into(), &input.value))
|
||||||
})
|
})
|
||||||
.map(Into::into)
|
.map(|_| ().into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_preferences(&self, _input: pb::Empty) -> Result<pb::Preferences> {
|
fn get_preferences(&self, _input: pb::Empty) -> Result<pb::Preferences> {
|
||||||
|
|
|
@ -187,6 +187,16 @@ impl DecksService for Backend {
|
||||||
})
|
})
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_current_deck(&self, input: pb::DeckId) -> Result<pb::OpChanges> {
|
||||||
|
self.with_col(|col| col.set_current_deck(input.did.into()))
|
||||||
|
.map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_current_deck(&self, _input: pb::Empty) -> Result<pb::Deck> {
|
||||||
|
self.with_col(|col| col.get_current_deck())
|
||||||
|
.map(|deck| (*deck).clone().into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<pb::DeckId> for DeckId {
|
impl From<pb::DeckId> for DeckId {
|
||||||
|
|
|
@ -16,7 +16,7 @@ impl From<OpChanges> for pb::OpChanges {
|
||||||
deck: c.changes.deck,
|
deck: c.changes.deck,
|
||||||
tag: c.changes.tag,
|
tag: c.changes.tag,
|
||||||
notetype: c.changes.notetype,
|
notetype: c.changes.notetype,
|
||||||
preference: c.changes.preference,
|
config: c.changes.config,
|
||||||
browser_table: c.requires_browser_table_redraw(),
|
browser_table: c.requires_browser_table_redraw(),
|
||||||
browser_sidebar: c.requires_browser_sidebar_redraw(),
|
browser_sidebar: c.requires_browser_sidebar_redraw(),
|
||||||
editor: c.requires_editor_redraw(),
|
editor: c.requires_editor_redraw(),
|
||||||
|
|
|
@ -239,7 +239,7 @@ impl Collection {
|
||||||
self.storage.set_search_table_to_card_ids(cards, false)?;
|
self.storage.set_search_table_to_card_ids(cards, false)?;
|
||||||
let sched = self.scheduler_version();
|
let sched = self.scheduler_version();
|
||||||
let usn = self.usn()?;
|
let usn = self.usn()?;
|
||||||
self.transact(Op::SetDeck, |col| {
|
self.transact(Op::SetCardDeck, |col| {
|
||||||
for mut card in col.storage.all_searched_cards()? {
|
for mut card in col.storage.all_searched_cards()? {
|
||||||
if card.deck_id == deck_id {
|
if card.deck_id == deck_id {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -69,7 +69,7 @@ impl Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_bool(&mut self, key: BoolKey, value: bool) -> Result<()> {
|
pub(crate) fn set_bool(&mut self, key: BoolKey, value: bool) -> Result<bool> {
|
||||||
self.set_config(key, &value)
|
self.set_config(key, &value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
// 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
|
||||||
|
|
||||||
use super::ConfigKey;
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
|
||||||
use strum::IntoStaticStr;
|
use strum::IntoStaticStr;
|
||||||
|
@ -20,11 +19,6 @@ impl DeckConfigKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Collection {
|
impl Collection {
|
||||||
pub(crate) fn get_current_deck_id(&self) -> DeckId {
|
|
||||||
self.get_config_optional(ConfigKey::CurrentDeckId)
|
|
||||||
.unwrap_or(DeckId(1))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn clear_aux_config_for_deck(&self, ntid: DeckId) -> Result<()> {
|
pub(crate) fn clear_aux_config_for_deck(&self, ntid: DeckId) -> Result<()> {
|
||||||
self.remove_config_prefix(&build_aux_deck_key(ntid, ""))
|
self.remove_config_prefix(&build_aux_deck_key(ntid, ""))
|
||||||
}
|
}
|
||||||
|
@ -38,7 +32,7 @@ impl Collection {
|
||||||
&mut self,
|
&mut self,
|
||||||
did: DeckId,
|
did: DeckId,
|
||||||
ntid: NotetypeId,
|
ntid: NotetypeId,
|
||||||
) -> Result<()> {
|
) -> Result<bool> {
|
||||||
let key = DeckConfigKey::LastNotetype.for_deck(did);
|
let key = DeckConfigKey::LastNotetype.for_deck(did);
|
||||||
self.set_config(key.as_str(), &ntid)
|
self.set_config(key.as_str(), &ntid)
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,8 @@ impl Collection {
|
||||||
self.get_config_optional(key).unwrap_or_default()
|
self.get_config_optional(key).unwrap_or_default()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_config<'a, T: Serialize, K>(&mut self, key: K, val: &T) -> Result<()>
|
/// True if added, or new value is different.
|
||||||
|
pub(crate) fn set_config<'a, T: Serialize, K>(&mut self, key: K, val: &T) -> Result<bool>
|
||||||
where
|
where
|
||||||
K: Into<&'a str>,
|
K: Into<&'a str>,
|
||||||
{
|
{
|
||||||
|
@ -148,6 +149,7 @@ impl Collection {
|
||||||
columns: Vec<browser_table::Column>,
|
columns: Vec<browser_table::Column>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.set_config(ConfigKey::DesktopBrowserCardColumns, &columns)
|
self.set_config(ConfigKey::DesktopBrowserCardColumns, &columns)
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_desktop_browser_note_columns(&self) -> Option<Vec<browser_table::Column>> {
|
pub(crate) fn get_desktop_browser_note_columns(&self) -> Option<Vec<browser_table::Column>> {
|
||||||
|
@ -159,6 +161,7 @@ impl Collection {
|
||||||
columns: Vec<browser_table::Column>,
|
columns: Vec<browser_table::Column>,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
self.set_config(ConfigKey::DesktopBrowserNoteColumns, &columns)
|
self.set_config(ConfigKey::DesktopBrowserNoteColumns, &columns)
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_creation_utc_offset(&self) -> Option<i32> {
|
pub(crate) fn get_creation_utc_offset(&self) -> Option<i32> {
|
||||||
|
@ -169,6 +172,7 @@ impl Collection {
|
||||||
self.state.scheduler_info = None;
|
self.state.scheduler_info = None;
|
||||||
if let Some(mins) = mins {
|
if let Some(mins) = mins {
|
||||||
self.set_config(ConfigKey::CreationOffset, &mins)
|
self.set_config(ConfigKey::CreationOffset, &mins)
|
||||||
|
.map(|_| ())
|
||||||
} else {
|
} else {
|
||||||
self.remove_config(ConfigKey::CreationOffset)
|
self.remove_config(ConfigKey::CreationOffset)
|
||||||
}
|
}
|
||||||
|
@ -180,7 +184,7 @@ impl Collection {
|
||||||
|
|
||||||
pub(crate) fn set_configured_utc_offset(&mut self, mins: i32) -> Result<()> {
|
pub(crate) fn set_configured_utc_offset(&mut self, mins: i32) -> Result<()> {
|
||||||
self.state.scheduler_info = None;
|
self.state.scheduler_info = None;
|
||||||
self.set_config(ConfigKey::LocalOffset, &mins)
|
self.set_config(ConfigKey::LocalOffset, &mins).map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_v2_rollover(&self) -> Option<u8> {
|
pub(crate) fn get_v2_rollover(&self) -> Option<u8> {
|
||||||
|
@ -190,7 +194,7 @@ impl Collection {
|
||||||
|
|
||||||
pub(crate) fn set_v2_rollover(&mut self, hour: u32) -> Result<()> {
|
pub(crate) fn set_v2_rollover(&mut self, hour: u32) -> Result<()> {
|
||||||
self.state.scheduler_info = None;
|
self.state.scheduler_info = None;
|
||||||
self.set_config(ConfigKey::Rollover, &hour)
|
self.set_config(ConfigKey::Rollover, &hour).map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_next_card_position(&self) -> u32 {
|
pub(crate) fn get_next_card_position(&self) -> u32 {
|
||||||
|
@ -207,6 +211,7 @@ impl Collection {
|
||||||
|
|
||||||
pub(crate) fn set_next_card_position(&mut self, pos: u32) -> Result<()> {
|
pub(crate) fn set_next_card_position(&mut self, pos: u32) -> Result<()> {
|
||||||
self.set_config(ConfigKey::NextNewCardPosition, &pos)
|
self.set_config(ConfigKey::NextNewCardPosition, &pos)
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn scheduler_version(&self) -> SchedulerVersion {
|
pub(crate) fn scheduler_version(&self) -> SchedulerVersion {
|
||||||
|
@ -218,6 +223,7 @@ impl Collection {
|
||||||
pub(crate) fn set_scheduler_version_config_key(&mut self, ver: SchedulerVersion) -> Result<()> {
|
pub(crate) fn set_scheduler_version_config_key(&mut self, ver: SchedulerVersion) -> Result<()> {
|
||||||
self.state.scheduler_info = None;
|
self.state.scheduler_info = None;
|
||||||
self.set_config(ConfigKey::SchedulerVersion, &ver)
|
self.set_config(ConfigKey::SchedulerVersion, &ver)
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn learn_ahead_secs(&self) -> u32 {
|
pub(crate) fn learn_ahead_secs(&self) -> u32 {
|
||||||
|
@ -227,6 +233,7 @@ impl Collection {
|
||||||
|
|
||||||
pub(crate) fn set_learn_ahead_secs(&mut self, secs: u32) -> Result<()> {
|
pub(crate) fn set_learn_ahead_secs(&mut self, secs: u32) -> Result<()> {
|
||||||
self.set_config(ConfigKey::LearnAheadSecs, &secs)
|
self.set_config(ConfigKey::LearnAheadSecs, &secs)
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_new_review_mix(&self) -> NewReviewMix {
|
pub(crate) fn get_new_review_mix(&self) -> NewReviewMix {
|
||||||
|
@ -239,6 +246,7 @@ impl Collection {
|
||||||
|
|
||||||
pub(crate) fn set_new_review_mix(&mut self, mix: NewReviewMix) -> Result<()> {
|
pub(crate) fn set_new_review_mix(&mut self, mix: NewReviewMix) -> Result<()> {
|
||||||
self.set_config(ConfigKey::NewReviewMix, &(mix as u8))
|
self.set_config(ConfigKey::NewReviewMix, &(mix as u8))
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_first_day_of_week(&self) -> Weekday {
|
pub(crate) fn get_first_day_of_week(&self) -> Weekday {
|
||||||
|
@ -248,6 +256,7 @@ impl Collection {
|
||||||
|
|
||||||
pub(crate) fn set_first_day_of_week(&mut self, weekday: Weekday) -> Result<()> {
|
pub(crate) fn set_first_day_of_week(&mut self, weekday: Weekday) -> Result<()> {
|
||||||
self.set_config(ConfigKey::FirstDayOfWeek, &weekday)
|
self.set_config(ConfigKey::FirstDayOfWeek, &weekday)
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_answer_time_limit_secs(&self) -> u32 {
|
pub(crate) fn get_answer_time_limit_secs(&self) -> u32 {
|
||||||
|
@ -257,6 +266,7 @@ impl Collection {
|
||||||
|
|
||||||
pub(crate) fn set_answer_time_limit_secs(&mut self, secs: u32) -> Result<()> {
|
pub(crate) fn set_answer_time_limit_secs(&mut self, secs: u32) -> Result<()> {
|
||||||
self.set_config(ConfigKey::AnswerTimeLimitSecs, &secs)
|
self.set_config(ConfigKey::AnswerTimeLimitSecs, &secs)
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn get_last_unburied_day(&self) -> u32 {
|
pub(crate) fn get_last_unburied_day(&self) -> u32 {
|
||||||
|
@ -266,6 +276,7 @@ impl Collection {
|
||||||
|
|
||||||
pub(crate) fn set_last_unburied_day(&mut self, day: u32) -> Result<()> {
|
pub(crate) fn set_last_unburied_day(&mut self, day: u32) -> Result<()> {
|
||||||
self.set_config(ConfigKey::LastUnburiedDay, &day)
|
self.set_config(ConfigKey::LastUnburiedDay, &day)
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ impl Collection {
|
||||||
|
|
||||||
pub(crate) fn set_current_notetype_id(&mut self, ntid: NotetypeId) -> Result<()> {
|
pub(crate) fn set_current_notetype_id(&mut self, ntid: NotetypeId) -> Result<()> {
|
||||||
self.set_config(ConfigKey::CurrentNotetypeId, &ntid)
|
self.set_config(ConfigKey::CurrentNotetypeId, &ntid)
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn clear_aux_config_for_notetype(&self, ntid: NotetypeId) -> Result<()> {
|
pub(crate) fn clear_aux_config_for_notetype(&self, ntid: NotetypeId) -> Result<()> {
|
||||||
|
@ -43,7 +44,7 @@ impl Collection {
|
||||||
|
|
||||||
pub(crate) fn set_last_deck_for_notetype(&mut self, id: NotetypeId, did: DeckId) -> Result<()> {
|
pub(crate) fn set_last_deck_for_notetype(&mut self, id: NotetypeId, did: DeckId) -> Result<()> {
|
||||||
let key = NotetypeConfigKey::LastDeckAddedTo.for_notetype(id);
|
let key = NotetypeConfigKey::LastDeckAddedTo.for_notetype(id);
|
||||||
self.set_config(key.as_str(), &did)
|
self.set_config(key.as_str(), &did).map(|_| ())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@ impl Collection {
|
||||||
.unwrap_or_else(|| default.to_string())
|
.unwrap_or_else(|| default.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_string(&mut self, key: StringKey, val: &str) -> Result<()> {
|
pub(crate) fn set_string(&mut self, key: StringKey, val: &str) -> Result<bool> {
|
||||||
self.set_config(key, &val)
|
self.set_config(key, &val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,16 +21,19 @@ impl Collection {
|
||||||
.get_config_entry(&entry.key)?
|
.get_config_entry(&entry.key)?
|
||||||
.ok_or_else(|| AnkiError::invalid_input("config disappeared"))?;
|
.ok_or_else(|| AnkiError::invalid_input("config disappeared"))?;
|
||||||
self.update_config_entry_undoable(entry, current)
|
self.update_config_entry_undoable(entry, current)
|
||||||
|
.map(|_| ())
|
||||||
}
|
}
|
||||||
UndoableConfigChange::Removed(entry) => self.add_config_entry_undoable(entry),
|
UndoableConfigChange::Removed(entry) => self.add_config_entry_undoable(entry),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn set_config_undoable(&mut self, entry: Box<ConfigEntry>) -> Result<()> {
|
/// True if added, or value changed.
|
||||||
|
pub(super) fn set_config_undoable(&mut self, entry: Box<ConfigEntry>) -> Result<bool> {
|
||||||
if let Some(original) = self.storage.get_config_entry(&entry.key)? {
|
if let Some(original) = self.storage.get_config_entry(&entry.key)? {
|
||||||
self.update_config_entry_undoable(entry, original)
|
self.update_config_entry_undoable(entry, original)
|
||||||
} else {
|
} else {
|
||||||
self.add_config_entry_undoable(entry)
|
self.add_config_entry_undoable(entry)?;
|
||||||
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,16 +52,19 @@ impl Collection {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// True if new value differed.
|
||||||
fn update_config_entry_undoable(
|
fn update_config_entry_undoable(
|
||||||
&mut self,
|
&mut self,
|
||||||
entry: Box<ConfigEntry>,
|
entry: Box<ConfigEntry>,
|
||||||
original: Box<ConfigEntry>,
|
original: Box<ConfigEntry>,
|
||||||
) -> Result<()> {
|
) -> Result<bool> {
|
||||||
if entry.value != original.value {
|
if entry.value != original.value {
|
||||||
self.save_undo(UndoableConfigChange::Updated(original));
|
self.save_undo(UndoableConfigChange::Updated(original));
|
||||||
self.storage.set_config_entry(&entry)?;
|
self.storage.set_config_entry(&entry)?;
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
Ok(false)
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
41
rslib/src/decks/current.rs
Normal file
41
rslib/src/decks/current.rs
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright: Ankitects Pty Ltd and contributors
|
||||||
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||||
|
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::{config::ConfigKey, prelude::*};
|
||||||
|
|
||||||
|
impl Collection {
|
||||||
|
pub fn set_current_deck(&mut self, deck: DeckId) -> Result<OpOutput<()>> {
|
||||||
|
self.transact(Op::SetCurrentDeck, |col| col.set_current_deck_inner(deck))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fetch the current deck, falling back to the default if the previously
|
||||||
|
/// selected deck is invalid.
|
||||||
|
pub fn get_current_deck(&mut self) -> Result<Arc<Deck>> {
|
||||||
|
if let Some(deck) = self.get_deck(self.get_current_deck_id())? {
|
||||||
|
return Ok(deck);
|
||||||
|
}
|
||||||
|
self.get_deck(DeckId(1))?.ok_or(AnkiError::NotFound)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collection {
|
||||||
|
/// The returned id may reference a deck that does not exist;
|
||||||
|
/// prefer using get_current_deck() instead.
|
||||||
|
pub(crate) fn get_current_deck_id(&self) -> DeckId {
|
||||||
|
self.get_config_optional(ConfigKey::CurrentDeckId)
|
||||||
|
.unwrap_or(DeckId(1))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_current_deck_inner(&mut self, deck: DeckId) -> Result<()> {
|
||||||
|
if self.set_current_deck_id(deck)? {
|
||||||
|
self.state.card_queues = None;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_current_deck_id(&mut self, did: DeckId) -> Result<bool> {
|
||||||
|
self.set_config(ConfigKey::CurrentDeckId, &did)
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
// 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 counts;
|
mod counts;
|
||||||
|
mod current;
|
||||||
mod filtered;
|
mod filtered;
|
||||||
mod schema11;
|
mod schema11;
|
||||||
mod tree;
|
mod tree;
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub enum Op {
|
||||||
RenameTag,
|
RenameTag,
|
||||||
ReparentTag,
|
ReparentTag,
|
||||||
ScheduleAsNew,
|
ScheduleAsNew,
|
||||||
SetDeck,
|
SetCardDeck,
|
||||||
SetDueDate,
|
SetDueDate,
|
||||||
SetFlag,
|
SetFlag,
|
||||||
SortCards,
|
SortCards,
|
||||||
|
@ -34,6 +34,7 @@ pub enum Op {
|
||||||
UpdateNote,
|
UpdateNote,
|
||||||
UpdatePreferences,
|
UpdatePreferences,
|
||||||
UpdateTag,
|
UpdateTag,
|
||||||
|
SetCurrentDeck,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Op {
|
impl Op {
|
||||||
|
@ -55,7 +56,7 @@ impl Op {
|
||||||
Op::UpdateNote => tr.undo_update_note(),
|
Op::UpdateNote => tr.undo_update_note(),
|
||||||
Op::UpdatePreferences => tr.preferences_preferences(),
|
Op::UpdatePreferences => tr.preferences_preferences(),
|
||||||
Op::UpdateTag => tr.undo_update_tag(),
|
Op::UpdateTag => tr.undo_update_tag(),
|
||||||
Op::SetDeck => tr.browsing_change_deck(),
|
Op::SetCardDeck => tr.browsing_change_deck(),
|
||||||
Op::SetFlag => tr.undo_set_flag(),
|
Op::SetFlag => tr.undo_set_flag(),
|
||||||
Op::FindAndReplace => tr.browsing_find_and_replace(),
|
Op::FindAndReplace => tr.browsing_find_and_replace(),
|
||||||
Op::ClearUnusedTags => tr.browsing_clear_unused_tags(),
|
Op::ClearUnusedTags => tr.browsing_clear_unused_tags(),
|
||||||
|
@ -68,6 +69,7 @@ impl Op {
|
||||||
Op::RebuildFilteredDeck => tr.undo_build_filtered_deck(),
|
Op::RebuildFilteredDeck => tr.undo_build_filtered_deck(),
|
||||||
Op::EmptyFilteredDeck => tr.studying_empty(),
|
Op::EmptyFilteredDeck => tr.studying_empty(),
|
||||||
Op::ExpandCollapse => tr.undo_expand_collapse(),
|
Op::ExpandCollapse => tr.undo_expand_collapse(),
|
||||||
|
Op::SetCurrentDeck => tr.browsing_change_deck(),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
@ -80,7 +82,7 @@ pub struct StateChanges {
|
||||||
pub deck: bool,
|
pub deck: bool,
|
||||||
pub tag: bool,
|
pub tag: bool,
|
||||||
pub notetype: bool,
|
pub notetype: bool,
|
||||||
pub preference: bool,
|
pub config: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
@ -134,7 +136,12 @@ impl OpChanges {
|
||||||
|
|
||||||
pub fn requires_study_queue_rebuild(&self) -> bool {
|
pub fn requires_study_queue_rebuild(&self) -> bool {
|
||||||
let c = &self.changes;
|
let c = &self.changes;
|
||||||
!matches!(self.op, Op::AnswerCard | Op::ExpandCollapse)
|
if self.op == Op::AnswerCard {
|
||||||
&& (c.card || c.deck || c.preference)
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
c.card
|
||||||
|
|| (c.deck && self.op != Op::ExpandCollapse)
|
||||||
|
|| (c.config && matches!(self.op, Op::SetCurrentDeck))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,7 @@ impl UndoManager {
|
||||||
UndoableChange::Tag(_) => changes.tag = true,
|
UndoableChange::Tag(_) => changes.tag = true,
|
||||||
UndoableChange::Revlog(_) => {}
|
UndoableChange::Revlog(_) => {}
|
||||||
UndoableChange::Queue(_) => {}
|
UndoableChange::Queue(_) => {}
|
||||||
UndoableChange::Config(_) => {} // fixme: preferences?
|
UndoableChange::Config(_) => changes.config = true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue