b2049209ff
There are some style differences compared to prettier, and not all are necessarily an improvement, but it's much faster now.
201 lines
6.6 KiB
TypeScript
201 lines
6.6 KiB
TypeScript
// Copyright: Ankitects Pty Ltd and contributors
|
|
// License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|
|
|
import * as tr from "@tslib/ftl";
|
|
import { Notetypes, notetypes } from "@tslib/proto";
|
|
import { isEqual } from "lodash-es";
|
|
import type { Readable } from "svelte/store";
|
|
import { readable } from "svelte/store";
|
|
|
|
function nullToNegativeOne(list: (number | null)[]): number[] {
|
|
return list.map((val) => val ?? -1);
|
|
}
|
|
|
|
/// Public only for tests.
|
|
export function negativeOneToNull(list: number[]): (number | null)[] {
|
|
return list.map((val) => (val === -1 ? null : val));
|
|
}
|
|
|
|
/// Wrapper for the protobuf message to make it more ergonomic.
|
|
export class ChangeNotetypeInfoWrapper {
|
|
fields: (number | null)[];
|
|
templates?: (number | null)[];
|
|
oldNotetypeName: string;
|
|
readonly info: Notetypes.ChangeNotetypeInfo;
|
|
|
|
constructor(info: Notetypes.ChangeNotetypeInfo) {
|
|
this.info = info;
|
|
const templates = info.input?.newTemplates ?? [];
|
|
if (templates.length > 0) {
|
|
this.templates = negativeOneToNull(templates);
|
|
}
|
|
this.fields = negativeOneToNull(info.input?.newFields ?? []);
|
|
this.oldNotetypeName = info.oldNotetypeName;
|
|
}
|
|
|
|
/// A list with an entry for each field/template in the new notetype, with
|
|
/// the values pointing back to indexes in the original notetype.
|
|
mapForContext(ctx: MapContext): (number | null)[] {
|
|
return ctx == MapContext.Template ? this.templates ?? [] : this.fields;
|
|
}
|
|
|
|
/// Return index of old fields/templates, with null values mapped to "Nothing"
|
|
/// at the end.
|
|
getOldIndex(ctx: MapContext, newIdx: number): number {
|
|
const map = this.mapForContext(ctx);
|
|
const val = map[newIdx];
|
|
return val ?? this.getOldNamesIncludingNothing(ctx).length - 1;
|
|
}
|
|
|
|
/// Return all the old names, with "Nothing" at the end.
|
|
getOldNamesIncludingNothing(ctx: MapContext): string[] {
|
|
return [...this.getOldNames(ctx), tr.changeNotetypeNothing()];
|
|
}
|
|
|
|
/// Old names without "Nothing" at the end.
|
|
getOldNames(ctx: MapContext): string[] {
|
|
return ctx == MapContext.Template
|
|
? this.info.oldTemplateNames
|
|
: this.info.oldFieldNames;
|
|
}
|
|
|
|
getOldNotetypeName(): string {
|
|
return this.info.oldNotetypeName;
|
|
}
|
|
|
|
getNewName(ctx: MapContext, idx: number): string {
|
|
return (
|
|
ctx == MapContext.Template
|
|
? this.info.newTemplateNames
|
|
: this.info.newFieldNames
|
|
)[idx];
|
|
}
|
|
|
|
unusedItems(ctx: MapContext): string[] {
|
|
const usedEntries = new Set(this.mapForContext(ctx).filter((v) => v !== null));
|
|
const oldNames = this.getOldNames(ctx);
|
|
const unusedIdxs = [...Array(oldNames.length).keys()].filter(
|
|
(idx) => !usedEntries.has(idx),
|
|
);
|
|
const unusedNames = unusedIdxs.map((idx) => oldNames[idx]);
|
|
return unusedNames;
|
|
}
|
|
|
|
unchanged(): boolean {
|
|
return (
|
|
this.input().newNotetypeId === this.input().oldNotetypeId
|
|
&& isEqual(this.fields, [...Array(this.fields.length).keys()])
|
|
&& isEqual(this.templates, [...Array(this.templates?.length ?? 0).keys()])
|
|
);
|
|
}
|
|
|
|
input(): Notetypes.ChangeNotetypeRequest {
|
|
return this.info.input as Notetypes.ChangeNotetypeRequest;
|
|
}
|
|
|
|
/// Pack changes back into input message for saving.
|
|
intoInput(): Notetypes.ChangeNotetypeRequest {
|
|
const input = this.info.input as Notetypes.ChangeNotetypeRequest;
|
|
input.newFields = nullToNegativeOne(this.fields);
|
|
if (this.templates) {
|
|
input.newTemplates = nullToNegativeOne(this.templates);
|
|
}
|
|
|
|
return input;
|
|
}
|
|
}
|
|
|
|
export interface NotetypeListEntry {
|
|
idx: number;
|
|
name: string;
|
|
current: boolean;
|
|
}
|
|
|
|
export enum MapContext {
|
|
Field,
|
|
Template,
|
|
}
|
|
|
|
export class ChangeNotetypeState {
|
|
readonly info: Readable<ChangeNotetypeInfoWrapper>;
|
|
readonly notetypes: Readable<NotetypeListEntry[]>;
|
|
|
|
private info_: ChangeNotetypeInfoWrapper;
|
|
private infoSetter!: (val: ChangeNotetypeInfoWrapper) => void;
|
|
private notetypeNames: Notetypes.NotetypeNames;
|
|
private notetypesSetter!: (val: NotetypeListEntry[]) => void;
|
|
|
|
constructor(
|
|
notetypes: Notetypes.NotetypeNames,
|
|
info: Notetypes.ChangeNotetypeInfo,
|
|
) {
|
|
this.info_ = new ChangeNotetypeInfoWrapper(info);
|
|
this.info = readable(this.info_, (set) => {
|
|
this.infoSetter = set;
|
|
});
|
|
this.notetypeNames = notetypes;
|
|
this.notetypes = readable(this.buildNotetypeList(), (set) => {
|
|
this.notetypesSetter = set;
|
|
return;
|
|
});
|
|
}
|
|
|
|
async setTargetNotetypeIndex(idx: number): Promise<void> {
|
|
this.info_.input().newNotetypeId = this.notetypeNames.entries[idx].id!;
|
|
this.notetypesSetter(this.buildNotetypeList());
|
|
const { oldNotetypeId, newNotetypeId } = this.info_.input();
|
|
const newInfo = await notetypes.getChangeNotetypeInfo(
|
|
Notetypes.GetChangeNotetypeInfoRequest.create({
|
|
oldNotetypeId,
|
|
newNotetypeId,
|
|
}),
|
|
);
|
|
|
|
this.info_ = new ChangeNotetypeInfoWrapper(newInfo);
|
|
this.info_.unusedItems(MapContext.Field);
|
|
this.infoSetter(this.info_);
|
|
}
|
|
|
|
setOldIndex(ctx: MapContext, newIdx: number, oldIdx: number): void {
|
|
const list = this.info_.mapForContext(ctx);
|
|
const oldNames = this.info_.getOldNames(ctx);
|
|
const realOldIdx = oldIdx < oldNames.length ? oldIdx : null;
|
|
const allowDupes = ctx == MapContext.Field;
|
|
|
|
// remove any existing references?
|
|
if (!allowDupes && realOldIdx !== null) {
|
|
for (let i = 0; i < list.length; i++) {
|
|
if (list[i] === realOldIdx) {
|
|
list[i] = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
list[newIdx] = realOldIdx;
|
|
this.infoSetter(this.info_);
|
|
}
|
|
|
|
dataForSaving(): Notetypes.ChangeNotetypeRequest {
|
|
return this.info_.intoInput();
|
|
}
|
|
|
|
async save(): Promise<void> {
|
|
if (this.info_.unchanged()) {
|
|
alert("No changes to save");
|
|
return;
|
|
}
|
|
await notetypes.changeNotetype(this.dataForSaving());
|
|
}
|
|
|
|
private buildNotetypeList(): NotetypeListEntry[] {
|
|
const currentId = this.info_.input().newNotetypeId;
|
|
return this.notetypeNames.entries.map(
|
|
(entry, idx) => ({
|
|
idx,
|
|
name: entry.name,
|
|
current: entry.id === currentId,
|
|
} as NotetypeListEntry),
|
|
);
|
|
}
|
|
}
|