mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-02-16 01:00:37 +00:00
Fixed:
- Fixed a problem with synchronisation taking a long time to start in some cases. - Now we can disable E2EE encryption. Improved: - `Setup Wizard` is now more clear. - `Minimal Setup` is now more simple. - Self-hosted LiveSync now be able to use even if there are vaults with the same name. - Now Self-hosted LiveSync waits until set-up is complete. - Show reload prompts when possibly recommended while settings. New feature: - A guidance dialogue prompting for settings will be shown after the installation. Changed - Some setting names has been changed
This commit is contained in:
@@ -5,7 +5,7 @@ import { PouchDB } from "./lib/src/pouchdb-browser.js";
|
||||
import { askSelectString, askYesNo, askString } from "./utils";
|
||||
import { decrypt, encrypt } from "./lib/src/e2ee_v2";
|
||||
import { LiveSyncCommands } from "./LiveSyncCommands";
|
||||
import { delay } from "./lib/src/utils";
|
||||
import { delay, fireAndForget } from "./lib/src/utils";
|
||||
import { confirmWithMessage } from "./dialogs";
|
||||
import { Platform } from "./deps";
|
||||
import { fetchAllUsedChunks } from "./lib/src/utils_couchdb";
|
||||
@@ -17,25 +17,25 @@ export class SetupLiveSync extends LiveSyncCommands {
|
||||
|
||||
this.plugin.addCommand({
|
||||
id: "livesync-copysetupuri",
|
||||
name: "Copy the setup URI",
|
||||
callback: this.command_copySetupURI.bind(this),
|
||||
name: "Copy settings as a new setup URI",
|
||||
callback: () => fireAndForget(this.command_copySetupURI()),
|
||||
});
|
||||
this.plugin.addCommand({
|
||||
id: "livesync-copysetupuri-short",
|
||||
name: "Copy the setup URI (With customization sync)",
|
||||
callback: this.command_copySetupURIWithSync.bind(this),
|
||||
name: "Copy settings as a new setup URI (With customization sync)",
|
||||
callback: () => fireAndForget(this.command_copySetupURIWithSync()),
|
||||
});
|
||||
|
||||
this.plugin.addCommand({
|
||||
id: "livesync-copysetupurifull",
|
||||
name: "Copy the setup URI (Full)",
|
||||
callback: this.command_copySetupURIFull.bind(this),
|
||||
name: "Copy settings as a new setup URI (Full)",
|
||||
callback: () => fireAndForget(this.command_copySetupURIFull()),
|
||||
});
|
||||
|
||||
this.plugin.addCommand({
|
||||
id: "livesync-opensetupuri",
|
||||
name: "Open the setup URI",
|
||||
callback: this.command_openSetupURI.bind(this),
|
||||
name: "Use the copied setup URI (Formerly Open setup URI)",
|
||||
callback: () => fireAndForget(this.command_openSetupURI()),
|
||||
});
|
||||
}
|
||||
onInitializeDatabase(showNotice: boolean) { }
|
||||
@@ -76,7 +76,7 @@ export class SetupLiveSync extends LiveSyncCommands {
|
||||
Logger("Setup URI copied to clipboard", LOG_LEVEL_NOTICE);
|
||||
}
|
||||
async command_copySetupURIWithSync() {
|
||||
this.command_copySetupURI(false);
|
||||
await this.command_copySetupURI(false);
|
||||
}
|
||||
async command_openSetupURI() {
|
||||
const setupURI = await askString(this.app, "Easy setup", "Set up URI", `${configURIBase}aaaaa`);
|
||||
@@ -108,6 +108,7 @@ export class SetupLiveSync extends LiveSyncCommands {
|
||||
newSettingW.configPassphraseStore = "";
|
||||
newSettingW.encryptedPassphrase = "";
|
||||
newSettingW.encryptedCouchDBConnection = "";
|
||||
newSettingW.additionalSuffixOfDatabaseName = `${("appId" in this.app ? this.app.appId : "")}`
|
||||
const setupJustImport = "Just import setting";
|
||||
const setupAsNew = "Set it up as secondary or subsequent device";
|
||||
const setupAsMerge = "Secondary device but try keeping local changes";
|
||||
@@ -115,6 +116,7 @@ export class SetupLiveSync extends LiveSyncCommands {
|
||||
const setupManually = "Leave everything to me";
|
||||
newSettingW.syncInternalFiles = false;
|
||||
newSettingW.usePluginSync = false;
|
||||
newSettingW.isConfigured = true;
|
||||
// Migrate completely obsoleted configuration.
|
||||
if (!newSettingW.useIndexedDBAdapter) {
|
||||
newSettingW.useIndexedDBAdapter = true;
|
||||
@@ -333,10 +335,18 @@ Of course, we are able to disable these features.`
|
||||
|
||||
const ret = await confirmWithMessage(this.plugin, "Database adapter", message, choices, CHOICE_YES, 10);
|
||||
if (ret == CHOICE_YES) {
|
||||
this.plugin.settings.useIndexedDBAdapter = false;
|
||||
this.plugin.settings.useIndexedDBAdapter = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
async resetLocalDatabase() {
|
||||
if (this.plugin.settings.isConfigured && this.plugin.settings.additionalSuffixOfDatabaseName == "") {
|
||||
// Discard the non-suffixed database
|
||||
await this.plugin.resetLocalDatabase();
|
||||
}
|
||||
this.plugin.settings.additionalSuffixOfDatabaseName = `${("appId" in this.app ? this.app.appId : "")}`
|
||||
await this.plugin.resetLocalDatabase();
|
||||
}
|
||||
async fetchRemoteChunks() {
|
||||
if (!this.plugin.settings.doNotSuspendOnFetching && this.plugin.settings.readChunksOnline) {
|
||||
Logger(`Fetching chunks`, LOG_LEVEL_NOTICE);
|
||||
@@ -351,10 +361,11 @@ Of course, we are able to disable these features.`
|
||||
}
|
||||
async fetchLocal() {
|
||||
this.suspendExtraSync();
|
||||
this.askUseNewAdapter();
|
||||
await this.askUseNewAdapter();
|
||||
this.plugin.settings.isConfigured = true;
|
||||
await this.suspendReflectingDatabase();
|
||||
await this.plugin.realizeSettingSyncMode();
|
||||
await this.plugin.resetLocalDatabase();
|
||||
await this.resetLocalDatabase();
|
||||
await delay(1000);
|
||||
await this.plugin.markRemoteResolved();
|
||||
await this.plugin.openDatabase();
|
||||
@@ -369,10 +380,11 @@ Of course, we are able to disable these features.`
|
||||
}
|
||||
async fetchLocalWithKeepLocal() {
|
||||
this.suspendExtraSync();
|
||||
this.askUseNewAdapter();
|
||||
await this.askUseNewAdapter();
|
||||
this.plugin.settings.isConfigured = true;
|
||||
await this.suspendReflectingDatabase();
|
||||
await this.plugin.realizeSettingSyncMode();
|
||||
await this.plugin.resetLocalDatabase();
|
||||
await this.resetLocalDatabase();
|
||||
await delay(1000);
|
||||
await this.plugin.initializeDatabase(true);
|
||||
await this.plugin.markRemoteResolved();
|
||||
@@ -388,6 +400,7 @@ Of course, we are able to disable these features.`
|
||||
}
|
||||
async rebuildRemote() {
|
||||
this.suspendExtraSync();
|
||||
this.plugin.settings.isConfigured = true;
|
||||
await this.plugin.realizeSettingSyncMode();
|
||||
await this.plugin.markRemoteLocked();
|
||||
await this.plugin.tryResetRemoteDatabase();
|
||||
@@ -401,9 +414,10 @@ Of course, we are able to disable these features.`
|
||||
}
|
||||
async rebuildEverything() {
|
||||
this.suspendExtraSync();
|
||||
this.askUseNewAdapter();
|
||||
await this.askUseNewAdapter();
|
||||
this.plugin.settings.isConfigured = true;
|
||||
await this.plugin.realizeSettingSyncMode();
|
||||
await this.plugin.resetLocalDatabase();
|
||||
await this.resetLocalDatabase();
|
||||
await delay(1000);
|
||||
await this.plugin.initializeDatabase(true);
|
||||
await this.plugin.markRemoteLocked();
|
||||
|
||||
@@ -26,6 +26,18 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
}
|
||||
this.plugin.addLog(`Connected to ${db.info.db_name}`, LOG_LEVEL_NOTICE);
|
||||
}
|
||||
askReload() {
|
||||
scheduleTask("configReload", 250, async () => {
|
||||
if (await askYesNo(this.app, "Do you want to restart and reload Obsidian now?") == "yes") {
|
||||
// @ts-ignore
|
||||
this.app.commands.executeCommandById("app:reload")
|
||||
}
|
||||
})
|
||||
}
|
||||
closeSetting() {
|
||||
// @ts-ignore
|
||||
this.plugin.app.setting.close()
|
||||
}
|
||||
display(): void {
|
||||
const { containerEl } = this;
|
||||
let encrypt = this.plugin.settings.encrypt;
|
||||
@@ -136,29 +148,27 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
const setupWizardEl = containerEl.createDiv();
|
||||
setupWizardEl.createEl("h3", { text: "Setup wizard" });
|
||||
new Setting(setupWizardEl)
|
||||
.setName("Discard the existing configuration and set up")
|
||||
.setName("Use the copied setup URI")
|
||||
.setDesc("To setup Self-hosted LiveSync, this method is the most preferred one.")
|
||||
.addButton((text) => {
|
||||
// eslint-disable-next-line require-await
|
||||
text.setButtonText("Next").onClick(async () => {
|
||||
if (JSON.stringify(this.plugin.settings) != JSON.stringify(DEFAULT_SETTINGS)) {
|
||||
this.plugin.replicator.closeReplication();
|
||||
this.plugin.settings = { ...DEFAULT_SETTINGS };
|
||||
this.plugin.saveSettings();
|
||||
Logger("Configuration has been flushed, please open it again", LOG_LEVEL_NOTICE)
|
||||
// @ts-ignore
|
||||
this.plugin.app.setting.close()
|
||||
} else {
|
||||
containerEl.addClass("isWizard");
|
||||
applyDisplayEnabled();
|
||||
inWizard = true;
|
||||
changeDisplay("0")
|
||||
}
|
||||
text.setButtonText("Use").onClick(async () => {
|
||||
this.closeSetting();
|
||||
await this.plugin.addOnSetup.command_openSetupURI();
|
||||
})
|
||||
})
|
||||
if (this.plugin.settings.isConfigured) {
|
||||
new Setting(setupWizardEl)
|
||||
.setName("Copy current settings as a new setup URI")
|
||||
.addButton((text) => {
|
||||
text.setButtonText("Copy").onClick(async () => {
|
||||
await this.plugin.addOnSetup.command_copySetupURI();
|
||||
})
|
||||
})
|
||||
}
|
||||
new Setting(setupWizardEl)
|
||||
.setName("Do not discard the existing configuration and set up again")
|
||||
.setName("Minimal setup")
|
||||
.addButton((text) => {
|
||||
text.setButtonText("Next").onClick(async () => {
|
||||
text.setButtonText("Start").onClick(async () => {
|
||||
this.plugin.settings.liveSync = false;
|
||||
this.plugin.settings.periodicReplication = false;
|
||||
this.plugin.settings.syncOnSave = false;
|
||||
@@ -172,27 +182,34 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
applyDisplayEnabled();
|
||||
inWizard = true;
|
||||
changeDisplay("0")
|
||||
|
||||
})
|
||||
})
|
||||
const infoWarnForSubsequent = setupWizardEl.createEl("div", { text: `To set up second or subsequent device, please use 'Copy setup URI' and 'Open setup URI'` });
|
||||
infoWarnForSubsequent.addClass("op-warn-info");
|
||||
new Setting(setupWizardEl)
|
||||
.setName("Copy setup URI")
|
||||
.addButton((text) => {
|
||||
text.setButtonText("Copy setup URI").onClick(() => {
|
||||
// @ts-ignore
|
||||
this.plugin.app.commands.executeCommandById("obsidian-livesync:livesync-copysetupuri")
|
||||
|
||||
if (!this.plugin.settings.isConfigured) {
|
||||
new Setting(setupWizardEl)
|
||||
.setName("Enable LiveSync on this device as the set-up was completed manually")
|
||||
.addButton((text) => {
|
||||
text.setButtonText("Enable").onClick(async () => {
|
||||
this.plugin.settings.isConfigured = true;
|
||||
await this.plugin.saveSettings();
|
||||
this.askReload();
|
||||
})
|
||||
})
|
||||
})
|
||||
.addButton((text) => {
|
||||
text.setButtonText("Open setup URI").onClick(() => {
|
||||
// @ts-ignore
|
||||
this.plugin.app.commands.executeCommandById("obsidian-livesync:livesync-opensetupuri")
|
||||
|
||||
}
|
||||
if (this.plugin.settings.isConfigured) {
|
||||
new Setting(setupWizardEl)
|
||||
.setName("Discard exist settings and databases")
|
||||
.addButton((text) => {
|
||||
text.setButtonText("Discard").onClick(async () => {
|
||||
if (await askYesNo(this.plugin.app, "Do you really want to discard exist settings and databases?") == "yes") {
|
||||
this.plugin.settings = { ...DEFAULT_SETTINGS };
|
||||
await this.plugin.saveSettingData();
|
||||
await this.plugin.resetLocalDatabase();
|
||||
// await this.plugin.initializeDatabase();
|
||||
this.askReload();
|
||||
}
|
||||
}).setWarning()
|
||||
})
|
||||
})
|
||||
}
|
||||
setupWizardEl.createEl("h3", { text: "Online Tips" });
|
||||
const repo = "vrtmrz/obsidian-livesync";
|
||||
const topPath = "/docs/troubleshooting.md";
|
||||
@@ -338,6 +355,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
|
||||
new Setting(containerRemoteDatabaseEl)
|
||||
.setName("Test Database Connection")
|
||||
.setClass("wizardHidden")
|
||||
.setDesc("Open database connection. If the remote database is not found and you have the privilege to create a database, the database will be created.")
|
||||
.addButton((button) =>
|
||||
button
|
||||
@@ -349,8 +367,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
);
|
||||
|
||||
new Setting(containerRemoteDatabaseEl)
|
||||
.setName("Check database configuration")
|
||||
// .setDesc("Open database connection. If the remote database is not found and you have the privilege to create a database, the database will be created.")
|
||||
.setName("Check and Fix database configuration")
|
||||
.setDesc("Check the database configuration, and fix if there are any problems.")
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Check")
|
||||
@@ -557,18 +575,18 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
passphraseSetting?.controlEl.toggleClass("sls-item-dirty", passphrase != this.plugin.settings.passphrase);
|
||||
dynamicIteration?.controlEl.toggleClass("sls-item-dirty", useDynamicIterationCount != this.plugin.settings.useDynamicIterationCount);
|
||||
usePathObfuscationEl?.controlEl.toggleClass("sls-item-dirty", usePathObfuscation != this.plugin.settings.usePathObfuscation);
|
||||
if (encrypt != this.plugin.settings.encrypt ||
|
||||
passphrase != this.plugin.settings.passphrase ||
|
||||
useDynamicIterationCount != this.plugin.settings.useDynamicIterationCount ||
|
||||
usePathObfuscation != this.plugin.settings.usePathObfuscation) {
|
||||
applyE2EButtons.settingEl.removeClass("sls-setting-hidden");
|
||||
} else {
|
||||
applyE2EButtons.settingEl.addClass("sls-setting-hidden");
|
||||
}
|
||||
|
||||
} else {
|
||||
passphraseSetting.settingEl.addClass("sls-setting-hidden");
|
||||
dynamicIteration.settingEl.addClass("sls-setting-hidden");
|
||||
usePathObfuscationEl.settingEl.addClass("sls-setting-hidden");
|
||||
}
|
||||
if (encrypt != this.plugin.settings.encrypt ||
|
||||
passphrase != this.plugin.settings.passphrase ||
|
||||
useDynamicIterationCount != this.plugin.settings.useDynamicIterationCount ||
|
||||
usePathObfuscation != this.plugin.settings.usePathObfuscation) {
|
||||
applyE2EButtons.settingEl.removeClass("sls-setting-hidden");
|
||||
} else {
|
||||
applyE2EButtons.settingEl.addClass("sls-setting-hidden");
|
||||
}
|
||||
}
|
||||
@@ -703,6 +721,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
this.plugin.settings.passphrase = passphrase;
|
||||
this.plugin.settings.useDynamicIterationCount = useDynamicIterationCount;
|
||||
this.plugin.settings.usePathObfuscation = usePathObfuscation;
|
||||
this.plugin.settings.isConfigured = true;
|
||||
await this.plugin.saveSettings();
|
||||
updateE2EControls();
|
||||
if (sendToServer) {
|
||||
@@ -731,26 +750,23 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
this.plugin.settings.passphrase = passphrase;
|
||||
this.plugin.settings.useDynamicIterationCount = useDynamicIterationCount;
|
||||
this.plugin.settings.usePathObfuscation = usePathObfuscation;
|
||||
this.plugin.settings.isConfigured = true;
|
||||
Logger("All synchronization have been temporarily disabled. Please enable them after the fetching, if you need them.", LOG_LEVEL_NOTICE)
|
||||
await this.plugin.saveSettings();
|
||||
updateE2EControls();
|
||||
applyDisplayEnabled();
|
||||
// @ts-ignore
|
||||
this.plugin.app.setting.close();
|
||||
this.closeSetting();
|
||||
await delay(2000);
|
||||
await performRebuildDB(this.plugin, method);
|
||||
}
|
||||
|
||||
|
||||
let rebuildRemote = false;
|
||||
|
||||
new Setting(containerRemoteDatabaseEl)
|
||||
.setName("")
|
||||
.setClass("wizardOnly")
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Next")
|
||||
.setClass("mod-cta")
|
||||
.setCta()
|
||||
.setDisabled(false)
|
||||
.onClick(() => {
|
||||
if (!this.plugin.settings.encrypt) {
|
||||
@@ -759,33 +775,13 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
if (isCloudantURI(this.plugin.settings.couchDB_URI)) {
|
||||
this.plugin.settings.customChunkSize = 0;
|
||||
} else {
|
||||
this.plugin.settings.customChunkSize = 100;
|
||||
this.plugin.settings.customChunkSize = 50;
|
||||
}
|
||||
rebuildRemote = false;
|
||||
changeDisplay("30")
|
||||
})
|
||||
);
|
||||
new Setting(containerRemoteDatabaseEl)
|
||||
.setName("")
|
||||
.setClass("wizardOnly")
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Discard existing database and proceed")
|
||||
.setDisabled(false)
|
||||
.setWarning()
|
||||
.onClick(() => {
|
||||
if (!this.plugin.settings.encrypt) {
|
||||
this.plugin.settings.passphrase = "";
|
||||
}
|
||||
if (isCloudantURI(this.plugin.settings.couchDB_URI)) {
|
||||
this.plugin.settings.customChunkSize = 0;
|
||||
} else {
|
||||
this.plugin.settings.customChunkSize = 100;
|
||||
}
|
||||
rebuildRemote = true;
|
||||
changeDisplay("30")
|
||||
})
|
||||
);
|
||||
)
|
||||
;
|
||||
|
||||
addScreenElement("0", containerRemoteDatabaseEl);
|
||||
|
||||
const containerGeneralSettingsEl = containerEl.createDiv();
|
||||
@@ -1049,25 +1045,20 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
...presetAllDisabled
|
||||
}
|
||||
}
|
||||
this.plugin.saveSettings();
|
||||
await this.plugin.saveSettings();
|
||||
this.display();
|
||||
await this.plugin.realizeSettingSyncMode();
|
||||
if (inWizard) {
|
||||
// @ts-ignore
|
||||
this.plugin.app.setting.close()
|
||||
await this.plugin.resetLocalDatabase();
|
||||
await this.plugin.initializeDatabase(true);
|
||||
if (rebuildRemote) {
|
||||
await this.plugin.markRemoteLocked();
|
||||
await this.plugin.tryResetRemoteDatabase();
|
||||
await this.plugin.markRemoteLocked();
|
||||
await this.plugin.markRemoteResolved();
|
||||
this.closeSetting();
|
||||
if (!this.plugin.settings.isConfigured) {
|
||||
this.plugin.settings.isConfigured = true;
|
||||
await this.plugin.saveSettings();
|
||||
await rebuildDB("localOnly");
|
||||
Logger("All done! Please set up subsequent devices with 'Copy current settings as a new setup URI' and 'Use the copied setup URI'.", LOG_LEVEL_NOTICE);
|
||||
await this.plugin.addOnSetup.command_openSetupURI();
|
||||
} else {
|
||||
this.askReload();
|
||||
}
|
||||
await this.plugin.replicate(true);
|
||||
|
||||
Logger("All done! Please set up subsequent devices with 'Copy setup URI' and 'Open setup URI'.", LOG_LEVEL_NOTICE);
|
||||
// @ts-ignore
|
||||
this.plugin.app.commands.executeCommandById("obsidian-livesync:livesync-copysetupuri")
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -1286,24 +1277,21 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
||||
.addButton((button) => {
|
||||
button.setButtonText("Merge")
|
||||
.onClick(async () => {
|
||||
// @ts-ignore
|
||||
this.plugin.app.setting.close()
|
||||
this.closeSetting()
|
||||
await this.plugin.addOnSetup.configureHiddenFileSync("MERGE");
|
||||
})
|
||||
})
|
||||
.addButton((button) => {
|
||||
button.setButtonText("Fetch")
|
||||
.onClick(async () => {
|
||||
// @ts-ignore
|
||||
this.plugin.app.setting.close()
|
||||
this.closeSetting()
|
||||
await this.plugin.addOnSetup.configureHiddenFileSync("FETCH");
|
||||
})
|
||||
})
|
||||
.addButton((button) => {
|
||||
button.setButtonText("Overwrite")
|
||||
.onClick(async () => {
|
||||
// @ts-ignore
|
||||
this.plugin.app.setting.close()
|
||||
this.closeSetting()
|
||||
await this.plugin.addOnSetup.configureHiddenFileSync("OVERWRITE");
|
||||
})
|
||||
});
|
||||
@@ -1694,6 +1682,18 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
c.addClass("op-warn");
|
||||
}
|
||||
}
|
||||
|
||||
new Setting(containerHatchEl)
|
||||
.setName("Back to non-configured")
|
||||
.addButton((button) =>
|
||||
button
|
||||
.setButtonText("Back")
|
||||
.setDisabled(false)
|
||||
.onClick(async () => {
|
||||
this.plugin.settings.isConfigured = false;
|
||||
await this.plugin.saveSettings();
|
||||
this.askReload();
|
||||
}));
|
||||
const hatchWarn = containerHatchEl.createEl("div", { text: `To stop the boot up sequence for fixing problems on databases, you can put redflag.md on top of your vault (Rebooting obsidian is required).` });
|
||||
hatchWarn.addClass("op-warn-info");
|
||||
|
||||
@@ -1844,12 +1844,7 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
toggle.setValue(this.plugin.settings.suspendFileWatching).onChange(async (value) => {
|
||||
this.plugin.settings.suspendFileWatching = value;
|
||||
await this.plugin.saveSettings();
|
||||
scheduleTask("configReload", 250, async () => {
|
||||
if (await askYesNo(this.app, "Do you want to restart and reload Obsidian now?") == "yes") {
|
||||
// @ts-ignore
|
||||
this.app.commands.executeCommandById("app:reload")
|
||||
}
|
||||
})
|
||||
this.askReload();
|
||||
})
|
||||
);
|
||||
new Setting(containerHatchEl)
|
||||
@@ -1859,12 +1854,7 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
toggle.setValue(this.plugin.settings.suspendParseReplicationResult).onChange(async (value) => {
|
||||
this.plugin.settings.suspendParseReplicationResult = value;
|
||||
await this.plugin.saveSettings();
|
||||
scheduleTask("configReload", 250, async () => {
|
||||
if (await askYesNo(this.app, "Do you want to restart and reload Obsidian now?") == "yes") {
|
||||
// @ts-ignore
|
||||
this.app.commands.executeCommandById("app:reload")
|
||||
}
|
||||
})
|
||||
this.askReload();
|
||||
})
|
||||
);
|
||||
new Setting(containerHatchEl)
|
||||
@@ -1949,7 +1939,7 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
let newDatabaseName = this.plugin.settings.additionalSuffixOfDatabaseName + "";
|
||||
new Setting(containerHatchEl)
|
||||
.setName("Database suffix")
|
||||
.setDesc("LiveSync could not treat multiple vaults which have same name, please add some suffix from here.")
|
||||
.setDesc("LiveSync could not handle multiple vaults which have same name without different prefix, This should be automatically configured.")
|
||||
.addText((text) => {
|
||||
text.setPlaceholder("")
|
||||
.setValue(newDatabaseName)
|
||||
@@ -2163,8 +2153,7 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
.setDisabled(false)
|
||||
.setWarning()
|
||||
.onClick(async () => {
|
||||
// @ts-ignore
|
||||
this.plugin.app.setting.close()
|
||||
this.closeSetting()
|
||||
await this.plugin.dbGC();
|
||||
})
|
||||
);
|
||||
@@ -2186,7 +2175,7 @@ ${stringifyYaml(pluginConfig)}`;
|
||||
applyDisplayEnabled();
|
||||
if (this.selectedScreen == "") {
|
||||
if (lastVersion != this.plugin.settings.lastReadUpdates) {
|
||||
if (JSON.stringify(this.plugin.settings) != JSON.stringify(DEFAULT_SETTINGS)) {
|
||||
if (this.plugin.settings.isConfigured) {
|
||||
changeDisplay("100");
|
||||
} else {
|
||||
changeDisplay("110")
|
||||
|
||||
@@ -91,6 +91,8 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
||||
}
|
||||
// Cache file and waiting to can be proceed.
|
||||
async appendWatchEvent(params: { type: FileEventType, file: TAbstractFile | InternalFileInfo, oldPath?: string }[], ctx?: any) {
|
||||
if (!this.plugin.settings.isConfigured) return;
|
||||
if (this.plugin.settings.suspendFileWatching) return;
|
||||
for (const param of params) {
|
||||
if (shouldBeIgnored(param.file.path)) {
|
||||
continue;
|
||||
@@ -106,7 +108,6 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
||||
}
|
||||
if (file instanceof TFolder) continue;
|
||||
if (!await this.plugin.isTargetFile(file.path)) continue;
|
||||
if (this.plugin.settings.suspendFileWatching) continue;
|
||||
|
||||
let cache: null | string | ArrayBuffer;
|
||||
// new file or something changed, cache the changes.
|
||||
|
||||
2
src/lib
2
src/lib
Submodule src/lib updated: 71d28f285d...46256dba3a
89
src/main.ts
89
src/main.ts
@@ -76,7 +76,13 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
replicator!: LiveSyncDBReplicator;
|
||||
|
||||
statusBar?: HTMLElement;
|
||||
suspended = false;
|
||||
_suspended = false;
|
||||
get suspended() {
|
||||
return this._suspended || !this.settings?.isConfigured;
|
||||
}
|
||||
set suspended(value: boolean) {
|
||||
this._suspended = value;
|
||||
}
|
||||
deviceAndVaultName = "";
|
||||
isMobile = false;
|
||||
isReady = false;
|
||||
@@ -403,7 +409,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
if (notes.length == 0) {
|
||||
Logger("There are no old documents");
|
||||
Logger(`Checking expired file history done`);
|
||||
|
||||
return;
|
||||
}
|
||||
for (const v of notes) {
|
||||
@@ -420,6 +425,43 @@ export default class ObsidianLiveSyncPlugin extends Plugin
|
||||
Logger(`Something went wrong! The local database is not ready`, LOG_LEVEL_NOTICE);
|
||||
return;
|
||||
}
|
||||
if (!this.settings.isConfigured) {
|
||||
const message = `Hello and welcome to Self-hosted LiveSync.
|
||||
|
||||
Your device seems to **not be configured yet**. Please finish the setup and synchronise your vaults!
|
||||
|
||||
Click anywhere to stop counting down.
|
||||
|
||||
## At the first device
|
||||
- With Setup URI -> Use \`Use the copied setup URI\`.
|
||||
If you have configured it automatically, you should have one.
|
||||
- Without Setup URI -> Use \`Setup wizard\` in setting dialogue. **\`Minimal setup\` is recommended**.
|
||||
- What is the Setup URI? -> Do not worry! We have [some docs](https://github.com/vrtmrz/obsidian-livesync/blob/main/README.md#how-to-use) now. Please refer to them once.
|
||||
|
||||
## At the subsequent device
|
||||
- With Setup URI -> Use \`Use the copied setup URI\`.
|
||||
If you do not have it yet, you can copy it on the first device.
|
||||
- Without Setup URI -> Use \`Setup wizard\` in setting dialogue, but **strongly recommends using setup URI**.
|
||||
`
|
||||
const OPEN_SETUP = "Open setting dialog";
|
||||
const USE_SETUP = "Use the copied setup URI";
|
||||
const DISMISS = "Dismiss";
|
||||
|
||||
const ret = await confirmWithMessage(this, "Welcome to Self-hosted LiveSync", message, [USE_SETUP, OPEN_SETUP, DISMISS], DISMISS, 40);
|
||||
if (ret === OPEN_SETUP) {
|
||||
try {
|
||||
//@ts-ignore: undocumented api
|
||||
this.app.setting.open();
|
||||
//@ts-ignore: undocumented api
|
||||
this.app.setting.openTabById("obsidian-livesync");
|
||||
} catch (ex) {
|
||||
Logger("Something went wrong on opening setting dialog, please open it manually", LOG_LEVEL_NOTICE);
|
||||
}
|
||||
} else if (ret == USE_SETUP) {
|
||||
fireAndForget(this.addOnSetup.command_openSetupURI());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.isRedFlagRaised() || this.isRedFlag2Raised() || this.isRedFlag3Raised()) {
|
||||
@@ -736,7 +778,7 @@ Note: We can always able to read V1 format. It will be progressively converted.
|
||||
this.statusBar = this.addStatusBarItem();
|
||||
this.statusBar.addClass("syncstatusbar");
|
||||
const lastVersion = ~~(versionNumberString2Number(manifestVersion) / 1000);
|
||||
if (lastVersion > this.settings.lastReadUpdates) {
|
||||
if (lastVersion > this.settings.lastReadUpdates && this.settings.isConfigured) {
|
||||
Logger("Self-hosted LiveSync has undergone a major upgrade. Please open the setting dialog, and check the information pane.", LOG_LEVEL_NOTICE);
|
||||
}
|
||||
|
||||
@@ -877,6 +919,16 @@ Note: We can always able to read V1 format. It will be progressively converted.
|
||||
|
||||
async loadSettings() {
|
||||
const settings = Object.assign({}, DEFAULT_SETTINGS, await this.loadData()) as ObsidianLiveSyncSettings;
|
||||
|
||||
if (typeof settings.isConfigured == "undefined") {
|
||||
// If migrated, mark true
|
||||
if (JSON.stringify(settings) !== JSON.stringify(DEFAULT_SETTINGS)) {
|
||||
settings.isConfigured = true;
|
||||
} else {
|
||||
settings.additionalSuffixOfDatabaseName = `${("appId" in this.app ? this.app.appId : "")}`
|
||||
settings.isConfigured = false;
|
||||
}
|
||||
}
|
||||
const passphrase = await this.getPassphrase(settings);
|
||||
if (passphrase === false) {
|
||||
Logger("Could not determine passphrase for reading data.json! DO NOT synchronize with the remote before making sure your configuration is!", LOG_LEVEL_URGENT);
|
||||
@@ -1193,8 +1245,8 @@ We can perform a command in this file.
|
||||
|
||||
async watchWindowVisibilityAsync() {
|
||||
if (this.settings.suspendFileWatching) return;
|
||||
if (!this.settings.isConfigured) return;
|
||||
if (!this.isReady) return;
|
||||
// if (this.suspended) return;
|
||||
const isHidden = document.hidden;
|
||||
await this.applyBatchChange();
|
||||
if (isHidden) {
|
||||
@@ -1307,6 +1359,7 @@ We can perform a command in this file.
|
||||
|
||||
watchWorkspaceOpen(file: TFile | null) {
|
||||
if (this.settings.suspendFileWatching) return;
|
||||
if (!this.settings.isConfigured) return;
|
||||
if (!this.isReady) return;
|
||||
if (!file) return;
|
||||
scheduleTask("watch-workspace-open", 500, () => fireAndForget(() => this.watchWorkspaceOpenAsync(file)));
|
||||
@@ -1314,6 +1367,7 @@ We can perform a command in this file.
|
||||
|
||||
async watchWorkspaceOpenAsync(file: TFile) {
|
||||
if (this.settings.suspendFileWatching) return;
|
||||
if (!this.settings.isConfigured) return;
|
||||
if (!this.isReady) return;
|
||||
await this.applyBatchChange();
|
||||
if (file == null) {
|
||||
@@ -1579,17 +1633,18 @@ We can perform a command in this file.
|
||||
localStorage.setItem(lsKey, saveData);
|
||||
}
|
||||
async loadQueuedFiles() {
|
||||
if (!this.settings.suspendParseReplicationResult) {
|
||||
const lsKey = "obsidian-livesync-queuefiles-" + this.getVaultName();
|
||||
const ids = [...new Set(JSON.parse(localStorage.getItem(lsKey) || "[]"))] as string[];
|
||||
const batchSize = 100;
|
||||
const chunkedIds = arrayToChunkedArray(ids, batchSize);
|
||||
for await (const idsBatch of chunkedIds) {
|
||||
const ret = await this.localDatabase.allDocsRaw<EntryDoc>({ keys: idsBatch, include_docs: true, limit: 100 });
|
||||
this.replicationResultProcessor.enqueueAll(ret.rows.map(doc => doc.doc));
|
||||
await this.replicationResultProcessor.waitForPipeline();
|
||||
}
|
||||
if (this.settings.suspendParseReplicationResult) return;
|
||||
if (!this.settings.isConfigured) return;
|
||||
const lsKey = "obsidian-livesync-queuefiles-" + this.getVaultName();
|
||||
const ids = [...new Set(JSON.parse(localStorage.getItem(lsKey) || "[]"))] as string[];
|
||||
const batchSize = 100;
|
||||
const chunkedIds = arrayToChunkedArray(ids, batchSize);
|
||||
for await (const idsBatch of chunkedIds) {
|
||||
const ret = await this.localDatabase.allDocsRaw<EntryDoc>({ keys: idsBatch, include_docs: true, limit: 100 });
|
||||
this.replicationResultProcessor.enqueueAll(ret.rows.map(doc => doc.doc));
|
||||
await this.replicationResultProcessor.waitForPipeline();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
databaseQueueCount = reactiveSource(0);
|
||||
@@ -1953,6 +2008,12 @@ Or if you are sure know what had been happened, we can unlock the database from
|
||||
|
||||
async syncAllFiles(showingNotice?: boolean) {
|
||||
// synchronize all files between database and storage.
|
||||
if (!this.settings.isConfigured) {
|
||||
if (showingNotice) {
|
||||
Logger("LiveSync is not configured yet. Synchronising between the storage and the local database is now prevented.", LOG_LEVEL_NOTICE, "syncAll");
|
||||
}
|
||||
return;
|
||||
}
|
||||
let initialScan = false;
|
||||
if (showingNotice) {
|
||||
Logger("Initializing", LOG_LEVEL_NOTICE, "syncAll");
|
||||
|
||||
Reference in New Issue
Block a user