chore(client/ts): port options

This commit is contained in:
Elian Doran
2025-01-11 11:40:22 +02:00
parent 7e61af1cc3
commit 05529b84ab
9 changed files with 150 additions and 75 deletions

View File

@@ -20,7 +20,7 @@ async function confirmDeleteNoteBoxWithNote(title: string) {
} }
async function prompt(props: PromptDialogOptions) { async function prompt(props: PromptDialogOptions) {
return new Promise((res) => appContext.triggerCommand("showPromptDialog", { ...props, callback: res })); return new Promise<string | null>((res) => appContext.triggerCommand("showPromptDialog", { ...props, callback: res }));
} }
export default { export default {

View File

@@ -31,15 +31,15 @@ export interface PromptDialogOptions {
title?: string; title?: string;
message?: string; message?: string;
defaultValue?: string; defaultValue?: string;
shown: PromptShownDialogCallback; shown?: PromptShownDialogCallback;
callback: (value: unknown) => void; callback?: (value: string | null) => void;
} }
export type PromptShownDialogCallback = ((callback: ShownCallbackData) => void) | null; export type PromptShownDialogCallback = ((callback: ShownCallbackData) => void) | null;
export default class PromptDialog extends BasicWidget { export default class PromptDialog extends BasicWidget {
private resolve: ((val: string | null) => void) | null; private resolve?: ((value: string | null) => void) | undefined | null;
private shownCb: PromptShownDialogCallback; private shownCb?: PromptShownDialogCallback | null;
private modal!: bootstrap.Modal; private modal!: bootstrap.Modal;
private $dialogBody!: JQuery<HTMLElement>; private $dialogBody!: JQuery<HTMLElement>;

View File

@@ -3,13 +3,14 @@ import { t } from "../../../services/i18n.js";
import OptionsWidget from "./options_widget.js"; import OptionsWidget from "./options_widget.js";
import server from "../../../services/server.js"; import server from "../../../services/server.js";
import toastService from "../../../services/toast.js"; import toastService from "../../../services/toast.js";
import type { OptionMap } from "../../../../../services/options_interface.js";
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section">
<h4>${t("backup.automatic_backup")}</h4> <h4>${t("backup.automatic_backup")}</h4>
<p>${t("backup.automatic_backup_description")}</p> <p>${t("backup.automatic_backup_description")}</p>
<ul style="list-style: none"> <ul style="list-style: none">
<li> <li>
<label> <label>
@@ -17,7 +18,7 @@ const TPL = `
${t("backup.enable_daily_backup")} ${t("backup.enable_daily_backup")}
</label> </label>
</li> </li>
<li> <li>
<label> <label>
<input type="checkbox" class="weekly-backup-enabled form-check-input"> <input type="checkbox" class="weekly-backup-enabled form-check-input">
${t("backup.enable_weekly_backup")} ${t("backup.enable_weekly_backup")}
@@ -30,19 +31,19 @@ const TPL = `
</label> </label>
</li> </li>
</ul> </ul>
<p>${t("backup.backup_recommendation")}</p> <p>${t("backup.backup_recommendation")}</p>
</div> </div>
<div class="options-section"> <div class="options-section">
<h4>${t("backup.backup_now")}</h4> <h4>${t("backup.backup_now")}</h4>
<button class="backup-database-button btn">${t("backup.backup_database_now")}</button> <button class="backup-database-button btn">${t("backup.backup_database_now")}</button>
</div> </div>
<div class="options-section"> <div class="options-section">
<h4>${t("backup.existing_backups")}</h4> <h4>${t("backup.existing_backups")}</h4>
<table class="table table-stripped"> <table class="table table-stripped">
<colgroup> <colgroup>
<col width="33%" /> <col width="33%" />
@@ -61,14 +62,32 @@ const TPL = `
</div> </div>
`; `;
// TODO: Deduplicate.
interface PostDatabaseResponse {
backupFile: string;
}
// TODO: Deduplicate
interface Backup {
filePath: string;
mtime: number;
}
export default class BackupOptions extends OptionsWidget { export default class BackupOptions extends OptionsWidget {
private $backupDatabaseButton!: JQuery<HTMLElement>;
private $dailyBackupEnabled!: JQuery<HTMLElement>;
private $weeklyBackupEnabled!: JQuery<HTMLElement>;
private $monthlyBackupEnabled!: JQuery<HTMLElement>;
private $existingBackupList!: JQuery<HTMLElement>;
doRender() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
this.$backupDatabaseButton = this.$widget.find(".backup-database-button"); this.$backupDatabaseButton = this.$widget.find(".backup-database-button");
this.$backupDatabaseButton.on("click", async () => { this.$backupDatabaseButton.on("click", async () => {
const { backupFile } = await server.post("database/backup-database"); const { backupFile } = await server.post<PostDatabaseResponse>("database/backup-database");
toastService.showMessage(t("backup.database_backed_up_to", { backupFilePath: backupFile }), 10000); 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"); this.$existingBackupList = this.$widget.find(".existing-backup-list-items");
} }
optionsLoaded(options) { optionsLoaded(options: OptionMap) {
this.setCheckboxState(this.$dailyBackupEnabled, options.dailyBackupEnabled); this.setCheckboxState(this.$dailyBackupEnabled, options.dailyBackupEnabled);
this.setCheckboxState(this.$weeklyBackupEnabled, options.weeklyBackupEnabled); this.setCheckboxState(this.$weeklyBackupEnabled, options.weeklyBackupEnabled);
this.setCheckboxState(this.$monthlyBackupEnabled, options.monthlyBackupEnabled); this.setCheckboxState(this.$monthlyBackupEnabled, options.monthlyBackupEnabled);
server.get("database/backups").then((backupFiles) => { server.get<Backup[]>("database/backups").then((backupFiles) => {
this.$existingBackupList.empty(); this.$existingBackupList.empty();
if (!backupFiles.length) { if (!backupFiles.length) {

View File

@@ -8,18 +8,18 @@ import toastService from "../../../services/toast.js";
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section">
<h4>${t("etapi.title")}</h4> <h4>${t("etapi.title")}</h4>
<p>${t("etapi.description")} <br/> <p>${t("etapi.description")} <br/>
${t("etapi.see_more")} <a href="https://triliumnext.github.io/Docs/Wiki/etapi.html">${t("etapi.wiki")}</a> ${t("etapi.and")} <a onclick="window.open('etapi/etapi.openapi.yaml')" href="etapi/etapi.openapi.yaml">${t("etapi.openapi_spec")}</a>.</p> ${t("etapi.see_more")} <a href="https://triliumnext.github.io/Docs/Wiki/etapi.html">${t("etapi.wiki")}</a> ${t("etapi.and")} <a onclick="window.open('etapi/etapi.openapi.yaml')" href="etapi/etapi.openapi.yaml">${t("etapi.openapi_spec")}</a>.</p>
<button type="button" class="create-etapi-token btn btn-sm">${t("etapi.create_token")}</button> <button type="button" class="create-etapi-token btn btn-sm">${t("etapi.create_token")}</button>
<hr /> <hr />
<h5>${t("etapi.existing_tokens")}</h5> <h5>${t("etapi.existing_tokens")}</h5>
<div class="no-tokens-yet">${t("etapi.no_tokens_yet")}</div> <div class="no-tokens-yet">${t("etapi.no_tokens_yet")}</div>
<div style="overflow: auto; height: 500px;"> <div style="overflow: auto; height: 500px;">
<table class="tokens-table table table-stripped"> <table class="tokens-table table table-stripped">
<thead> <thead>
@@ -44,13 +44,26 @@ const TPL = `
border: 1px solid transparent; border: 1px solid transparent;
border-radius: var(--button-border-radius); border-radius: var(--button-border-radius);
} }
.token-table-button:hover { .token-table-button:hover {
border: 1px solid var(--button-border-color); border: 1px solid var(--button-border-color);
} }
</style>`; </style>`;
// TODO: Deduplicate
interface PostTokensResponse {
authToken: string;
}
// TODO: Deduplicate
interface Token {
name: string;
utcDateCreated: number;
etapiTokenId: string;
}
export default class EtapiOptions extends OptionsWidget { export default class EtapiOptions extends OptionsWidget {
doRender() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
@@ -61,12 +74,12 @@ export default class EtapiOptions extends OptionsWidget {
defaultValue: t("etapi.default_token_name") defaultValue: t("etapi.default_token_name")
}); });
if (!tokenName.trim()) { if (!tokenName?.trim()) {
toastService.showError(t("etapi.error_empty_name")); toastService.showError(t("etapi.error_empty_name"));
return; return;
} }
const { authToken } = await server.post("etapi-tokens", { tokenName }); const { authToken } = await server.post<PostTokensResponse>("etapi-tokens", { tokenName });
await dialogService.prompt({ await dialogService.prompt({
title: t("etapi.token_created_title"), 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 $noTokensYet = this.$widget.find(".no-tokens-yet");
const $tokensTable = this.$widget.find(".tokens-table"); const $tokensTable = this.$widget.find(".tokens-table");
const tokens = await server.get("etapi-tokens"); const tokens = await server.get<Token[]>("etapi-tokens");
$noTokensYet.toggle(tokens.length === 0); $noTokensYet.toggle(tokens.length === 0);
$tokensTable.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({ const tokenName = await dialogService.prompt({
title: t("etapi.rename_token_title"), title: t("etapi.rename_token_title"),
message: t("etapi.rename_token_message"), message: t("etapi.rename_token_message"),
@@ -123,7 +136,7 @@ export default class EtapiOptions extends OptionsWidget {
this.refreshTokens(); this.refreshTokens();
} }
async deleteToken(etapiTokenId, name) { async deleteToken(etapiTokenId: string, name: string) {
if (!(await dialogService.confirm(t("etapi.delete_token_confirmation", { name })))) { if (!(await dialogService.confirm(t("etapi.delete_token_confirmation", { name })))) {
return; return;
} }

View File

@@ -3,31 +3,32 @@ import server from "../../../services/server.js";
import protectedSessionHolder from "../../../services/protected_session_holder.js"; import protectedSessionHolder from "../../../services/protected_session_holder.js";
import toastService from "../../../services/toast.js"; import toastService from "../../../services/toast.js";
import OptionsWidget from "./options_widget.js"; import OptionsWidget from "./options_widget.js";
import type { OptionMap } from "../../../../../services/options_interface.js";
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section">
<h4 class="password-heading">${t("password.heading")}</h4> <h4 class="password-heading">${t("password.heading")}</h4>
<div class="alert alert-warning" role="alert" style="font-weight: bold; color: red !important;"> <div class="alert alert-warning" role="alert" style="font-weight: bold; color: red !important;">
${t("password.alert_message")} <a class="reset-password-button" href="javascript:">${t("password.reset_link")}</a> ${t("password.alert_message")} <a class="reset-password-button" href="javascript:">${t("password.reset_link")}</a>
</div> </div>
<form class="change-password-form"> <form class="change-password-form">
<div class="old-password-form-group form-group"> <div class="old-password-form-group form-group">
<label for="old-password">${t("password.old_password")}</label> <label for="old-password">${t("password.old_password")}</label>
<input id="old-password" class="old-password form-control" type="password"> <input id="old-password" class="old-password form-control" type="password">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="new-password1">${t("password.new_password")}</label> <label for="new-password1">${t("password.new_password")}</label>
<input id="new-password1" class="new-password1 form-control" type="password"> <input id="new-password1" class="new-password1 form-control" type="password">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="new-password2">${t("password.new_password_confirmation")}</label> <label for="new-password2">${t("password.new_password_confirmation")}</label>
<input id="new-password2" class="new-password2 form-control" type="password"> <input id="new-password2" class="new-password2 form-control" type="password">
</div> </div>
<button class="save-password-button btn btn-primary">${t("password.change_password")}</button> <button class="save-password-button btn btn-primary">${t("password.change_password")}</button>
</form> </form>
</div> </div>
@@ -43,7 +44,23 @@ const TPL = `
</div> </div>
</div>`; </div>`;
// TODO: Deduplicate
interface ChangePasswordResponse {
success: boolean;
message?: string;
}
export default class PasswordOptions extends OptionsWidget { export default class PasswordOptions extends OptionsWidget {
private $passwordHeading!: JQuery<HTMLElement>;
private $changePasswordForm!: JQuery<HTMLElement>;
private $oldPassword!: JQuery<HTMLElement>;
private $newPassword1!: JQuery<HTMLElement>;
private $newPassword2!: JQuery<HTMLElement>;
private $savePasswordButton!: JQuery<HTMLElement>;
private $resetPasswordButton!: JQuery<HTMLElement>;
private $protectedSessionTimeout!: JQuery<HTMLElement>;
doRender() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
@@ -59,7 +76,7 @@ export default class PasswordOptions extends OptionsWidget {
if (confirm(t("password.reset_confirmation"))) { if (confirm(t("password.reset_confirmation"))) {
await server.post("password/reset?really=yesIReallyWantToResetPasswordAndLoseAccessToMyProtectedNotes"); await server.post("password/reset?really=yesIReallyWantToResetPasswordAndLoseAccessToMyProtectedNotes");
const options = await server.get("options"); const options = await server.get<OptionMap>("options");
this.optionsLoaded(options); this.optionsLoaded(options);
toastService.showError(t("password.reset_success_message")); 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())); this.$protectedSessionTimeout.on("change", () => this.updateOption("protectedSessionTimeout", this.$protectedSessionTimeout.val()));
} }
optionsLoaded(options) { optionsLoaded(options: OptionMap) {
const isPasswordSet = options.isPasswordSet === "true"; const isPasswordSet = options.isPasswordSet === "true";
this.$widget.find(".old-password-form-group").toggle(isPasswordSet); this.$widget.find(".old-password-form-group").toggle(isPasswordSet);
@@ -96,7 +113,7 @@ export default class PasswordOptions extends OptionsWidget {
} }
server server
.post("password/change", { .post<ChangePasswordResponse>("password/change", {
current_password: oldPassword, current_password: oldPassword,
new_password: newPassword1 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 // password changed so current protected session is invalid and needs to be cleared
protectedSessionHolder.resetProtectedSession(); protectedSessionHolder.resetProtectedSession();
} else { } else if (result.message) {
toastService.showError(result.message); toastService.showError(result.message);
} }
}); });

View File

@@ -3,40 +3,42 @@ import utils from "../../../services/utils.js";
import dialogService from "../../../services/dialog.js"; import dialogService from "../../../services/dialog.js";
import OptionsWidget from "./options_widget.js"; import OptionsWidget from "./options_widget.js";
import { t } from "../../../services/i18n.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 = ` const TPL = `
<div class="options-section shortcuts-options-section"> <div class="options-section shortcuts-options-section">
<style> <style>
.shortcuts-options-section { .shortcuts-options-section {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
height: 100%; height: 100%;
} }
.shortcuts-table-container { .shortcuts-table-container {
overflow: auto; overflow: auto;
flex-grow: 1; flex-grow: 1;
flex-shrink: 1; flex-shrink: 1;
} }
.shortcuts-options-buttons { .shortcuts-options-buttons {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
margin: 15px 15px 0 15px; margin: 15px 15px 0 15px;
} }
</style> </style>
<h4>${t("shortcuts.keyboard_shortcuts")}</h4> <h4>${t("shortcuts.keyboard_shortcuts")}</h4>
<p> <p>
${t("shortcuts.multiple_shortcuts")} ${t("shortcuts.multiple_shortcuts")}
${t("shortcuts.electron_documentation")} ${t("shortcuts.electron_documentation")}
</p> </p>
<div class="form-group"> <div class="form-group">
<input type="text" class="keyboard-shortcut-filter form-control" placeholder="${t("shortcuts.type_text_to_filter")}"> <input type="text" class="keyboard-shortcut-filter form-control" placeholder="${t("shortcuts.type_text_to_filter")}">
</div> </div>
<div class="shortcuts-table-container"> <div class="shortcuts-table-container">
<table class="keyboard-shortcut-table" cellpadding="10"> <table class="keyboard-shortcut-table" cellpadding="10">
<thead> <thead>
@@ -50,15 +52,15 @@ const TPL = `
<tbody></tbody> <tbody></tbody>
</table> </table>
</div> </div>
<div class="shortcuts-options-buttons"> <div class="shortcuts-options-buttons">
<button class="options-keyboard-shortcuts-reload-app btn btn-primary">${t("shortcuts.reload_app")}</button> <button class="options-keyboard-shortcuts-reload-app btn btn-primary">${t("shortcuts.reload_app")}</button>
<button class="options-keyboard-shortcuts-set-all-to-default btn">${t("shortcuts.set_all_to_default")}</button> <button class="options-keyboard-shortcuts-set-all-to-default btn">${t("shortcuts.set_all_to_default")}</button>
</div> </div>
</div>`; </div>`;
let globActions; let globActions: KeyboardShortcut[];
export default class KeyboardShortcutsOptions extends OptionsWidget { export default class KeyboardShortcutsOptions extends OptionsWidget {
doRender() { doRender() {
@@ -68,7 +70,7 @@ export default class KeyboardShortcutsOptions extends OptionsWidget {
const $table = this.$widget.find(".keyboard-shortcut-table tbody"); const $table = this.$widget.find(".keyboard-shortcut-table tbody");
server.get("keyboard-actions").then((actions) => { server.get<KeyboardShortcut[]>("keyboard-actions").then((actions) => {
globActions = actions; globActions = actions;
for (const action of actions) { for (const action of actions) {
@@ -76,18 +78,18 @@ export default class KeyboardShortcutsOptions extends OptionsWidget {
if (action.separator) { if (action.separator) {
$tr.append($('<td colspan="4">').attr("style", "background-color: var(--accented-background-color); font-weight: bold;").text(action.separator)); $tr.append($('<td colspan="4">').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($("<td>").text(action.actionName)) $tr.append($("<td>").text(action.actionName))
.append( .append(
$("<td>").append( $("<td>").append(
$(`<input type="text" class="form-control">`) $(`<input type="text" class="form-control">`)
.val(action.effectiveShortcuts.join(", ")) .val((action.effectiveShortcuts ?? []).join(", "))
.attr("data-keyboard-action-name", action.actionName) .attr("data-keyboard-action-name", action.actionName)
.attr("data-default-keyboard-shortcuts", action.defaultShortcuts.join(", ")) .attr("data-default-keyboard-shortcuts", action.defaultShortcuts.join(", "))
) )
) )
.append($("<td>").text(action.defaultShortcuts.join(", "))) .append($("<td>").text(action.defaultShortcuts.join(", ")))
.append($("<td>").text(action.description)); .append($("<td>").text(action.description ?? ""));
} }
$table.append($tr); $table.append($tr);
@@ -97,8 +99,11 @@ export default class KeyboardShortcutsOptions extends OptionsWidget {
$table.on("change", "input.form-control", (e) => { $table.on("change", "input.form-control", (e) => {
const $input = this.$widget.find(e.target); const $input = this.$widget.find(e.target);
const actionName = $input.attr("data-keyboard-action-name"); const actionName = $input.attr("data-keyboard-action-name");
const shortcuts = $input if (!actionName) {
.val() return;
}
const shortcuts = ($input.val() as String)
.replace("+,", "+Comma") .replace("+,", "+Comma")
.split(",") .split(",")
.map((shortcut) => shortcut.replace("+Comma", "+,")) .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)}`; 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 () => { 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) => { $table.find("input.form-control").each((_index, el) => {
const defaultShortcuts = this.$widget.find(el).attr("data-default-keyboard-shortcuts"); 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"); 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"); const $filter = this.$widget.find(".keyboard-shortcut-filter");
$filter.on("keyup", () => { $filter.on("keyup", () => {
const filter = $filter.val().trim().toLowerCase(); const filter = String($filter.val()).trim().toLowerCase();
$table.find("tr").each((i, el) => { $table.find("tr").each((i, el) => {
if (!filter) { if (!filter) {
@@ -143,7 +148,7 @@ export default class KeyboardShortcutsOptions extends OptionsWidget {
const action = globActions.find((act) => act.actionName === actionName); const action = globActions.find((act) => act.actionName === actionName);
if (!action) { if (!action || !action.actionName) {
this.$widget.find(el).hide(); this.$widget.find(el).hide();
return; return;
} }
@@ -153,8 +158,8 @@ export default class KeyboardShortcutsOptions extends OptionsWidget {
.toggle( .toggle(
!!( !!(
action.actionName.toLowerCase().includes(filter) || action.actionName.toLowerCase().includes(filter) ||
action.defaultShortcuts.some((shortcut) => shortcut.toLowerCase().includes(filter)) || (action.defaultShortcuts ?? []).some((shortcut) => shortcut.toLowerCase().includes(filter)) ||
action.effectiveShortcuts.some((shortcut) => shortcut.toLowerCase().includes(filter)) || (action.effectiveShortcuts ?? []).some((shortcut) => shortcut.toLowerCase().includes(filter)) ||
(action.description && action.description.toLowerCase().includes(filter)) (action.description && action.description.toLowerCase().includes(filter))
) )
); );

View File

@@ -1,6 +1,7 @@
import utils from "../../../services/utils.js"; import utils from "../../../services/utils.js";
import OptionsWidget from "./options_widget.js"; import OptionsWidget from "./options_widget.js";
import { t } from "../../../services/i18n.js"; import { t } from "../../../services/i18n.js";
import type { OptionMap } from "../../../../../services/options_interface.js";
const TPL_WEB = ` const TPL_WEB = `
<div class="options-section"> <div class="options-section">
@@ -29,11 +30,16 @@ const TPL_ELECTRON = `
</div> </div>
<p>${t("spellcheck.multiple_languages_info")}</p> <p>${t("spellcheck.multiple_languages_info")}</p>
<p><strong>${t("spellcheck.available_language_codes_label")} </strong> <span class="available-language-codes"></span></p> <p><strong>${t("spellcheck.available_language_codes_label")} </strong> <span class="available-language-codes"></span></p>
</div>`; </div>`;
export default class SpellcheckOptions extends OptionsWidget { export default class SpellcheckOptions extends OptionsWidget {
private $spellCheckEnabled!: JQuery<HTMLElement>;
private $spellCheckLanguageCode!: JQuery<HTMLElement>;
private $availableLanguageCodes!: JQuery<HTMLElement>;
doRender() { doRender() {
const template = utils.isElectron() ? TPL_ELECTRON : TPL_WEB; const template = utils.isElectron() ? TPL_ELECTRON : TPL_WEB;
this.$widget = $(template); 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.setCheckboxState(this.$spellCheckEnabled, options.spellCheckEnabled);
this.$spellCheckLanguageCode.val(options.spellCheckLanguageCode); this.$spellCheckLanguageCode.val(options.spellCheckLanguageCode);
} }

View File

@@ -2,33 +2,34 @@ import server from "../../../services/server.js";
import toastService from "../../../services/toast.js"; import toastService from "../../../services/toast.js";
import OptionsWidget from "./options_widget.js"; import OptionsWidget from "./options_widget.js";
import { t } from "../../../services/i18n.js"; import { t } from "../../../services/i18n.js";
import type { OptionMap } from "../../../../../services/options_interface.js";
const TPL = ` const TPL = `
<div class="options-section"> <div class="options-section">
<h4 style="margin-top: 0px;">${t("sync_2.config_title")}</h4> <h4 style="margin-top: 0px;">${t("sync_2.config_title")}</h4>
<form class="sync-setup-form"> <form class="sync-setup-form">
<div class="form-group"> <div class="form-group">
<label for="sync-server-host" >${t("sync_2.server_address")}</label> <label for="sync-server-host" >${t("sync_2.server_address")}</label>
<input id="sync-server-host" class="sync-server-host form-control" placeholder="https://<host>:<port>"> <input id="sync-server-host" class="sync-server-host form-control" placeholder="https://<host>:<port>">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="sync-server-timeout" >${t("sync_2.timeout")}</label> <label for="sync-server-timeout" >${t("sync_2.timeout")}</label>
<input id="sync-server-timeout" class="sync-server-timeout form-control" min="1" max="10000000" type="number" style="text-align: left;"> <input id="sync-server-timeout" class="sync-server-timeout form-control" min="1" max="10000000" type="number" style="text-align: left;">
</div> </div>
<div class="form-group"> <div class="form-group">
<label for="sync-proxy form-control" >${t("sync_2.proxy_label")}</label> <label for="sync-proxy form-control" >${t("sync_2.proxy_label")}</label>
<input id="sync-proxy form-control" class="sync-proxy form-control" placeholder="https://<host>:<port>"> <input id="sync-proxy form-control" class="sync-proxy form-control" placeholder="https://<host>:<port>">
<p><strong>${t("sync_2.note")}:</strong> ${t("sync_2.note_description")}</p> <p><strong>${t("sync_2.note")}:</strong> ${t("sync_2.note_description")}</p>
<p>${t("sync_2.special_value_description")}</p> <p>${t("sync_2.special_value_description")}</p>
</div> </div>
<div style="display: flex; justify-content: space-between;"> <div style="display: flex; justify-content: space-between;">
<button class="btn btn-primary">${t("sync_2.save")}</button> <button class="btn btn-primary">${t("sync_2.save")}</button>
<button class="btn" type="button" data-help-page="synchronization.html">${t("sync_2.help")}</button> <button class="btn" type="button" data-help-page="synchronization.html">${t("sync_2.help")}</button>
</div> </div>
</form> </form>
@@ -36,13 +37,26 @@ const TPL = `
<div class="options-section"> <div class="options-section">
<h4>${t("sync_2.test_title")}</h4> <h4>${t("sync_2.test_title")}</h4>
<p>${t("sync_2.test_description")}</p> <p>${t("sync_2.test_description")}</p>
<button class="test-sync-button btn">${t("sync_2.test_button")}</button> <button class="test-sync-button btn">${t("sync_2.test_button")}</button>
</div>`; </div>`;
// TODO: Deduplicate
interface TestResponse {
success: boolean;
message: string;
}
export default class SyncOptions extends OptionsWidget { export default class SyncOptions extends OptionsWidget {
private $form!: JQuery<HTMLElement>;
private $syncServerHost!: JQuery<HTMLElement>;
private $syncServerTimeout!: JQuery<HTMLElement>;
private $syncProxy!: JQuery<HTMLElement>;
private $testSyncButton!: JQuery<HTMLElement>;
doRender() { doRender() {
this.$widget = $(TPL); this.$widget = $(TPL);
@@ -55,7 +69,7 @@ export default class SyncOptions extends OptionsWidget {
this.$form.on("submit", () => this.save()); this.$form.on("submit", () => this.save());
this.$testSyncButton.on("click", async () => { this.$testSyncButton.on("click", async () => {
const result = await server.post("sync/test"); const result = await server.post<TestResponse>("sync/test");
if (result.success) { if (result.success) {
toastService.showMessage(result.message); 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.$syncServerHost.val(options.syncServerHost);
this.$syncServerTimeout.val(options.syncServerTimeout); this.$syncServerTimeout.val(options.syncServerTimeout);
this.$syncProxy.val(options.syncProxy); this.$syncProxy.val(options.syncProxy);
@@ -73,9 +87,9 @@ export default class SyncOptions extends OptionsWidget {
save() { save() {
this.updateMultipleOptions({ this.updateMultipleOptions({
syncServerHost: this.$syncServerHost.val(), syncServerHost: String(this.$syncServerHost.val()),
syncServerTimeout: this.$syncServerTimeout.val(), syncServerTimeout: String(this.$syncServerTimeout.val()),
syncProxy: this.$syncProxy.val() syncProxy: String(this.$syncProxy.val())
}); });
return false; return false;

View File

@@ -69,6 +69,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions<KeyboardActi
firstDayOfWeek: number; firstDayOfWeek: number;
initialized: boolean; initialized: boolean;
isPasswordSet: boolean;
overrideThemeFonts: boolean; overrideThemeFonts: boolean;
spellCheckEnabled: boolean; spellCheckEnabled: boolean;
autoFixConsistencyIssues: boolean; autoFixConsistencyIssues: boolean;