implement deck config updating

This commit is contained in:
Damien Elmes 2021-04-20 09:00:25 +10:00
parent 4d4603c078
commit 4d1cedc8b2
7 changed files with 144 additions and 79 deletions

View File

@ -58,7 +58,7 @@ impl DeckConfigService for Backend {
}
fn remove_deck_config(&self, input: pb::DeckConfigId) -> Result<pb::Empty> {
self.with_col(|col| col.transact_no_undo(|col| col.remove_deck_config(input.into())))
self.with_col(|col| col.transact_no_undo(|col| col.remove_deck_config_inner(input.into())))
.map(Into::into)
}

View File

@ -76,6 +76,13 @@ impl Default for DeckConf {
}
}
impl DeckConf {
pub(crate) fn set_modified(&mut self, usn: Usn) {
self.mtime_secs = TimestampSecs::now();
self.usn = usn;
}
}
impl Collection {
/// If fallback is true, guaranteed to return a deck config.
pub fn get_deck_config(&self, dcid: DeckConfId, fallback: bool) -> Result<Option<DeckConf>> {
@ -92,29 +99,64 @@ impl Collection {
Ok(None)
}
}
}
impl Collection {
pub(crate) fn add_or_update_deck_config(
&self,
conf: &mut DeckConf,
&mut self,
config: &mut DeckConf,
preserve_usn_and_mtime: bool,
) -> Result<()> {
if !preserve_usn_and_mtime {
conf.mtime_secs = TimestampSecs::now();
conf.usn = self.usn()?;
}
let orig = self.storage.get_deck_config(conf.id)?;
if let Some(_orig) = orig {
self.storage.update_deck_conf(&conf)
let usn = if preserve_usn_and_mtime {
None
} else {
if conf.id.0 == 0 {
conf.id.0 = TimestampMillis::now().0;
}
self.storage.add_deck_conf(conf)
Some(self.usn()?)
};
if config.id.0 == 0 {
self.add_deck_config_inner(config, usn)
} else {
let original = self
.storage
.get_deck_config(config.id)?
.ok_or(AnkiError::NotFound)?;
self.update_deck_config_inner(config, original, usn)
}
}
/// Assigns an id and adds to DB. If usn is provided, modification time and
/// usn will be updated.
pub(crate) fn add_deck_config_inner(
&mut self,
config: &mut DeckConf,
usn: Option<Usn>,
) -> Result<()> {
if let Some(usn) = usn {
config.set_modified(usn);
}
config.id.0 = TimestampMillis::now().0;
self.add_deck_config_undoable(config)
}
/// Update an existing deck config. If usn is provided, modification time
/// and usn will be updated.
pub(crate) fn update_deck_config_inner(
&mut self,
config: &mut DeckConf,
original: DeckConf,
usn: Option<Usn>,
) -> Result<()> {
if config == &original {
return Ok(());
}
if let Some(usn) = usn {
config.set_modified(usn);
}
self.update_deck_config_undoable(config, original)
}
/// Remove a deck configuration. This will force a full sync.
pub(crate) fn remove_deck_config(&mut self, dcid: DeckConfId) -> Result<()> {
pub(crate) fn remove_deck_config_inner(&mut self, dcid: DeckConfId) -> Result<()> {
if dcid.0 == 1 {
return Err(AnkiError::invalid_input("can't delete default conf"));
}

View File

@ -11,8 +11,6 @@ pub(crate) enum UndoableDeckConfigChange {
Removed(Box<DeckConf>),
}
// fixme: schema mod
impl Collection {
pub(crate) fn undo_deck_config_change(
&mut self,
@ -20,12 +18,12 @@ impl Collection {
) -> Result<()> {
match change {
UndoableDeckConfigChange::Added(config) => self.remove_deck_config_undoable(*config),
UndoableDeckConfigChange::Updated(mut config) => {
UndoableDeckConfigChange::Updated(config) => {
let current = self
.storage
.get_deck_config(config.id)?
.ok_or_else(|| AnkiError::invalid_input("deck config disappeared"))?;
self.update_single_deck_config_undoable(&mut *config, current)
self.update_deck_config_undoable(&config, current)
}
UndoableDeckConfigChange::Removed(config) => self.restore_deleted_deck_config(*config),
}
@ -37,7 +35,6 @@ impl Collection {
Ok(())
}
#[allow(dead_code)]
pub(super) fn add_deck_config_undoable(
&mut self,
config: &mut DeckConf,
@ -47,26 +44,15 @@ impl Collection {
Ok(())
}
pub(super) fn update_single_deck_config_undoable(
pub(super) fn update_deck_config_undoable(
&mut self,
config: &mut DeckConf,
config: &DeckConf,
original: DeckConf,
) -> Result<()> {
self.save_undo(UndoableDeckConfigChange::Updated(Box::new(original)));
self.storage.update_deck_conf(config)
}
#[allow(dead_code)]
fn add_or_update_deck_config_with_existing_id_undoable(
&mut self,
config: &mut DeckConf,
) -> Result<(), AnkiError> {
self.storage
.add_or_update_deck_config_with_existing_id(config)?;
self.save_undo(UndoableDeckConfigChange::Added(Box::new(config.clone())));
Ok(())
}
fn restore_deleted_deck_config(&mut self, config: DeckConf) -> Result<()> {
self.storage
.add_or_update_deck_config_with_existing_id(&config)?;

View File

@ -1,6 +1,8 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
//! Updating configs in bulk, from the deck config screen.
use std::collections::{HashMap, HashSet};
use crate::{
@ -98,15 +100,42 @@ impl Collection {
.collect())
}
fn update_deck_configs_inner(&mut self, input: UpdateDeckConfigsIn) -> Result<()> {
fn update_deck_configs_inner(&mut self, mut input: UpdateDeckConfigsIn) -> Result<()> {
if input.configs.is_empty() {
return Err(AnkiError::invalid_input("config not provided"));
}
// handle removals first
for dcid in &input.removed_config_ids {
self.remove_deck_config(*dcid)?;
self.remove_deck_config_inner(*dcid)?;
}
todo!();
// add/update provided configs
for conf in &mut input.configs {
self.add_or_update_deck_config(conf, false)?;
}
// point current deck to last config
let usn = self.usn()?;
let config_id = input.configs.last().unwrap().id;
let mut deck = self
.storage
.get_deck(input.target_deck_id)?
.ok_or(AnkiError::NotFound)?;
let original = deck.clone();
deck.normal_mut()?.config_id = config_id.0;
self.update_deck_inner(&mut deck, original, usn)?;
if input.apply_to_children {
for mut child_deck in self.storage.child_decks(&deck)? {
let child_original = child_deck.clone();
if let Ok(normal) = child_deck.normal_mut() {
normal.config_id = config_id.0;
self.update_deck_inner(&mut child_deck, child_original, usn)?;
}
}
}
Ok(())
}
}

View File

@ -1,8 +1,37 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
//! Adding and updating.
use super::name::immediate_parent_name;
use crate::{error::FilteredDeckError, prelude::*};
impl Collection {
/// Add a new deck. The id must be 0, as it will be automatically assigned.
pub fn add_deck(&mut self, deck: &mut Deck) -> Result<OpOutput<()>> {
self.transact(Op::AddDeck, |col| col.add_deck_inner(deck, col.usn()?))
}
pub fn update_deck(&mut self, deck: &mut Deck) -> Result<OpOutput<()>> {
self.transact(Op::UpdateDeck, |col| {
let existing_deck = col.storage.get_deck(deck.id)?.ok_or(AnkiError::NotFound)?;
col.update_deck_inner(deck, existing_deck, col.usn()?)
})
}
/// Add or update an existing deck modified by the user. May add parents,
/// or rename children as required. Prefer add_deck() or update_deck() to
/// be explicit about your intentions; this function mainly exists so we
/// can integrate with older Python code that behaved this way.
pub fn add_or_update_deck(&mut self, deck: &mut Deck) -> Result<OpOutput<()>> {
if deck.id.0 == 0 {
self.add_deck(deck)
} else {
self.update_deck(deck)
}
}
}
impl Collection {
/// Rename deck if not unique. Bumps mtime and usn if
/// name was changed, but otherwise leaves it the same.
@ -13,41 +42,16 @@ impl Collection {
self.ensure_deck_name_unique(deck, usn)
}
/// Add or update an existing deck modified by the user. May add parents,
/// or rename children as required. Prefer add_deck() or update_deck() to
/// be explicit about your intentions; this function mainly exists so we
/// can integrate with older Python code that behaved this way.
pub(crate) fn add_or_update_deck(&mut self, deck: &mut Deck) -> Result<OpOutput<()>> {
if deck.id.0 == 0 {
self.add_deck(deck)
} else {
self.update_deck(deck)
}
}
/// Add a new deck. The id must be 0, as it will be automatically assigned.
pub fn add_deck(&mut self, deck: &mut Deck) -> Result<OpOutput<()>> {
pub(crate) fn add_deck_inner(&mut self, deck: &mut Deck, usn: Usn) -> Result<()> {
if deck.id.0 != 0 {
return Err(AnkiError::invalid_input("deck to add must have id 0"));
}
self.transact(Op::AddDeck, |col| col.add_deck_inner(deck, col.usn()?))
}
pub(crate) fn add_deck_inner(&mut self, deck: &mut Deck, usn: Usn) -> Result<()> {
self.prepare_deck_for_update(deck, usn)?;
deck.set_modified(usn);
self.match_or_create_parents(deck, usn)?;
self.add_deck_undoable(deck)
}
pub fn update_deck(&mut self, deck: &mut Deck) -> Result<OpOutput<()>> {
self.transact(Op::UpdateDeck, |col| {
let existing_deck = col.storage.get_deck(deck.id)?.ok_or(AnkiError::NotFound)?;
col.update_deck_inner(deck, existing_deck, col.usn()?)
})
}
pub(crate) fn update_deck_inner(
&mut self,
deck: &mut Deck,
@ -91,9 +95,7 @@ impl Collection {
deck.set_modified(usn);
self.add_or_update_single_deck_with_existing_id(&mut deck, usn)
}
}
impl Collection {
/// Add a single, normal deck with the provided name for a child deck.
/// Caller must have done necessarily validation on name.
fn add_parent_deck(&mut self, machine_name: &str, usn: Usn) -> Result<()> {

View File

@ -1,7 +1,7 @@
// Copyright: Ankitects Pty Ltd and contributors
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
mod add;
mod addupdate;
mod counts;
mod current;
mod filtered;
@ -134,6 +134,20 @@ impl Deck {
}
}
impl Collection {
pub fn get_or_create_normal_deck(&mut self, human_name: &str) -> Result<Deck> {
let name = NativeDeckName::from_human_name(human_name);
if let Some(did) = self.storage.get_deck_id(name.as_native_str())? {
self.storage.get_deck(did).map(|opt| opt.unwrap())
} else {
let mut deck = Deck::new_normal();
deck.name = name;
self.add_or_update_deck(&mut deck)?;
Ok(deck)
}
}
}
impl Collection {
pub(crate) fn get_deck(&mut self, did: DeckId) -> Result<Option<Arc<Deck>>> {
if let Some(deck) = self.state.deck_cache.get(&did) {
@ -152,18 +166,6 @@ impl Collection {
self.storage.deck_is_empty(DeckId(1))
}
pub fn get_or_create_normal_deck(&mut self, human_name: &str) -> Result<Deck> {
let name = NativeDeckName::from_human_name(human_name);
if let Some(did) = self.storage.get_deck_id(name.as_native_str())? {
self.storage.get_deck(did).map(|opt| opt.unwrap())
} else {
let mut deck = Deck::new_normal();
deck.name = name;
self.add_or_update_deck(&mut deck)?;
Ok(deck)
}
}
/// Get a deck based on its human name. If you have a machine name,
/// use the method in storage instead.
pub(crate) fn get_deck_id(&self, human_name: &str) -> Result<Option<DeckId>> {

View File

@ -51,6 +51,7 @@ export class DeckConfigState {
private selectedIdx: number;
private configListSetter!: (val: ConfigListEntry[]) => void;
private parentLimitsSetter!: (val: ParentLimits) => void;
private modifiedConfigs: Set<DeckConfigId> = new Set();
private removedConfigs: DeckConfigId[] = [];
private schemaModified: boolean;
@ -107,8 +108,11 @@ export class DeckConfigState {
return;
}
const uniqueName = this.ensureNewNameUnique(name);
this.configs[this.selectedIdx].config.name = uniqueName;
this.configs[this.selectedIdx].config.mtimeSecs = 0;
const config = this.configs[this.selectedIdx].config;
config.name = uniqueName;
if (config.id) {
this.modifiedConfigs.add(config.id);
}
this.updateConfigList();
}