mirror of
				https://github.com/TriliumNext/Notes.git
				synced 2025-11-04 07:01:31 +08:00 
			
		
		
		
	chore(client/ts): port options
This commit is contained in:
		
							parent
							
								
									7e61af1cc3
								
							
						
					
					
						commit
						05529b84ab
					
				@ -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<string | null>((res) => appContext.triggerCommand("showPromptDialog", { ...props, callback: res }));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
 | 
			
		||||
@ -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<HTMLElement>;
 | 
			
		||||
 | 
			
		||||
@ -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 = `
 | 
			
		||||
<div class="options-section">
 | 
			
		||||
    <h4>${t("backup.automatic_backup")}</h4>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <p>${t("backup.automatic_backup_description")}</p>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <ul style="list-style: none">
 | 
			
		||||
        <li>
 | 
			
		||||
            <label>
 | 
			
		||||
@ -17,7 +18,7 @@ const TPL = `
 | 
			
		||||
                ${t("backup.enable_daily_backup")}
 | 
			
		||||
            </label>
 | 
			
		||||
        </li>
 | 
			
		||||
        <li>    
 | 
			
		||||
        <li>
 | 
			
		||||
            <label>
 | 
			
		||||
                <input type="checkbox" class="weekly-backup-enabled form-check-input">
 | 
			
		||||
                ${t("backup.enable_weekly_backup")}
 | 
			
		||||
@ -30,19 +31,19 @@ const TPL = `
 | 
			
		||||
            </label>
 | 
			
		||||
        </li>
 | 
			
		||||
    </ul>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <p>${t("backup.backup_recommendation")}</p>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="options-section">
 | 
			
		||||
    <h4>${t("backup.backup_now")}</h4>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <button class="backup-database-button btn">${t("backup.backup_database_now")}</button>
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
<div class="options-section">
 | 
			
		||||
    <h4>${t("backup.existing_backups")}</h4>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <table class="table table-stripped">
 | 
			
		||||
        <colgroup>
 | 
			
		||||
            <col width="33%" />
 | 
			
		||||
@ -61,14 +62,32 @@ const TPL = `
 | 
			
		||||
</div>
 | 
			
		||||
`;
 | 
			
		||||
 | 
			
		||||
// TODO: Deduplicate.
 | 
			
		||||
interface PostDatabaseResponse {
 | 
			
		||||
    backupFile: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: Deduplicate
 | 
			
		||||
interface Backup {
 | 
			
		||||
    filePath: string;
 | 
			
		||||
    mtime: number;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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() {
 | 
			
		||||
        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<PostDatabaseResponse>("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<Backup[]>("database/backups").then((backupFiles) => {
 | 
			
		||||
            this.$existingBackupList.empty();
 | 
			
		||||
 | 
			
		||||
            if (!backupFiles.length) {
 | 
			
		||||
@ -8,18 +8,18 @@ import toastService from "../../../services/toast.js";
 | 
			
		||||
const TPL = `
 | 
			
		||||
<div class="options-section">
 | 
			
		||||
    <h4>${t("etapi.title")}</h4>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <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>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <button type="button" class="create-etapi-token btn btn-sm">${t("etapi.create_token")}</button>
 | 
			
		||||
 | 
			
		||||
    <hr />
 | 
			
		||||
 | 
			
		||||
    <h5>${t("etapi.existing_tokens")}</h5>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <div class="no-tokens-yet">${t("etapi.no_tokens_yet")}</div>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <div style="overflow: auto; height: 500px;">
 | 
			
		||||
        <table class="tokens-table table table-stripped">
 | 
			
		||||
        <thead>
 | 
			
		||||
@ -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);
 | 
			
		||||
    }
 | 
			
		||||
</style>`;
 | 
			
		||||
 | 
			
		||||
// 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<PostTokensResponse>("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<Token[]>("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;
 | 
			
		||||
        }
 | 
			
		||||
@ -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 = `
 | 
			
		||||
<div class="options-section">
 | 
			
		||||
    <h4 class="password-heading">${t("password.heading")}</h4>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <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>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <form class="change-password-form">
 | 
			
		||||
        <div class="old-password-form-group form-group">
 | 
			
		||||
            <label for="old-password">${t("password.old_password")}</label>
 | 
			
		||||
            <input id="old-password" class="old-password form-control" type="password">
 | 
			
		||||
        </div>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        <div class="form-group">
 | 
			
		||||
            <label for="new-password1">${t("password.new_password")}</label>
 | 
			
		||||
            <input id="new-password1" class="new-password1 form-control" type="password">
 | 
			
		||||
        </div>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        <div class="form-group">
 | 
			
		||||
            <label for="new-password2">${t("password.new_password_confirmation")}</label>
 | 
			
		||||
            <input id="new-password2" class="new-password2 form-control" type="password">
 | 
			
		||||
        </div>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        <button class="save-password-button btn btn-primary">${t("password.change_password")}</button>
 | 
			
		||||
    </form>
 | 
			
		||||
</div>
 | 
			
		||||
@ -43,7 +44,23 @@ const TPL = `
 | 
			
		||||
    </div>
 | 
			
		||||
</div>`;
 | 
			
		||||
 | 
			
		||||
// TODO: Deduplicate
 | 
			
		||||
interface ChangePasswordResponse {
 | 
			
		||||
    success: boolean;
 | 
			
		||||
    message?: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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() {
 | 
			
		||||
        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<OptionMap>("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<ChangePasswordResponse>("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);
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
@ -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 = `
 | 
			
		||||
<div class="options-section shortcuts-options-section">
 | 
			
		||||
    <style>
 | 
			
		||||
        .shortcuts-options-section {
 | 
			
		||||
            display: flex; 
 | 
			
		||||
            flex-direction: column; 
 | 
			
		||||
            display: flex;
 | 
			
		||||
            flex-direction: column;
 | 
			
		||||
            height: 100%;
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        .shortcuts-table-container {
 | 
			
		||||
            overflow: auto; 
 | 
			
		||||
            flex-grow: 1; 
 | 
			
		||||
            flex-shrink: 1; 
 | 
			
		||||
        }    
 | 
			
		||||
        
 | 
			
		||||
            overflow: auto;
 | 
			
		||||
            flex-grow: 1;
 | 
			
		||||
            flex-shrink: 1;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        .shortcuts-options-buttons {
 | 
			
		||||
            display: flex; 
 | 
			
		||||
            display: flex;
 | 
			
		||||
            justify-content: space-between;
 | 
			
		||||
            margin: 15px 15px 0 15px;
 | 
			
		||||
        }
 | 
			
		||||
    </style>
 | 
			
		||||
 | 
			
		||||
    <h4>${t("shortcuts.keyboard_shortcuts")}</h4>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <p>
 | 
			
		||||
      ${t("shortcuts.multiple_shortcuts")}
 | 
			
		||||
      ${t("shortcuts.electron_documentation")}
 | 
			
		||||
    </p>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <div class="form-group">
 | 
			
		||||
        <input type="text" class="keyboard-shortcut-filter form-control" placeholder="${t("shortcuts.type_text_to_filter")}">
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <div class="shortcuts-table-container">
 | 
			
		||||
        <table class="keyboard-shortcut-table" cellpadding="10">
 | 
			
		||||
        <thead>
 | 
			
		||||
@ -50,15 +52,15 @@ const TPL = `
 | 
			
		||||
        <tbody></tbody>
 | 
			
		||||
        </table>
 | 
			
		||||
    </div>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <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-set-all-to-default btn">${t("shortcuts.set_all_to_default")}</button>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>`;
 | 
			
		||||
 | 
			
		||||
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<KeyboardShortcut[]>("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($('<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))
 | 
			
		||||
                        .append(
 | 
			
		||||
                            $("<td>").append(
 | 
			
		||||
                                $(`<input type="text" class="form-control">`)
 | 
			
		||||
                                    .val(action.effectiveShortcuts.join(", "))
 | 
			
		||||
                                    .val((action.effectiveShortcuts ?? []).join(", "))
 | 
			
		||||
                                    .attr("data-keyboard-action-name", action.actionName)
 | 
			
		||||
                                    .attr("data-default-keyboard-shortcuts", action.defaultShortcuts.join(", "))
 | 
			
		||||
                            )
 | 
			
		||||
                        )
 | 
			
		||||
                        .append($("<td>").text(action.defaultShortcuts.join(", ")))
 | 
			
		||||
                        .append($("<td>").text(action.description));
 | 
			
		||||
                        .append($("<td>").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))
 | 
			
		||||
                        )
 | 
			
		||||
                    );
 | 
			
		||||
@ -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 = `
 | 
			
		||||
<div class="options-section">
 | 
			
		||||
@ -29,11 +30,16 @@ const TPL_ELECTRON = `
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <p>${t("spellcheck.multiple_languages_info")}</p>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <p><strong>${t("spellcheck.available_language_codes_label")} </strong> <span class="available-language-codes"></span></p>
 | 
			
		||||
</div>`;
 | 
			
		||||
 | 
			
		||||
export default class SpellcheckOptions extends OptionsWidget {
 | 
			
		||||
 | 
			
		||||
    private $spellCheckEnabled!: JQuery<HTMLElement>;
 | 
			
		||||
    private $spellCheckLanguageCode!: JQuery<HTMLElement>;
 | 
			
		||||
    private $availableLanguageCodes!: JQuery<HTMLElement>;
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
@ -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 = `
 | 
			
		||||
<div class="options-section">
 | 
			
		||||
    <h4 style="margin-top: 0px;">${t("sync_2.config_title")}</h4>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <form class="sync-setup-form">
 | 
			
		||||
        <div class="form-group">
 | 
			
		||||
            <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>">
 | 
			
		||||
        </div>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        <div class="form-group">
 | 
			
		||||
            <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;">
 | 
			
		||||
        </div>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        <div class="form-group">
 | 
			
		||||
            <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>">
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
            <p><strong>${t("sync_2.note")}:</strong> ${t("sync_2.note_description")}</p>
 | 
			
		||||
            <p>${t("sync_2.special_value_description")}</p>
 | 
			
		||||
        </div>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
        <div style="display: flex; justify-content: space-between;">
 | 
			
		||||
            <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>
 | 
			
		||||
        </div>
 | 
			
		||||
    </form>
 | 
			
		||||
@ -36,13 +37,26 @@ const TPL = `
 | 
			
		||||
 | 
			
		||||
<div class="options-section">
 | 
			
		||||
    <h4>${t("sync_2.test_title")}</h4>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <p>${t("sync_2.test_description")}</p>
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    <button class="test-sync-button btn">${t("sync_2.test_button")}</button>
 | 
			
		||||
</div>`;
 | 
			
		||||
 | 
			
		||||
// TODO: Deduplicate
 | 
			
		||||
interface TestResponse {
 | 
			
		||||
    success: boolean;
 | 
			
		||||
    message: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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() {
 | 
			
		||||
        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<TestResponse>("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;
 | 
			
		||||
@ -69,6 +69,7 @@ export interface OptionDefinitions extends KeyboardShortcutsOptions<KeyboardActi
 | 
			
		||||
    firstDayOfWeek: number;
 | 
			
		||||
 | 
			
		||||
    initialized: boolean;
 | 
			
		||||
    isPasswordSet: boolean;
 | 
			
		||||
    overrideThemeFonts: boolean;
 | 
			
		||||
    spellCheckEnabled: boolean;
 | 
			
		||||
    autoFixConsistencyIssues: boolean;
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user