From 05529b84ab2ee843c193609fa50a4dab3666b581 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sat, 11 Jan 2025 11:40:22 +0200 Subject: [PATCH] chore(client/ts): port options --- src/public/app/services/dialog.ts | 2 +- src/public/app/widgets/dialogs/prompt.ts | 8 +-- .../options/{backup.js => backup.ts} | 37 +++++++++--- .../options/{etapi.js => etapi.ts} | 33 +++++++---- .../options/{password.js => password.ts} | 35 ++++++++--- .../options/{shortcuts.js => shortcuts.ts} | 59 ++++++++++--------- .../options/{spellcheck.js => spellcheck.ts} | 10 +++- .../type_widgets/options/{sync.js => sync.ts} | 40 +++++++++---- src/services/options_interface.ts | 1 + 9 files changed, 150 insertions(+), 75 deletions(-) rename src/public/app/widgets/type_widgets/options/{backup.js => backup.ts} (84%) rename src/public/app/widgets/type_widgets/options/{etapi.js => etapi.ts} (88%) rename src/public/app/widgets/type_widgets/options/{password.js => password.ts} (85%) rename src/public/app/widgets/type_widgets/options/{shortcuts.js => shortcuts.ts} (79%) rename src/public/app/widgets/type_widgets/options/{spellcheck.js => spellcheck.ts} (88%) rename src/public/app/widgets/type_widgets/options/{sync.js => sync.ts} (79%) diff --git a/src/public/app/services/dialog.ts b/src/public/app/services/dialog.ts index f419232cc..99da784e1 100644 --- a/src/public/app/services/dialog.ts +++ b/src/public/app/services/dialog.ts @@ -20,7 +20,7 @@ async function confirmDeleteNoteBoxWithNote(title: string) { } async function prompt(props: PromptDialogOptions) { - return new Promise((res) => appContext.triggerCommand("showPromptDialog", { ...props, callback: res })); + return new Promise((res) => appContext.triggerCommand("showPromptDialog", { ...props, callback: res })); } export default { diff --git a/src/public/app/widgets/dialogs/prompt.ts b/src/public/app/widgets/dialogs/prompt.ts index 67ea50953..ea7c39a9d 100644 --- a/src/public/app/widgets/dialogs/prompt.ts +++ b/src/public/app/widgets/dialogs/prompt.ts @@ -31,15 +31,15 @@ export interface PromptDialogOptions { title?: string; message?: string; defaultValue?: string; - shown: PromptShownDialogCallback; - callback: (value: unknown) => void; + shown?: PromptShownDialogCallback; + callback?: (value: string | null) => void; } export type PromptShownDialogCallback = ((callback: ShownCallbackData) => void) | null; export default class PromptDialog extends BasicWidget { - private resolve: ((val: string | null) => void) | null; - private shownCb: PromptShownDialogCallback; + private resolve?: ((value: string | null) => void) | undefined | null; + private shownCb?: PromptShownDialogCallback | null; private modal!: bootstrap.Modal; private $dialogBody!: JQuery; diff --git a/src/public/app/widgets/type_widgets/options/backup.js b/src/public/app/widgets/type_widgets/options/backup.ts similarity index 84% rename from src/public/app/widgets/type_widgets/options/backup.js rename to src/public/app/widgets/type_widgets/options/backup.ts index 9ced52b3a..3d2e77f37 100644 --- a/src/public/app/widgets/type_widgets/options/backup.js +++ b/src/public/app/widgets/type_widgets/options/backup.ts @@ -3,13 +3,14 @@ import { t } from "../../../services/i18n.js"; import OptionsWidget from "./options_widget.js"; import server from "../../../services/server.js"; import toastService from "../../../services/toast.js"; +import type { OptionMap } from "../../../../../services/options_interface.js"; const TPL = `

${t("backup.automatic_backup")}

- +

${t("backup.automatic_backup_description")}

- +
  • -
  • +
- +

${t("backup.backup_recommendation")}

${t("backup.backup_now")}

- +

${t("backup.existing_backups")}

- + @@ -61,14 +62,32 @@ const TPL = ` `; +// TODO: Deduplicate. +interface PostDatabaseResponse { + backupFile: string; +} + +// TODO: Deduplicate +interface Backup { + filePath: string; + mtime: number; +} + export default class BackupOptions extends OptionsWidget { + + private $backupDatabaseButton!: JQuery; + private $dailyBackupEnabled!: JQuery; + private $weeklyBackupEnabled!: JQuery; + private $monthlyBackupEnabled!: JQuery; + private $existingBackupList!: JQuery; + doRender() { this.$widget = $(TPL); this.$backupDatabaseButton = this.$widget.find(".backup-database-button"); this.$backupDatabaseButton.on("click", async () => { - const { backupFile } = await server.post("database/backup-database"); + const { backupFile } = await server.post("database/backup-database"); toastService.showMessage(t("backup.database_backed_up_to", { backupFilePath: backupFile }), 10000); @@ -88,12 +107,12 @@ export default class BackupOptions extends OptionsWidget { this.$existingBackupList = this.$widget.find(".existing-backup-list-items"); } - optionsLoaded(options) { + optionsLoaded(options: OptionMap) { this.setCheckboxState(this.$dailyBackupEnabled, options.dailyBackupEnabled); this.setCheckboxState(this.$weeklyBackupEnabled, options.weeklyBackupEnabled); this.setCheckboxState(this.$monthlyBackupEnabled, options.monthlyBackupEnabled); - server.get("database/backups").then((backupFiles) => { + server.get("database/backups").then((backupFiles) => { this.$existingBackupList.empty(); if (!backupFiles.length) { diff --git a/src/public/app/widgets/type_widgets/options/etapi.js b/src/public/app/widgets/type_widgets/options/etapi.ts similarity index 88% rename from src/public/app/widgets/type_widgets/options/etapi.js rename to src/public/app/widgets/type_widgets/options/etapi.ts index 356d84230..6b3cf28ee 100644 --- a/src/public/app/widgets/type_widgets/options/etapi.js +++ b/src/public/app/widgets/type_widgets/options/etapi.ts @@ -8,18 +8,18 @@ import toastService from "../../../services/toast.js"; const TPL = `

${t("etapi.title")}

- +

${t("etapi.description")}
${t("etapi.see_more")} ${t("etapi.wiki")} ${t("etapi.and")} ${t("etapi.openapi_spec")}.

- +
${t("etapi.existing_tokens")}
- +
${t("etapi.no_tokens_yet")}
- +
@@ -44,13 +44,26 @@ const TPL = ` border: 1px solid transparent; border-radius: var(--button-border-radius); } - + .token-table-button:hover { border: 1px solid var(--button-border-color); } `; +// TODO: Deduplicate +interface PostTokensResponse { + authToken: string; +} + +// TODO: Deduplicate +interface Token { + name: string; + utcDateCreated: number; + etapiTokenId: string; +} + export default class EtapiOptions extends OptionsWidget { + doRender() { this.$widget = $(TPL); @@ -61,12 +74,12 @@ export default class EtapiOptions extends OptionsWidget { defaultValue: t("etapi.default_token_name") }); - if (!tokenName.trim()) { + if (!tokenName?.trim()) { toastService.showError(t("etapi.error_empty_name")); return; } - const { authToken } = await server.post("etapi-tokens", { tokenName }); + const { authToken } = await server.post("etapi-tokens", { tokenName }); await dialogService.prompt({ title: t("etapi.token_created_title"), @@ -84,7 +97,7 @@ export default class EtapiOptions extends OptionsWidget { const $noTokensYet = this.$widget.find(".no-tokens-yet"); const $tokensTable = this.$widget.find(".tokens-table"); - const tokens = await server.get("etapi-tokens"); + const tokens = await server.get("etapi-tokens"); $noTokensYet.toggle(tokens.length === 0); $tokensTable.toggle(tokens.length > 0); @@ -107,7 +120,7 @@ export default class EtapiOptions extends OptionsWidget { } } - async renameToken(etapiTokenId, oldName) { + async renameToken(etapiTokenId: string, oldName: string) { const tokenName = await dialogService.prompt({ title: t("etapi.rename_token_title"), message: t("etapi.rename_token_message"), @@ -123,7 +136,7 @@ export default class EtapiOptions extends OptionsWidget { this.refreshTokens(); } - async deleteToken(etapiTokenId, name) { + async deleteToken(etapiTokenId: string, name: string) { if (!(await dialogService.confirm(t("etapi.delete_token_confirmation", { name })))) { return; } diff --git a/src/public/app/widgets/type_widgets/options/password.js b/src/public/app/widgets/type_widgets/options/password.ts similarity index 85% rename from src/public/app/widgets/type_widgets/options/password.js rename to src/public/app/widgets/type_widgets/options/password.ts index 0f6cb1e91..9abdda179 100644 --- a/src/public/app/widgets/type_widgets/options/password.js +++ b/src/public/app/widgets/type_widgets/options/password.ts @@ -3,31 +3,32 @@ import server from "../../../services/server.js"; import protectedSessionHolder from "../../../services/protected_session_holder.js"; import toastService from "../../../services/toast.js"; import OptionsWidget from "./options_widget.js"; +import type { OptionMap } from "../../../../../services/options_interface.js"; const TPL = `

${t("password.heading")}

- + - +
- +
- +
- +
@@ -43,7 +44,23 @@ const TPL = ` `; +// TODO: Deduplicate +interface ChangePasswordResponse { + success: boolean; + message?: string; +} + export default class PasswordOptions extends OptionsWidget { + + private $passwordHeading!: JQuery; + private $changePasswordForm!: JQuery; + private $oldPassword!: JQuery; + private $newPassword1!: JQuery; + private $newPassword2!: JQuery; + private $savePasswordButton!: JQuery; + private $resetPasswordButton!: JQuery; + private $protectedSessionTimeout!: JQuery; + doRender() { this.$widget = $(TPL); @@ -59,7 +76,7 @@ export default class PasswordOptions extends OptionsWidget { if (confirm(t("password.reset_confirmation"))) { await server.post("password/reset?really=yesIReallyWantToResetPasswordAndLoseAccessToMyProtectedNotes"); - const options = await server.get("options"); + const options = await server.get("options"); this.optionsLoaded(options); toastService.showError(t("password.reset_success_message")); @@ -72,7 +89,7 @@ export default class PasswordOptions extends OptionsWidget { this.$protectedSessionTimeout.on("change", () => this.updateOption("protectedSessionTimeout", this.$protectedSessionTimeout.val())); } - optionsLoaded(options) { + optionsLoaded(options: OptionMap) { const isPasswordSet = options.isPasswordSet === "true"; this.$widget.find(".old-password-form-group").toggle(isPasswordSet); @@ -96,7 +113,7 @@ export default class PasswordOptions extends OptionsWidget { } server - .post("password/change", { + .post("password/change", { current_password: oldPassword, new_password: newPassword1 }) @@ -106,7 +123,7 @@ export default class PasswordOptions extends OptionsWidget { // password changed so current protected session is invalid and needs to be cleared protectedSessionHolder.resetProtectedSession(); - } else { + } else if (result.message) { toastService.showError(result.message); } }); diff --git a/src/public/app/widgets/type_widgets/options/shortcuts.js b/src/public/app/widgets/type_widgets/options/shortcuts.ts similarity index 79% rename from src/public/app/widgets/type_widgets/options/shortcuts.js rename to src/public/app/widgets/type_widgets/options/shortcuts.ts index f453859f1..ed058cfc0 100644 --- a/src/public/app/widgets/type_widgets/options/shortcuts.js +++ b/src/public/app/widgets/type_widgets/options/shortcuts.ts @@ -3,40 +3,42 @@ import utils from "../../../services/utils.js"; import dialogService from "../../../services/dialog.js"; import OptionsWidget from "./options_widget.js"; import { t } from "../../../services/i18n.js"; +import type { KeyboardShortcut } from "../../../../../services/keyboard_actions_interface.js"; +import type { OptionNames } from "../../../../../services/options_interface.js"; const TPL = `

${t("shortcuts.keyboard_shortcuts")}

- +

${t("shortcuts.multiple_shortcuts")} ${t("shortcuts.electron_documentation")}

- +
- +
@@ -50,15 +52,15 @@ const TPL = `
- +
- +
`; -let globActions; +let globActions: KeyboardShortcut[]; export default class KeyboardShortcutsOptions extends OptionsWidget { doRender() { @@ -68,7 +70,7 @@ export default class KeyboardShortcutsOptions extends OptionsWidget { const $table = this.$widget.find(".keyboard-shortcut-table tbody"); - server.get("keyboard-actions").then((actions) => { + server.get("keyboard-actions").then((actions) => { globActions = actions; for (const action of actions) { @@ -76,18 +78,18 @@ export default class KeyboardShortcutsOptions extends OptionsWidget { if (action.separator) { $tr.append($('').attr("style", "background-color: var(--accented-background-color); font-weight: bold;").text(action.separator)); - } else if (action.defaultShortcuts) { + } else if (action.defaultShortcuts && action.actionName) { $tr.append($("").text(action.actionName)) .append( $("").append( $(``) - .val(action.effectiveShortcuts.join(", ")) + .val((action.effectiveShortcuts ?? []).join(", ")) .attr("data-keyboard-action-name", action.actionName) .attr("data-default-keyboard-shortcuts", action.defaultShortcuts.join(", ")) ) ) .append($("").text(action.defaultShortcuts.join(", "))) - .append($("").text(action.description)); + .append($("").text(action.description ?? "")); } $table.append($tr); @@ -97,8 +99,11 @@ export default class KeyboardShortcutsOptions extends OptionsWidget { $table.on("change", "input.form-control", (e) => { const $input = this.$widget.find(e.target); const actionName = $input.attr("data-keyboard-action-name"); - const shortcuts = $input - .val() + if (!actionName) { + return; + } + + const shortcuts = ($input.val() as String) .replace("+,", "+Comma") .split(",") .map((shortcut) => shortcut.replace("+Comma", "+,")) @@ -106,7 +111,7 @@ export default class KeyboardShortcutsOptions extends OptionsWidget { const optionName = `keyboardShortcuts${actionName.substr(0, 1).toUpperCase()}${actionName.substr(1)}`; - this.updateOption(optionName, JSON.stringify(shortcuts)); + this.updateOption(optionName as OptionNames, JSON.stringify(shortcuts)); }); this.$widget.find(".options-keyboard-shortcuts-set-all-to-default").on("click", async () => { @@ -117,7 +122,7 @@ export default class KeyboardShortcutsOptions extends OptionsWidget { $table.find("input.form-control").each((_index, el) => { const defaultShortcuts = this.$widget.find(el).attr("data-default-keyboard-shortcuts"); - if (this.$widget.find(el).val() !== defaultShortcuts) { + if (defaultShortcuts && this.$widget.find(el).val() !== defaultShortcuts) { this.$widget.find(el).val(defaultShortcuts).trigger("change"); } }); @@ -126,7 +131,7 @@ export default class KeyboardShortcutsOptions extends OptionsWidget { const $filter = this.$widget.find(".keyboard-shortcut-filter"); $filter.on("keyup", () => { - const filter = $filter.val().trim().toLowerCase(); + const filter = String($filter.val()).trim().toLowerCase(); $table.find("tr").each((i, el) => { if (!filter) { @@ -143,7 +148,7 @@ export default class KeyboardShortcutsOptions extends OptionsWidget { const action = globActions.find((act) => act.actionName === actionName); - if (!action) { + if (!action || !action.actionName) { this.$widget.find(el).hide(); return; } @@ -153,8 +158,8 @@ export default class KeyboardShortcutsOptions extends OptionsWidget { .toggle( !!( action.actionName.toLowerCase().includes(filter) || - action.defaultShortcuts.some((shortcut) => shortcut.toLowerCase().includes(filter)) || - action.effectiveShortcuts.some((shortcut) => shortcut.toLowerCase().includes(filter)) || + (action.defaultShortcuts ?? []).some((shortcut) => shortcut.toLowerCase().includes(filter)) || + (action.effectiveShortcuts ?? []).some((shortcut) => shortcut.toLowerCase().includes(filter)) || (action.description && action.description.toLowerCase().includes(filter)) ) ); diff --git a/src/public/app/widgets/type_widgets/options/spellcheck.js b/src/public/app/widgets/type_widgets/options/spellcheck.ts similarity index 88% rename from src/public/app/widgets/type_widgets/options/spellcheck.js rename to src/public/app/widgets/type_widgets/options/spellcheck.ts index 86d684e6d..dd9782ad8 100644 --- a/src/public/app/widgets/type_widgets/options/spellcheck.js +++ b/src/public/app/widgets/type_widgets/options/spellcheck.ts @@ -1,6 +1,7 @@ import utils from "../../../services/utils.js"; import OptionsWidget from "./options_widget.js"; import { t } from "../../../services/i18n.js"; +import type { OptionMap } from "../../../../../services/options_interface.js"; const TPL_WEB = `
@@ -29,11 +30,16 @@ const TPL_ELECTRON = `

${t("spellcheck.multiple_languages_info")}

- +

${t("spellcheck.available_language_codes_label")}

`; export default class SpellcheckOptions extends OptionsWidget { + + private $spellCheckEnabled!: JQuery; + private $spellCheckLanguageCode!: JQuery; + private $availableLanguageCodes!: JQuery; + doRender() { const template = utils.isElectron() ? TPL_ELECTRON : TPL_WEB; this.$widget = $(template); @@ -54,7 +60,7 @@ export default class SpellcheckOptions extends OptionsWidget { } } - optionsLoaded(options) { + optionsLoaded(options: OptionMap) { this.setCheckboxState(this.$spellCheckEnabled, options.spellCheckEnabled); this.$spellCheckLanguageCode.val(options.spellCheckLanguageCode); } diff --git a/src/public/app/widgets/type_widgets/options/sync.js b/src/public/app/widgets/type_widgets/options/sync.ts similarity index 79% rename from src/public/app/widgets/type_widgets/options/sync.js rename to src/public/app/widgets/type_widgets/options/sync.ts index 0e69f3d9f..6317c3147 100644 --- a/src/public/app/widgets/type_widgets/options/sync.js +++ b/src/public/app/widgets/type_widgets/options/sync.ts @@ -2,33 +2,34 @@ import server from "../../../services/server.js"; import toastService from "../../../services/toast.js"; import OptionsWidget from "./options_widget.js"; import { t } from "../../../services/i18n.js"; +import type { OptionMap } from "../../../../../services/options_interface.js"; const TPL = `

${t("sync_2.config_title")}

- +
- +
- +
- +

${t("sync_2.note")}: ${t("sync_2.note_description")}

${t("sync_2.special_value_description")}

- +
- +
@@ -36,13 +37,26 @@ const TPL = `

${t("sync_2.test_title")}

- +

${t("sync_2.test_description")}

- +
`; +// TODO: Deduplicate +interface TestResponse { + success: boolean; + message: string; +} + export default class SyncOptions extends OptionsWidget { + + private $form!: JQuery; + private $syncServerHost!: JQuery; + private $syncServerTimeout!: JQuery; + private $syncProxy!: JQuery; + private $testSyncButton!: JQuery; + doRender() { this.$widget = $(TPL); @@ -55,7 +69,7 @@ export default class SyncOptions extends OptionsWidget { this.$form.on("submit", () => this.save()); this.$testSyncButton.on("click", async () => { - const result = await server.post("sync/test"); + const result = await server.post("sync/test"); if (result.success) { toastService.showMessage(result.message); @@ -65,7 +79,7 @@ export default class SyncOptions extends OptionsWidget { }); } - optionsLoaded(options) { + optionsLoaded(options: OptionMap) { this.$syncServerHost.val(options.syncServerHost); this.$syncServerTimeout.val(options.syncServerTimeout); this.$syncProxy.val(options.syncProxy); @@ -73,9 +87,9 @@ export default class SyncOptions extends OptionsWidget { save() { this.updateMultipleOptions({ - syncServerHost: this.$syncServerHost.val(), - syncServerTimeout: this.$syncServerTimeout.val(), - syncProxy: this.$syncProxy.val() + syncServerHost: String(this.$syncServerHost.val()), + syncServerTimeout: String(this.$syncServerTimeout.val()), + syncProxy: String(this.$syncProxy.val()) }); return false; diff --git a/src/services/options_interface.ts b/src/services/options_interface.ts index 9e30019c9..2632b7b2e 100644 --- a/src/services/options_interface.ts +++ b/src/services/options_interface.ts @@ -69,6 +69,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions