mirror of
https://github.com/ankitects/anki.git
synced 2025-09-18 14:02:21 -04:00
Add option to calculate all weights at once
This commit is contained in:
parent
c67f510b9a
commit
452e012c71
14 changed files with 162 additions and 46 deletions
|
@ -293,6 +293,7 @@ deck-config-confirm-remove-name = Remove { $name }?
|
|||
|
||||
deck-config-save-button = Save
|
||||
deck-config-save-to-all-subdecks = Save to All Subdecks
|
||||
deck-config-save-and-optimize = Optimize All Presets
|
||||
deck-config-revert-button-tooltip = Restore this setting to its default value.
|
||||
|
||||
## These strings are shown via the Description button at the bottom of the
|
||||
|
@ -409,6 +410,13 @@ deck-config-a-100-day-interval =
|
|||
[one] A 100 day interval will become { $days } day.
|
||||
*[other] A 100 day interval will become { $days } days.
|
||||
}
|
||||
deck-config-percent-of-reviews =
|
||||
{ $reviews ->
|
||||
[one] { $pct }% of { $reviews } review
|
||||
*[other] { $pct }% of { $reviews } reviews
|
||||
}
|
||||
deck-config-optimizing-preset = Optimizing preset { $current_count }/{ $total_count }...
|
||||
deck-config-fsrs-must-be-enabled = FSRS must be enabled first.
|
||||
|
||||
deck-config-wait-for-audio = Wait for audio
|
||||
deck-config-show-reminder = Show Reminder
|
||||
|
|
|
@ -134,9 +134,15 @@ message Progress {
|
|||
}
|
||||
|
||||
message ComputeWeightsProgress {
|
||||
// Current iteration
|
||||
uint32 current = 1;
|
||||
// Total iterations
|
||||
uint32 total = 2;
|
||||
uint32 fsrs_items = 3;
|
||||
// Only used in 'compute all weights' case
|
||||
uint32 current_preset = 4;
|
||||
// Only used in 'compute all weights' case
|
||||
uint32 total_presets = 5;
|
||||
}
|
||||
|
||||
message ComputeRetentionProgress {
|
||||
|
|
|
@ -200,13 +200,19 @@ message DeckConfigsForUpdate {
|
|||
bool apply_all_parent_limits = 9;
|
||||
}
|
||||
|
||||
enum UpdateDeckConfigsMode {
|
||||
UPDATE_DECK_CONFIGS_MODE_NORMAL = 0;
|
||||
UPDATE_DECK_CONFIGS_MODE_APPLY_TO_CHILDREN = 1;
|
||||
UPDATE_DECK_CONFIGS_MODE_COMPUTE_ALL_WEIGHTS = 2;
|
||||
}
|
||||
|
||||
message UpdateDeckConfigsRequest {
|
||||
int64 target_deck_id = 1;
|
||||
/// Unchanged, non-selected configs can be omitted. Deck will
|
||||
/// be set to whichever entry comes last.
|
||||
repeated DeckConfig configs = 2;
|
||||
repeated int64 removed_config_ids = 3;
|
||||
bool apply_to_children = 4;
|
||||
UpdateDeckConfigsMode mode = 4;
|
||||
string card_state_customizer = 5;
|
||||
DeckConfigsForUpdate.CurrentDeck.Limits limits = 6;
|
||||
bool new_cards_ignore_review_limit = 7;
|
||||
|
|
|
@ -36,7 +36,7 @@ from aqt.operations import on_op_finished
|
|||
from aqt.operations.deck import update_deck_configs as update_deck_configs_op
|
||||
from aqt.progress import ProgressUpdate
|
||||
from aqt.qt import *
|
||||
from aqt.utils import aqt_data_path, show_warning
|
||||
from aqt.utils import aqt_data_path, show_warning, tr
|
||||
|
||||
app = flask.Flask(__name__, root_path="/fake")
|
||||
flask_cors.CORS(app, resources={r"/*": {"origins": "127.0.0.1"}})
|
||||
|
@ -433,12 +433,26 @@ def update_deck_configs() -> bytes:
|
|||
input.ParseFromString(request.data)
|
||||
|
||||
def on_progress(progress: Progress, update: ProgressUpdate) -> None:
|
||||
if not progress.HasField("compute_memory"):
|
||||
if progress.HasField("compute_memory"):
|
||||
val = progress.compute_memory
|
||||
update.max = val.total_cards
|
||||
update.value = val.current_cards
|
||||
update.label = val.label
|
||||
elif progress.HasField("compute_weights"):
|
||||
val2 = progress.compute_weights
|
||||
update.max = val2.total
|
||||
update.value = val2.current
|
||||
pct = str(int(val2.current / val2.total * 100) if val2.total > 0 else 0)
|
||||
label = tr.deck_config_optimizing_preset(
|
||||
current_count=val2.current_preset, total_count=val2.total_presets
|
||||
)
|
||||
update.label = (
|
||||
label
|
||||
+ "\n"
|
||||
+ tr.deck_config_percent_of_reviews(pct=pct, reviews=val2.fsrs_items)
|
||||
)
|
||||
else:
|
||||
return
|
||||
val = progress.compute_memory
|
||||
update.max = val.total_cards
|
||||
update.value = val.current_cards
|
||||
update.label = val.label
|
||||
if update.user_wants_abort:
|
||||
update.abort = True
|
||||
|
||||
|
|
|
@ -94,11 +94,12 @@ impl From<DeckConfig> for anki_proto::deck_config::DeckConfig {
|
|||
|
||||
impl From<anki_proto::deck_config::UpdateDeckConfigsRequest> for UpdateDeckConfigsRequest {
|
||||
fn from(c: anki_proto::deck_config::UpdateDeckConfigsRequest) -> Self {
|
||||
let mode = c.mode();
|
||||
UpdateDeckConfigsRequest {
|
||||
target_deck_id: c.target_deck_id.into(),
|
||||
configs: c.configs.into_iter().map(Into::into).collect(),
|
||||
removed_config_ids: c.removed_config_ids.into_iter().map(Into::into).collect(),
|
||||
apply_to_children: c.apply_to_children,
|
||||
mode,
|
||||
card_state_customizer: c.card_state_customizer,
|
||||
limits: c.limits.unwrap_or_default(),
|
||||
new_cards_ignore_review_limit: c.new_cards_ignore_review_limit,
|
||||
|
|
|
@ -10,6 +10,7 @@ use std::iter;
|
|||
use anki_proto::deck_config::deck_configs_for_update::current_deck::Limits;
|
||||
use anki_proto::deck_config::deck_configs_for_update::ConfigWithExtra;
|
||||
use anki_proto::deck_config::deck_configs_for_update::CurrentDeck;
|
||||
use anki_proto::deck_config::UpdateDeckConfigsMode;
|
||||
use anki_proto::decks::deck::normal::DayLimit;
|
||||
use fsrs::DEFAULT_WEIGHTS;
|
||||
|
||||
|
@ -27,7 +28,7 @@ pub struct UpdateDeckConfigsRequest {
|
|||
/// Deck will be set to last provided deck config.
|
||||
pub configs: Vec<DeckConfig>,
|
||||
pub removed_config_ids: Vec<DeckConfigId>,
|
||||
pub apply_to_children: bool,
|
||||
pub mode: UpdateDeckConfigsMode,
|
||||
pub card_state_customizer: String,
|
||||
pub limits: Limits,
|
||||
pub new_cards_ignore_review_limit: bool,
|
||||
|
@ -136,6 +137,10 @@ impl Collection {
|
|||
configs_after_update.remove(dcid);
|
||||
}
|
||||
|
||||
if req.mode == UpdateDeckConfigsMode::ComputeAllWeights {
|
||||
self.compute_all_weights(&mut req)?;
|
||||
}
|
||||
|
||||
// add/update provided configs
|
||||
for conf in &mut req.configs {
|
||||
let weight_len = conf.inner.fsrs_weights.len();
|
||||
|
@ -147,7 +152,7 @@ impl Collection {
|
|||
}
|
||||
|
||||
// get selected deck and possibly children
|
||||
let selected_deck_ids: HashSet<_> = if req.apply_to_children {
|
||||
let selected_deck_ids: HashSet<_> = if req.mode == UpdateDeckConfigsMode::ApplyToChildren {
|
||||
let deck = self
|
||||
.storage
|
||||
.get_deck(req.target_deck_id)?
|
||||
|
@ -295,6 +300,46 @@ impl Collection {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
fn compute_all_weights(&mut self, req: &mut UpdateDeckConfigsRequest) -> Result<()> {
|
||||
require!(req.fsrs, "FSRS must be enabled");
|
||||
|
||||
// frontend didn't include any unmodified deck configs, so we need to fill them
|
||||
// in
|
||||
let changed_configs: HashSet<_> = req.configs.iter().map(|c| c.id).collect();
|
||||
let previous_last = req.configs.pop().or_invalid("no configs provided")?;
|
||||
for config in self.storage.all_deck_config()? {
|
||||
if !changed_configs.contains(&config.id) {
|
||||
req.configs.push(config);
|
||||
}
|
||||
}
|
||||
// other parts of the code expect the currently-selected preset to come last
|
||||
req.configs.push(previous_last);
|
||||
|
||||
// calculate and apply weights to each preset
|
||||
let config_len = req.configs.len() as u32;
|
||||
for (idx, config) in req.configs.iter_mut().enumerate() {
|
||||
let search = if config.inner.weight_search.trim().is_empty() {
|
||||
SearchNode::Preset(config.name.clone())
|
||||
.try_into_search()?
|
||||
.to_string()
|
||||
} else {
|
||||
config.inner.weight_search.clone()
|
||||
};
|
||||
match self.compute_weights(&search, idx as u32 + 1, config_len) {
|
||||
Ok(weights) => {
|
||||
if weights.fsrs_items >= 1000 {
|
||||
println!("{}: {:?}", config.name, weights.weights);
|
||||
config.inner.fsrs_weights = weights.weights;
|
||||
}
|
||||
}
|
||||
Err(AnkiError::Interrupted) => return Err(AnkiError::Interrupted),
|
||||
Err(err) => {
|
||||
println!("{}: {}", config.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn normal_deck_to_limits(deck: &NormalDeck, today: u32) -> Limits {
|
||||
|
@ -383,7 +428,7 @@ mod test {
|
|||
.map(|c| c.config.unwrap().into())
|
||||
.collect(),
|
||||
removed_config_ids: vec![],
|
||||
apply_to_children: false,
|
||||
mode: UpdateDeckConfigsMode::Normal,
|
||||
card_state_customizer: "".to_string(),
|
||||
limits: Limits::default(),
|
||||
new_cards_ignore_review_limit: false,
|
||||
|
|
|
@ -211,9 +211,11 @@ pub(crate) fn progress_to_proto(
|
|||
),
|
||||
Progress::ComputeWeights(progress) => {
|
||||
Value::ComputeWeights(anki_proto::collection::ComputeWeightsProgress {
|
||||
current: progress.current,
|
||||
total: progress.total,
|
||||
current: progress.current_iteration,
|
||||
total: progress.total_iterations,
|
||||
fsrs_items: progress.fsrs_items,
|
||||
current_preset: progress.current_preset,
|
||||
total_presets: progress.total_presets,
|
||||
})
|
||||
}
|
||||
Progress::ComputeRetention(progress) => {
|
||||
|
|
|
@ -27,13 +27,25 @@ use crate::search::SortMode;
|
|||
pub(crate) type Weights = Vec<f32>;
|
||||
|
||||
impl Collection {
|
||||
pub fn compute_weights(&mut self, search: &str) -> Result<ComputeFsrsWeightsResponse> {
|
||||
/// Note this does not return an error if there are less than 1000 items -
|
||||
/// the caller should instead check the fsrs_items count in the return
|
||||
/// value.
|
||||
pub fn compute_weights(
|
||||
&mut self,
|
||||
search: &str,
|
||||
current_preset: u32,
|
||||
total_presets: u32,
|
||||
) -> Result<ComputeFsrsWeightsResponse> {
|
||||
let mut anki_progress = self.new_progress_handler::<ComputeWeightsProgress>();
|
||||
let timing = self.timing_today()?;
|
||||
let revlogs = self.revlog_for_srs(search)?;
|
||||
let items = fsrs_items_for_training(revlogs, timing.next_day_at);
|
||||
let fsrs_items = items.len() as u32;
|
||||
anki_progress.update(false, |p| p.fsrs_items = fsrs_items)?;
|
||||
anki_progress.update(false, |p| {
|
||||
p.fsrs_items = fsrs_items;
|
||||
p.current_preset = current_preset;
|
||||
p.total_presets = total_presets;
|
||||
})?;
|
||||
// adapt the progress handler to our built-in progress handling
|
||||
let progress = CombinedProgressState::new_shared();
|
||||
let progress2 = progress.clone();
|
||||
|
@ -43,9 +55,9 @@ impl Collection {
|
|||
thread::sleep(Duration::from_millis(100));
|
||||
let mut guard = progress.lock().unwrap();
|
||||
if let Err(_err) = anki_progress.update(false, |s| {
|
||||
s.total = guard.total() as u32;
|
||||
s.current = guard.current() as u32;
|
||||
finished = s.total > 0 && s.total == s.current;
|
||||
s.total_iterations = guard.total() as u32;
|
||||
s.current_iteration = guard.current() as u32;
|
||||
finished = guard.finished();
|
||||
}) {
|
||||
guard.want_abort = true;
|
||||
return;
|
||||
|
@ -112,8 +124,8 @@ impl Collection {
|
|||
Ok(fsrs.evaluate(items, |ip| {
|
||||
anki_progress
|
||||
.update(false, |p| {
|
||||
p.total = ip.total as u32;
|
||||
p.current = ip.current as u32;
|
||||
p.total_iterations = ip.total as u32;
|
||||
p.current_iteration = ip.current as u32;
|
||||
})
|
||||
.is_ok()
|
||||
})?)
|
||||
|
@ -122,9 +134,13 @@ impl Collection {
|
|||
|
||||
#[derive(Default, Clone, Copy, Debug)]
|
||||
pub struct ComputeWeightsProgress {
|
||||
pub current: u32,
|
||||
pub total: u32,
|
||||
pub current_iteration: u32,
|
||||
pub total_iterations: u32,
|
||||
pub fsrs_items: u32,
|
||||
/// Only used in 'compute all weights' case
|
||||
pub current_preset: u32,
|
||||
/// Only used in 'compute all weights' case
|
||||
pub total_presets: u32,
|
||||
}
|
||||
|
||||
/// Convert a series of revlog entries sorted by card id into FSRS items.
|
||||
|
|
|
@ -254,7 +254,7 @@ impl crate::services::SchedulerService for Collection {
|
|||
&mut self,
|
||||
input: scheduler::ComputeFsrsWeightsRequest,
|
||||
) -> Result<scheduler::ComputeFsrsWeightsResponse> {
|
||||
self.compute_weights(&input.search)
|
||||
self.compute_weights(&input.search, 1, 1)
|
||||
}
|
||||
|
||||
fn compute_optimal_retention(
|
||||
|
|
|
@ -47,6 +47,12 @@ pub(super) fn write_nodes(nodes: &[Node]) -> String {
|
|||
nodes.iter().map(write_node).collect()
|
||||
}
|
||||
|
||||
impl ToString for Node {
|
||||
fn to_string(&self) -> String {
|
||||
write_node(self)
|
||||
}
|
||||
}
|
||||
|
||||
fn write_node(node: &Node) -> String {
|
||||
use Node::*;
|
||||
match node {
|
||||
|
|
|
@ -202,12 +202,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
if (!val || !val.total) {
|
||||
return "";
|
||||
}
|
||||
let pct = ((val.current / val.total) * 100).toFixed(1);
|
||||
pct = `${pct}%`;
|
||||
const pct = ((val.current / val.total) * 100).toFixed(1);
|
||||
if (val instanceof ComputeRetentionProgress) {
|
||||
return pct;
|
||||
return `${pct}%`;
|
||||
} else {
|
||||
return `${pct} of ${val.fsrsItems} reviews`;
|
||||
return tr.deckConfigPercentOfReviews({ pct, reviews: val.fsrsItems });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,10 +3,12 @@ Copyright: Ankitects Pty Ltd and contributors
|
|||
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
||||
-->
|
||||
<script lang="ts">
|
||||
import { UpdateDeckConfigsMode } from "@tslib/anki/deck_config_pb";
|
||||
import * as tr from "@tslib/ftl";
|
||||
import { withCollapsedWhitespace } from "@tslib/i18n";
|
||||
import { getPlatformString } from "@tslib/shortcuts";
|
||||
import { createEventDispatcher, tick } from "svelte";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
import DropdownDivider from "../components/DropdownDivider.svelte";
|
||||
import DropdownItem from "../components/DropdownItem.svelte";
|
||||
|
@ -57,9 +59,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
}
|
||||
}
|
||||
|
||||
async function save(applyToChildDecks: boolean): Promise<void> {
|
||||
async function save(mode: UpdateDeckConfigsMode): Promise<void> {
|
||||
await commitEditing();
|
||||
state.save(applyToChildDecks);
|
||||
if (!get(state.fsrs)) {
|
||||
alert(tr.deckConfigFsrsMustBeEnabled());
|
||||
return;
|
||||
}
|
||||
state.save(mode);
|
||||
}
|
||||
|
||||
const saveKeyCombination = "Control+Enter";
|
||||
|
@ -69,14 +75,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
|
||||
<LabelButton
|
||||
primary
|
||||
on:click={() => save(false)}
|
||||
on:click={() => save(UpdateDeckConfigsMode.NORMAL)}
|
||||
tooltip={getPlatformString(saveKeyCombination)}
|
||||
--border-left-radius={!rtl ? "var(--border-radius)" : "0"}
|
||||
--border-right-radius={rtl ? "var(--border-radius)" : "0"}
|
||||
>
|
||||
<div class="save">{tr.deckConfigSaveButton()}</div>
|
||||
</LabelButton>
|
||||
<Shortcut keyCombination={saveKeyCombination} on:action={() => save(false)} />
|
||||
<Shortcut
|
||||
keyCombination={saveKeyCombination}
|
||||
on:action={() => save(UpdateDeckConfigsMode.NORMAL)}
|
||||
/>
|
||||
|
||||
<WithFloating
|
||||
show={showFloating}
|
||||
|
@ -108,9 +117,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
{tr.deckConfigRemoveGroup()}
|
||||
</DropdownItem>
|
||||
<DropdownDivider />
|
||||
<DropdownItem on:click={() => save(true)}>
|
||||
<DropdownItem on:click={() => save(UpdateDeckConfigsMode.APPLY_TO_CHILDREN)}>
|
||||
{tr.deckConfigSaveToAllSubdecks()}
|
||||
</DropdownItem>
|
||||
<DropdownItem on:click={() => save(UpdateDeckConfigsMode.COMPUTE_ALL_WEIGHTS)}>
|
||||
{tr.deckConfigSaveAndOptimize()}
|
||||
</DropdownItem>
|
||||
</Popover>
|
||||
</WithFloating>
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
*/
|
||||
|
||||
import { protoBase64 } from "@bufbuild/protobuf";
|
||||
import { DeckConfig_Config_LeechAction, DeckConfigsForUpdate } from "@tslib/anki/deck_config_pb";
|
||||
import { DeckConfig_Config_LeechAction, DeckConfigsForUpdate, UpdateDeckConfigsMode } from "@tslib/anki/deck_config_pb";
|
||||
import { get } from "svelte/store";
|
||||
|
||||
import { DeckOptionsState } from "./lib";
|
||||
|
@ -203,7 +203,7 @@ test("deck list", () => {
|
|||
expect(get(state.currentConfig).newPerDay).toBe(10);
|
||||
|
||||
// only the pre-existing deck should be listed for removal
|
||||
const out = state.dataForSaving(false);
|
||||
const out = state.dataForSaving(UpdateDeckConfigsMode.NORMAL);
|
||||
expect(out.removedConfigIds).toStrictEqual([1618570764780n]);
|
||||
});
|
||||
|
||||
|
@ -221,24 +221,24 @@ test("duplicate name", () => {
|
|||
|
||||
test("saving", () => {
|
||||
let state = startingState();
|
||||
let out = state.dataForSaving(false);
|
||||
let out = state.dataForSaving(UpdateDeckConfigsMode.NORMAL);
|
||||
expect(out.removedConfigIds).toStrictEqual([]);
|
||||
expect(out.targetDeckId).toBe(123n);
|
||||
// in no-changes case, currently selected config should
|
||||
// be returned
|
||||
expect(out.configs!.length).toBe(1);
|
||||
expect(out.configs![0].name).toBe("another one");
|
||||
expect(out.applyToChildren).toBe(false);
|
||||
expect(out.mode).toBe(UpdateDeckConfigsMode.NORMAL);
|
||||
|
||||
// rename, then change current deck
|
||||
state.setCurrentName("zzz");
|
||||
out = state.dataForSaving(true);
|
||||
out = state.dataForSaving(UpdateDeckConfigsMode.APPLY_TO_CHILDREN);
|
||||
state.setCurrentIndex(0);
|
||||
|
||||
// renamed deck should be in changes, with current deck as last element
|
||||
out = state.dataForSaving(true);
|
||||
out = state.dataForSaving(UpdateDeckConfigsMode.APPLY_TO_CHILDREN);
|
||||
expect(out.configs!.map((c) => c.name)).toStrictEqual(["zzz", "Default"]);
|
||||
expect(out.applyToChildren).toBe(true);
|
||||
expect(out.mode).toBe(UpdateDeckConfigsMode.APPLY_TO_CHILDREN);
|
||||
|
||||
// start again, adding new deck
|
||||
state = startingState();
|
||||
|
@ -246,7 +246,7 @@ test("saving", () => {
|
|||
|
||||
// deleting it should not change removedConfigs
|
||||
state.removeCurrentConfig();
|
||||
out = state.dataForSaving(true);
|
||||
out = state.dataForSaving(UpdateDeckConfigsMode.APPLY_TO_CHILDREN);
|
||||
expect(out.removedConfigIds).toStrictEqual([]);
|
||||
|
||||
// select the other non-default deck & remove
|
||||
|
@ -255,7 +255,7 @@ test("saving", () => {
|
|||
|
||||
// should be listed in removedConfigs, and modified should
|
||||
// only contain Default, which is the new current deck
|
||||
out = state.dataForSaving(true);
|
||||
out = state.dataForSaving(UpdateDeckConfigsMode.APPLY_TO_CHILDREN);
|
||||
expect(out.removedConfigIds).toStrictEqual([1618570764780n]);
|
||||
expect(out.configs!.map((c) => c.name)).toStrictEqual(["Default"]);
|
||||
});
|
||||
|
@ -283,7 +283,7 @@ test("aux data", () => {
|
|||
});
|
||||
|
||||
// ensure changes serialize
|
||||
const out = state.dataForSaving(true);
|
||||
const out = state.dataForSaving(UpdateDeckConfigsMode.APPLY_TO_CHILDREN);
|
||||
expect(out.configs!.length).toBe(2);
|
||||
const json = out.configs!.map((c) => JSON.parse(new TextDecoder().decode(c.config!.other)));
|
||||
expect(json).toStrictEqual([
|
||||
|
|
|
@ -5,6 +5,7 @@ import type { PlainMessage } from "@bufbuild/protobuf";
|
|||
import type {
|
||||
DeckConfigsForUpdate,
|
||||
DeckConfigsForUpdate_CurrentDeck,
|
||||
UpdateDeckConfigsMode,
|
||||
UpdateDeckConfigsRequest,
|
||||
} from "@tslib/anki/deck_config_pb";
|
||||
import { DeckConfig, DeckConfig_Config, DeckConfigsForUpdate_CurrentDeck_Limits } from "@tslib/anki/deck_config_pb";
|
||||
|
@ -178,7 +179,7 @@ export class DeckOptionsState {
|
|||
}
|
||||
|
||||
dataForSaving(
|
||||
applyToChildren: boolean,
|
||||
mode: UpdateDeckConfigsMode,
|
||||
): PlainMessage<UpdateDeckConfigsRequest> {
|
||||
const modifiedConfigsExcludingCurrent = this.configs
|
||||
.map((c) => c.config)
|
||||
|
@ -197,7 +198,7 @@ export class DeckOptionsState {
|
|||
targetDeckId: this.targetDeckId,
|
||||
removedConfigIds: this.removedConfigs,
|
||||
configs,
|
||||
applyToChildren,
|
||||
mode,
|
||||
cardStateCustomizer: get(this.cardStateCustomizer),
|
||||
limits: get(this.deckLimits),
|
||||
newCardsIgnoreReviewLimit: get(this.newCardsIgnoreReviewLimit),
|
||||
|
@ -210,9 +211,9 @@ export class DeckOptionsState {
|
|||
return this._presetAssignmentsChanged;
|
||||
}
|
||||
|
||||
async save(applyToChildren: boolean): Promise<void> {
|
||||
async save(mode: UpdateDeckConfigsMode): Promise<void> {
|
||||
await updateDeckConfigs(
|
||||
this.dataForSaving(applyToChildren),
|
||||
this.dataForSaving(mode),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue