2020-05-11 10:35:15 +02:00
|
|
|
// Copyright: Ankitects Pty Ltd and contributors
|
|
|
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
|
|
|
|
use crate::{
|
|
|
|
backend_proto::{
|
2021-04-18 10:29:20 +02:00
|
|
|
preferences::{scheduling::NewReviewMix as NewRevMixPB, Editing, Reviewing, Scheduling},
|
2021-03-10 09:20:37 +01:00
|
|
|
Preferences,
|
2020-05-11 10:35:15 +02:00
|
|
|
},
|
|
|
|
collection::Collection,
|
2021-03-07 14:43:18 +01:00
|
|
|
config::BoolKey,
|
2021-04-01 08:06:24 +02:00
|
|
|
error::Result,
|
2021-03-13 11:10:03 +01:00
|
|
|
prelude::*,
|
2021-03-01 01:34:04 +01:00
|
|
|
scheduler::timing::local_minutes_west_for_stamp,
|
2020-05-11 10:35:15 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
impl Collection {
|
|
|
|
pub fn get_preferences(&self) -> Result<Preferences> {
|
|
|
|
Ok(Preferences {
|
2021-03-10 09:20:37 +01:00
|
|
|
scheduling: Some(self.get_scheduling_preferences()?),
|
|
|
|
reviewing: Some(self.get_reviewing_preferences()?),
|
|
|
|
editing: Some(self.get_editing_preferences()?),
|
2020-05-11 10:35:15 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
undoable ops now return changes directly; add new *_ops.py files
- Introduced a new transact() method that wraps the return value
in a separate struct that describes the changes that were made.
- Changes are now gathered from the undo log, so we don't need to
guess at what was changed - eg if update_note() is called with identical
note contents, no changes are returned. Card changes will only be set
if cards were actually generated by the update_note() call, and tag
will only be set if a new tag was added.
- mw.perform_op() has been updated to expect the op to return the changes,
or a structure with the changes in it, and it will use them to fire the
change hook, instead of fetching the changes from undo_status(), so there
is no risk of race conditions.
- the various calls to mw.perform_op() have been split into separate
files like card_ops.py. Aside from making the code cleaner, this works
around a rather annoying issue with mypy. Because we run it with
no_strict_optional, mypy is happy to accept an operation that returns None,
despite the type signature saying it requires changes to be returned.
Turning no_strict_optional on for the whole codebase is not practical
at the moment, but we can enable it for individual files.
Still todo:
- The cursor keeps moving back to the start of a field when typing -
we need to ignore the refresh hook when we are the initiator.
- The busy cursor icon should probably be delayed a few hundreds ms.
- Still need to think about a nicer way of handling saveNow()
- op_made_changes(), op_affects_study_queue() might be better embedded
as properties in the object instead
2021-03-16 05:26:42 +01:00
|
|
|
pub fn set_preferences(&mut self, prefs: Preferences) -> Result<OpOutput<()>> {
|
|
|
|
self.transact(Op::UpdatePreferences, |col| {
|
2021-03-13 11:10:03 +01:00
|
|
|
col.set_preferences_inner(prefs)
|
|
|
|
})
|
2021-03-10 09:20:37 +01:00
|
|
|
}
|
|
|
|
|
undoable ops now return changes directly; add new *_ops.py files
- Introduced a new transact() method that wraps the return value
in a separate struct that describes the changes that were made.
- Changes are now gathered from the undo log, so we don't need to
guess at what was changed - eg if update_note() is called with identical
note contents, no changes are returned. Card changes will only be set
if cards were actually generated by the update_note() call, and tag
will only be set if a new tag was added.
- mw.perform_op() has been updated to expect the op to return the changes,
or a structure with the changes in it, and it will use them to fire the
change hook, instead of fetching the changes from undo_status(), so there
is no risk of race conditions.
- the various calls to mw.perform_op() have been split into separate
files like card_ops.py. Aside from making the code cleaner, this works
around a rather annoying issue with mypy. Because we run it with
no_strict_optional, mypy is happy to accept an operation that returns None,
despite the type signature saying it requires changes to be returned.
Turning no_strict_optional on for the whole codebase is not practical
at the moment, but we can enable it for individual files.
Still todo:
- The cursor keeps moving back to the start of a field when typing -
we need to ignore the refresh hook when we are the initiator.
- The busy cursor icon should probably be delayed a few hundreds ms.
- Still need to think about a nicer way of handling saveNow()
- op_made_changes(), op_affects_study_queue() might be better embedded
as properties in the object instead
2021-03-16 05:26:42 +01:00
|
|
|
fn set_preferences_inner(&mut self, prefs: Preferences) -> Result<()> {
|
2021-03-10 05:11:59 +01:00
|
|
|
if let Some(sched) = prefs.scheduling {
|
2021-03-10 09:20:37 +01:00
|
|
|
self.set_scheduling_preferences(sched)?;
|
|
|
|
}
|
|
|
|
if let Some(reviewing) = prefs.reviewing {
|
|
|
|
self.set_reviewing_preferences(reviewing)?;
|
|
|
|
}
|
|
|
|
if let Some(editing) = prefs.editing {
|
|
|
|
self.set_editing_preferences(editing)?;
|
2020-05-11 10:35:15 +02:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-03-10 09:20:37 +01:00
|
|
|
pub fn get_scheduling_preferences(&self) -> Result<Scheduling> {
|
2021-03-10 05:11:59 +01:00
|
|
|
Ok(Scheduling {
|
2021-02-21 06:50:41 +01:00
|
|
|
scheduler_version: match self.scheduler_version() {
|
2020-05-11 10:35:15 +02:00
|
|
|
crate::config::SchedulerVersion::V1 => 1,
|
|
|
|
crate::config::SchedulerVersion::V2 => 2,
|
|
|
|
},
|
|
|
|
rollover: self.rollover_for_current_scheduler()? as u32,
|
|
|
|
learn_ahead_secs: self.learn_ahead_secs(),
|
|
|
|
new_review_mix: match self.get_new_review_mix() {
|
|
|
|
crate::config::NewReviewMix::Mix => NewRevMixPB::Distribute,
|
|
|
|
crate::config::NewReviewMix::ReviewsFirst => NewRevMixPB::ReviewsFirst,
|
|
|
|
crate::config::NewReviewMix::NewFirst => NewRevMixPB::NewFirst,
|
|
|
|
} as i32,
|
2021-01-12 12:29:22 +01:00
|
|
|
new_timezone: self.get_creation_utc_offset().is_some(),
|
2021-03-07 14:43:18 +01:00
|
|
|
day_learn_first: self.get_bool(BoolKey::ShowDayLearningCardsFirst),
|
2020-05-11 10:35:15 +02:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2021-03-10 09:20:37 +01:00
|
|
|
pub(crate) fn set_scheduling_preferences(&mut self, settings: Scheduling) -> Result<()> {
|
2020-05-11 10:35:15 +02:00
|
|
|
let s = settings;
|
|
|
|
|
2021-03-07 14:43:18 +01:00
|
|
|
self.set_bool(BoolKey::ShowDayLearningCardsFirst, s.day_learn_first)?;
|
2020-05-11 10:35:15 +02:00
|
|
|
self.set_learn_ahead_secs(s.learn_ahead_secs)?;
|
|
|
|
|
|
|
|
self.set_new_review_mix(match s.new_review_mix() {
|
|
|
|
NewRevMixPB::Distribute => crate::config::NewReviewMix::Mix,
|
|
|
|
NewRevMixPB::NewFirst => crate::config::NewReviewMix::NewFirst,
|
|
|
|
NewRevMixPB::ReviewsFirst => crate::config::NewReviewMix::ReviewsFirst,
|
|
|
|
})?;
|
|
|
|
|
|
|
|
let created = self.storage.creation_stamp()?;
|
|
|
|
|
|
|
|
if self.rollover_for_current_scheduler()? != s.rollover as u8 {
|
|
|
|
self.set_rollover_for_current_scheduler(s.rollover as u8)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
if s.new_timezone {
|
2021-01-12 12:29:22 +01:00
|
|
|
if self.get_creation_utc_offset().is_none() {
|
|
|
|
self.set_creation_utc_offset(Some(local_minutes_west_for_stamp(created.0)))?;
|
2020-05-11 10:35:15 +02:00
|
|
|
}
|
|
|
|
} else {
|
2021-01-12 12:29:22 +01:00
|
|
|
self.set_creation_utc_offset(None)?;
|
2020-09-03 04:43:18 +02:00
|
|
|
}
|
|
|
|
|
2020-05-11 10:35:15 +02:00
|
|
|
Ok(())
|
|
|
|
}
|
2021-03-10 09:20:37 +01:00
|
|
|
|
|
|
|
pub fn get_reviewing_preferences(&self) -> Result<Reviewing> {
|
|
|
|
Ok(Reviewing {
|
|
|
|
hide_audio_play_buttons: self.get_bool(BoolKey::HideAudioPlayButtons),
|
|
|
|
interrupt_audio_when_answering: self.get_bool(BoolKey::InterruptAudioWhenAnswering),
|
|
|
|
show_remaining_due_counts: self.get_bool(BoolKey::ShowRemainingDueCountsInStudy),
|
|
|
|
show_intervals_on_buttons: self.get_bool(BoolKey::ShowIntervalsAboveAnswerButtons),
|
|
|
|
time_limit_secs: self.get_answer_time_limit_secs(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn set_reviewing_preferences(&mut self, settings: Reviewing) -> Result<()> {
|
|
|
|
let s = settings;
|
|
|
|
self.set_bool(BoolKey::HideAudioPlayButtons, s.hide_audio_play_buttons)?;
|
|
|
|
self.set_bool(
|
|
|
|
BoolKey::InterruptAudioWhenAnswering,
|
|
|
|
s.interrupt_audio_when_answering,
|
|
|
|
)?;
|
|
|
|
self.set_bool(
|
|
|
|
BoolKey::ShowRemainingDueCountsInStudy,
|
|
|
|
s.show_remaining_due_counts,
|
|
|
|
)?;
|
|
|
|
self.set_bool(
|
|
|
|
BoolKey::ShowIntervalsAboveAnswerButtons,
|
|
|
|
s.show_intervals_on_buttons,
|
|
|
|
)?;
|
|
|
|
self.set_answer_time_limit_secs(s.time_limit_secs)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn get_editing_preferences(&self) -> Result<Editing> {
|
|
|
|
Ok(Editing {
|
|
|
|
adding_defaults_to_current_deck: self.get_bool(BoolKey::AddingDefaultsToCurrentDeck),
|
|
|
|
paste_images_as_png: self.get_bool(BoolKey::PasteImagesAsPng),
|
|
|
|
paste_strips_formatting: self.get_bool(BoolKey::PasteStripsFormatting),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn set_editing_preferences(&mut self, settings: Editing) -> Result<()> {
|
|
|
|
let s = settings;
|
|
|
|
self.set_bool(
|
|
|
|
BoolKey::AddingDefaultsToCurrentDeck,
|
|
|
|
s.adding_defaults_to_current_deck,
|
|
|
|
)?;
|
|
|
|
self.set_bool(BoolKey::PasteImagesAsPng, s.paste_images_as_png)?;
|
|
|
|
self.set_bool(BoolKey::PasteStripsFormatting, s.paste_strips_formatting)?;
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-05-11 10:35:15 +02:00
|
|
|
}
|