import { Eye, EyeOff, createElement } from "lucide"; import { type App, Modal, Notice, Platform, PluginSettingTab, Setting, requireApiVersion, } from "obsidian"; import type { TextComponent } from "obsidian"; import type { CipherMethodType, ConflictActionType, EmptyFolderCleanType, QRExportType, SUPPORTED_SERVICES_TYPE, SUPPORTED_SERVICES_TYPE_WITH_REMOTE_BASE_DIR, SyncDirectionType, WebdavAuthType, } from "./baseTypes"; import cloneDeep from "lodash/cloneDeep"; import { generateGoogleDriveSettingsPart } from "../pro/src/settingsGoogleDrive"; import { generateProSettingsPart } from "../pro/src/settingsPro"; import { API_VER_ENSURE_REQURL_OK, VALID_REQURL } from "./baseTypesObs"; import { messyConfigToNormal } from "./configPersist"; import { exportVaultProfilerResultsToFiles, exportVaultSyncPlansToFiles, } from "./debugMode"; import { DEFAULT_DROPBOX_CONFIG, getAuthUrlAndVerifier as getAuthUrlAndVerifierDropbox, sendAuthReq as sendAuthReqDropbox, setConfigBySuccessfullAuthInplace, } from "./fsDropbox"; import { getClient } from "./fsGetter"; import { DEFAULT_ONEDRIVE_CONFIG, getAuthUrlAndVerifier as getAuthUrlAndVerifierOnedrive, } from "./fsOnedrive"; import { simpleTransRemotePrefix } from "./fsS3"; import type { TransItemType } from "./i18n"; import { exportQrCodeUri, importQrCodeUri, parseUriByHand, } from "./importExport"; import { clearAllPrevSyncRecordByVault, clearAllSyncPlanRecords, destroyDBs, upsertLastSuccessSyncTimeByVault, } from "./localdb"; import type RemotelySavePlugin from "./main"; // unavoidable import { changeMobileStatusBar, checkHasSpecialCharForDir, stringToFragment, } from "./misc"; import { DEFAULT_PROFILER_CONFIG } from "./profiler"; class PasswordModal extends Modal { plugin: RemotelySavePlugin; newPassword: string; constructor(app: App, plugin: RemotelySavePlugin, newPassword: string) { super(app); this.plugin = plugin; this.newPassword = newPassword; } onOpen() { const { contentEl } = this; const t = (x: TransItemType, vars?: any) => { return this.plugin.i18n.t(x, vars); }; // contentEl.setText("Add Or change password."); contentEl.createEl("h2", { text: t("modal_password_title") }); t("modal_password_shortdesc") .split("\n") .forEach((val, idx) => { contentEl.createEl("p", { text: val, }); }); [ t("modal_password_attn1"), t("modal_password_attn2"), t("modal_password_attn3"), t("modal_password_attn4"), t("modal_password_attn5"), ].forEach((val, idx) => { if (idx < 3) { contentEl.createEl("p", { text: val, cls: "password-disclaimer", }); } else { contentEl.createEl("p", { text: val, }); } }); new Setting(contentEl) .addButton((button) => { button.setButtonText(t("modal_password_secondconfirm")); button.onClick(async () => { this.plugin.settings.password = this.newPassword; await this.plugin.saveSettings(); new Notice(t("modal_password_notice")); this.close(); }); button.setClass("password-second-confirm"); }) .addButton((button) => { button.setButtonText(t("goback")); button.onClick(() => { this.close(); }); }); } onClose() { const { contentEl } = this; contentEl.empty(); } } class EncryptionMethodModal extends Modal { plugin: RemotelySavePlugin; constructor(app: App, plugin: RemotelySavePlugin) { super(app); this.plugin = plugin; } onOpen() { const { contentEl } = this; const t = (x: TransItemType, vars?: any) => { return this.plugin.i18n.t(x, vars); }; // contentEl.setText("Add Or change password."); contentEl.createEl("h2", { text: t("modal_encryptionmethod_title") }); t("modal_encryptionmethod_shortdesc") .split("\n") .forEach((val, idx) => { contentEl.createEl("p", { text: stringToFragment(val), }); }); new Setting(contentEl).addButton((button) => { button.setButtonText(t("confirm")); button.onClick(async () => { this.close(); }); button.setClass("encryptionmethod-second-confirm"); }); } onClose() { const { contentEl } = this; contentEl.empty(); } } export class ChangeRemoteBaseDirModal extends Modal { readonly plugin: RemotelySavePlugin; readonly newRemoteBaseDir: string; readonly service: SUPPORTED_SERVICES_TYPE_WITH_REMOTE_BASE_DIR; constructor( app: App, plugin: RemotelySavePlugin, newRemoteBaseDir: string, service: SUPPORTED_SERVICES_TYPE_WITH_REMOTE_BASE_DIR ) { super(app); this.plugin = plugin; this.newRemoteBaseDir = newRemoteBaseDir; this.service = service; } onOpen() { const { contentEl } = this; const t = (x: TransItemType, vars?: any) => { return this.plugin.i18n.t(x, vars); }; contentEl.createEl("h2", { text: t("modal_remotebasedir_title") }); t("modal_remotebasedir_shortdesc") .split("\n") .forEach((val, idx) => { contentEl.createEl("p", { text: val, }); }); if ( this.newRemoteBaseDir === "" || this.newRemoteBaseDir === this.app.vault.getName() ) { new Setting(contentEl) .addButton((button) => { button.setButtonText( t("modal_remotebasedir_secondconfirm_vaultname") ); button.onClick(async () => { // in the settings, the value is reset to the special case "" this.plugin.settings[this.service].remoteBaseDir = ""; await this.plugin.saveSettings(); new Notice(t("modal_remotebasedir_notice")); this.close(); }); button.setClass("remotebasedir-second-confirm"); }) .addButton((button) => { button.setButtonText(t("goback")); button.onClick(() => { this.close(); }); }); } else if (checkHasSpecialCharForDir(this.newRemoteBaseDir)) { contentEl.createEl("p", { text: t("modal_remotebasedir_invaliddirhint"), }); new Setting(contentEl).addButton((button) => { button.setButtonText(t("goback")); button.onClick(() => { this.close(); }); }); } else { new Setting(contentEl) .addButton((button) => { button.setButtonText(t("modal_remotebasedir_secondconfirm_change")); button.onClick(async () => { this.plugin.settings[this.service].remoteBaseDir = this.newRemoteBaseDir; await this.plugin.saveSettings(); new Notice(t("modal_remotebasedir_notice")); this.close(); }); button.setClass("remotebasedir-second-confirm"); }) .addButton((button) => { button.setButtonText(t("goback")); button.onClick(() => { this.close(); }); }); } } onClose() { const { contentEl } = this; contentEl.empty(); } } /** * s3 is special and do not necessarily the same as others * thus a new Modal here */ class ChangeRemotePrefixModal extends Modal { readonly plugin: RemotelySavePlugin; readonly newRemotePrefix: string; constructor(app: App, plugin: RemotelySavePlugin, newRemotePrefix: string) { super(app); this.plugin = plugin; this.newRemotePrefix = newRemotePrefix; } onOpen() { const { contentEl } = this; const t = (x: TransItemType, vars?: any) => { return this.plugin.i18n.t(x, vars); }; contentEl.createEl("h2", { text: t("modal_remoteprefix_title") }); t("modal_remoteprefix_shortdesc") .split("\n") .forEach((val, idx) => { contentEl.createEl("p", { text: val, }); }); contentEl.createEl("p", { text: t("modal_remoteprefix_tosave", { prefix: this.newRemotePrefix }), }); if ( this.newRemotePrefix === "" || this.newRemotePrefix === this.app.vault.getName() ) { new Setting(contentEl) .addButton((button) => { button.setButtonText(t("modal_remoteprefix_secondconfirm_empty")); button.onClick(async () => { // in the settings, the value is reset to the special case "" this.plugin.settings.s3.remotePrefix = ""; await this.plugin.saveSettings(); new Notice(t("modal_remoteprefix_notice")); this.close(); }); button.setClass("remoteprefix-second-confirm"); }) .addButton((button) => { button.setButtonText(t("goback")); button.onClick(() => { this.close(); }); }); } else { new Setting(contentEl) .addButton((button) => { button.setButtonText(t("modal_remoteprefix_secondconfirm_change")); button.onClick(async () => { this.plugin.settings.s3.remotePrefix = this.newRemotePrefix; await this.plugin.saveSettings(); new Notice(t("modal_remoteprefix_notice")); this.close(); }); button.setClass("remoteprefix-second-confirm"); }) .addButton((button) => { button.setButtonText(t("goback")); button.onClick(() => { this.close(); }); }); } } onClose() { const { contentEl } = this; contentEl.empty(); } } class DropboxAuthModal extends Modal { readonly plugin: RemotelySavePlugin; readonly authDiv: HTMLDivElement; readonly revokeAuthDiv: HTMLDivElement; readonly revokeAuthSetting: Setting; constructor( app: App, plugin: RemotelySavePlugin, authDiv: HTMLDivElement, revokeAuthDiv: HTMLDivElement, revokeAuthSetting: Setting ) { super(app); this.plugin = plugin; this.authDiv = authDiv; this.revokeAuthDiv = revokeAuthDiv; this.revokeAuthSetting = revokeAuthSetting; } async onOpen() { const { contentEl } = this; const t = (x: TransItemType, vars?: any) => { return this.plugin.i18n.t(x, vars); }; let needManualPatse = false; const userAgent = window.navigator.userAgent.toLocaleLowerCase() || ""; // some users report that, // the Linux would open another instance Obsidian if jumping back, // so fallback to manual paste on Linux if ( Platform.isDesktopApp && !Platform.isMacOS && (/linux/.test(userAgent) || /ubuntu/.test(userAgent) || /debian/.test(userAgent) || /fedora/.test(userAgent) || /centos/.test(userAgent)) ) { needManualPatse = true; } const { authUrl, verifier } = await getAuthUrlAndVerifierDropbox( this.plugin.settings.dropbox.clientID, needManualPatse ); if (needManualPatse) { t("modal_dropboxauth_manualsteps") .split("\n") .forEach((val) => { contentEl.createEl("p", { text: val, }); }); } else { this.plugin.oauth2Info.verifier = verifier; t("modal_dropboxauth_autosteps") .split("\n") .forEach((val) => { contentEl.createEl("p", { text: val, }); }); } const div2 = contentEl.createDiv(); div2.createEl( "button", { text: t("modal_dropboxauth_copybutton"), }, (el) => { el.onclick = async () => { await navigator.clipboard.writeText(authUrl); new Notice(t("modal_dropboxauth_copynotice")); }; } ); contentEl.createEl("p").createEl("a", { href: authUrl, text: authUrl, }); if (needManualPatse) { let authCode = ""; new Setting(contentEl) .setName(t("modal_dropboxauth_maualinput")) .setDesc(t("modal_dropboxauth_maualinput_desc")) .addText((text) => text .setPlaceholder("") .setValue("") .onChange((val) => { authCode = val.trim(); }) ) .addButton(async (button) => { button.setButtonText(t("submit")); button.onClick(async () => { new Notice(t("modal_dropboxauth_maualinput_notice")); try { const authRes = await sendAuthReqDropbox( this.plugin.settings.dropbox.clientID, verifier, authCode, async (e: any) => { new Notice(t("protocol_dropbox_connect_fail")); new Notice(`${e}`); throw e; } ); const self = this; setConfigBySuccessfullAuthInplace( this.plugin.settings.dropbox, authRes!, () => self.plugin.saveSettings() ); const client = getClient( this.plugin.settings, this.app.vault.getName(), () => this.plugin.saveSettings() ); const username = await client.getUserDisplayName(); this.plugin.settings.dropbox.username = username; await this.plugin.saveSettings(); new Notice( t("modal_dropboxauth_maualinput_conn_succ", { username: username, }) ); this.authDiv.toggleClass( "dropbox-auth-button-hide", this.plugin.settings.dropbox.username !== "" ); this.revokeAuthDiv.toggleClass( "dropbox-revoke-auth-button-hide", this.plugin.settings.dropbox.username === "" ); this.revokeAuthSetting.setDesc( t("modal_dropboxauth_maualinput_conn_succ_revoke", { username: this.plugin.settings.dropbox.username, }) ); this.close(); } catch (err) { console.error(err); new Notice(t("modal_dropboxauth_maualinput_conn_fail")); } }); }); } } onClose() { const { contentEl } = this; contentEl.empty(); } } export class OnedriveAuthModal extends Modal { readonly plugin: RemotelySavePlugin; readonly authDiv: HTMLDivElement; readonly revokeAuthDiv: HTMLDivElement; readonly revokeAuthSetting: Setting; constructor( app: App, plugin: RemotelySavePlugin, authDiv: HTMLDivElement, revokeAuthDiv: HTMLDivElement, revokeAuthSetting: Setting ) { super(app); this.plugin = plugin; this.authDiv = authDiv; this.revokeAuthDiv = revokeAuthDiv; this.revokeAuthSetting = revokeAuthSetting; } async onOpen() { const { contentEl } = this; const { authUrl, verifier } = await getAuthUrlAndVerifierOnedrive( this.plugin.settings.onedrive.clientID, this.plugin.settings.onedrive.authority ); this.plugin.oauth2Info.verifier = verifier; const t = (x: TransItemType, vars?: any) => { return this.plugin.i18n.t(x, vars); }; t("modal_onedriveauth_shortdesc") .split("\n") .forEach((val) => { contentEl.createEl("p", { text: val, }); }); if (Platform.isLinux) { t("modal_onedriveauth_shortdesc_linux") .split("\n") .forEach((val) => { contentEl.createEl("p", { text: stringToFragment(val), }); }); } const div2 = contentEl.createDiv(); div2.createEl( "button", { text: t("modal_onedriveauth_copybutton"), }, (el) => { el.onclick = async () => { await navigator.clipboard.writeText(authUrl); new Notice(t("modal_onedriveauth_copynotice")); }; } ); contentEl.createEl("p").createEl("a", { href: authUrl, text: authUrl, }); } onClose() { const { contentEl } = this; contentEl.empty(); } } export class OnedriveRevokeAuthModal extends Modal { readonly plugin: RemotelySavePlugin; readonly authDiv: HTMLDivElement; readonly revokeAuthDiv: HTMLDivElement; constructor( app: App, plugin: RemotelySavePlugin, authDiv: HTMLDivElement, revokeAuthDiv: HTMLDivElement ) { super(app); this.plugin = plugin; this.authDiv = authDiv; this.revokeAuthDiv = revokeAuthDiv; } async onOpen() { const { contentEl } = this; const t = (x: TransItemType, vars?: any) => { return this.plugin.i18n.t(x, vars); }; contentEl.createEl("p", { text: t("modal_onedriverevokeauth_step1"), }); const consentUrl = "https://microsoft.com/consent"; contentEl.createEl("p").createEl("a", { href: consentUrl, text: consentUrl, }); contentEl.createEl("p", { text: t("modal_onedriverevokeauth_step2"), }); new Setting(contentEl) .setName(t("modal_onedriverevokeauth_clean")) .setDesc(t("modal_onedriverevokeauth_clean_desc")) .addButton(async (button) => { button.setButtonText(t("modal_onedriverevokeauth_clean_button")); button.onClick(async () => { try { this.plugin.settings.onedrive = JSON.parse( JSON.stringify(DEFAULT_ONEDRIVE_CONFIG) ); await this.plugin.saveSettings(); this.authDiv.toggleClass( "onedrive-auth-button-hide", this.plugin.settings.onedrive.username !== "" ); this.revokeAuthDiv.toggleClass( "onedrive-revoke-auth-button-hide", this.plugin.settings.onedrive.username === "" ); new Notice(t("modal_onedriverevokeauth_clean_notice")); this.close(); } catch (err) { console.error(err); new Notice(t("modal_onedriverevokeauth_clean_fail")); } }); }); } onClose() { const { contentEl } = this; contentEl.empty(); } } class SyncConfigDirModal extends Modal { plugin: RemotelySavePlugin; saveDropdownFunc: () => void; constructor( app: App, plugin: RemotelySavePlugin, saveDropdownFunc: () => void ) { super(app); this.plugin = plugin; this.saveDropdownFunc = saveDropdownFunc; } async onOpen() { const { contentEl } = this; const t = (x: TransItemType, vars?: any) => { return this.plugin.i18n.t(x, vars); }; t("modal_syncconfig_attn") .split("\n") .forEach((val) => { contentEl.createEl("p", { text: val, }); }); new Setting(contentEl) .addButton((button) => { button.setButtonText(t("modal_syncconfig_secondconfirm")); button.onClick(async () => { this.plugin.settings.syncConfigDir = true; await this.plugin.saveSettings(); this.saveDropdownFunc(); new Notice(t("modal_syncconfig_notice")); this.close(); }); }) .addButton((button) => { button.setButtonText(t("goback")); button.onClick(() => { this.close(); }); }); } onClose() { const { contentEl } = this; contentEl.empty(); } } class ExportSettingsQrCodeModal extends Modal { plugin: RemotelySavePlugin; exportType: QRExportType; constructor(app: App, plugin: RemotelySavePlugin, exportType: QRExportType) { super(app); this.plugin = plugin; this.exportType = exportType; } async onOpen() { const { contentEl } = this; const t = (x: TransItemType, vars?: any) => { return this.plugin.i18n.t(x, vars); }; const { rawUri, imgUri } = await exportQrCodeUri( this.plugin.settings, this.app.vault.getName(), this.plugin.manifest.version, this.exportType ); const div1 = contentEl.createDiv(); t("modal_qr_shortdesc") .split("\n") .forEach((val) => { div1.createEl("p", { text: val, }); }); const div2 = contentEl.createDiv(); div2.createEl( "button", { text: t("modal_qr_button"), }, (el) => { el.onclick = async () => { await navigator.clipboard.writeText(rawUri); new Notice(t("modal_qr_button_notice")); }; } ); const div3 = contentEl.createDiv(); div3.createEl( "img", { cls: "qrcode-img", }, async (el) => { el.src = imgUri; } ); } onClose() { const { contentEl } = this; contentEl.empty(); } } const getEyesElements = () => { const eyeEl = createElement(Eye); const eyeOffEl = createElement(EyeOff); return { eye: eyeEl.outerHTML, eyeOff: eyeOffEl.outerHTML, }; }; const wrapTextWithPasswordHide = (text: TextComponent) => { const { eye, eyeOff } = getEyesElements(); const hider = text.inputEl.insertAdjacentElement("afterend", createSpan())!; // the init type of hider is "hidden" === eyeOff === password hider.innerHTML = eyeOff; hider.addEventListener("click", (e) => { const isText = text.inputEl.getAttribute("type") === "text"; hider.innerHTML = isText ? eyeOff : eye; text.inputEl.setAttribute("type", isText ? "password" : "text"); text.inputEl.focus(); }); // the init type of text el is password text.inputEl.setAttribute("type", "password"); return text; }; export class RemotelySaveSettingTab extends PluginSettingTab { readonly plugin: RemotelySavePlugin; constructor(app: App, plugin: RemotelySavePlugin) { super(app, plugin); this.plugin = plugin; } display(): void { const { containerEl } = this; containerEl.style.setProperty("overflow-wrap", "break-word"); containerEl.empty(); const t = (x: TransItemType, vars?: any) => { return this.plugin.i18n.t(x, vars); }; containerEl.createEl("h1", { text: "Remotely Save" }); ////////////////////////////////////////////////// // below for service chooser (part 1/2) ////////////////////////////////////////////////// // we need to create the div in advance of any other service divs const serviceChooserDiv = containerEl.createDiv(); serviceChooserDiv.createEl("h2", { text: t("settings_chooseservice") }); ////////////////////////////////////////////////// // below for s3 ////////////////////////////////////////////////// const s3Div = containerEl.createEl("div", { cls: "s3-hide" }); s3Div.toggleClass("s3-hide", this.plugin.settings.serviceType !== "s3"); s3Div.createEl("h2", { text: t("settings_s3") }); const s3LongDescDiv = s3Div.createEl("div", { cls: "settings-long-desc" }); for (const c of [ t("settings_s3_disclaimer1"), t("settings_s3_disclaimer2"), ]) { s3LongDescDiv.createEl("p", { text: c, cls: "s3-disclaimer", }); } if (!VALID_REQURL) { s3LongDescDiv.createEl("p", { text: t("settings_s3_cors"), }); } s3LongDescDiv.createEl("p", { text: t("settings_s3_prod"), }); const s3LinksUl = s3LongDescDiv.createEl("ul"); s3LinksUl.createEl("li").createEl("a", { href: "https://docs.aws.amazon.com/general/latest/gr/s3.html", text: t("settings_s3_prod1"), }); s3LinksUl.createEl("li").createEl("a", { href: "https://docs.aws.amazon.com/sdk-for-javascript/v3/developer-guide/getting-your-credentials.html", text: t("settings_s3_prod2"), }); if (!VALID_REQURL) { s3LinksUl.createEl("li").createEl("a", { href: "https://docs.aws.amazon.com/AmazonS3/latest/userguide/enabling-cors-examples.html", text: t("settings_s3_prod3"), }); } new Setting(s3Div) .setName(t("settings_s3_endpoint")) .setDesc(t("settings_s3_endpoint")) .addText((text) => text .setPlaceholder("") .setValue(this.plugin.settings.s3.s3Endpoint) .onChange(async (value) => { this.plugin.settings.s3.s3Endpoint = value.trim(); await this.plugin.saveSettings(); }) ); new Setting(s3Div) .setName(t("settings_s3_region")) .setDesc(t("settings_s3_region_desc")) .addText((text) => text .setPlaceholder("") .setValue(`${this.plugin.settings.s3.s3Region}`) .onChange(async (value) => { this.plugin.settings.s3.s3Region = value.trim(); await this.plugin.saveSettings(); }) ); new Setting(s3Div) .setName(t("settings_s3_accesskeyid")) .setDesc(t("settings_s3_accesskeyid_desc")) .addText((text) => { wrapTextWithPasswordHide(text); text .setPlaceholder("") .setValue(`${this.plugin.settings.s3.s3AccessKeyID}`) .onChange(async (value) => { this.plugin.settings.s3.s3AccessKeyID = value.trim(); await this.plugin.saveSettings(); }); }); new Setting(s3Div) .setName(t("settings_s3_secretaccesskey")) .setDesc(t("settings_s3_secretaccesskey_desc")) .addText((text) => { wrapTextWithPasswordHide(text); text .setPlaceholder("") .setValue(`${this.plugin.settings.s3.s3SecretAccessKey}`) .onChange(async (value) => { this.plugin.settings.s3.s3SecretAccessKey = value.trim(); await this.plugin.saveSettings(); }); }); new Setting(s3Div) .setName(t("settings_s3_bucketname")) .setDesc(t("settings_s3_bucketname")) .addText((text) => text .setPlaceholder("") .setValue(`${this.plugin.settings.s3.s3BucketName}`) .onChange(async (value) => { this.plugin.settings.s3.s3BucketName = value.trim(); await this.plugin.saveSettings(); }) ); new Setting(s3Div) .setName(t("settings_s3_urlstyle")) .setDesc(t("settings_s3_urlstyle_desc")) .addDropdown((dropdown) => { dropdown.addOption( "virtualHostedStyle", "Virtual Hosted-Style (default)" ); dropdown.addOption("pathStyle", "Path-Style"); dropdown .setValue( this.plugin.settings.s3.forcePathStyle ? "pathStyle" : "virtualHostedStyle" ) .onChange(async (val: string) => { this.plugin.settings.s3.forcePathStyle = val === "pathStyle"; await this.plugin.saveSettings(); }); }); if (VALID_REQURL && !requireApiVersion(API_VER_ENSURE_REQURL_OK)) { new Setting(s3Div) .setName(t("settings_s3_bypasscorslocally")) .setDesc(t("settings_s3_bypasscorslocally_desc")) .addDropdown((dropdown) => { dropdown .addOption("disable", t("disable")) .addOption("enable", t("enable")); dropdown .setValue( `${ this.plugin.settings.s3.bypassCorsLocally ? "enable" : "disable" }` ) .onChange(async (value) => { if (value === "enable") { this.plugin.settings.s3.bypassCorsLocally = true; } else { this.plugin.settings.s3.bypassCorsLocally = false; } await this.plugin.saveSettings(); }); }); } new Setting(s3Div) .setName(t("settings_s3_parts")) .setDesc(t("settings_s3_parts_desc")) .addDropdown((dropdown) => { dropdown.addOption("1", "1"); dropdown.addOption("2", "2"); dropdown.addOption("3", "3"); dropdown.addOption("5", "5"); dropdown.addOption("10", "10"); dropdown.addOption("15", "15"); dropdown.addOption("20", "20 (default)"); dropdown .setValue(`${this.plugin.settings.s3.partsConcurrency}`) .onChange(async (val) => { const realVal = Number.parseInt(val); this.plugin.settings.s3.partsConcurrency = realVal; await this.plugin.saveSettings(); }); }); new Setting(s3Div) .setName(t("settings_s3_accuratemtime")) .setDesc(t("settings_s3_accuratemtime_desc")) .addDropdown((dropdown) => { dropdown .addOption("disable", t("disable")) .addOption("enable", t("enable")); dropdown .setValue( `${this.plugin.settings.s3.useAccurateMTime ? "enable" : "disable"}` ) .onChange(async (val) => { if (val === "enable") { this.plugin.settings.s3.useAccurateMTime = true; } else { this.plugin.settings.s3.useAccurateMTime = false; } await this.plugin.saveSettings(); }); }); let newS3RemotePrefix = this.plugin.settings.s3.remotePrefix || ""; new Setting(s3Div) .setName(t("settings_remoteprefix")) .setDesc(t("settings_remoteprefix_desc")) .addText((text) => text .setPlaceholder("") .setValue(newS3RemotePrefix) .onChange((value) => { newS3RemotePrefix = simpleTransRemotePrefix(value.trim()); }) ) .addButton((button) => { button.setButtonText(t("confirm")); button.onClick(() => { new ChangeRemotePrefixModal( this.app, this.plugin, simpleTransRemotePrefix(newS3RemotePrefix.trim()) ).open(); }); }); new Setting(s3Div) .setName(t("settings_s3_reverse_proxy_no_sign_url")) .setDesc(t("settings_s3_reverse_proxy_no_sign_url_desc")) .addText((text) => text .setPlaceholder("") .setValue(this.plugin.settings.s3.reverseProxyNoSignUrl ?? "") .onChange(async (value) => { this.plugin.settings.s3.reverseProxyNoSignUrl = value.trim(); await this.plugin.saveSettings(); }) ); new Setting(s3Div) .setName(t("settings_s3_generatefolderobject")) .setDesc(t("settings_s3_generatefolderobject_desc")) .addDropdown((dropdown) => { dropdown .addOption( "notgenerate", t("settings_s3_generatefolderobject_notgenerate") ) .addOption( "generate", t("settings_s3_generatefolderobject_generate") ); dropdown .setValue( `${ this.plugin.settings.s3.generateFolderObject ? "generate" : "notgenerate" }` ) .onChange(async (val) => { if (val === "generate") { this.plugin.settings.s3.generateFolderObject = true; } else { this.plugin.settings.s3.generateFolderObject = false; } await this.plugin.saveSettings(); }); }); new Setting(s3Div) .setName(t("settings_checkonnectivity")) .setDesc(t("settings_checkonnectivity_desc")) .addButton(async (button) => { button.setButtonText(t("settings_checkonnectivity_button")); button.onClick(async () => { new Notice(t("settings_checkonnectivity_checking")); const client = getClient( this.plugin.settings, this.app.vault.getName(), () => this.plugin.saveSettings() ); const errors = { msg: "" }; const res = await client.checkConnect((err: any) => { errors.msg = err; }); if (res) { new Notice(t("settings_s3_connect_succ")); } else { new Notice(t("settings_s3_connect_fail")); new Notice(errors.msg); } }); }); ////////////////////////////////////////////////// // below for dropbpx ////////////////////////////////////////////////// const dropboxDiv = containerEl.createEl("div", { cls: "dropbox-hide" }); dropboxDiv.toggleClass( "dropbox-hide", this.plugin.settings.serviceType !== "dropbox" ); dropboxDiv.createEl("h2", { text: t("settings_dropbox") }); const dropboxLongDescDiv = dropboxDiv.createEl("div", { cls: "settings-long-desc", }); for (const c of [ t("settings_dropbox_disclaimer1"), t("settings_dropbox_disclaimer2"), ]) { dropboxLongDescDiv.createEl("p", { text: c, cls: "dropbox-disclaimer", }); } dropboxLongDescDiv.createEl("p", { text: t("settings_dropbox_folder", { pluginID: this.plugin.manifest.id, remoteBaseDir: this.plugin.settings.dropbox.remoteBaseDir || this.app.vault.getName(), }), }); const dropboxSelectAuthDiv = dropboxDiv.createDiv(); const dropboxAuthDiv = dropboxSelectAuthDiv.createDiv({ cls: "dropbox-auth-button-hide settings-auth-related", }); const dropboxRevokeAuthDiv = dropboxSelectAuthDiv.createDiv({ cls: "dropbox-revoke-auth-button-hide settings-auth-related", }); const dropboxRevokeAuthSetting = new Setting(dropboxRevokeAuthDiv) .setName(t("settings_dropbox_revoke")) .setDesc( t("settings_dropbox_revoke_desc", { username: this.plugin.settings.dropbox.username, }) ) .addButton(async (button) => { button.setButtonText(t("settings_dropbox_revoke_button")); button.onClick(async () => { try { const client = getClient( this.plugin.settings, this.app.vault.getName(), () => this.plugin.saveSettings() ); await client.revokeAuth(); this.plugin.settings.dropbox = JSON.parse( JSON.stringify(DEFAULT_DROPBOX_CONFIG) ); await this.plugin.saveSettings(); dropboxAuthDiv.toggleClass( "dropbox-auth-button-hide", this.plugin.settings.dropbox.username !== "" ); dropboxRevokeAuthDiv.toggleClass( "dropbox-revoke-auth-button-hide", this.plugin.settings.dropbox.username === "" ); new Notice(t("settings_dropbox_revoke_notice")); } catch (err) { console.error(err); new Notice(t("settings_dropbox_revoke_noticeerr")); } }); }); new Setting(dropboxRevokeAuthDiv) .setName(t("settings_dropbox_clearlocal")) .setDesc(t("settings_dropbox_clearlocal_desc")) .addButton(async (button) => { button.setButtonText(t("settings_dropbox_clearlocal_button")); button.onClick(async () => { this.plugin.settings.dropbox = JSON.parse( JSON.stringify(DEFAULT_DROPBOX_CONFIG) ); await this.plugin.saveSettings(); dropboxAuthDiv.toggleClass( "dropbox-auth-button-hide", this.plugin.settings.dropbox.username !== "" ); dropboxRevokeAuthDiv.toggleClass( "dropbox-revoke-auth-button-hide", this.plugin.settings.dropbox.username === "" ); new Notice(t("settings_dropbox_clearlocal_notice")); }); }); new Setting(dropboxAuthDiv) .setName(t("settings_dropbox_auth")) .setDesc(t("settings_dropbox_auth_desc")) .addButton(async (button) => { button.setButtonText(t("settings_dropbox_auth_button")); button.onClick(async () => { const modal = new DropboxAuthModal( this.app, this.plugin, dropboxAuthDiv, dropboxRevokeAuthDiv, dropboxRevokeAuthSetting ); this.plugin.oauth2Info.helperModal = modal; this.plugin.oauth2Info.authDiv = dropboxAuthDiv; this.plugin.oauth2Info.revokeDiv = dropboxRevokeAuthDiv; this.plugin.oauth2Info.revokeAuthSetting = dropboxRevokeAuthSetting; modal.open(); }); }); dropboxAuthDiv.toggleClass( "dropbox-auth-button-hide", this.plugin.settings.dropbox.username !== "" ); dropboxRevokeAuthDiv.toggleClass( "dropbox-revoke-auth-button-hide", this.plugin.settings.dropbox.username === "" ); let newDropboxRemoteBaseDir = this.plugin.settings.dropbox.remoteBaseDir || ""; new Setting(dropboxDiv) .setName(t("settings_remotebasedir")) .setDesc(t("settings_remotebasedir_desc")) .addText((text) => text .setPlaceholder(this.app.vault.getName()) .setValue(newDropboxRemoteBaseDir) .onChange((value) => { newDropboxRemoteBaseDir = value.trim(); }) ) .addButton((button) => { button.setButtonText(t("confirm")); button.onClick(() => { new ChangeRemoteBaseDirModal( this.app, this.plugin, newDropboxRemoteBaseDir, "dropbox" ).open(); }); }); new Setting(dropboxDiv) .setName(t("settings_checkonnectivity")) .setDesc(t("settings_checkonnectivity_desc")) .addButton(async (button) => { button.setButtonText(t("settings_checkonnectivity_button")); button.onClick(async () => { new Notice(t("settings_checkonnectivity_checking")); const client = getClient( this.plugin.settings, this.app.vault.getName(), () => this.plugin.saveSettings() ); const errors = { msg: "" }; const res = await client.checkConnect((err: any) => { errors.msg = `${err}`; }); if (res) { new Notice(t("settings_dropbox_connect_succ")); } else { new Notice(t("settings_dropbox_connect_fail")); new Notice(errors.msg); } }); }); ////////////////////////////////////////////////// // below for onedrive ////////////////////////////////////////////////// const onedriveDiv = containerEl.createEl("div", { cls: "onedrive-hide" }); onedriveDiv.toggleClass( "onedrive-hide", this.plugin.settings.serviceType !== "onedrive" ); onedriveDiv.createEl("h2", { text: t("settings_onedrive") }); const onedriveLongDescDiv = onedriveDiv.createEl("div", { cls: "settings-long-desc", }); for (const c of [ t("settings_onedrive_disclaimer1"), t("settings_onedrive_disclaimer2"), ]) { onedriveLongDescDiv.createEl("p", { text: c, cls: "onedrive-disclaimer", }); } onedriveLongDescDiv.createEl("p", { text: t("settings_onedrive_folder", { pluginID: this.plugin.manifest.id, remoteBaseDir: this.plugin.settings.onedrive.remoteBaseDir || this.app.vault.getName(), }), }); onedriveLongDescDiv.createEl("p", { text: t("settings_onedrive_nobiz"), }); const onedriveSelectAuthDiv = onedriveDiv.createDiv(); const onedriveAuthDiv = onedriveSelectAuthDiv.createDiv({ cls: "onedrive-auth-button-hide settings-auth-related", }); const onedriveRevokeAuthDiv = onedriveSelectAuthDiv.createDiv({ cls: "onedrive-revoke-auth-button-hide settings-auth-related", }); const onedriveRevokeAuthSetting = new Setting(onedriveRevokeAuthDiv) .setName(t("settings_onedrive_revoke")) .setDesc( t("settings_onedrive_revoke_desc", { username: this.plugin.settings.onedrive.username, }) ) .addButton(async (button) => { button.setButtonText(t("settings_onedrive_revoke_button")); button.onClick(async () => { new OnedriveRevokeAuthModal( this.app, this.plugin, onedriveAuthDiv, onedriveRevokeAuthDiv ).open(); }); }); new Setting(onedriveAuthDiv) .setName(t("settings_onedrive_auth")) .setDesc(t("settings_onedrive_auth_desc")) .addButton(async (button) => { button.setButtonText(t("settings_onedrive_auth_button")); button.onClick(async () => { const modal = new OnedriveAuthModal( this.app, this.plugin, onedriveAuthDiv, onedriveRevokeAuthDiv, onedriveRevokeAuthSetting ); this.plugin.oauth2Info.helperModal = modal; this.plugin.oauth2Info.authDiv = onedriveAuthDiv; this.plugin.oauth2Info.revokeDiv = onedriveRevokeAuthDiv; this.plugin.oauth2Info.revokeAuthSetting = onedriveRevokeAuthSetting; modal.open(); }); }); onedriveAuthDiv.toggleClass( "onedrive-auth-button-hide", this.plugin.settings.onedrive.username !== "" ); onedriveRevokeAuthDiv.toggleClass( "onedrive-revoke-auth-button-hide", this.plugin.settings.onedrive.username === "" ); let newOnedriveRemoteBaseDir = this.plugin.settings.onedrive.remoteBaseDir || ""; new Setting(onedriveDiv) .setName(t("settings_remotebasedir")) .setDesc(t("settings_remotebasedir_desc")) .addText((text) => text .setPlaceholder(this.app.vault.getName()) .setValue(newOnedriveRemoteBaseDir) .onChange((value) => { newOnedriveRemoteBaseDir = value.trim(); }) ) .addButton((button) => { button.setButtonText(t("confirm")); button.onClick(() => { new ChangeRemoteBaseDirModal( this.app, this.plugin, newOnedriveRemoteBaseDir, "onedrive" ).open(); }); }); new Setting(onedriveDiv) .setName(t("settings_onedrive_emptyfile")) .setDesc(t("settings_onedrive_emptyfile_desc")) .addDropdown(async (dropdown) => { dropdown .addOption("skip", t("settings_onedrive_emptyfile_skip")) .addOption("error", t("settings_onedrive_emptyfile_error")) .setValue(this.plugin.settings.onedrive.emptyFile) .onChange(async (val) => { this.plugin.settings.onedrive.emptyFile = val as any; await this.plugin.saveSettings(); }); }); new Setting(onedriveDiv) .setName(t("settings_checkonnectivity")) .setDesc(t("settings_checkonnectivity_desc")) .addButton(async (button) => { button.setButtonText(t("settings_checkonnectivity_button")); button.onClick(async () => { new Notice(t("settings_checkonnectivity_checking")); const client = getClient( this.plugin.settings, this.app.vault.getName(), () => this.plugin.saveSettings() ); const errors = { msg: "" }; const res = await client.checkConnect((err: any) => { errors.msg = `${err}`; }); if (res) { new Notice(t("settings_onedrive_connect_succ")); } else { new Notice(t("settings_onedrive_connect_fail")); new Notice(errors.msg); } }); }); ////////////////////////////////////////////////// // below for webdav ////////////////////////////////////////////////// const webdavDiv = containerEl.createEl("div", { cls: "webdav-hide" }); webdavDiv.toggleClass( "webdav-hide", this.plugin.settings.serviceType !== "webdav" ); webdavDiv.createEl("h2", { text: t("settings_webdav") }); const webdavLongDescDiv = webdavDiv.createEl("div", { cls: "settings-long-desc", }); webdavLongDescDiv.createEl("p", { text: t("settings_webdav_disclaimer1"), cls: "webdav-disclaimer", }); if (!VALID_REQURL) { webdavLongDescDiv.createEl("p", { text: t("settings_webdav_cors_os"), }); webdavLongDescDiv.createEl("p", { text: t("settings_webdav_cors"), }); } webdavLongDescDiv.createEl("p", { text: t("settings_webdav_folder", { remoteBaseDir: this.plugin.settings.webdav.remoteBaseDir || this.app.vault.getName(), }), }); new Setting(webdavDiv) .setName(t("settings_webdav_addr")) .setDesc(t("settings_webdav_addr_desc")) .addText((text) => text .setPlaceholder("") .setValue(this.plugin.settings.webdav.address) .onChange(async (value) => { this.plugin.settings.webdav.address = value.trim(); // deprecate auto on 20240116, force to manual_1 if ( this.plugin.settings.webdav.depth === "auto" || this.plugin.settings.webdav.depth === "auto_1" || this.plugin.settings.webdav.depth === "auto_infinity" || this.plugin.settings.webdav.depth === "auto_unknown" ) { this.plugin.settings.webdav.depth = "manual_1"; } // normally saved await this.plugin.saveSettings(); }) ); new Setting(webdavDiv) .setName(t("settings_webdav_user")) .setDesc(t("settings_webdav_user_desc")) .addText((text) => { wrapTextWithPasswordHide(text); text .setPlaceholder("") .setValue(this.plugin.settings.webdav.username) .onChange(async (value) => { this.plugin.settings.webdav.username = value.trim(); // deprecate auto on 20240116, force to manual_1 if ( this.plugin.settings.webdav.depth === "auto" || this.plugin.settings.webdav.depth === "auto_1" || this.plugin.settings.webdav.depth === "auto_infinity" || this.plugin.settings.webdav.depth === "auto_unknown" ) { this.plugin.settings.webdav.depth = "manual_1"; } await this.plugin.saveSettings(); }); }); new Setting(webdavDiv) .setName(t("settings_webdav_password")) .setDesc(t("settings_webdav_password_desc")) .addText((text) => { wrapTextWithPasswordHide(text); text .setPlaceholder("") .setValue(this.plugin.settings.webdav.password) .onChange(async (value) => { this.plugin.settings.webdav.password = value.trim(); // deprecate auto on 20240116, force to manual_1 if ( this.plugin.settings.webdav.depth === "auto" || this.plugin.settings.webdav.depth === "auto_1" || this.plugin.settings.webdav.depth === "auto_infinity" || this.plugin.settings.webdav.depth === "auto_unknown" ) { this.plugin.settings.webdav.depth = "manual_1"; } await this.plugin.saveSettings(); }); }); new Setting(webdavDiv) .setName(t("settings_webdav_auth")) .setDesc(t("settings_webdav_auth_desc")) .addDropdown(async (dropdown) => { dropdown.addOption("basic", "basic"); if (VALID_REQURL) { dropdown.addOption("digest", "digest"); } // new version config, copied to old version, we need to reset it if (!VALID_REQURL && this.plugin.settings.webdav.authType !== "basic") { this.plugin.settings.webdav.authType = "basic"; await this.plugin.saveSettings(); } dropdown .setValue(this.plugin.settings.webdav.authType) .onChange(async (val) => { this.plugin.settings.webdav.authType = val as WebdavAuthType; await this.plugin.saveSettings(); }); }); new Setting(webdavDiv) .setName(t("settings_webdav_depth")) .setDesc(t("settings_webdav_depth_desc")) .addDropdown((dropdown) => { dropdown.addOption("manual_1", t("settings_webdav_depth_1")); dropdown.addOption("manual_infinity", t("settings_webdav_depth_inf")); dropdown .setValue(this.plugin.settings.webdav.depth || "manual_1") .onChange(async (val) => { if (val === "manual_1") { this.plugin.settings.webdav.depth = "manual_1"; this.plugin.settings.webdav.manualRecursive = true; } else if (val === "manual_infinity") { this.plugin.settings.webdav.depth = "manual_infinity"; this.plugin.settings.webdav.manualRecursive = false; } // normally save await this.plugin.saveSettings(); }); }); let newWebdavRemoteBaseDir = this.plugin.settings.webdav.remoteBaseDir || ""; new Setting(webdavDiv) .setName(t("settings_remotebasedir")) .setDesc(t("settings_remotebasedir_desc")) .addText((text) => text .setPlaceholder(this.app.vault.getName()) .setValue(newWebdavRemoteBaseDir) .onChange((value) => { newWebdavRemoteBaseDir = value.trim(); }) ) .addButton((button) => { button.setButtonText(t("confirm")); button.onClick(() => { new ChangeRemoteBaseDirModal( this.app, this.plugin, newWebdavRemoteBaseDir, "webdav" ).open(); }); }); new Setting(webdavDiv) .setName(t("settings_checkonnectivity")) .setDesc(t("settings_checkonnectivity_desc")) .addButton(async (button) => { button.setButtonText(t("settings_checkonnectivity_button")); button.onClick(async () => { new Notice(t("settings_checkonnectivity_checking")); const client = getClient( this.plugin.settings, this.app.vault.getName(), () => this.plugin.saveSettings() ); const errors = { msg: "" }; const res = await client.checkConnect((err: any) => { errors.msg = `${err}`; }); if (res) { new Notice(t("settings_webdav_connect_succ")); } else { if (VALID_REQURL) { new Notice(t("settings_webdav_connect_fail")); } else { new Notice(t("settings_webdav_connect_fail_withcors")); } new Notice(errors.msg); } }); }); ////////////////////////////////////////////////// // below for webdis ////////////////////////////////////////////////// const webdisDiv = containerEl.createEl("div", { cls: "webdis-hide" }); webdisDiv.toggleClass( "webdis-hide", this.plugin.settings.serviceType !== "webdis" ); webdisDiv.createEl("h2", { text: t("settings_webdis") }); const webdisLongDescDiv = webdisDiv.createEl("div", { cls: "settings-long-desc", }); for (const c of [ t("settings_webdis_disclaimer1"), t("settings_webdis_disclaimer2"), ]) { webdisLongDescDiv.createEl("p", { text: c, cls: "webdis-disclaimer", }); } webdisLongDescDiv.createEl("p", { text: t("settings_webdis_folder", { remoteBaseDir: this.plugin.settings.webdis.remoteBaseDir || this.app.vault.getName(), }), }); new Setting(webdisDiv) .setName(t("settings_webdis_addr")) .setDesc(t("settings_webdis_addr_desc")) .addText((text) => text .setPlaceholder("https://") .setValue(this.plugin.settings.webdis.address) .onChange(async (value) => { this.plugin.settings.webdis.address = value.trim(); // normally saved await this.plugin.saveSettings(); }) ); new Setting(webdisDiv) .setName(t("settings_webdis_user")) .setDesc(t("settings_webdis_user_desc")) .addText((text) => { wrapTextWithPasswordHide(text); text .setPlaceholder("") .setValue(this.plugin.settings.webdis.username ?? "") .onChange(async (value) => { this.plugin.settings.webdis.username = (value ?? "").trim(); await this.plugin.saveSettings(); }); }); new Setting(webdisDiv) .setName(t("settings_webdis_password")) .setDesc(t("settings_webdis_password_desc")) .addText((text) => { wrapTextWithPasswordHide(text); text .setPlaceholder("") .setValue(this.plugin.settings.webdis.password ?? "") .onChange(async (value) => { this.plugin.settings.webdis.password = (value ?? "").trim(); await this.plugin.saveSettings(); }); }); let newWebdisRemoteBaseDir = this.plugin.settings.webdis.remoteBaseDir || ""; new Setting(webdisDiv) .setName(t("settings_remotebasedir")) .setDesc(t("settings_remotebasedir_desc")) .addText((text) => text .setPlaceholder(this.app.vault.getName()) .setValue(newWebdisRemoteBaseDir) .onChange((value) => { newWebdisRemoteBaseDir = value.trim(); }) ) .addButton((button) => { button.setButtonText(t("confirm")); button.onClick(() => { new ChangeRemoteBaseDirModal( this.app, this.plugin, newWebdisRemoteBaseDir, "webdis" ).open(); }); }); new Setting(webdisDiv) .setName(t("settings_checkonnectivity")) .setDesc(t("settings_checkonnectivity_desc")) .addButton(async (button) => { button.setButtonText(t("settings_checkonnectivity_button")); button.onClick(async () => { new Notice(t("settings_checkonnectivity_checking")); const client = getClient( this.plugin.settings, this.app.vault.getName(), () => this.plugin.saveSettings() ); const errors = { msg: "" }; const res = await client.checkConnect((err: any) => { errors.msg = `${err}`; }); if (res) { new Notice(t("settings_webdis_connect_succ")); } else { new Notice(t("settings_webdis_connect_fail")); new Notice(errors.msg); } }); }); ////////////////////////////////////////////////// // below for googledrive ////////////////////////////////////////////////// const { googleDriveDiv, googleDriveAllowedToUsedDiv, googleDriveNotShowUpHintSetting, } = generateGoogleDriveSettingsPart( containerEl, t, this.app, this.plugin, () => this.plugin.saveSettings() ); ////////////////////////////////////////////////// // below for general chooser (part 2/2) ////////////////////////////////////////////////// // we need to create chooser // after all service-div-s being created new Setting(serviceChooserDiv) .setName(t("settings_chooseservice")) .setDesc(t("settings_chooseservice_desc")) .addDropdown(async (dropdown) => { dropdown.addOption("s3", t("settings_chooseservice_s3")); dropdown.addOption("dropbox", t("settings_chooseservice_dropbox")); dropdown.addOption("webdav", t("settings_chooseservice_webdav")); dropdown.addOption("onedrive", t("settings_chooseservice_onedrive")); dropdown.addOption("webdis", t("settings_chooseservice_webdis")); dropdown.addOption( "googledrive", t("settings_chooseservice_googledrive") ); dropdown .setValue(this.plugin.settings.serviceType) .onChange(async (val) => { this.plugin.settings.serviceType = val as SUPPORTED_SERVICES_TYPE; s3Div.toggleClass( "s3-hide", this.plugin.settings.serviceType !== "s3" ); dropboxDiv.toggleClass( "dropbox-hide", this.plugin.settings.serviceType !== "dropbox" ); onedriveDiv.toggleClass( "onedrive-hide", this.plugin.settings.serviceType !== "onedrive" ); webdavDiv.toggleClass( "webdav-hide", this.plugin.settings.serviceType !== "webdav" ); webdisDiv.toggleClass( "webdis-hide", this.plugin.settings.serviceType !== "webdis" ); googleDriveDiv.toggleClass( "googledrive-hide", this.plugin.settings.serviceType !== "googledrive" ); await this.plugin.saveSettings(); }); }); ////////////////////////////////////////////////// // below for basic settings ////////////////////////////////////////////////// const basicDiv = containerEl.createEl("div"); basicDiv.createEl("h2", { text: t("settings_basic") }); let newPassword = `${this.plugin.settings.password}`; new Setting(basicDiv) .setName(t("settings_password")) .setDesc(t("settings_password_desc")) .addText((text) => { wrapTextWithPasswordHide(text); text .setPlaceholder("") .setValue(`${this.plugin.settings.password}`) .onChange(async (value) => { newPassword = value.trim(); }); }) .addButton(async (button) => { button.setButtonText(t("confirm")); button.onClick(async () => { new PasswordModal(this.app, this.plugin, newPassword).open(); }); }); new Setting(basicDiv) .setName(t("settings_encryptionmethod")) .setDesc(stringToFragment(t("settings_encryptionmethod_desc"))) .addDropdown((dropdown) => { dropdown .addOption("rclone-base64", t("settings_encryptionmethod_rclone")) .addOption("openssl-base64", t("settings_encryptionmethod_openssl")) .setValue(this.plugin.settings.encryptionMethod ?? "rclone-base64") .onChange(async (val: string) => { this.plugin.settings.encryptionMethod = val as CipherMethodType; await this.plugin.saveSettings(); if (this.plugin.settings.password !== "") { new EncryptionMethodModal(this.app, this.plugin).open(); } }); }); new Setting(basicDiv) .setName(t("settings_autorun")) .setDesc(t("settings_autorun_desc")) .addDropdown((dropdown) => { dropdown.addOption("-1", t("settings_autorun_notset")); dropdown.addOption(`${1000 * 60 * 1}`, t("settings_autorun_1min")); dropdown.addOption(`${1000 * 60 * 5}`, t("settings_autorun_5min")); dropdown.addOption(`${1000 * 60 * 10}`, t("settings_autorun_10min")); dropdown.addOption(`${1000 * 60 * 30}`, t("settings_autorun_30min")); dropdown .setValue(`${this.plugin.settings.autoRunEveryMilliseconds}`) .onChange(async (val: string) => { const realVal = Number.parseInt(val); this.plugin.settings.autoRunEveryMilliseconds = realVal; await this.plugin.saveSettings(); if ( (realVal === undefined || realVal === null || realVal <= 0) && this.plugin.autoRunIntervalID !== undefined ) { // clear window.clearInterval(this.plugin.autoRunIntervalID); this.plugin.autoRunIntervalID = undefined; } else if ( realVal !== undefined && realVal !== null && realVal > 0 ) { const intervalID = window.setInterval(() => { console.info("auto run from settings.ts"); this.plugin.syncRun("auto"); }, realVal); this.plugin.autoRunIntervalID = intervalID; this.plugin.registerInterval(intervalID); } }); }); new Setting(basicDiv) .setName(t("settings_runoncestartup")) .setDesc(t("settings_runoncestartup_desc")) .addDropdown((dropdown) => { dropdown.addOption("-1", t("settings_runoncestartup_notset")); dropdown.addOption( `${1000 * 1 * 1}`, t("settings_runoncestartup_1sec") ); dropdown.addOption( `${1000 * 10 * 1}`, t("settings_runoncestartup_10sec") ); dropdown.addOption( `${1000 * 30 * 1}`, t("settings_runoncestartup_30sec") ); dropdown .setValue(`${this.plugin.settings.initRunAfterMilliseconds}`) .onChange(async (val: string) => { const realVal = Number.parseInt(val); this.plugin.settings.initRunAfterMilliseconds = realVal; await this.plugin.saveSettings(); }); }); new Setting(basicDiv) .setName(t("settings_synconsave")) .setDesc(t("settings_synconsave_desc")) .addDropdown((dropdown) => { dropdown.addOption("-1", t("settings_synconsave_disable")); dropdown.addOption("1000", t("settings_synconsave_enable")); // for backward compatibility, we need to use a number representing seconds let syncOnSaveEnabled = false; if ((this.plugin.settings.syncOnSaveAfterMilliseconds ?? -1) > 0) { syncOnSaveEnabled = true; } dropdown .setValue(`${syncOnSaveEnabled ? "1000" : "-1"}`) .onChange(async (val: string) => { this.plugin.settings.syncOnSaveAfterMilliseconds = Number.parseInt(val); await this.plugin.saveSettings(); this.plugin.toggleSyncOnSaveIfSet(); }); }); new Setting(basicDiv) .setName(t("settings_skiplargefiles")) .setDesc(t("settings_skiplargefiles_desc")) .addDropdown((dropdown) => { dropdown.addOption("-1", t("settings_skiplargefiles_notset")); const mbs = [1, 5, 10, 20, 50, 100, 200, 500, 1000]; for (const mb of mbs) { dropdown.addOption(`${mb * 1000 * 1000}`, `${mb} MB`); } dropdown .setValue(`${this.plugin.settings.skipSizeLargerThan}`) .onChange(async (val) => { this.plugin.settings.skipSizeLargerThan = Number.parseInt(val); await this.plugin.saveSettings(); }); }); // custom status bar items is not supported on mobile if (!Platform.isMobileApp) { new Setting(basicDiv) .setName(t("settings_enablestatusbar_info")) .setDesc(t("settings_enablestatusbar_info_desc")) .addToggle((toggle) => { toggle .setValue(this.plugin.settings.enableStatusBarInfo ?? false) .onChange(async (val) => { this.plugin.settings.enableStatusBarInfo = val; await this.plugin.saveSettings(); new Notice(t("settings_enablestatusbar_reloadrequired_notice")); }); }); new Setting(basicDiv) .setName(t("settings_resetstatusbar_time")) .setDesc(t("settings_resetstatusbar_time_desc")) .addButton((button) => { button.setButtonText(t("settings_resetstatusbar_button")); button.onClick(async () => { // reset last sync time await upsertLastSuccessSyncTimeByVault( this.plugin.db, this.plugin.vaultRandomID, -1 ); this.plugin.updateLastSuccessSyncMsg(-1); new Notice(t("settings_resetstatusbar_notice")); }); }); } new Setting(basicDiv) .setName(t("settings_ignorepaths")) .setDesc(t("settings_ignorepaths_desc")) .setClass("ignorepaths-settings") .addTextArea((textArea) => { textArea .setValue(`${(this.plugin.settings.ignorePaths ?? []).join("\n")}`) .onChange(async (value) => { this.plugin.settings.ignorePaths = value .trim() .split("\n") .filter((x) => x.trim() !== ""); await this.plugin.saveSettings(); }); textArea.inputEl.rows = 10; textArea.inputEl.cols = 30; textArea.inputEl.addClass("ignorepaths-textarea"); }); ////////////////////////////////////////////////// // below for advanced settings ////////////////////////////////////////////////// const advDiv = containerEl.createEl("div"); advDiv.createEl("h2", { text: t("settings_adv"), }); new Setting(advDiv) .setName(t("settings_concurrency")) .setDesc(t("settings_concurrency_desc")) .addDropdown((dropdown) => { dropdown.addOption("1", "1"); dropdown.addOption("2", "2"); dropdown.addOption("3", "3"); dropdown.addOption("5", "5 (default)"); dropdown.addOption("10", "10"); dropdown.addOption("15", "15"); dropdown.addOption("20", "20"); dropdown .setValue(`${this.plugin.settings.concurrency}`) .onChange(async (val) => { const realVal = Number.parseInt(val); this.plugin.settings.concurrency = realVal; await this.plugin.saveSettings(); }); }); new Setting(advDiv) .setName(t("settings_syncunderscore")) .setDesc(t("settings_syncunderscore_desc")) .addDropdown((dropdown) => { dropdown.addOption("disable", t("disable")); dropdown.addOption("enable", t("enable")); dropdown .setValue( `${this.plugin.settings.syncUnderscoreItems ? "enable" : "disable"}` ) .onChange(async (val) => { this.plugin.settings.syncUnderscoreItems = val === "enable"; await this.plugin.saveSettings(); }); }); new Setting(advDiv) .setName(t("settings_configdir")) .setDesc( t("settings_configdir_desc", { configDir: this.app.vault.configDir, }) ) .addDropdown((dropdown) => { dropdown.addOption("disable", t("disable")); dropdown.addOption("enable", t("enable")); const bridge = { secondConfirm: false, }; dropdown .setValue( `${this.plugin.settings.syncConfigDir ? "enable" : "disable"}` ) .onChange(async (val) => { if (val === "enable" && !bridge.secondConfirm) { dropdown.setValue("disable"); new SyncConfigDirModal(this.app, this.plugin, () => { bridge.secondConfirm = true; dropdown.setValue("enable"); }).open(); } else { bridge.secondConfirm = false; this.plugin.settings.syncConfigDir = false; await this.plugin.saveSettings(); } }); }); new Setting(advDiv) .setName(t("settings_deletetowhere")) .setDesc(t("settings_deletetowhere_desc")) .addDropdown((dropdown) => { dropdown.addOption("system", t("settings_deletetowhere_system_trash")); dropdown.addOption( "obsidian", t("settings_deletetowhere_obsidian_trash") ); dropdown .setValue(this.plugin.settings.deleteToWhere ?? "system") .onChange(async (val) => { this.plugin.settings.deleteToWhere = val as "system" | "obsidian"; await this.plugin.saveSettings(); }); }); let conflictActionSettingOrigDesc = t("settings_conflictaction_desc"); if ( (this.plugin.settings.conflictAction ?? "keep_newer") === "smart_conflict" ) { conflictActionSettingOrigDesc += t( "settings_conflictaction_smart_conflict_desc" ); } const conflictActionSetting = new Setting(advDiv) .setName(t("settings_conflictaction")) .setDesc(stringToFragment(conflictActionSettingOrigDesc)); conflictActionSetting.addDropdown((dropdown) => { dropdown .addOption("keep_newer", t("settings_conflictaction_keep_newer")) .addOption("keep_larger", t("settings_conflictaction_keep_larger")) .addOption( "smart_conflict", t("settings_conflictaction_smart_conflict") ) .setValue(this.plugin.settings.conflictAction ?? "keep_newer") .onChange(async (val) => { this.plugin.settings.conflictAction = val as ConflictActionType; await this.plugin.saveSettings(); conflictActionSettingOrigDesc = t("settings_conflictaction_desc"); if ( (this.plugin.settings.conflictAction ?? "keep_newer") === "smart_conflict" ) { conflictActionSettingOrigDesc += t( "settings_conflictaction_smart_conflict_desc" ); } conflictActionSetting.setDesc( stringToFragment(conflictActionSettingOrigDesc) ); }); }); new Setting(advDiv) .setName(t("settings_cleanemptyfolder")) .setDesc(t("settings_cleanemptyfolder_desc")) .addDropdown((dropdown) => { dropdown.addOption("skip", t("settings_cleanemptyfolder_skip")); dropdown.addOption( "clean_both", t("settings_cleanemptyfolder_clean_both") ); dropdown .setValue(this.plugin.settings.howToCleanEmptyFolder ?? "clean_both") .onChange(async (val) => { this.plugin.settings.howToCleanEmptyFolder = val as EmptyFolderCleanType; await this.plugin.saveSettings(); }); }); const percentage1 = new Setting(advDiv) .setName(t("settings_protectmodifypercentage")) .setDesc(t("settings_protectmodifypercentage_desc")); const percentage2 = new Setting(advDiv) .setName(t("settings_protectmodifypercentage_customfield")) .setDesc(t("settings_protectmodifypercentage_customfield_desc")); if ((this.plugin.settings.protectModifyPercentage ?? 50) % 10 === 0) { percentage2.settingEl.addClass("settings-percentage-custom-hide"); } let percentage2Text: TextComponent | undefined = undefined; percentage2.addText((text) => { text.inputEl.type = "number"; percentage2Text = text; text .setPlaceholder("0 ~ 100") .setValue(`${this.plugin.settings.protectModifyPercentage ?? 50}`) .onChange(async (val) => { let k = Number.parseFloat(val.trim()); if (Number.isNaN(k)) { // do nothing! } else { if (k < 0) { k = 0; } else if (k > 100) { k = 100; } this.plugin.settings.protectModifyPercentage = k; await this.plugin.saveSettings(); } }); }); percentage1.addDropdown((dropdown) => { for (const i of Array.from({ length: 11 }, (x, i) => i * 10)) { let desc = `${i}`; if (i === 0) { desc = t("settings_protectmodifypercentage_000_desc"); } else if (i === 50) { desc = t("settings_protectmodifypercentage_050_desc"); } else if (i === 100) { desc = t("settings_protectmodifypercentage_100_desc"); } dropdown.addOption(`${i}`, desc); } dropdown.addOption( "custom", t("settings_protectmodifypercentage_custom_desc") ); const p = this.plugin.settings.protectModifyPercentage ?? 50; let initVal = "custom"; if (p % 10 === 0) { initVal = `${p}`; } else { // show custom percentage2.settingEl.removeClass("settings-percentage-custom"); } dropdown.setValue(initVal).onChange(async (val) => { const k = Number.parseInt(val); if (val === "custom" || Number.isNaN(k)) { // do nothing until user changes something in custom field percentage2.settingEl.removeClass("settings-percentage-custom-hide"); } else { this.plugin.settings.protectModifyPercentage = k; percentage2.settingEl.addClass("settings-percentage-custom-hide"); percentage2Text?.setValue(`${k}`); await this.plugin.saveSettings(); } }); }); new Setting(advDiv) .setName(t("setting_syncdirection")) .setDesc(t("setting_syncdirection_desc")) .addDropdown((dropdown) => { dropdown.addOption( "bidirectional", t("setting_syncdirection_bidirectional_desc") ); dropdown.addOption( "incremental_push_only", t("setting_syncdirection_incremental_push_only_desc") ); dropdown.addOption( "incremental_pull_only", t("setting_syncdirection_incremental_pull_only_desc") ); dropdown .setValue(this.plugin.settings.syncDirection ?? "bidirectional") .onChange(async (val) => { this.plugin.settings.syncDirection = val as SyncDirectionType; await this.plugin.saveSettings(); }); }); if (Platform.isMobile) { new Setting(advDiv) .setName(t("settings_enablemobilestatusbar")) .setDesc(t("settings_enablemobilestatusbar_desc")) .addDropdown(async (dropdown) => { dropdown .addOption("enable", t("enable")) .addOption("disable", t("disable")); dropdown .setValue( `${ this.plugin.settings.enableMobileStatusBar ? "enable" : "disable" }` ) .onChange(async (val) => { if (val === "enable") { this.plugin.settings.enableMobileStatusBar = true; this.plugin.appContainerObserver = changeMobileStatusBar("enable"); } else { this.plugin.settings.enableMobileStatusBar = false; changeMobileStatusBar( "disable", this.plugin.appContainerObserver ); this.plugin.appContainerObserver?.disconnect(); this.plugin.appContainerObserver = undefined; } await this.plugin.saveSettings(); }); }); } ////////////////////////////////////////////////// // below for import and export functions ////////////////////////////////////////////////// // import and export const importExportDiv = containerEl.createEl("div"); importExportDiv.createEl("h2", { text: t("settings_importexport"), }); const importExportDivSetting1 = new Setting(importExportDiv) .setName(t("settings_export")) .setDesc(t("settings_export_desc")); importExportDivSetting1.settingEl.addClass("setting-need-wrapping"); importExportDivSetting1 .addButton(async (button) => { button.setButtonText(t("settings_export_basic_and_advanced_button")); button.onClick(async () => { new ExportSettingsQrCodeModal( this.app, this.plugin, "basic_and_advanced" ).open(); }); }) .addButton(async (button) => { button.setButtonText(t("settings_export_s3_button")); button.onClick(async () => { new ExportSettingsQrCodeModal(this.app, this.plugin, "s3").open(); }); }) .addButton(async (button) => { button.setButtonText(t("settings_export_dropbox_button")); button.onClick(async () => { new ExportSettingsQrCodeModal( this.app, this.plugin, "dropbox" ).open(); }); }) .addButton(async (button) => { button.setButtonText(t("settings_export_onedrive_button")); button.onClick(async () => { new ExportSettingsQrCodeModal( this.app, this.plugin, "onedrive" ).open(); }); }) .addButton(async (button) => { button.setButtonText(t("settings_export_webdav_button")); button.onClick(async () => { new ExportSettingsQrCodeModal(this.app, this.plugin, "webdav").open(); }); }) .addButton(async (button) => { button.setButtonText(t("settings_export_webdis_button")); button.onClick(async () => { new ExportSettingsQrCodeModal(this.app, this.plugin, "webdis").open(); }); }) .addButton(async (button) => { button.setButtonText(t("settings_export_googledrive_button")); button.onClick(async () => { new ExportSettingsQrCodeModal( this.app, this.plugin, "googledrive" ).open(); }); }); let importSettingVal = ""; new Setting(importExportDiv) .setName(t("settings_import")) .setDesc(t("settings_import_desc")) .addText((text) => text .setPlaceholder("obsidian://remotely-save?func=settings&...") .setValue("") .onChange((val) => { importSettingVal = val; }) ) .addButton(async (button) => { button.setButtonText(t("confirm")); button.onClick(async () => { if (importSettingVal !== "") { // console.debug(importSettingVal); try { const inputParams = parseUriByHand(importSettingVal); const parsed = importQrCodeUri( inputParams, this.app.vault.getName() ); if (parsed.status === "error") { new Notice(parsed.message); } else { const copied = cloneDeep(parsed.result); // new Notice(JSON.stringify(copied)) this.plugin.settings = Object.assign( {}, this.plugin.settings, copied ); this.plugin.saveSettings(); new Notice( t("protocol_saveqr", { manifestName: this.plugin.manifest.name, }) ); } } catch (e) { new Notice(`${e}`); } importSettingVal = ""; } else { new Notice(t("settings_import_error_notice")); importSettingVal = ""; } }); }); ////////////////////////////////////////////////// // below for pro ////////////////////////////////////////////////// const proDiv = containerEl.createEl("div"); generateProSettingsPart( proDiv, t, this.app, this.plugin, () => this.plugin.saveSettings(), googleDriveAllowedToUsedDiv, googleDriveNotShowUpHintSetting ); ////////////////////////////////////////////////// // below for debug ////////////////////////////////////////////////// const debugDiv = containerEl.createEl("div"); debugDiv.createEl("h2", { text: t("settings_debug") }); new Setting(debugDiv) .setName(t("settings_debuglevel")) .setDesc(t("settings_debuglevel_desc")) .addDropdown(async (dropdown) => { dropdown.addOption("info", "info"); dropdown.addOption("debug", "debug"); dropdown .setValue(this.plugin.settings.currLogLevel ?? "info") .onChange(async (val: string) => { this.plugin.settings.currLogLevel = val; await this.plugin.saveSettings(); console.info(`the log level is changed to ${val}`); }); }); new Setting(debugDiv) .setName(t("settings_outputsettingsconsole")) .setDesc(t("settings_outputsettingsconsole_desc")) .addButton(async (button) => { button.setButtonText(t("settings_outputsettingsconsole_button")); button.onClick(async () => { const c = messyConfigToNormal(await this.plugin.loadData()); console.info(c); new Notice(t("settings_outputsettingsconsole_notice")); }); }); new Setting(debugDiv) .setName(t("settings_obfuscatesettingfile")) .setDesc(t("settings_obfuscatesettingfile_desc")) .addDropdown(async (dropdown) => { dropdown .addOption("enable", t("enable")) .addOption("disable", t("disable")); dropdown .setValue( `${ this.plugin.settings.obfuscateSettingFile ? "enable" : "disable" }` ) .onChange(async (val) => { if (val === "enable") { this.plugin.settings.obfuscateSettingFile = true; } else { this.plugin.settings.obfuscateSettingFile = false; } await this.plugin.saveSettings(); }); }); new Setting(debugDiv) .setName(t("settings_viewconsolelog")) .setDesc(stringToFragment(t("settings_viewconsolelog_desc"))); const debugDivExportSyncPlans = new Setting(debugDiv) .setName(t("settings_syncplans")) .setDesc(t("settings_syncplans_desc")); debugDivExportSyncPlans.settingEl.addClass("setting-need-wrapping"); debugDivExportSyncPlans .addButton(async (button) => { button.setButtonText(t("settings_syncplans_button_1_only_change")); button.onClick(async () => { await exportVaultSyncPlansToFiles( this.plugin.db, this.app.vault, this.plugin.vaultRandomID, 1, true ); new Notice(t("settings_syncplans_notice")); }); }) .addButton(async (button) => { button.setButtonText(t("settings_syncplans_button_1")); button.onClick(async () => { await exportVaultSyncPlansToFiles( this.plugin.db, this.app.vault, this.plugin.vaultRandomID, 1, false ); new Notice(t("settings_syncplans_notice")); }); }) .addButton(async (button) => { button.setButtonText(t("settings_syncplans_button_5")); button.onClick(async () => { await exportVaultSyncPlansToFiles( this.plugin.db, this.app.vault, this.plugin.vaultRandomID, 5, false ); new Notice(t("settings_syncplans_notice")); }); }) .addButton(async (button) => { button.setButtonText(t("settings_syncplans_button_all")); button.onClick(async () => { await exportVaultSyncPlansToFiles( this.plugin.db, this.app.vault, this.plugin.vaultRandomID, -1, false ); new Notice(t("settings_syncplans_notice")); }); }); new Setting(debugDiv) .setName(t("settings_delsyncplans")) .setDesc(t("settings_delsyncplans_desc")) .addButton(async (button) => { button.setButtonText(t("settings_delsyncplans_button")); button.onClick(async () => { await clearAllSyncPlanRecords(this.plugin.db); new Notice(t("settings_delsyncplans_notice")); }); }); new Setting(debugDiv) .setName(t("settings_delprevsync")) .setDesc(t("settings_delprevsync_desc")) .addButton(async (button) => { button.setButtonText(t("settings_delprevsync_button")); button.onClick(async () => { await clearAllPrevSyncRecordByVault( this.plugin.db, this.plugin.vaultRandomID ); new Notice(t("settings_delprevsync_notice")); }); }); new Setting(debugDiv) .setName(t("settings_profiler_results")) .setDesc(t("settings_profiler_results_desc")) .addButton(async (button) => { button.setButtonText(t("settings_profiler_results_button_all")); button.onClick(async () => { await exportVaultProfilerResultsToFiles( this.plugin.db, this.app.vault, this.plugin.vaultRandomID ); new Notice(t("settings_profiler_results_notice")); }); }); new Setting(debugDiv) .setName(t("settings_profiler_enabledebugprint")) .setDesc(t("settings_profiler_enabledebugprint_desc")) .addDropdown((dropdown) => { dropdown.addOption("enable", t("enable")); dropdown.addOption("disable", t("disable")); dropdown .setValue( this.plugin.settings.profiler?.enablePrinting ? "enable" : "disable" ) .onChange(async (val: string) => { if (this.plugin.settings.profiler === undefined) { this.plugin.settings.profiler = DEFAULT_PROFILER_CONFIG; } this.plugin.settings.profiler.enablePrinting = val === "enable"; await this.plugin.saveSettings(); }); }); new Setting(debugDiv) .setName(t("settings_profiler_recordsize")) .setDesc(t("settings_profiler_recordsize_desc")) .addDropdown((dropdown) => { dropdown.addOption("enable", t("enable")); dropdown.addOption("disable", t("disable")); dropdown .setValue( this.plugin.settings.profiler?.recordSize ? "enable" : "disable" ) .onChange(async (val: string) => { if (this.plugin.settings.profiler === undefined) { this.plugin.settings.profiler = DEFAULT_PROFILER_CONFIG; } this.plugin.settings.profiler.recordSize = val === "enable"; await this.plugin.saveSettings(); }); }); new Setting(debugDiv) .setName(t("settings_outputbasepathvaultid")) .setDesc(t("settings_outputbasepathvaultid_desc")) .addButton(async (button) => { button.setButtonText(t("settings_outputbasepathvaultid_button")); button.onClick(async () => { new Notice(this.plugin.getVaultBasePath()); new Notice(this.plugin.vaultRandomID); }); }); new Setting(debugDiv) .setName(t("settings_resetcache")) .setDesc(t("settings_resetcache_desc")) .addButton(async (button) => { button.setButtonText(t("settings_resetcache_button")); button.onClick(async () => { await destroyDBs(); new Notice(t("settings_resetcache_notice")); this.plugin.unload(); }); }); } hide() { const { containerEl } = this; containerEl.empty(); super.hide(); } }