mirror of
https://github.com/ankitects/anki.git
synced 2025-09-19 06:22:22 -04:00
expose undoable config changes to frontend; refresh sidebar
The browser header handling still needs updating
This commit is contained in:
parent
99b7da49a9
commit
3d4cf26758
24 changed files with 140 additions and 94 deletions
|
@ -52,3 +52,4 @@ actions-expand-collapse = Expand/Collapse
|
||||||
actions-add-notetype = Add Notetype
|
actions-add-notetype = Add Notetype
|
||||||
actions-remove-notetype = Remove Notetype
|
actions-remove-notetype = Remove Notetype
|
||||||
actions-update-notetype = Update Notetype
|
actions-update-notetype = Update Notetype
|
||||||
|
actions-update-config = Update Config
|
||||||
|
|
|
@ -58,6 +58,7 @@ from anki.utils import (
|
||||||
intTime,
|
intTime,
|
||||||
splitFields,
|
splitFields,
|
||||||
stripHTMLMedia,
|
stripHTMLMedia,
|
||||||
|
to_json_bytes,
|
||||||
)
|
)
|
||||||
|
|
||||||
anki.latex.setup_hook()
|
anki.latex.setup_hook()
|
||||||
|
@ -788,11 +789,11 @@ class Collection:
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return default
|
return default
|
||||||
|
|
||||||
def set_config(self, key: str, val: Any) -> None:
|
def set_config(self, key: str, val: Any) -> OpChanges:
|
||||||
self.conf.set(key, val)
|
return self._backend.set_config_json(key=key, value_json=to_json_bytes(val))
|
||||||
|
|
||||||
def remove_config(self, key: str) -> None:
|
def remove_config(self, key: str) -> OpChanges:
|
||||||
self.conf.remove(key)
|
return self.conf.remove(key)
|
||||||
|
|
||||||
def all_config(self) -> Dict[str, Any]:
|
def all_config(self) -> Dict[str, Any]:
|
||||||
"This is a debugging aid. Prefer .get_config() when you know the key you need."
|
"This is a debugging aid. Prefer .get_config() when you know the key you need."
|
||||||
|
@ -801,14 +802,14 @@ class Collection:
|
||||||
def get_config_bool(self, key: Config.Bool.Key.V) -> bool:
|
def get_config_bool(self, key: Config.Bool.Key.V) -> bool:
|
||||||
return self._backend.get_config_bool(key)
|
return self._backend.get_config_bool(key)
|
||||||
|
|
||||||
def set_config_bool(self, key: Config.Bool.Key.V, value: bool) -> None:
|
def set_config_bool(self, key: Config.Bool.Key.V, value: bool) -> OpChanges:
|
||||||
self._backend.set_config_bool(key=key, value=value)
|
return self._backend.set_config_bool(key=key, value=value)
|
||||||
|
|
||||||
def get_config_string(self, key: Config.String.Key.V) -> str:
|
def get_config_string(self, key: Config.String.Key.V) -> str:
|
||||||
return self._backend.get_config_string(key)
|
return self._backend.get_config_string(key)
|
||||||
|
|
||||||
def set_config_string(self, key: Config.String.Key.V, value: str) -> None:
|
def set_config_string(self, key: Config.String.Key.V, value: str) -> OpChanges:
|
||||||
self._backend.set_config_string(key=key, value=value)
|
return self._backend.set_config_string(key=key, value=value)
|
||||||
|
|
||||||
# Stats
|
# Stats
|
||||||
##########################################################################
|
##########################################################################
|
||||||
|
|
|
@ -14,6 +14,7 @@ For legacy reasons, the config is also exposed as a dict interface
|
||||||
as col.conf. To support old code that was mutating inner values,
|
as col.conf. To support old code that was mutating inner values,
|
||||||
using col.conf["key"] needs to wrap lists and dicts when returning them.
|
using col.conf["key"] needs to wrap lists and dicts when returning them.
|
||||||
As this is less efficient, please use the col.*_config() API in new code.
|
As this is less efficient, please use the col.*_config() API in new code.
|
||||||
|
The legacy set also does not support the new undo handling.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
@ -25,6 +26,7 @@ from weakref import ref
|
||||||
|
|
||||||
import anki
|
import anki
|
||||||
from anki._backend import backend_pb2 as _pb
|
from anki._backend import backend_pb2 as _pb
|
||||||
|
from anki.collection import OpChanges
|
||||||
from anki.errors import NotFoundError
|
from anki.errors import NotFoundError
|
||||||
from anki.utils import from_json_bytes, to_json_bytes
|
from anki.utils import from_json_bytes, to_json_bytes
|
||||||
|
|
||||||
|
@ -42,10 +44,12 @@ class ConfigManager:
|
||||||
raise KeyError from exc
|
raise KeyError from exc
|
||||||
|
|
||||||
def set(self, key: str, val: Any) -> None:
|
def set(self, key: str, val: Any) -> None:
|
||||||
self.col._backend.set_config_json(key=key, value_json=to_json_bytes(val))
|
self.col._backend.set_config_json_no_undo(
|
||||||
|
key=key, value_json=to_json_bytes(val)
|
||||||
|
)
|
||||||
|
|
||||||
def remove(self, key: str) -> None:
|
def remove(self, key: str) -> OpChanges:
|
||||||
self.col._backend.remove_config(key)
|
return self.col._backend.remove_config(key)
|
||||||
|
|
||||||
# Legacy dict interface
|
# Legacy dict interface
|
||||||
#########################
|
#########################
|
||||||
|
|
|
@ -115,7 +115,7 @@ class Browser(QMainWindow):
|
||||||
focused = current_top_level_widget() == self
|
focused = current_top_level_widget() == self
|
||||||
self.table.op_executed(changes, handler, focused)
|
self.table.op_executed(changes, handler, focused)
|
||||||
self.sidebar.op_executed(changes, handler, focused)
|
self.sidebar.op_executed(changes, handler, focused)
|
||||||
if changes.note or changes.notetype:
|
if changes.editor:
|
||||||
if handler is not self.editor:
|
if handler is not self.editor:
|
||||||
# fixme: this will leave the splitter shown, but with no current
|
# fixme: this will leave the splitter shown, but with no current
|
||||||
# note being edited
|
# note being edited
|
||||||
|
@ -129,7 +129,8 @@ class Browser(QMainWindow):
|
||||||
self.editor.set_note(note)
|
self.editor.set_note(note)
|
||||||
|
|
||||||
self._renderPreview()
|
self._renderPreview()
|
||||||
elif changes.card:
|
|
||||||
|
if changes.browser_table and changes.card:
|
||||||
self.card = self.table.get_current_card()
|
self.card = self.table.get_current_card()
|
||||||
|
|
||||||
def on_focus_change(self, new: Optional[QWidget], old: Optional[QWidget]) -> None:
|
def on_focus_change(self, new: Optional[QWidget], old: Optional[QWidget]) -> None:
|
||||||
|
|
|
@ -21,7 +21,7 @@ from aqt.browser.sidebar.toolbar import SidebarTool, SidebarToolbar
|
||||||
from aqt.clayout import CardLayout
|
from aqt.clayout import CardLayout
|
||||||
from aqt.flags import load_flags
|
from aqt.flags import load_flags
|
||||||
from aqt.models import Models
|
from aqt.models import Models
|
||||||
from aqt.operations import QueryOp
|
from aqt.operations import CollectionOp, QueryOp
|
||||||
from aqt.operations.deck import (
|
from aqt.operations.deck import (
|
||||||
remove_decks,
|
remove_decks,
|
||||||
rename_deck,
|
rename_deck,
|
||||||
|
@ -444,7 +444,10 @@ class SidebarTreeView(QTreeView):
|
||||||
type: Optional[SidebarItemType] = None,
|
type: Optional[SidebarItemType] = None,
|
||||||
) -> SidebarItem:
|
) -> SidebarItem:
|
||||||
def update(expanded: bool) -> None:
|
def update(expanded: bool) -> None:
|
||||||
self.col.set_config_bool(collapse_key, not expanded)
|
CollectionOp(
|
||||||
|
self.browser,
|
||||||
|
lambda col: col.set_config_bool(collapse_key, not expanded),
|
||||||
|
).run_in_background(initiator=self)
|
||||||
|
|
||||||
top = SidebarItem(
|
top = SidebarItem(
|
||||||
name,
|
name,
|
||||||
|
|
|
@ -188,13 +188,14 @@ service SyncService {
|
||||||
|
|
||||||
service ConfigService {
|
service ConfigService {
|
||||||
rpc GetConfigJson(String) returns (Json);
|
rpc GetConfigJson(String) returns (Json);
|
||||||
rpc SetConfigJson(SetConfigJsonIn) returns (Empty);
|
rpc SetConfigJson(SetConfigJsonIn) returns (OpChanges);
|
||||||
rpc RemoveConfig(String) returns (Empty);
|
rpc SetConfigJsonNoUndo(SetConfigJsonIn) returns (Empty);
|
||||||
|
rpc RemoveConfig(String) returns (OpChanges);
|
||||||
rpc GetAllConfig(Empty) returns (Json);
|
rpc GetAllConfig(Empty) returns (Json);
|
||||||
rpc GetConfigBool(Config.Bool) returns (Bool);
|
rpc GetConfigBool(Config.Bool) returns (Bool);
|
||||||
rpc SetConfigBool(SetConfigBoolIn) returns (Empty);
|
rpc SetConfigBool(SetConfigBoolIn) returns (OpChanges);
|
||||||
rpc GetConfigString(Config.String) returns (String);
|
rpc GetConfigString(Config.String) returns (String);
|
||||||
rpc SetConfigString(SetConfigStringIn) returns (Empty);
|
rpc SetConfigString(SetConfigStringIn) returns (OpChanges);
|
||||||
rpc GetPreferences(Empty) returns (Preferences);
|
rpc GetPreferences(Empty) returns (Preferences);
|
||||||
rpc SetPreferences(Preferences) returns (OpChanges);
|
rpc SetPreferences(Preferences) returns (OpChanges);
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ impl Collection {
|
||||||
) -> Result<DeckAndNotetype> {
|
) -> Result<DeckAndNotetype> {
|
||||||
let deck_id;
|
let deck_id;
|
||||||
let notetype_id;
|
let notetype_id;
|
||||||
if self.get_bool(BoolKey::AddingDefaultsToCurrentDeck) {
|
if self.get_config_bool(BoolKey::AddingDefaultsToCurrentDeck) {
|
||||||
deck_id = self
|
deck_id = self
|
||||||
.get_current_deck_for_adding(home_deck_of_reviewer_card)?
|
.get_current_deck_for_adding(home_deck_of_reviewer_card)?
|
||||||
.id;
|
.id;
|
||||||
|
|
|
@ -62,19 +62,24 @@ impl ConfigService for Backend {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_config_json(&self, input: pb::SetConfigJsonIn) -> Result<pb::Empty> {
|
fn set_config_json(&self, input: pb::SetConfigJsonIn) -> Result<pb::OpChanges> {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
col.transact_no_undo(|col| {
|
let val: Value = serde_json::from_slice(&input.value_json)?;
|
||||||
// ensure it's a well-formed object
|
col.set_config_json(input.key.as_str(), &val)
|
||||||
let val: Value = serde_json::from_slice(&input.value_json)?;
|
|
||||||
col.set_config(input.key.as_str(), &val).map(|_| ())
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_config(&self, input: pb::String) -> Result<pb::Empty> {
|
fn set_config_json_no_undo(&self, input: pb::SetConfigJsonIn) -> Result<pb::Empty> {
|
||||||
self.with_col(|col| col.transact_no_undo(|col| col.remove_config(input.val.as_str())))
|
self.with_col(|col| {
|
||||||
|
let val: Value = serde_json::from_slice(&input.value_json)?;
|
||||||
|
col.transact_no_undo(|col| col.set_config(input.key.as_str(), &val).map(|_| ()))
|
||||||
|
})
|
||||||
|
.map(Into::into)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remove_config(&self, input: pb::String) -> Result<pb::OpChanges> {
|
||||||
|
self.with_col(|col| col.remove_config(input.val.as_str()))
|
||||||
.map(Into::into)
|
.map(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,31 +94,27 @@ impl ConfigService for Backend {
|
||||||
fn get_config_bool(&self, input: pb::config::Bool) -> Result<pb::Bool> {
|
fn get_config_bool(&self, input: pb::config::Bool) -> Result<pb::Bool> {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
Ok(pb::Bool {
|
Ok(pb::Bool {
|
||||||
val: col.get_bool(input.key().into()),
|
val: col.get_config_bool(input.key().into()),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_config_bool(&self, input: pb::SetConfigBoolIn) -> Result<pb::Empty> {
|
fn set_config_bool(&self, input: pb::SetConfigBoolIn) -> Result<pb::OpChanges> {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| col.set_config_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> {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| {
|
||||||
Ok(pb::String {
|
Ok(pb::String {
|
||||||
val: col.get_string(input.key().into()),
|
val: col.get_config_string(input.key().into()),
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_config_string(&self, input: pb::SetConfigStringIn) -> Result<pb::Empty> {
|
fn set_config_string(&self, input: pb::SetConfigStringIn) -> Result<pb::OpChanges> {
|
||||||
self.with_col(|col| {
|
self.with_col(|col| col.set_config_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> {
|
||||||
|
|
|
@ -196,7 +196,7 @@ impl Collection {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn browser_row_for_id(&mut self, id: i64) -> Result<pb::BrowserRow> {
|
pub fn browser_row_for_id(&mut self, id: i64) -> Result<pb::BrowserRow> {
|
||||||
let notes_mode = self.get_bool(BoolKey::BrowserTableShowNotesMode);
|
let notes_mode = self.get_config_bool(BoolKey::BrowserTableShowNotesMode);
|
||||||
let columns = Arc::clone(
|
let columns = Arc::clone(
|
||||||
self.state
|
self.state
|
||||||
.active_browser_columns
|
.active_browser_columns
|
||||||
|
|
|
@ -48,7 +48,7 @@ pub enum BoolKey {
|
||||||
struct BoolLike(#[serde(deserialize_with = "deserialize_bool_from_anything")] bool);
|
struct BoolLike(#[serde(deserialize_with = "deserialize_bool_from_anything")] bool);
|
||||||
|
|
||||||
impl Collection {
|
impl Collection {
|
||||||
pub(crate) fn get_bool(&self, key: BoolKey) -> bool {
|
pub fn get_config_bool(&self, key: BoolKey) -> bool {
|
||||||
match key {
|
match key {
|
||||||
BoolKey::BrowserSortBackwards => {
|
BoolKey::BrowserSortBackwards => {
|
||||||
// older clients were storing this as an int
|
// older clients were storing this as an int
|
||||||
|
@ -70,7 +70,15 @@ impl Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_bool(&mut self, key: BoolKey, value: bool) -> Result<bool> {
|
pub fn set_config_bool(&mut self, key: BoolKey, value: bool) -> Result<OpOutput<()>> {
|
||||||
|
self.transact(Op::UpdateConfig, |col| {
|
||||||
|
col.set_config(key, &value).map(|_| ())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collection {
|
||||||
|
pub(crate) fn set_config_bool_inner(&mut self, key: BoolKey, value: bool) -> Result<bool> {
|
||||||
self.set_config(key, &value)
|
self.set_config(key, &value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,6 +68,17 @@ pub enum SchedulerVersion {
|
||||||
V1 = 1,
|
V1 = 1,
|
||||||
V2 = 2,
|
V2 = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Collection {
|
||||||
|
pub fn set_config_json<T: Serialize>(&mut self, key: &str, val: &T) -> Result<OpOutput<()>> {
|
||||||
|
self.transact(Op::UpdateConfig, |col| col.set_config(key, val).map(|_| ()))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove_config(&mut self, key: &str) -> Result<OpOutput<()>> {
|
||||||
|
self.transact(Op::UpdateConfig, |col| col.remove_config_inner(key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Collection {
|
impl Collection {
|
||||||
/// Get config item, returning None if missing/invalid.
|
/// Get config item, returning None if missing/invalid.
|
||||||
pub(crate) fn get_config_optional<'a, T, K>(&self, key: K) -> Option<T>
|
pub(crate) fn get_config_optional<'a, T, K>(&self, key: K) -> Option<T>
|
||||||
|
@ -109,7 +120,7 @@ impl Collection {
|
||||||
self.set_config_undoable(entry)
|
self.set_config_undoable(entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn remove_config<'a, K>(&mut self, key: K) -> Result<()>
|
pub(crate) fn remove_config_inner<'a, K>(&mut self, key: K) -> Result<()>
|
||||||
where
|
where
|
||||||
K: Into<&'a str>,
|
K: Into<&'a str>,
|
||||||
{
|
{
|
||||||
|
@ -119,7 +130,7 @@ impl Collection {
|
||||||
/// Remove all keys starting with provided prefix, which must end with '_'.
|
/// Remove all keys starting with provided prefix, which must end with '_'.
|
||||||
pub(crate) fn remove_config_prefix(&mut self, key: &str) -> Result<()> {
|
pub(crate) fn remove_config_prefix(&mut self, key: &str) -> Result<()> {
|
||||||
for (key, _val) in self.storage.get_config_prefix(key)? {
|
for (key, _val) in self.storage.get_config_prefix(key)? {
|
||||||
self.remove_config(key.as_str())?;
|
self.remove_config_inner(key.as_str())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -134,7 +145,7 @@ impl Collection {
|
||||||
self.set_config(ConfigKey::CreationOffset, &mins)
|
self.set_config(ConfigKey::CreationOffset, &mins)
|
||||||
.map(|_| ())
|
.map(|_| ())
|
||||||
} else {
|
} else {
|
||||||
self.remove_config(ConfigKey::CreationOffset)
|
self.remove_config_inner(ConfigKey::CreationOffset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ pub enum StringKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Collection {
|
impl Collection {
|
||||||
pub(crate) fn get_string(&self, key: StringKey) -> String {
|
pub fn get_config_string(&self, key: StringKey) -> String {
|
||||||
let default = match key {
|
let default = match key {
|
||||||
StringKey::SetDueBrowser => "0",
|
StringKey::SetDueBrowser => "0",
|
||||||
StringKey::SetDueReviewer => "1",
|
StringKey::SetDueReviewer => "1",
|
||||||
|
@ -23,7 +23,15 @@ 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<bool> {
|
pub fn set_config_string(&mut self, key: StringKey, val: &str) -> Result<OpOutput<()>> {
|
||||||
|
self.transact(Op::UpdateConfig, |col| {
|
||||||
|
col.set_config_string_inner(key, val).map(|_| ())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Collection {
|
||||||
|
pub(crate) fn set_config_string_inner(&mut self, key: StringKey, val: &str) -> Result<bool> {
|
||||||
self.set_config(key, &val)
|
self.set_config(key, &val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,35 +82,35 @@ mod test {
|
||||||
let key = BoolKey::NormalizeNoteText;
|
let key = BoolKey::NormalizeNoteText;
|
||||||
|
|
||||||
// not set by default, but defaults to true
|
// not set by default, but defaults to true
|
||||||
assert_eq!(col.get_bool(key), true);
|
assert_eq!(col.get_config_bool(key), true);
|
||||||
|
|
||||||
// first set adds the key
|
// first set adds the key
|
||||||
col.transact(op.clone(), |col| col.set_bool(key, false))?;
|
col.transact(op.clone(), |col| col.set_config_bool_inner(key, false))?;
|
||||||
assert_eq!(col.get_bool(key), false);
|
assert_eq!(col.get_config_bool(key), false);
|
||||||
|
|
||||||
// mutate it twice
|
// mutate it twice
|
||||||
col.transact(op.clone(), |col| col.set_bool(key, true))?;
|
col.transact(op.clone(), |col| col.set_config_bool_inner(key, true))?;
|
||||||
assert_eq!(col.get_bool(key), true);
|
assert_eq!(col.get_config_bool(key), true);
|
||||||
col.transact(op.clone(), |col| col.set_bool(key, false))?;
|
col.transact(op.clone(), |col| col.set_config_bool_inner(key, false))?;
|
||||||
assert_eq!(col.get_bool(key), false);
|
assert_eq!(col.get_config_bool(key), false);
|
||||||
|
|
||||||
// when we remove it, it goes back to its default
|
// when we remove it, it goes back to its default
|
||||||
col.transact(op, |col| col.remove_config(key))?;
|
col.transact(op, |col| col.remove_config_inner(key))?;
|
||||||
assert_eq!(col.get_bool(key), true);
|
assert_eq!(col.get_config_bool(key), true);
|
||||||
|
|
||||||
// undo the removal
|
// undo the removal
|
||||||
col.undo()?;
|
col.undo()?;
|
||||||
assert_eq!(col.get_bool(key), false);
|
assert_eq!(col.get_config_bool(key), false);
|
||||||
|
|
||||||
// undo the mutations
|
// undo the mutations
|
||||||
col.undo()?;
|
col.undo()?;
|
||||||
assert_eq!(col.get_bool(key), true);
|
assert_eq!(col.get_config_bool(key), true);
|
||||||
col.undo()?;
|
col.undo()?;
|
||||||
assert_eq!(col.get_bool(key), false);
|
assert_eq!(col.get_config_bool(key), false);
|
||||||
|
|
||||||
// and undo the initial add
|
// and undo the initial add
|
||||||
col.undo()?;
|
col.undo()?;
|
||||||
assert_eq!(col.get_bool(key), true);
|
assert_eq!(col.get_config_bool(key), true);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -204,7 +204,7 @@ impl Collection {
|
||||||
F: FnMut(DatabaseCheckProgress, bool),
|
F: FnMut(DatabaseCheckProgress, bool),
|
||||||
{
|
{
|
||||||
let nids_by_notetype = self.storage.all_note_ids_by_notetype()?;
|
let nids_by_notetype = self.storage.all_note_ids_by_notetype()?;
|
||||||
let norm = self.get_bool(BoolKey::NormalizeNoteText);
|
let norm = self.get_config_bool(BoolKey::NormalizeNoteText);
|
||||||
let usn = self.usn()?;
|
let usn = self.usn()?;
|
||||||
let stamp = TimestampMillis::now();
|
let stamp = TimestampMillis::now();
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ impl Collection {
|
||||||
.storage
|
.storage
|
||||||
.get_collection_timestamps()?
|
.get_collection_timestamps()?
|
||||||
.schema_changed_since_sync(),
|
.schema_changed_since_sync(),
|
||||||
v3_scheduler: self.get_bool(BoolKey::Sched2021),
|
v3_scheduler: self.get_config_bool(BoolKey::Sched2021),
|
||||||
have_addons: false,
|
have_addons: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -281,7 +281,7 @@ impl Collection {
|
||||||
let days_elapsed = self.timing_for_timestamp(now)?.days_elapsed;
|
let days_elapsed = self.timing_for_timestamp(now)?.days_elapsed;
|
||||||
let learn_cutoff = (now.0 as u32) + self.learn_ahead_secs();
|
let learn_cutoff = (now.0 as u32) + self.learn_ahead_secs();
|
||||||
let sched_ver = self.scheduler_version();
|
let sched_ver = self.scheduler_version();
|
||||||
let v3 = self.get_bool(BoolKey::Sched2021);
|
let v3 = self.get_config_bool(BoolKey::Sched2021);
|
||||||
let counts = self.due_counts(days_elapsed, learn_cutoff, limit, v3)?;
|
let counts = self.due_counts(days_elapsed, learn_cutoff, limit, v3)?;
|
||||||
let dconf = self.storage.get_deck_config_map()?;
|
let dconf = self.storage.get_deck_config_map()?;
|
||||||
add_counts(&mut tree, &counts);
|
add_counts(&mut tree, &counts);
|
||||||
|
|
|
@ -49,7 +49,7 @@ impl Collection {
|
||||||
field_name: Option<String>,
|
field_name: Option<String>,
|
||||||
) -> Result<OpOutput<usize>> {
|
) -> Result<OpOutput<usize>> {
|
||||||
self.transact(Op::FindAndReplace, |col| {
|
self.transact(Op::FindAndReplace, |col| {
|
||||||
let norm = col.get_bool(BoolKey::NormalizeNoteText);
|
let norm = col.get_config_bool(BoolKey::NormalizeNoteText);
|
||||||
let search = if norm {
|
let search = if norm {
|
||||||
normalize_to_nfc(search_re)
|
normalize_to_nfc(search_re)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -77,7 +77,7 @@ impl Collection {
|
||||||
.ok_or_else(|| AnkiError::invalid_input("missing note type"))?;
|
.ok_or_else(|| AnkiError::invalid_input("missing note type"))?;
|
||||||
let last_deck = col.get_last_deck_added_to_for_notetype(note.notetype_id);
|
let last_deck = col.get_last_deck_added_to_for_notetype(note.notetype_id);
|
||||||
let ctx = CardGenContext::new(&nt, last_deck, col.usn()?);
|
let ctx = CardGenContext::new(&nt, last_deck, col.usn()?);
|
||||||
let norm = col.get_bool(BoolKey::NormalizeNoteText);
|
let norm = col.get_config_bool(BoolKey::NormalizeNoteText);
|
||||||
col.add_note_inner(&ctx, note, did, norm)
|
col.add_note_inner(&ctx, note, did, norm)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -387,7 +387,7 @@ impl Collection {
|
||||||
.ok_or_else(|| AnkiError::invalid_input("missing note type"))?;
|
.ok_or_else(|| AnkiError::invalid_input("missing note type"))?;
|
||||||
let last_deck = self.get_last_deck_added_to_for_notetype(note.notetype_id);
|
let last_deck = self.get_last_deck_added_to_for_notetype(note.notetype_id);
|
||||||
let ctx = CardGenContext::new(&nt, last_deck, self.usn()?);
|
let ctx = CardGenContext::new(&nt, last_deck, self.usn()?);
|
||||||
let norm = self.get_bool(BoolKey::NormalizeNoteText);
|
let norm = self.get_config_bool(BoolKey::NormalizeNoteText);
|
||||||
self.update_note_inner_generating_cards(&ctx, note, &existing_note, true, norm, true)?;
|
self.update_note_inner_generating_cards(&ctx, note, &existing_note, true, norm, true)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -475,7 +475,7 @@ impl Collection {
|
||||||
F: FnMut(&mut Note, &Notetype) -> Result<TransformNoteOutput>,
|
F: FnMut(&mut Note, &Notetype) -> Result<TransformNoteOutput>,
|
||||||
{
|
{
|
||||||
let nids_by_notetype = self.storage.note_ids_by_notetype(nids)?;
|
let nids_by_notetype = self.storage.note_ids_by_notetype(nids)?;
|
||||||
let norm = self.get_bool(BoolKey::NormalizeNoteText);
|
let norm = self.get_config_bool(BoolKey::NormalizeNoteText);
|
||||||
let mut changed_notes = 0;
|
let mut changed_notes = 0;
|
||||||
let usn = self.usn()?;
|
let usn = self.usn()?;
|
||||||
|
|
||||||
|
@ -531,7 +531,7 @@ impl Collection {
|
||||||
|
|
||||||
pub(crate) fn note_is_duplicate_or_empty(&self, note: &Note) -> Result<DuplicateState> {
|
pub(crate) fn note_is_duplicate_or_empty(&self, note: &Note) -> Result<DuplicateState> {
|
||||||
if let Some(field1) = note.fields.get(0) {
|
if let Some(field1) = note.fields.get(0) {
|
||||||
let field1 = if self.get_bool(BoolKey::NormalizeNoteText) {
|
let field1 = if self.get_config_bool(BoolKey::NormalizeNoteText) {
|
||||||
normalize_to_nfc(field1)
|
normalize_to_nfc(field1)
|
||||||
} else {
|
} else {
|
||||||
field1.into()
|
field1.into()
|
||||||
|
|
|
@ -482,7 +482,7 @@ impl Collection {
|
||||||
original: Option<Notetype>,
|
original: Option<Notetype>,
|
||||||
usn: Usn,
|
usn: Usn,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let normalize = self.get_bool(BoolKey::NormalizeNoteText);
|
let normalize = self.get_config_bool(BoolKey::NormalizeNoteText);
|
||||||
notetype.prepare_for_update(original.as_ref())?;
|
notetype.prepare_for_update(original.as_ref())?;
|
||||||
self.ensure_notetype_name_unique(notetype, usn)?;
|
self.ensure_notetype_name_unique(notetype, usn)?;
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ pub enum Op {
|
||||||
Suspend,
|
Suspend,
|
||||||
UnburyUnsuspend,
|
UnburyUnsuspend,
|
||||||
UpdateCard,
|
UpdateCard,
|
||||||
|
UpdateConfig,
|
||||||
UpdateDeck,
|
UpdateDeck,
|
||||||
UpdateDeckConfig,
|
UpdateDeckConfig,
|
||||||
UpdateNote,
|
UpdateNote,
|
||||||
|
@ -79,6 +80,7 @@ impl Op {
|
||||||
Op::AddNotetype => tr.actions_add_notetype(),
|
Op::AddNotetype => tr.actions_add_notetype(),
|
||||||
Op::RemoveNotetype => tr.actions_remove_notetype(),
|
Op::RemoveNotetype => tr.actions_remove_notetype(),
|
||||||
Op::UpdateNotetype => tr.actions_update_notetype(),
|
Op::UpdateNotetype => tr.actions_update_notetype(),
|
||||||
|
Op::UpdateConfig => tr.actions_update_config(),
|
||||||
Op::Custom(name) => name.into(),
|
Op::Custom(name) => name.into(),
|
||||||
}
|
}
|
||||||
.into()
|
.into()
|
||||||
|
@ -136,13 +138,14 @@ impl OpChanges {
|
||||||
let c = &self.changes;
|
let c = &self.changes;
|
||||||
c.card
|
c.card
|
||||||
|| c.notetype
|
|| c.notetype
|
||||||
|
|| c.config
|
||||||
|| (c.note && self.op != Op::AddNote)
|
|| (c.note && self.op != Op::AddNote)
|
||||||
|| (c.deck && self.op != Op::ExpandCollapse)
|
|| (c.deck && self.op != Op::ExpandCollapse)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn requires_browser_sidebar_redraw(&self) -> bool {
|
pub fn requires_browser_sidebar_redraw(&self) -> bool {
|
||||||
let c = &self.changes;
|
let c = &self.changes;
|
||||||
c.tag || c.deck || c.notetype
|
c.tag || c.deck || c.notetype || c.config
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn requires_editor_redraw(&self) -> bool {
|
pub fn requires_editor_redraw(&self) -> bool {
|
||||||
|
|
|
@ -46,7 +46,7 @@ impl Collection {
|
||||||
scheduler_version: match self.scheduler_version() {
|
scheduler_version: match self.scheduler_version() {
|
||||||
crate::config::SchedulerVersion::V1 => 1,
|
crate::config::SchedulerVersion::V1 => 1,
|
||||||
crate::config::SchedulerVersion::V2 => {
|
crate::config::SchedulerVersion::V2 => {
|
||||||
if self.get_bool(BoolKey::Sched2021) {
|
if self.get_config_bool(BoolKey::Sched2021) {
|
||||||
3
|
3
|
||||||
} else {
|
} else {
|
||||||
2
|
2
|
||||||
|
@ -61,14 +61,14 @@ impl Collection {
|
||||||
crate::config::NewReviewMix::NewFirst => NewRevMixPB::NewFirst,
|
crate::config::NewReviewMix::NewFirst => NewRevMixPB::NewFirst,
|
||||||
} as i32,
|
} as i32,
|
||||||
new_timezone: self.get_creation_utc_offset().is_some(),
|
new_timezone: self.get_creation_utc_offset().is_some(),
|
||||||
day_learn_first: self.get_bool(BoolKey::ShowDayLearningCardsFirst),
|
day_learn_first: self.get_config_bool(BoolKey::ShowDayLearningCardsFirst),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_scheduling_preferences(&mut self, settings: Scheduling) -> Result<()> {
|
pub(crate) fn set_scheduling_preferences(&mut self, settings: Scheduling) -> Result<()> {
|
||||||
let s = settings;
|
let s = settings;
|
||||||
|
|
||||||
self.set_bool(BoolKey::ShowDayLearningCardsFirst, s.day_learn_first)?;
|
self.set_config_bool_inner(BoolKey::ShowDayLearningCardsFirst, s.day_learn_first)?;
|
||||||
self.set_learn_ahead_secs(s.learn_ahead_secs)?;
|
self.set_learn_ahead_secs(s.learn_ahead_secs)?;
|
||||||
|
|
||||||
self.set_new_review_mix(match s.new_review_mix() {
|
self.set_new_review_mix(match s.new_review_mix() {
|
||||||
|
@ -96,26 +96,28 @@ impl Collection {
|
||||||
|
|
||||||
pub fn get_reviewing_preferences(&self) -> Result<Reviewing> {
|
pub fn get_reviewing_preferences(&self) -> Result<Reviewing> {
|
||||||
Ok(Reviewing {
|
Ok(Reviewing {
|
||||||
hide_audio_play_buttons: self.get_bool(BoolKey::HideAudioPlayButtons),
|
hide_audio_play_buttons: self.get_config_bool(BoolKey::HideAudioPlayButtons),
|
||||||
interrupt_audio_when_answering: self.get_bool(BoolKey::InterruptAudioWhenAnswering),
|
interrupt_audio_when_answering: self
|
||||||
show_remaining_due_counts: self.get_bool(BoolKey::ShowRemainingDueCountsInStudy),
|
.get_config_bool(BoolKey::InterruptAudioWhenAnswering),
|
||||||
show_intervals_on_buttons: self.get_bool(BoolKey::ShowIntervalsAboveAnswerButtons),
|
show_remaining_due_counts: self.get_config_bool(BoolKey::ShowRemainingDueCountsInStudy),
|
||||||
|
show_intervals_on_buttons: self
|
||||||
|
.get_config_bool(BoolKey::ShowIntervalsAboveAnswerButtons),
|
||||||
time_limit_secs: self.get_answer_time_limit_secs(),
|
time_limit_secs: self.get_answer_time_limit_secs(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_reviewing_preferences(&mut self, settings: Reviewing) -> Result<()> {
|
pub(crate) fn set_reviewing_preferences(&mut self, settings: Reviewing) -> Result<()> {
|
||||||
let s = settings;
|
let s = settings;
|
||||||
self.set_bool(BoolKey::HideAudioPlayButtons, s.hide_audio_play_buttons)?;
|
self.set_config_bool_inner(BoolKey::HideAudioPlayButtons, s.hide_audio_play_buttons)?;
|
||||||
self.set_bool(
|
self.set_config_bool_inner(
|
||||||
BoolKey::InterruptAudioWhenAnswering,
|
BoolKey::InterruptAudioWhenAnswering,
|
||||||
s.interrupt_audio_when_answering,
|
s.interrupt_audio_when_answering,
|
||||||
)?;
|
)?;
|
||||||
self.set_bool(
|
self.set_config_bool_inner(
|
||||||
BoolKey::ShowRemainingDueCountsInStudy,
|
BoolKey::ShowRemainingDueCountsInStudy,
|
||||||
s.show_remaining_due_counts,
|
s.show_remaining_due_counts,
|
||||||
)?;
|
)?;
|
||||||
self.set_bool(
|
self.set_config_bool_inner(
|
||||||
BoolKey::ShowIntervalsAboveAnswerButtons,
|
BoolKey::ShowIntervalsAboveAnswerButtons,
|
||||||
s.show_intervals_on_buttons,
|
s.show_intervals_on_buttons,
|
||||||
)?;
|
)?;
|
||||||
|
@ -125,20 +127,21 @@ impl Collection {
|
||||||
|
|
||||||
pub fn get_editing_preferences(&self) -> Result<Editing> {
|
pub fn get_editing_preferences(&self) -> Result<Editing> {
|
||||||
Ok(Editing {
|
Ok(Editing {
|
||||||
adding_defaults_to_current_deck: self.get_bool(BoolKey::AddingDefaultsToCurrentDeck),
|
adding_defaults_to_current_deck: self
|
||||||
paste_images_as_png: self.get_bool(BoolKey::PasteImagesAsPng),
|
.get_config_bool(BoolKey::AddingDefaultsToCurrentDeck),
|
||||||
paste_strips_formatting: self.get_bool(BoolKey::PasteStripsFormatting),
|
paste_images_as_png: self.get_config_bool(BoolKey::PasteImagesAsPng),
|
||||||
|
paste_strips_formatting: self.get_config_bool(BoolKey::PasteStripsFormatting),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_editing_preferences(&mut self, settings: Editing) -> Result<()> {
|
pub(crate) fn set_editing_preferences(&mut self, settings: Editing) -> Result<()> {
|
||||||
let s = settings;
|
let s = settings;
|
||||||
self.set_bool(
|
self.set_config_bool_inner(
|
||||||
BoolKey::AddingDefaultsToCurrentDeck,
|
BoolKey::AddingDefaultsToCurrentDeck,
|
||||||
s.adding_defaults_to_current_deck,
|
s.adding_defaults_to_current_deck,
|
||||||
)?;
|
)?;
|
||||||
self.set_bool(BoolKey::PasteImagesAsPng, s.paste_images_as_png)?;
|
self.set_config_bool_inner(BoolKey::PasteImagesAsPng, s.paste_images_as_png)?;
|
||||||
self.set_bool(BoolKey::PasteStripsFormatting, s.paste_strips_formatting)?;
|
self.set_config_bool_inner(BoolKey::PasteStripsFormatting, s.paste_strips_formatting)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,7 +111,7 @@ impl Collection {
|
||||||
}
|
}
|
||||||
col.storage.clear_searched_cards_table()?;
|
col.storage.clear_searched_cards_table()?;
|
||||||
if let Some(key) = context {
|
if let Some(key) = context {
|
||||||
col.set_string(key, days)?;
|
col.set_config_string_inner(key, days)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub(crate) struct SqlWriter<'a> {
|
||||||
|
|
||||||
impl SqlWriter<'_> {
|
impl SqlWriter<'_> {
|
||||||
pub(crate) fn new(col: &mut Collection, item_type: ReturnItemType) -> SqlWriter<'_> {
|
pub(crate) fn new(col: &mut Collection, item_type: ReturnItemType) -> SqlWriter<'_> {
|
||||||
let normalize_note_text = col.get_bool(BoolKey::NormalizeNoteText);
|
let normalize_note_text = col.get_config_bool(BoolKey::NormalizeNoteText);
|
||||||
let sql = String::new();
|
let sql = String::new();
|
||||||
let args = vec![];
|
let args = vec![];
|
||||||
SqlWriter {
|
SqlWriter {
|
||||||
|
|
|
@ -56,9 +56,10 @@ impl Collection {
|
||||||
pub(crate) fn get_graph_preferences(&self) -> pb::GraphPreferences {
|
pub(crate) fn get_graph_preferences(&self) -> pb::GraphPreferences {
|
||||||
pb::GraphPreferences {
|
pb::GraphPreferences {
|
||||||
calendar_first_day_of_week: self.get_first_day_of_week() as i32,
|
calendar_first_day_of_week: self.get_first_day_of_week() as i32,
|
||||||
card_counts_separate_inactive: self.get_bool(BoolKey::CardCountsSeparateInactive),
|
card_counts_separate_inactive: self
|
||||||
|
.get_config_bool(BoolKey::CardCountsSeparateInactive),
|
||||||
browser_links_supported: true,
|
browser_links_supported: true,
|
||||||
future_due_show_backlog: self.get_bool(BoolKey::FutureDueShowBacklog),
|
future_due_show_backlog: self.get_config_bool(BoolKey::FutureDueShowBacklog),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,11 +70,11 @@ impl Collection {
|
||||||
6 => Weekday::Saturday,
|
6 => Weekday::Saturday,
|
||||||
_ => Weekday::Sunday,
|
_ => Weekday::Sunday,
|
||||||
})?;
|
})?;
|
||||||
self.set_bool(
|
self.set_config_bool_inner(
|
||||||
BoolKey::CardCountsSeparateInactive,
|
BoolKey::CardCountsSeparateInactive,
|
||||||
prefs.card_counts_separate_inactive,
|
prefs.card_counts_separate_inactive,
|
||||||
)?;
|
)?;
|
||||||
self.set_bool(BoolKey::FutureDueShowBacklog, prefs.future_due_show_backlog)?;
|
self.set_config_bool_inner(BoolKey::FutureDueShowBacklog, prefs.future_due_show_backlog)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue