remotely-save/src/settings.ts

2581 lines
80 KiB
TypeScript

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 { 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();
}
}
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 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
.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"
);
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();
});
});
new Setting(advDiv)
.setName(t("settings_conflictaction"))
.setDesc(t("settings_conflictaction_desc"))
.addDropdown((dropdown) => {
dropdown.addOption(
"keep_newer",
t("settings_conflictaction_keep_newer")
);
dropdown.addOption(
"keep_larger",
t("settings_conflictaction_keep_larger")
);
dropdown
.setValue(this.plugin.settings.conflictAction ?? "keep_newer")
.onChange(async (val) => {
this.plugin.settings.conflictAction = val as ConflictActionType;
await this.plugin.saveSettings();
});
});
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();
});
});
new Setting(advDiv)
.setName(t("settings_protectmodifypercentage"))
.setDesc(t("settings_protectmodifypercentage_desc"))
.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
.setValue(`${this.plugin.settings.protectModifyPercentage ?? 50}`)
.onChange(async (val) => {
this.plugin.settings.protectModifyPercentage = Number.parseInt(val);
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_all_but_oauth2_button"));
button.onClick(async () => {
new ExportSettingsQrCodeModal(
this.app,
this.plugin,
"all_but_oauth2"
).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();
});
});
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 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();
}
}