mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-14 18:57:50 +01:00
core: migrate settings to its own collection
This commit is contained in:
@@ -78,7 +78,13 @@ module.exports = {
|
||||
semi: ["error", "always"],
|
||||
"@typescript-eslint/no-empty-function": "off",
|
||||
"react/prop-types": "off",
|
||||
"header/header": ["error", "block", LICENSE, 1]
|
||||
"header/header": ["error", "block", LICENSE, 1],
|
||||
"@typescript-eslint/no-empty-interface": [
|
||||
"error",
|
||||
{
|
||||
allowSingleExtends: true
|
||||
}
|
||||
]
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
|
||||
@@ -17,14 +17,14 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { TEST_NOTE, databaseTest, loginFakeUser, notebookTest } from "./utils";
|
||||
import v52Backup from "./__fixtures__/backup.v5.2.json";
|
||||
import v52BackupCopy from "./__fixtures__/backup.v5.2.copy.json";
|
||||
import v56BackupCopy from "./__fixtures__/backup.v5.6.json";
|
||||
import v58BackupCopy from "./__fixtures__/backup.v5.8.json";
|
||||
import qclone from "qclone";
|
||||
import { test, expect, describe } from "vitest";
|
||||
import { getId, makeId } from "../src/utils/id";
|
||||
import { TEST_NOTE, loginFakeUser, notebookTest } from "./utils";
|
||||
// import v52Backup from "./__fixtures__/backup.v5.2.json";
|
||||
// import v52BackupCopy from "./__fixtures__/backup.v5.2.copy.json";
|
||||
// import v56BackupCopy from "./__fixtures__/backup.v5.6.json";
|
||||
// import v58BackupCopy from "./__fixtures__/backup.v5.8.json";
|
||||
// import qclone from "qclone";
|
||||
import { test, expect } from "vitest";
|
||||
// import { getId, makeId } from "../src/utils/id";
|
||||
|
||||
test("export backup", () =>
|
||||
notebookTest().then(async ({ db }) => {
|
||||
@@ -114,103 +114,103 @@ test("import tempered backup", () =>
|
||||
await expect(db.backup.import(backup)).rejects.toThrow(/tempered/);
|
||||
}));
|
||||
|
||||
describe.each([
|
||||
["v5.2", v52Backup],
|
||||
["v5.2 copy", v52BackupCopy],
|
||||
["v5.6", v56BackupCopy],
|
||||
["v5.8", v58BackupCopy]
|
||||
])("testing backup version: %s", (version, data) => {
|
||||
test(`import ${version} backup`, () => {
|
||||
return databaseTest().then(async (db) => {
|
||||
await db.backup.import(qclone(data));
|
||||
// describe.each([
|
||||
// ["v5.2", v52Backup],
|
||||
// ["v5.2 copy", v52BackupCopy],
|
||||
// ["v5.6", v56BackupCopy],
|
||||
// ["v5.8", v58BackupCopy]
|
||||
// ])("testing backup version: %s", (version, data) => {
|
||||
// test(`import ${version} backup`, () => {
|
||||
// return databaseTest().then(async (db) => {
|
||||
// await db.backup.import(qclone(data));
|
||||
|
||||
expect(db.settings.raw.id).toBeDefined();
|
||||
expect(db.settings.raw.dateModified).toBeDefined();
|
||||
expect(db.settings.raw.dateEdited).toBeUndefined();
|
||||
expect(db.settings.raw.pins).toBeUndefined();
|
||||
// expect(db.settings.raw.id).toBeDefined();
|
||||
// expect(db.settings.raw.dateModified).toBeDefined();
|
||||
// expect(db.settings.raw.dateEdited).toBeUndefined();
|
||||
// expect(db.settings.raw.pins).toBeUndefined();
|
||||
|
||||
expect(
|
||||
db.notes.all.every((v) => {
|
||||
const doesNotHaveContent = !v.content;
|
||||
const doesNotHaveColors = !v.colors; // && (!v.color || v.color.length);
|
||||
const hasTopicsInAllNotebooks =
|
||||
!v.notebooks ||
|
||||
v.notebooks.every((nb) => !!nb.id && !!nb.topics && !nb.topic);
|
||||
const hasDateModified = v.dateModified > 0;
|
||||
const doesNotHaveTags = !v.tags;
|
||||
const doesNotHaveColor = !v.color;
|
||||
if (!doesNotHaveTags) console.log(v);
|
||||
return (
|
||||
doesNotHaveTags &&
|
||||
doesNotHaveColor &&
|
||||
doesNotHaveContent &&
|
||||
!v.notebook &&
|
||||
hasTopicsInAllNotebooks &&
|
||||
doesNotHaveColors &&
|
||||
hasDateModified
|
||||
);
|
||||
})
|
||||
).toBeTruthy();
|
||||
// expect(
|
||||
// db.notes.all.every((v) => {
|
||||
// const doesNotHaveContent = !v.content;
|
||||
// const doesNotHaveColors = !v.colors; // && (!v.color || v.color.length);
|
||||
// const hasTopicsInAllNotebooks =
|
||||
// !v.notebooks ||
|
||||
// v.notebooks.every((nb) => !!nb.id && !!nb.topics && !nb.topic);
|
||||
// const hasDateModified = v.dateModified > 0;
|
||||
// const doesNotHaveTags = !v.tags;
|
||||
// const doesNotHaveColor = !v.color;
|
||||
// if (!doesNotHaveTags) console.log(v);
|
||||
// return (
|
||||
// doesNotHaveTags &&
|
||||
// doesNotHaveColor &&
|
||||
// doesNotHaveContent &&
|
||||
// !v.notebook &&
|
||||
// hasTopicsInAllNotebooks &&
|
||||
// doesNotHaveColors &&
|
||||
// hasDateModified
|
||||
// );
|
||||
// })
|
||||
// ).toBeTruthy();
|
||||
|
||||
expect(
|
||||
db.tags.all.every((t) => makeId(t.title) !== t.id && !t.noteIds)
|
||||
).toBeTruthy();
|
||||
// expect(
|
||||
// db.tags.all.every((t) => makeId(t.title) !== t.id && !t.noteIds)
|
||||
// ).toBeTruthy();
|
||||
|
||||
expect(
|
||||
db.colors.all.every(
|
||||
(t) => makeId(t.title) !== t.id && !t.noteIds && !!t.colorCode
|
||||
)
|
||||
).toBeTruthy();
|
||||
// expect(
|
||||
// db.colors.all.every(
|
||||
// (t) => makeId(t.title) !== t.id && !t.noteIds && !!t.colorCode
|
||||
// )
|
||||
// ).toBeTruthy();
|
||||
|
||||
expect(
|
||||
db.notebooks.all.every((v) => v.title != null && v.dateModified > 0)
|
||||
).toBeTruthy();
|
||||
// expect(
|
||||
// db.notebooks.all.every((v) => v.title != null && v.dateModified > 0)
|
||||
// ).toBeTruthy();
|
||||
|
||||
expect(db.notebooks.all.every((v) => !v.topics)).toBeTruthy();
|
||||
// expect(db.notebooks.all.every((v) => !v.topics)).toBeTruthy();
|
||||
|
||||
expect(
|
||||
db.attachments.all.every((v) => v.dateModified > 0 && !v.dateEdited)
|
||||
).toBeTruthy();
|
||||
// expect(
|
||||
// db.attachments.all.every((v) => v.dateModified > 0 && !v.dateEdited)
|
||||
// ).toBeTruthy();
|
||||
|
||||
expect(db.attachments.all.every((a) => !a.noteIds)).toBeTruthy();
|
||||
// expect(db.attachments.all.every((a) => !a.noteIds)).toBeTruthy();
|
||||
|
||||
if (data.data.settings.pins)
|
||||
expect(db.shortcuts.all).toHaveLength(data.data.settings.pins.length);
|
||||
// if (data.data.settings.pins)
|
||||
// expect(db.shortcuts.all).toHaveLength(data.data.settings.pins.length);
|
||||
|
||||
const allContent = await db.content.all();
|
||||
expect(
|
||||
allContent.every((v) => v.type === "tiptap" || v.deleted)
|
||||
).toBeTruthy();
|
||||
expect(allContent.every((v) => !v.persistDateEdited)).toBeTruthy();
|
||||
expect(allContent.every((v) => v.dateModified > 0)).toBeTruthy();
|
||||
// const allContent = await db.content.all();
|
||||
// expect(
|
||||
// allContent.every((v) => v.type === "tiptap" || v.deleted)
|
||||
// ).toBeTruthy();
|
||||
// expect(allContent.every((v) => !v.persistDateEdited)).toBeTruthy();
|
||||
// expect(allContent.every((v) => v.dateModified > 0)).toBeTruthy();
|
||||
|
||||
expect(
|
||||
allContent.every(
|
||||
(v) =>
|
||||
!v.data.includes("tox-checklist") &&
|
||||
!v.data.includes("tox-checklist--checked")
|
||||
)
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
// expect(
|
||||
// allContent.every(
|
||||
// (v) =>
|
||||
// !v.data.includes("tox-checklist") &&
|
||||
// !v.data.includes("tox-checklist--checked")
|
||||
// )
|
||||
// ).toBeTruthy();
|
||||
// });
|
||||
// });
|
||||
|
||||
test(`verify indices of ${version} backup`, () => {
|
||||
return databaseTest().then(async (db) => {
|
||||
await db.backup.import(qclone(data));
|
||||
// test(`verify indices of ${version} backup`, () => {
|
||||
// return databaseTest().then(async (db) => {
|
||||
// await db.backup.import(qclone(data));
|
||||
|
||||
const keys = await db.storage().getAllKeys();
|
||||
for (let key in data.data) {
|
||||
const item = data.data[key];
|
||||
if (item && !item.type && item.deleted) continue;
|
||||
if (
|
||||
key.startsWith("_uk_") ||
|
||||
key === "hasConflicts" ||
|
||||
key === "monographs" ||
|
||||
key === "token"
|
||||
)
|
||||
continue;
|
||||
expect(keys.some((k) => k.startsWith(key))).toBeTruthy();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
// const keys = await db.storage().getAllKeys();
|
||||
// for (let key in data.data) {
|
||||
// const item = data.data[key];
|
||||
// if (item && !item.type && item.deleted) continue;
|
||||
// if (
|
||||
// key.startsWith("_uk_") ||
|
||||
// key === "hasConflicts" ||
|
||||
// key === "monographs" ||
|
||||
// key === "token"
|
||||
// )
|
||||
// continue;
|
||||
// expect(keys.some((k) => k.startsWith(key))).toBeTruthy();
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
// });
|
||||
|
||||
@@ -20,23 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
import { databaseTest } from "./utils";
|
||||
import { test, expect } from "vitest";
|
||||
|
||||
test("settings' dateModified should not update on init", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
const beforeDateModified = db.settings.raw.dateModified;
|
||||
await db.settings.init();
|
||||
const afterDateModified = db.settings.raw.dateModified;
|
||||
expect(beforeDateModified).toBe(afterDateModified);
|
||||
}));
|
||||
|
||||
test("settings' dateModified should update after merge conflict resolve", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
// await db.storage.write("lastSynced", 0);
|
||||
const beforeDateModified = (db.settings.raw.dateModified = 1);
|
||||
await db.settings.merge({ groupOptions: {}, aliases: {} }, 0);
|
||||
const afterDateModified = db.settings.raw.dateModified;
|
||||
expect(afterDateModified).toBeGreaterThan(beforeDateModified);
|
||||
}));
|
||||
|
||||
test("save group options", () =>
|
||||
databaseTest().then(async (db) => {
|
||||
const groupOptions = {
|
||||
|
||||
@@ -32,7 +32,7 @@ import Backup from "../database/backup";
|
||||
import Session from "./session";
|
||||
import Hosts from "../utils/constants";
|
||||
import { EV, EVENTS } from "../common";
|
||||
import Settings from "../collections/settings";
|
||||
import { LegacySettings } from "../collections/legacy-settings";
|
||||
import Migrations from "./migrations";
|
||||
import UserManager from "./user-manager";
|
||||
import http from "../utils/http";
|
||||
@@ -59,6 +59,7 @@ import {
|
||||
} from "../interfaces";
|
||||
import TokenManager from "./token-manager";
|
||||
import { Attachment } from "../types";
|
||||
import { Settings } from "../collections/settings";
|
||||
|
||||
type EventSourceConstructor = new (
|
||||
uri: string,
|
||||
@@ -127,6 +128,7 @@ class Database {
|
||||
vault = new Vault(this);
|
||||
lookup = new Lookup(this);
|
||||
backup = new Backup(this);
|
||||
legacySettings = new LegacySettings(this);
|
||||
settings = new Settings(this);
|
||||
migrations = new Migrations(this);
|
||||
monographs = new Monographs(this);
|
||||
@@ -189,9 +191,10 @@ class Database {
|
||||
}
|
||||
|
||||
async initCollections() {
|
||||
await this.settings.init();
|
||||
await this.legacySettings.init();
|
||||
// collections
|
||||
|
||||
await this.settings.init();
|
||||
await this.notebooks.init();
|
||||
await this.tags.init();
|
||||
await this.colors.init();
|
||||
|
||||
@@ -42,14 +42,6 @@ class Collector {
|
||||
throw new Error("User encryption key not generated. Please relogin.");
|
||||
}
|
||||
|
||||
const settings = await this.prepareChunk(
|
||||
[this.db.settings.raw],
|
||||
lastSyncedTimestamp,
|
||||
isForceSync,
|
||||
key
|
||||
);
|
||||
if (settings) yield { items: settings, type: "settings" };
|
||||
|
||||
const attachments = await this.prepareChunk(
|
||||
this.db.attachments.syncable,
|
||||
lastSyncedTimestamp,
|
||||
|
||||
@@ -29,7 +29,6 @@ import {
|
||||
MaybeDeletedItem,
|
||||
Note,
|
||||
Notebook,
|
||||
SettingsItem,
|
||||
TrashOrItem,
|
||||
isDeleted
|
||||
} from "../../types";
|
||||
@@ -94,7 +93,8 @@ class Merger {
|
||||
case "color":
|
||||
case "note":
|
||||
case "relation":
|
||||
case "notebook": {
|
||||
case "notebook":
|
||||
case "settingitem": {
|
||||
const localItem = this.db[SYNC_COLLECTIONS_MAP[type]].collection.getRaw(
|
||||
remoteItem.id
|
||||
);
|
||||
@@ -158,23 +158,11 @@ class Merger {
|
||||
}
|
||||
|
||||
async mergeItem(
|
||||
remoteItem: SettingsItem | MaybeDeletedItem<Attachment>,
|
||||
remoteItem: MaybeDeletedItem<Attachment>,
|
||||
type: "settings" | "attachment",
|
||||
lastSynced: number
|
||||
_lastSynced: number
|
||||
) {
|
||||
switch (type) {
|
||||
case "settings": {
|
||||
if (isDeleted(remoteItem) || remoteItem.type !== "settings") return;
|
||||
|
||||
const localItem = this.db.settings.raw;
|
||||
if (
|
||||
!localItem ||
|
||||
this.isConflicted(localItem, remoteItem, lastSynced, 1000)
|
||||
) {
|
||||
await this.db.settings.merge(remoteItem, lastSynced);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "attachment": {
|
||||
if (isDeleted(remoteItem)) return remoteItem;
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@ export type SyncableItemType =
|
||||
| "relation"
|
||||
| "color"
|
||||
| "tag"
|
||||
| "settings";
|
||||
| "settings"
|
||||
| "settingitem";
|
||||
|
||||
export type SyncItem = {
|
||||
id: string;
|
||||
@@ -43,7 +44,8 @@ export const SYNC_COLLECTIONS_MAP = {
|
||||
reminder: "reminders",
|
||||
relation: "relations",
|
||||
tag: "tags",
|
||||
color: "colors"
|
||||
color: "colors",
|
||||
settingitem: "settings"
|
||||
} as const;
|
||||
|
||||
export type SyncTransferItem = {
|
||||
|
||||
55
packages/core/src/collections/legacy-settings.ts
Normal file
55
packages/core/src/collections/legacy-settings.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
This file is part of the Notesnook project (https://notesnook.com/)
|
||||
|
||||
Copyright (C) 2023 Streetwriters (Private) Limited
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { getId } from "../utils/id";
|
||||
import Database from "../api";
|
||||
import { LegacySettingsItem } from "../types";
|
||||
import { ICollection } from "./collection";
|
||||
|
||||
/**
|
||||
* @deprecated only kept here for migration purposes
|
||||
*/
|
||||
export class LegacySettings implements ICollection {
|
||||
name = "legacy-settings";
|
||||
private settings: LegacySettingsItem = {
|
||||
type: "settings",
|
||||
dateModified: 0,
|
||||
dateCreated: 0,
|
||||
id: getId()
|
||||
};
|
||||
constructor(private readonly db: Database) {}
|
||||
|
||||
async init() {
|
||||
const settings = await this.db
|
||||
.storage()
|
||||
.read<LegacySettingsItem>("settings");
|
||||
if (settings) this.settings = settings;
|
||||
}
|
||||
|
||||
get raw() {
|
||||
return this.settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated only kept here for migration purposes
|
||||
*/
|
||||
getAlias(id: string) {
|
||||
return this.settings.aliases && this.settings.aliases[id];
|
||||
}
|
||||
}
|
||||
@@ -17,192 +17,156 @@ You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import { EV, EVENTS } from "../common";
|
||||
import { getId } from "../utils/id";
|
||||
import { makeId } from "../utils/id";
|
||||
import Database from "../api";
|
||||
import {
|
||||
DefaultNotebook,
|
||||
GroupOptions,
|
||||
GroupingKey,
|
||||
SettingsItem,
|
||||
SettingItem,
|
||||
SettingItemMap,
|
||||
ToolbarConfig,
|
||||
TrashCleanupInterval,
|
||||
isDeleted
|
||||
ToolbarConfigPlatforms,
|
||||
TrashCleanupInterval
|
||||
} from "../types";
|
||||
import { ICollection } from "./collection";
|
||||
import { CachedCollection } from "../database/cached-collection";
|
||||
import { TimeFormat } from "../utils/date";
|
||||
|
||||
class Settings implements ICollection {
|
||||
name = "settings";
|
||||
private settings: SettingsItem = {
|
||||
type: "settings",
|
||||
dateModified: 0,
|
||||
dateCreated: 0,
|
||||
id: getId()
|
||||
};
|
||||
constructor(private readonly db: Database) {}
|
||||
const DEFAULT_GROUP_OPTIONS = (key: GroupingKey) =>
|
||||
({
|
||||
groupBy: "default",
|
||||
sortBy:
|
||||
key === "trash"
|
||||
? "dateDeleted"
|
||||
: key === "tags"
|
||||
? "dateCreated"
|
||||
: key === "reminders"
|
||||
? "dueDate"
|
||||
: "dateEdited",
|
||||
sortDirection: key === "reminders" ? "asc" : "desc"
|
||||
} satisfies GroupOptions);
|
||||
|
||||
async init() {
|
||||
const settings = await this.db.storage().read<SettingsItem>("settings");
|
||||
this.reset(settings);
|
||||
await this.save(false);
|
||||
const defaultSettings: SettingItemMap = {
|
||||
timeFormat: "12-hour",
|
||||
dateFormat: "DD-MM-YYYY",
|
||||
titleFormat: "Note $date$ $time$",
|
||||
defaultNotebook: undefined,
|
||||
trashCleanupInterval: 7,
|
||||
|
||||
EV.subscribe(EVENTS.userLoggedOut, async () => {
|
||||
this.reset();
|
||||
await this.save(false);
|
||||
});
|
||||
"groupOptions:trash": DEFAULT_GROUP_OPTIONS("trash"),
|
||||
"groupOptions:tags": DEFAULT_GROUP_OPTIONS("tags"),
|
||||
"groupOptions:notes": DEFAULT_GROUP_OPTIONS("notes"),
|
||||
"groupOptions:notebooks": DEFAULT_GROUP_OPTIONS("notebooks"),
|
||||
"groupOptions:favorites": DEFAULT_GROUP_OPTIONS("favorites"),
|
||||
"groupOptions:home": DEFAULT_GROUP_OPTIONS("home"),
|
||||
"groupOptions:reminders": DEFAULT_GROUP_OPTIONS("reminders"),
|
||||
|
||||
"toolbarConfig:desktop": undefined,
|
||||
"toolbarConfig:mobile": undefined
|
||||
};
|
||||
|
||||
export class Settings implements ICollection {
|
||||
name = "settingsv2";
|
||||
readonly collection: CachedCollection<"settingsv2", SettingItem>;
|
||||
constructor(db: Database) {
|
||||
this.collection = new CachedCollection(
|
||||
db.storage,
|
||||
"settingsv2",
|
||||
db.eventManager
|
||||
);
|
||||
}
|
||||
|
||||
init() {
|
||||
return this.collection.init();
|
||||
}
|
||||
|
||||
get raw() {
|
||||
return this.settings;
|
||||
return this.collection.raw();
|
||||
}
|
||||
|
||||
async merge(remoteItem: SettingsItem, lastSynced: number) {
|
||||
if (this.settings.dateModified > lastSynced) {
|
||||
this.settings = {
|
||||
...this.settings,
|
||||
...(isDeleted(remoteItem)
|
||||
? {}
|
||||
: {
|
||||
...remoteItem,
|
||||
groupOptions: {
|
||||
...this.settings.groupOptions,
|
||||
...remoteItem.groupOptions
|
||||
},
|
||||
toolbarConfig: {
|
||||
...this.settings.toolbarConfig,
|
||||
...remoteItem.toolbarConfig
|
||||
},
|
||||
aliases: {
|
||||
...this.settings.aliases,
|
||||
...remoteItem.aliases
|
||||
}
|
||||
})
|
||||
};
|
||||
this.settings.dateModified = Date.now();
|
||||
} else {
|
||||
this.reset(remoteItem);
|
||||
}
|
||||
await this.save(false);
|
||||
private async set<TKey extends keyof SettingItemMap>(
|
||||
key: TKey,
|
||||
value: SettingItemMap[TKey]
|
||||
) {
|
||||
const id = makeId(key);
|
||||
const oldItem = this.collection.get(id);
|
||||
if (oldItem && oldItem.key !== key) throw new Error("Key conflict.");
|
||||
|
||||
await this.collection.add({
|
||||
id,
|
||||
key,
|
||||
value,
|
||||
type: "settingitem",
|
||||
dateCreated: oldItem?.dateCreated || Date.now(),
|
||||
dateModified: oldItem?.dateCreated || Date.now()
|
||||
});
|
||||
return id;
|
||||
}
|
||||
|
||||
async setGroupOptions(key: GroupingKey, groupOptions: GroupOptions) {
|
||||
if (!this.settings.groupOptions) this.settings.groupOptions = {};
|
||||
this.settings.groupOptions[key] = groupOptions;
|
||||
await this.save();
|
||||
private get<TKey extends keyof SettingItemMap>(
|
||||
key: TKey
|
||||
): SettingItemMap[TKey] {
|
||||
const item = this.collection.get(makeId(key)) as
|
||||
| SettingItem<TKey>
|
||||
| undefined;
|
||||
if (!item || item.key !== key) return defaultSettings[key];
|
||||
return item.value;
|
||||
}
|
||||
|
||||
getGroupOptions(key: GroupingKey) {
|
||||
return (
|
||||
(this.settings.groupOptions && this.settings.groupOptions[key]) || {
|
||||
groupBy: "default",
|
||||
sortBy:
|
||||
key === "trash"
|
||||
? "dateDeleted"
|
||||
: key === "tags"
|
||||
? "dateCreated"
|
||||
: key === "reminders"
|
||||
? "dueDate"
|
||||
: "dateEdited",
|
||||
sortDirection: key === "reminders" ? "asc" : "desc"
|
||||
}
|
||||
);
|
||||
return this.get(`groupOptions:${key}`);
|
||||
}
|
||||
|
||||
async setToolbarConfig(key: string, config: ToolbarConfig) {
|
||||
if (!this.settings.toolbarConfig) this.settings.toolbarConfig = {};
|
||||
this.settings.toolbarConfig[key] = config;
|
||||
await this.save();
|
||||
setGroupOptions(key: GroupingKey, groupOptions: GroupOptions) {
|
||||
return this.set(`groupOptions:${key}`, groupOptions);
|
||||
}
|
||||
|
||||
getToolbarConfig(key: string) {
|
||||
return this.settings.toolbarConfig && this.settings.toolbarConfig[key];
|
||||
setToolbarConfig(platform: ToolbarConfigPlatforms, config: ToolbarConfig) {
|
||||
return this.set(`toolbarConfig:${platform}`, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Setting to -1 means never clear trash.
|
||||
*/
|
||||
async setTrashCleanupInterval(interval: TrashCleanupInterval) {
|
||||
this.settings.trashCleanupInterval = interval;
|
||||
await this.save();
|
||||
getToolbarConfig(platform: ToolbarConfigPlatforms) {
|
||||
return this.get(`toolbarConfig:${platform}`);
|
||||
}
|
||||
|
||||
setTrashCleanupInterval(interval: TrashCleanupInterval) {
|
||||
return this.set("trashCleanupInterval", interval);
|
||||
}
|
||||
|
||||
getTrashCleanupInterval() {
|
||||
return this.settings.trashCleanupInterval || 7;
|
||||
return this.get("trashCleanupInterval");
|
||||
}
|
||||
|
||||
async setDefaultNotebook(item: DefaultNotebook | undefined) {
|
||||
this.settings.defaultNotebook = !item
|
||||
? undefined
|
||||
: {
|
||||
id: item.id,
|
||||
topic: item.topic
|
||||
};
|
||||
await this.save();
|
||||
setDefaultNotebook(item: DefaultNotebook | undefined) {
|
||||
return this.set("defaultNotebook", item);
|
||||
}
|
||||
|
||||
getDefaultNotebook() {
|
||||
return this.settings.defaultNotebook;
|
||||
return this.get("defaultNotebook");
|
||||
}
|
||||
|
||||
async setTitleFormat(format: string) {
|
||||
this.settings.titleFormat = format;
|
||||
await this.save();
|
||||
setTitleFormat(format: string) {
|
||||
return this.set("titleFormat", format);
|
||||
}
|
||||
|
||||
getTitleFormat() {
|
||||
return this.settings.titleFormat || "Note $date$ $time$";
|
||||
return this.get("titleFormat");
|
||||
}
|
||||
|
||||
getDateFormat() {
|
||||
return this.settings.dateFormat || "DD-MM-YYYY";
|
||||
return this.get("dateFormat");
|
||||
}
|
||||
|
||||
async setDateFormat(format: string) {
|
||||
this.settings.dateFormat = format;
|
||||
await this.save();
|
||||
setDateFormat(format: string) {
|
||||
return this.set("dateFormat", format);
|
||||
}
|
||||
|
||||
getTimeFormat() {
|
||||
return this.settings.timeFormat || "12-hour";
|
||||
return this.get("timeFormat");
|
||||
}
|
||||
|
||||
async setTimeFormat(format: TimeFormat) {
|
||||
this.settings.timeFormat = format || "12-hour";
|
||||
await this.save();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated only kept here for migration purposes.
|
||||
*/
|
||||
getAlias(id: string) {
|
||||
return this.settings.aliases && this.settings.aliases[id];
|
||||
}
|
||||
|
||||
private reset(settings?: Partial<SettingsItem>) {
|
||||
this.settings = {
|
||||
type: "settings",
|
||||
id: getId(),
|
||||
dateModified: 0,
|
||||
dateCreated: 0,
|
||||
...(settings || {})
|
||||
};
|
||||
}
|
||||
|
||||
private async save(updateDateModified = true) {
|
||||
this.db.eventManager.publish(
|
||||
EVENTS.databaseUpdated,
|
||||
"settings",
|
||||
this.settings
|
||||
);
|
||||
|
||||
if (updateDateModified) {
|
||||
this.settings.dateModified = Date.now();
|
||||
this.settings.synced = false;
|
||||
}
|
||||
delete this.settings.remote;
|
||||
|
||||
await this.db.storage().write("settings", this.settings);
|
||||
setTimeFormat(format: TimeFormat) {
|
||||
return this.set("timeFormat", format);
|
||||
}
|
||||
}
|
||||
export default Settings;
|
||||
|
||||
@@ -25,10 +25,12 @@ import { getId, makeId } from "./utils/id";
|
||||
import {
|
||||
Color,
|
||||
ContentItem,
|
||||
GroupingKey,
|
||||
HistorySession,
|
||||
Item,
|
||||
ItemMap,
|
||||
ItemType
|
||||
ItemType,
|
||||
ToolbarConfigPlatforms
|
||||
} from "./types";
|
||||
import { isCipher } from "./database/crypto";
|
||||
import { IndexedCollection } from "./database/indexed-collection";
|
||||
@@ -188,7 +190,7 @@ const migrations: Migration[] = [
|
||||
version: 5.9,
|
||||
items: {
|
||||
tag: async (item, db) => {
|
||||
const alias = db.settings.getAlias(item.id);
|
||||
const alias = db.legacySettings.getAlias(item.id);
|
||||
item.title = alias || item.title;
|
||||
item.id = getId(item.dateCreated);
|
||||
|
||||
@@ -207,7 +209,7 @@ const migrations: Migration[] = [
|
||||
for (const tag of item.tags || []) {
|
||||
const oldTagId = makeId(tag);
|
||||
const oldTag = db.tags.tag(oldTagId);
|
||||
const alias = db.settings.getAlias(oldTagId);
|
||||
const alias = db.legacySettings.getAlias(oldTagId);
|
||||
const newTag = db.tags.all.find(
|
||||
(t) => [alias, tag].includes(t.title) && t.id !== oldTagId
|
||||
);
|
||||
@@ -228,7 +230,7 @@ const migrations: Migration[] = [
|
||||
if (item.color) {
|
||||
const oldColorId = makeId(item.color);
|
||||
const oldColor = db.tags.tag(oldColorId);
|
||||
const alias = db.settings.getAlias(oldColorId);
|
||||
const alias = db.legacySettings.getAlias(oldColorId);
|
||||
const newColor = db.tags.all.find(
|
||||
(t) => [alias, item.color].includes(t.title) && t.id !== oldColorId
|
||||
);
|
||||
@@ -289,6 +291,36 @@ const migrations: Migration[] = [
|
||||
item.item = { type: "notebook", id: item.item.id };
|
||||
return true;
|
||||
}
|
||||
},
|
||||
settings: async (item, db) => {
|
||||
if (item.trashCleanupInterval)
|
||||
await db.settings.setTrashCleanupInterval(item.trashCleanupInterval);
|
||||
if (item.defaultNotebook)
|
||||
await db.settings.setDefaultNotebook(item.defaultNotebook);
|
||||
|
||||
if (item.titleFormat)
|
||||
await db.settings.setTitleFormat(item.titleFormat);
|
||||
if (item.dateFormat) await db.settings.setDateFormat(item.dateFormat);
|
||||
if (item.timeFormat) await db.settings.setTimeFormat(item.timeFormat);
|
||||
|
||||
if (item.groupOptions) {
|
||||
for (const key in item.groupOptions) {
|
||||
const value = item.groupOptions[key as GroupingKey];
|
||||
if (!value) continue;
|
||||
await db.settings.setGroupOptions(key as GroupingKey, value);
|
||||
}
|
||||
}
|
||||
if (item.toolbarConfig) {
|
||||
for (const key in item.toolbarConfig) {
|
||||
const value = item.toolbarConfig[key as ToolbarConfigPlatforms];
|
||||
if (!value) continue;
|
||||
await db.settings.setToolbarConfig(
|
||||
key as ToolbarConfigPlatforms,
|
||||
value
|
||||
);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -33,7 +33,7 @@ export type GroupingKey =
|
||||
| "notes"
|
||||
| "notebooks"
|
||||
| "tags"
|
||||
| "topics"
|
||||
//| "topics"
|
||||
| "trash"
|
||||
| "favorites"
|
||||
| "reminders";
|
||||
@@ -49,7 +49,6 @@ export type GroupHeader = {
|
||||
export type Collections = {
|
||||
notes: "note" | "trash";
|
||||
notebooks: "notebook" | "trash";
|
||||
topics: "topic";
|
||||
attachments: "attachment";
|
||||
reminders: "reminder";
|
||||
relations: "relation";
|
||||
@@ -59,17 +58,21 @@ export type Collections = {
|
||||
colors: "color";
|
||||
notehistory: "session";
|
||||
sessioncontent: "sessioncontent";
|
||||
settingsv2: "settingitem";
|
||||
|
||||
/**
|
||||
* @deprecated only kept here for migration purposes
|
||||
*/
|
||||
settings: "settings";
|
||||
/**
|
||||
* @deprecated only kept here for migration purposes
|
||||
*/
|
||||
topics: "topic";
|
||||
};
|
||||
|
||||
export type CollectionType = keyof Collections;
|
||||
|
||||
export type ItemType =
|
||||
| ValueOf<Collections>
|
||||
// TODO: ideally there should be no extra types here.
|
||||
// everything should have its own collection
|
||||
| "topic"
|
||||
| "settings";
|
||||
export type ItemType = ValueOf<Collections>;
|
||||
|
||||
export type Item = ValueOf<ItemMap>;
|
||||
export type GroupableItem = ValueOf<
|
||||
@@ -83,13 +86,13 @@ export type GroupableItem = ValueOf<
|
||||
| "session"
|
||||
| "sessioncontent"
|
||||
| "settings"
|
||||
| "settingsv2"
|
||||
>
|
||||
>;
|
||||
|
||||
export type ItemMap = {
|
||||
note: Note;
|
||||
notebook: Notebook;
|
||||
topic: Topic;
|
||||
attachment: Attachment;
|
||||
tag: Tag;
|
||||
color: Color;
|
||||
@@ -102,7 +105,16 @@ export type ItemMap = {
|
||||
content: ContentItem;
|
||||
session: HistorySession;
|
||||
sessioncontent: SessionContentItem;
|
||||
settings: SettingsItem;
|
||||
settingitem: SettingItem;
|
||||
|
||||
/**
|
||||
* @deprecated only kept here for migration purposes
|
||||
*/
|
||||
topic: Topic;
|
||||
/**
|
||||
* @deprecated only kept here for migration purposes
|
||||
*/
|
||||
settings: LegacySettingsItem;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -307,9 +319,12 @@ export interface SessionContentItem extends BaseItem<"sessioncontent"> {
|
||||
export type TrashCleanupInterval = 1 | 7 | 30 | 365 | -1;
|
||||
export type ToolbarConfig = { preset: string; config?: any[] };
|
||||
export type DefaultNotebook = { id: string; topic?: string };
|
||||
export interface SettingsItem extends BaseItem<"settings"> {
|
||||
/**
|
||||
* @deprecated only kept here for migration purposes
|
||||
*/
|
||||
export interface LegacySettingsItem extends BaseItem<"settings"> {
|
||||
groupOptions?: Partial<Record<GroupingKey, GroupOptions>>;
|
||||
toolbarConfig?: Record<string, ToolbarConfig>;
|
||||
toolbarConfig?: Record<ToolbarConfigPlatforms, ToolbarConfig>;
|
||||
trashCleanupInterval?: TrashCleanupInterval;
|
||||
titleFormat?: string;
|
||||
timeFormat?: TimeFormat;
|
||||
@@ -329,6 +344,23 @@ export interface SettingsItem extends BaseItem<"settings"> {
|
||||
}[];
|
||||
}
|
||||
|
||||
export type ToolbarConfigPlatforms = "desktop" | "mobile";
|
||||
export type SettingItemMap = {
|
||||
trashCleanupInterval: TrashCleanupInterval;
|
||||
titleFormat: string;
|
||||
timeFormat: TimeFormat;
|
||||
dateFormat: string;
|
||||
defaultNotebook: DefaultNotebook | undefined;
|
||||
} & Record<`groupOptions:${GroupingKey}`, GroupOptions> &
|
||||
Record<`toolbarConfig:${ToolbarConfigPlatforms}`, ToolbarConfig | undefined>;
|
||||
|
||||
export interface SettingItem<
|
||||
TKey extends keyof SettingItemMap = keyof SettingItemMap
|
||||
> extends BaseItem<"settingitem"> {
|
||||
key: TKey;
|
||||
value: SettingItemMap[TKey];
|
||||
}
|
||||
|
||||
export interface DeletedItem {
|
||||
id: string;
|
||||
deleted: true;
|
||||
|
||||
Reference in New Issue
Block a user