Anki/ts/deck-options/DisplayOrder.svelte
RumovZ 14de8451dc
Merging Notetypes on Import (#2612)
* Remember original id when importing notetype

* Reuse notetypes with matching original id

* Add field and template ids

* Enable merging imported notetypes

* Fix test

Note should be updated if the incoming note's notetype is
remapped to the existing note's notetype.
On the other hand, it should be skipped if its notetype id is mapped
to some new notetype.

* Change field and template ids to i32

* Add merge notetypes flag to proto message

* Add dialog for apkg import

* Move HelpModal into components

* Generalize import dialog

* Move SettingTitle into components

* Add help modal to ImportAnkiPackagePage

* Move SwitchRow into components

* Fix backend method import

* Make testable in browser

* Fix broken modal

* Wrap in container and fix margins

* Update commented Anki version of new proto fields

* Check ids when comparing notetype schemas

* Add tooltip for merging notetypes.

* Allow updating notes regardless of mtime

* Gitignore yarn-error.log

* Allow updating notetypes regardless of mtime

* Fix apkg help carousel

* Use i64s for template and field ids

* Add option to omit importing scheduling info

* Restore last settings in apkg import dialog

* Display error when getting metadata in webview

* Update manual links for apkg importing

* Apply suggestions from code review

Co-authored-by: Damien Elmes <dae@users.noreply.github.com>

* Omit schduling -> Import all cards as new cards

* Tweak importing-update-notes-help

* UpdateCondition → ImportAnkiPackageUpdateCondition

* Load keyboard.ftl

* Skip updating dupes in 'update alwyas' case

* Explain more when merging notetypes is required

* "omit scheduling" → "with scheduling"

* Skip updating notetype dupes if 'update always'

* Merge duplicated notetypes from previous imports

* Fix rebase aftermath

* Fix panic when merging

* Clarify 'update notetypes' help

* Mention 'merge notetypes' in the log

* Add a test which covers the previously panicking path

* Use nested ftl messages to ensure consistency

* Make order of merged fields deterministic

* Rewrite test to trigger panic

* Update version comment on new fields
2023-09-09 09:00:55 +10:00

226 lines
8 KiB
Svelte

<!--
Copyright: Ankitects Pty Ltd and contributors
License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
-->
<script lang="ts">
import {
DeckConfig_Config_NewCardGatherPriority as GatherOrder,
DeckConfig_Config_NewCardSortOrder as SortOrder,
} from "@tslib/anki/deck_config_pb";
import * as tr from "@tslib/ftl";
import { HelpPage } from "@tslib/help-page";
import type Carousel from "bootstrap/js/dist/carousel";
import type Modal from "bootstrap/js/dist/modal";
import DynamicallySlottable from "../components/DynamicallySlottable.svelte";
import EnumSelectorRow from "../components/EnumSelectorRow.svelte";
import HelpModal from "../components/HelpModal.svelte";
import Item from "../components/Item.svelte";
import SettingTitle from "../components/SettingTitle.svelte";
import TitledContainer from "../components/TitledContainer.svelte";
import type { HelpItem } from "../components/types";
import type { DeckOptionsState } from "./lib";
import { reviewMixChoices } from "./strings";
export let state: DeckOptionsState;
export let api: Record<string, never>;
const config = state.currentConfig;
const defaults = state.defaults;
const currentDeck = "\n\n" + tr.deckConfigDisplayOrderWillUseCurrentDeck();
const newGatherPriorityChoices = [
tr.deckConfigNewGatherPriorityDeck(),
tr.deckConfigNewGatherPriorityPositionLowestFirst(),
tr.deckConfigNewGatherPriorityPositionHighestFirst(),
tr.deckConfigNewGatherPriorityRandomNotes(),
tr.deckConfigNewGatherPriorityRandomCards(),
];
const newSortOrderChoices = [
tr.deckConfigSortOrderTemplateThenGather(),
tr.deckConfigSortOrderGather(),
tr.deckConfigSortOrderCardTemplateThenRandom(),
tr.deckConfigSortOrderRandomNoteThenTemplate(),
tr.deckConfigSortOrderRandom(),
];
const reviewOrderChoices = [
tr.deckConfigSortOrderDueDateThenRandom(),
tr.deckConfigSortOrderDueDateThenDeck(),
tr.deckConfigSortOrderDeckThenDueDate(),
tr.deckConfigSortOrderAscendingIntervals(),
tr.deckConfigSortOrderDescendingIntervals(),
tr.deckConfigSortOrderAscendingEase(),
tr.deckConfigSortOrderDescendingEase(),
tr.deckConfigSortOrderRelativeOverdueness(),
tr.deckConfigSortOrderRandom(),
];
let disabledNewSortOrders: number[] = [];
$: {
switch ($config.newCardGatherPriority) {
case GatherOrder.RANDOM_NOTES:
disabledNewSortOrders = [
// same as TEMPLATE
SortOrder.TEMPLATE_THEN_RANDOM,
// same as NO_SORT
SortOrder.RANDOM_NOTE_THEN_TEMPLATE,
];
break;
case GatherOrder.RANDOM_CARDS:
disabledNewSortOrders = [
// same as TEMPLATE
SortOrder.TEMPLATE_THEN_RANDOM,
// not useful if siblings are not gathered together
SortOrder.RANDOM_NOTE_THEN_TEMPLATE,
// same as NO_SORT
SortOrder.RANDOM_CARD,
];
break;
default:
disabledNewSortOrders = [];
break;
}
// disabled options aren't deselected automatically
if (disabledNewSortOrders.includes($config.newCardSortOrder)) {
// default option should never be disabled
$config.newCardSortOrder = 0;
}
// check for invalid index from previous version
if (!($config.newCardSortOrder in SortOrder)) {
$config.newCardSortOrder = 0;
}
}
const settings = {
newGatherPriority: {
title: tr.deckConfigNewGatherPriority(),
help: tr.deckConfigNewGatherPriorityTooltip2() + currentDeck,
},
newCardSortOrder: {
title: tr.deckConfigNewCardSortOrder(),
help: tr.deckConfigNewCardSortOrderTooltip2() + currentDeck,
},
newReviewPriority: {
title: tr.deckConfigNewReviewPriority(),
help: tr.deckConfigNewReviewPriorityTooltip() + currentDeck,
},
interdayStepPriority: {
title: tr.deckConfigInterdayStepPriority(),
help: tr.deckConfigInterdayStepPriorityTooltip() + currentDeck,
},
reviewSortOrder: {
title: tr.deckConfigReviewSortOrder(),
help: tr.deckConfigReviewSortOrderTooltip() + currentDeck,
},
};
const helpSections = Object.values(settings) as HelpItem[];
let modal: Modal;
let carousel: Carousel;
function openHelpModal(index: number): void {
modal.show();
carousel.to(index);
}
</script>
<TitledContainer title={tr.deckConfigOrderingTitle()}>
<HelpModal
title={tr.deckConfigOrderingTitle()}
url={HelpPage.DeckOptions.displayOrder}
slot="tooltip"
{helpSections}
on:mount={(e) => {
modal = e.detail.modal;
carousel = e.detail.carousel;
}}
/>
<DynamicallySlottable slotHost={Item} {api}>
<Item>
<EnumSelectorRow
bind:value={$config.newCardGatherPriority}
defaultValue={defaults.newCardGatherPriority}
choices={newGatherPriorityChoices}
>
<SettingTitle
on:click={() =>
openHelpModal(
Object.keys(settings).indexOf("newGatherPriority"),
)}
>
{settings.newGatherPriority.title}
</SettingTitle>
</EnumSelectorRow>
</Item>
<Item>
<EnumSelectorRow
bind:value={$config.newCardSortOrder}
defaultValue={defaults.newCardSortOrder}
choices={newSortOrderChoices}
disabled={disabledNewSortOrders}
>
<SettingTitle
on:click={() =>
openHelpModal(
Object.keys(settings).indexOf("newCardSortOrder"),
)}
>
{settings.newCardSortOrder.title}
</SettingTitle>
</EnumSelectorRow>
</Item>
<Item>
<EnumSelectorRow
bind:value={$config.newMix}
defaultValue={defaults.newMix}
choices={reviewMixChoices()}
>
<SettingTitle
on:click={() =>
openHelpModal(
Object.keys(settings).indexOf("newReviewPriority"),
)}
>
{settings.newReviewPriority.title}
</SettingTitle>
</EnumSelectorRow>
</Item>
<Item>
<EnumSelectorRow
bind:value={$config.interdayLearningMix}
defaultValue={defaults.interdayLearningMix}
choices={reviewMixChoices()}
>
<SettingTitle
on:click={() =>
openHelpModal(
Object.keys(settings).indexOf("interdayStepPriority"),
)}
>
{settings.interdayStepPriority.title}
</SettingTitle>
</EnumSelectorRow>
</Item>
<Item>
<EnumSelectorRow
bind:value={$config.reviewOrder}
defaultValue={defaults.reviewOrder}
choices={reviewOrderChoices}
>
<SettingTitle
on:click={() =>
openHelpModal(Object.keys(settings).indexOf("reviewSortOrder"))}
>
{settings.reviewSortOrder.title}
</SettingTitle>
</EnumSelectorRow>
</Item>
</DynamicallySlottable>
</TitledContainer>