mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-06-11 08:50:17 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 5d1074065c | |||
| b444082b0c | |||
| d5e6419504 | |||
| 1bf1e1540d | |||
| be1e6b11ac | |||
| a486788572 | |||
| e5784a1da6 | |||
| 2100e22276 | |||
| ec08dc5fe8 | |||
| c92e94e552 |
+2
-1
@@ -1,3 +1,4 @@
|
|||||||
npm node_modules
|
node_modules
|
||||||
build
|
build
|
||||||
.eslintrc.js.bak
|
.eslintrc.js.bak
|
||||||
|
src/lib/src/patches/pouchdb-utils
|
||||||
+3
-1
@@ -25,14 +25,16 @@ esbuild
|
|||||||
"MANIFEST_VERSION": `"${manifestJson.version}"`,
|
"MANIFEST_VERSION": `"${manifestJson.version}"`,
|
||||||
"PACKAGE_VERSION": `"${packageJson.version}"`,
|
"PACKAGE_VERSION": `"${packageJson.version}"`,
|
||||||
"UPDATE_INFO": `${updateInfo}`,
|
"UPDATE_INFO": `${updateInfo}`,
|
||||||
|
"global":"window",
|
||||||
},
|
},
|
||||||
external: ["obsidian", "electron", ...builtins],
|
external: ["obsidian", "electron", "crypto"],
|
||||||
format: "cjs",
|
format: "cjs",
|
||||||
watch: !prod,
|
watch: !prod,
|
||||||
target: "es2018",
|
target: "es2018",
|
||||||
logLevel: "info",
|
logLevel: "info",
|
||||||
sourcemap: prod ? false : "inline",
|
sourcemap: prod ? false : "inline",
|
||||||
treeShaking: true,
|
treeShaking: true,
|
||||||
|
platform: "browser",
|
||||||
plugins: [
|
plugins: [
|
||||||
sveltePlugin({
|
sveltePlugin({
|
||||||
preprocess: sveltePreprocess(),
|
preprocess: sveltePreprocess(),
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.17.7",
|
"version": "0.17.11",
|
||||||
"minAppVersion": "0.9.12",
|
"minAppVersion": "0.9.12",
|
||||||
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"author": "vorotamoroz",
|
"author": "vorotamoroz",
|
||||||
|
|||||||
Generated
+850
-8
File diff suppressed because it is too large
Load Diff
+10
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.17.7",
|
"version": "0.17.11",
|
||||||
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
@@ -24,11 +24,20 @@
|
|||||||
"eslint": "^8.28.0",
|
"eslint": "^8.28.0",
|
||||||
"eslint-config-airbnb-base": "^15.0.0",
|
"eslint-config-airbnb-base": "^15.0.0",
|
||||||
"eslint-plugin-import": "^2.26.0",
|
"eslint-plugin-import": "^2.26.0",
|
||||||
|
"events": "^3.3.0",
|
||||||
"obsidian": "^0.16.3",
|
"obsidian": "^0.16.3",
|
||||||
"postcss": "^8.4.19",
|
"postcss": "^8.4.19",
|
||||||
"postcss-load-config": "^4.0.1",
|
"postcss-load-config": "^4.0.1",
|
||||||
|
"pouchdb-adapter-http": "^8.0.0",
|
||||||
|
"pouchdb-adapter-idb": "^8.0.0",
|
||||||
|
"pouchdb-core": "^8.0.0",
|
||||||
|
"pouchdb-find": "^8.0.0",
|
||||||
|
"pouchdb-mapreduce": "^8.0.0",
|
||||||
|
"pouchdb-replication": "^8.0.0",
|
||||||
|
"pouchdb-utils": "file:src/lib/src/patches/pouchdb-utils",
|
||||||
"svelte": "^3.53.1",
|
"svelte": "^3.53.1",
|
||||||
"svelte-preprocess": "^4.10.7",
|
"svelte-preprocess": "^4.10.7",
|
||||||
|
"transform-pouch": "^2.0.0",
|
||||||
"tslib": "^2.4.1",
|
"tslib": "^2.4.1",
|
||||||
"typescript": "^4.9.3"
|
"typescript": "^4.9.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
if (this.plugin.settings.syncOnFileOpen) return true;
|
if (this.plugin.settings.syncOnFileOpen) return true;
|
||||||
if (this.plugin.settings.syncOnSave) return true;
|
if (this.plugin.settings.syncOnSave) return true;
|
||||||
if (this.plugin.settings.syncOnStart) return true;
|
if (this.plugin.settings.syncOnStart) return true;
|
||||||
|
if (this.plugin.settings.syncAfterMerge) return true;
|
||||||
if (this.plugin.localDatabase.syncStatus == "CONNECTED") return true;
|
if (this.plugin.localDatabase.syncStatus == "CONNECTED") return true;
|
||||||
if (this.plugin.localDatabase.syncStatus == "PAUSED") return true;
|
if (this.plugin.localDatabase.syncStatus == "PAUSED") return true;
|
||||||
return false;
|
return false;
|
||||||
@@ -144,7 +145,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
new Setting(setupWizardEl)
|
new Setting(setupWizardEl)
|
||||||
.setName("Discard the existing configuration and set up")
|
.setName("Discard the existing configuration and set up")
|
||||||
.addButton((text) => {
|
.addButton((text) => {
|
||||||
text.setButtonText("Next").onClick(async () => {
|
text.setButtonText("Next").onClick(() => {
|
||||||
if (JSON.stringify(this.plugin.settings) != JSON.stringify(DEFAULT_SETTINGS)) {
|
if (JSON.stringify(this.plugin.settings) != JSON.stringify(DEFAULT_SETTINGS)) {
|
||||||
this.plugin.localDatabase.closeReplication();
|
this.plugin.localDatabase.closeReplication();
|
||||||
this.plugin.settings = { ...DEFAULT_SETTINGS };
|
this.plugin.settings = { ...DEFAULT_SETTINGS };
|
||||||
@@ -170,6 +171,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
this.plugin.settings.syncOnSave = false;
|
this.plugin.settings.syncOnSave = false;
|
||||||
this.plugin.settings.syncOnStart = false;
|
this.plugin.settings.syncOnStart = false;
|
||||||
this.plugin.settings.syncOnFileOpen = false;
|
this.plugin.settings.syncOnFileOpen = false;
|
||||||
|
this.plugin.settings.syncAfterMerge = false;
|
||||||
this.plugin.localDatabase.closeReplication();
|
this.plugin.localDatabase.closeReplication();
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
containerEl.addClass("isWizard");
|
containerEl.addClass("isWizard");
|
||||||
@@ -226,7 +228,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
syncLive.forEach((e) => {
|
syncLive.forEach((e) => {
|
||||||
e.setDisabled(false).setTooltip("");
|
e.setDisabled(false).setTooltip("");
|
||||||
});
|
});
|
||||||
} else if (this.plugin.settings.syncOnFileOpen || this.plugin.settings.syncOnSave || this.plugin.settings.syncOnStart || this.plugin.settings.periodicReplication) {
|
} else if (this.plugin.settings.syncOnFileOpen || this.plugin.settings.syncOnSave || this.plugin.settings.syncOnStart || this.plugin.settings.periodicReplication || this.plugin.settings.syncAfterMerge) {
|
||||||
syncNonLive.forEach((e) => {
|
syncNonLive.forEach((e) => {
|
||||||
e.setDisabled(false).setTooltip("");
|
e.setDisabled(false).setTooltip("");
|
||||||
});
|
});
|
||||||
@@ -387,6 +389,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
this.plugin.settings.syncOnSave = false;
|
this.plugin.settings.syncOnSave = false;
|
||||||
this.plugin.settings.syncOnStart = false;
|
this.plugin.settings.syncOnStart = false;
|
||||||
this.plugin.settings.syncOnFileOpen = false;
|
this.plugin.settings.syncOnFileOpen = false;
|
||||||
|
this.plugin.settings.syncAfterMerge = false;
|
||||||
this.plugin.settings.encrypt = this.plugin.settings.workingEncrypt;
|
this.plugin.settings.encrypt = this.plugin.settings.workingEncrypt;
|
||||||
this.plugin.settings.passphrase = this.plugin.settings.workingPassphrase;
|
this.plugin.settings.passphrase = this.plugin.settings.workingPassphrase;
|
||||||
this.plugin.settings.useDynamicIterationCount = this.plugin.settings.workingUseDynamicIterationCount;
|
this.plugin.settings.useDynamicIterationCount = this.plugin.settings.workingUseDynamicIterationCount;
|
||||||
@@ -433,7 +436,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
this.plugin.settings.syncOnSave = false;
|
this.plugin.settings.syncOnSave = false;
|
||||||
this.plugin.settings.syncOnStart = false;
|
this.plugin.settings.syncOnStart = false;
|
||||||
this.plugin.settings.syncOnFileOpen = false;
|
this.plugin.settings.syncOnFileOpen = false;
|
||||||
|
this.plugin.settings.syncAfterMerge = false;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
|
|
||||||
applyDisplayEnabled();
|
applyDisplayEnabled();
|
||||||
@@ -693,7 +696,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
.setButtonText("Next")
|
.setButtonText("Next")
|
||||||
.setClass("mod-cta")
|
.setClass("mod-cta")
|
||||||
.setDisabled(false)
|
.setDisabled(false)
|
||||||
.onClick(async () => {
|
.onClick(() => {
|
||||||
if (!this.plugin.settings.encrypt) {
|
if (!this.plugin.settings.encrypt) {
|
||||||
this.plugin.settings.passphrase = "";
|
this.plugin.settings.passphrase = "";
|
||||||
}
|
}
|
||||||
@@ -714,7 +717,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
.setButtonText("Discard exist database and proceed")
|
.setButtonText("Discard exist database and proceed")
|
||||||
.setDisabled(false)
|
.setDisabled(false)
|
||||||
.setWarning()
|
.setWarning()
|
||||||
.onClick(async () => {
|
.onClick(() => {
|
||||||
if (!this.plugin.settings.encrypt) {
|
if (!this.plugin.settings.encrypt) {
|
||||||
this.plugin.settings.passphrase = "";
|
this.plugin.settings.passphrase = "";
|
||||||
}
|
}
|
||||||
@@ -793,7 +796,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
button
|
button
|
||||||
.setButtonText("Next")
|
.setButtonText("Next")
|
||||||
.setDisabled(false)
|
.setDisabled(false)
|
||||||
.onClick(async () => {
|
.onClick(() => {
|
||||||
changeDisplay("40");
|
changeDisplay("40");
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@@ -949,7 +952,17 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
applyDisplayEnabled();
|
applyDisplayEnabled();
|
||||||
})
|
})
|
||||||
)
|
),
|
||||||
|
new Setting(containerSyncSettingEl)
|
||||||
|
.setName("Sync after merging file")
|
||||||
|
.setDesc("Sync automatically after merging files")
|
||||||
|
.addToggle((toggle) =>
|
||||||
|
toggle.setValue(this.plugin.settings.syncAfterMerge).onChange(async (value) => {
|
||||||
|
this.plugin.settings.syncAfterMerge = value;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
applyDisplayEnabled();
|
||||||
|
})
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
new Setting(containerSyncSettingEl)
|
new Setting(containerSyncSettingEl)
|
||||||
@@ -1171,8 +1184,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
.setValue(this.plugin.settings.customChunkSize + "")
|
.setValue(this.plugin.settings.customChunkSize + "")
|
||||||
.onChange(async (value) => {
|
.onChange(async (value) => {
|
||||||
let v = Number(value);
|
let v = Number(value);
|
||||||
if (isNaN(v) || v < 100) {
|
if (isNaN(v) || v < 1) {
|
||||||
v = 100;
|
v = 1;
|
||||||
}
|
}
|
||||||
this.plugin.settings.customChunkSize = v;
|
this.plugin.settings.customChunkSize = v;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
@@ -1272,6 +1285,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
this.plugin.settings.syncOnSave = false;
|
this.plugin.settings.syncOnSave = false;
|
||||||
this.plugin.settings.syncOnStart = false;
|
this.plugin.settings.syncOnStart = false;
|
||||||
this.plugin.settings.syncOnFileOpen = false;
|
this.plugin.settings.syncOnFileOpen = false;
|
||||||
|
this.plugin.settings.syncAfterMerge = false;
|
||||||
if (currentPreset == "LIVESYNC") {
|
if (currentPreset == "LIVESYNC") {
|
||||||
this.plugin.settings.liveSync = true;
|
this.plugin.settings.liveSync = true;
|
||||||
Logger("Synchronization setting configured as LiveSync.", LOG_LEVEL.NOTICE);
|
Logger("Synchronization setting configured as LiveSync.", LOG_LEVEL.NOTICE);
|
||||||
@@ -1281,6 +1295,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
this.plugin.settings.syncOnSave = false;
|
this.plugin.settings.syncOnSave = false;
|
||||||
this.plugin.settings.syncOnStart = true;
|
this.plugin.settings.syncOnStart = true;
|
||||||
this.plugin.settings.syncOnFileOpen = true;
|
this.plugin.settings.syncOnFileOpen = true;
|
||||||
|
this.plugin.settings.syncAfterMerge = true;
|
||||||
Logger("Synchronization setting configured as Periodic sync with batch database update.", LOG_LEVEL.NOTICE);
|
Logger("Synchronization setting configured as Periodic sync with batch database update.", LOG_LEVEL.NOTICE);
|
||||||
} else {
|
} else {
|
||||||
Logger("All synchronization disabled.", LOG_LEVEL.NOTICE);
|
Logger("All synchronization disabled.", LOG_LEVEL.NOTICE);
|
||||||
@@ -1408,7 +1423,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
const releaser = await semaphore.acquire(1, "verifyAndRepair");
|
const releaser = await semaphore.acquire(1, "verifyAndRepair");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Logger(`Update into ${file.path}`);
|
Logger(`UPDATE DATABASE ${file.path}`);
|
||||||
await this.plugin.updateIntoDB(file, false, null, true);
|
await this.plugin.updateIntoDB(file, false, null, true);
|
||||||
i++;
|
i++;
|
||||||
Logger(`${i}/${files.length}\n${file.path}`, LOG_LEVEL.NOTICE, "verify");
|
Logger(`${i}/${files.length}\n${file.path}`, LOG_LEVEL.NOTICE, "verify");
|
||||||
|
|||||||
+1
-1
Submodule src/lib updated: bfad1f86d3...39628ac8e6
+148
-149
@@ -2,7 +2,7 @@ import { debounce, Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbst
|
|||||||
import { Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
|
import { Diff, DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
|
||||||
|
|
||||||
import { EntryDoc, LoadedEntry, ObsidianLiveSyncSettings, diff_check_result, diff_result_leaf, EntryBody, LOG_LEVEL, VER, DEFAULT_SETTINGS, diff_result, FLAGMD_REDFLAG, SYNCINFO_ID, InternalFileEntry } from "./lib/src/types";
|
import { EntryDoc, LoadedEntry, ObsidianLiveSyncSettings, diff_check_result, diff_result_leaf, EntryBody, LOG_LEVEL, VER, DEFAULT_SETTINGS, diff_result, FLAGMD_REDFLAG, SYNCINFO_ID, InternalFileEntry } from "./lib/src/types";
|
||||||
import { PluginDataEntry, PERIODIC_PLUGIN_SWEEP, PluginList, DevicePluginList, InternalFileInfo } from "./types";
|
import { PluginDataEntry, PERIODIC_PLUGIN_SWEEP, PluginList, DevicePluginList, InternalFileInfo, queueItem } from "./types";
|
||||||
import {
|
import {
|
||||||
base64ToString,
|
base64ToString,
|
||||||
arrayBufferToBase64,
|
arrayBufferToBase64,
|
||||||
@@ -11,14 +11,13 @@ import {
|
|||||||
versionNumberString2Number,
|
versionNumberString2Number,
|
||||||
runWithLock,
|
runWithLock,
|
||||||
shouldBeIgnored,
|
shouldBeIgnored,
|
||||||
getProcessingCounts,
|
|
||||||
setLockNotifier,
|
|
||||||
isPlainText,
|
isPlainText,
|
||||||
setNoticeClass,
|
setNoticeClass,
|
||||||
NewNotice,
|
NewNotice,
|
||||||
getLocks,
|
|
||||||
WrappedNotice,
|
WrappedNotice,
|
||||||
Semaphore,
|
Semaphore,
|
||||||
|
getDocData,
|
||||||
|
isDocContentSame,
|
||||||
} from "./lib/src/utils";
|
} from "./lib/src/utils";
|
||||||
import { Logger, setLogger } from "./lib/src/logger";
|
import { Logger, setLogger } from "./lib/src/logger";
|
||||||
import { LocalPouchDB } from "./LocalPouchDB";
|
import { LocalPouchDB } from "./LocalPouchDB";
|
||||||
@@ -36,6 +35,8 @@ const isDebug = false;
|
|||||||
|
|
||||||
import { InputStringDialog, PluginDialogModal, PopoverSelectString } from "./dialogs";
|
import { InputStringDialog, PluginDialogModal, PopoverSelectString } from "./dialogs";
|
||||||
import { isCloudantURI } from "./lib/src/utils_couchdb";
|
import { isCloudantURI } from "./lib/src/utils_couchdb";
|
||||||
|
import { getGlobalStore, observeStores } from "./lib/src/store";
|
||||||
|
import { lockStore } from "./lib/src/stores";
|
||||||
|
|
||||||
setNoticeClass(Notice);
|
setNoticeClass(Notice);
|
||||||
|
|
||||||
@@ -309,6 +310,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.settings.syncOnSave = false;
|
this.settings.syncOnSave = false;
|
||||||
this.settings.syncOnStart = false;
|
this.settings.syncOnStart = false;
|
||||||
this.settings.syncOnFileOpen = false;
|
this.settings.syncOnFileOpen = false;
|
||||||
|
this.settings.syncAfterMerge = false;
|
||||||
this.settings.periodicReplication = false;
|
this.settings.periodicReplication = false;
|
||||||
this.settings.versionUpFlash = "Self-hosted LiveSync has been upgraded and some behaviors have changed incompatibly. All automatic synchronization is now disabled temporary. Ensure that other devices are also upgraded, and enable synchronization again.";
|
this.settings.versionUpFlash = "Self-hosted LiveSync has been upgraded and some behaviors have changed incompatibly. All automatic synchronization is now disabled temporary. Ensure that other devices are also upgraded, and enable synchronization again.";
|
||||||
this.saveSettings();
|
this.saveSettings();
|
||||||
@@ -342,7 +344,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
|
|
||||||
this.statusBar = this.addStatusBarItem();
|
this.statusBar = this.addStatusBarItem();
|
||||||
this.statusBar.addClass("syncstatusbar");
|
this.statusBar.addClass("syncstatusbar");
|
||||||
this.refreshStatusText = this.refreshStatusText.bind(this);
|
|
||||||
|
|
||||||
this.statusBar2 = this.addStatusBarItem();
|
this.statusBar2 = this.addStatusBarItem();
|
||||||
this.watchVaultChange = this.watchVaultChange.bind(this);
|
this.watchVaultChange = this.watchVaultChange.bind(this);
|
||||||
@@ -357,6 +358,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.parseReplicationResult = this.parseReplicationResult.bind(this);
|
this.parseReplicationResult = this.parseReplicationResult.bind(this);
|
||||||
|
|
||||||
this.setPeriodicSync = this.setPeriodicSync.bind(this);
|
this.setPeriodicSync = this.setPeriodicSync.bind(this);
|
||||||
|
this.clearPeriodicSync = this.clearPeriodicSync.bind(this);
|
||||||
this.periodicSync = this.periodicSync.bind(this);
|
this.periodicSync = this.periodicSync.bind(this);
|
||||||
this.loadQueuedFiles = this.loadQueuedFiles.bind(this);
|
this.loadQueuedFiles = this.loadQueuedFiles.bind(this);
|
||||||
|
|
||||||
@@ -375,6 +377,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.settings.syncOnSave = false;
|
this.settings.syncOnSave = false;
|
||||||
this.settings.syncOnStart = false;
|
this.settings.syncOnStart = false;
|
||||||
this.settings.syncOnFileOpen = false;
|
this.settings.syncOnFileOpen = false;
|
||||||
|
this.settings.syncAfterMerge = false;
|
||||||
this.settings.autoSweepPlugins = false;
|
this.settings.autoSweepPlugins = false;
|
||||||
this.settings.usePluginSync = false;
|
this.settings.usePluginSync = false;
|
||||||
this.settings.suspendFileWatching = true;
|
this.settings.suspendFileWatching = true;
|
||||||
@@ -630,9 +633,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
|
|
||||||
this.triggerRealizeSettingSyncMode = debounce(this.triggerRealizeSettingSyncMode.bind(this), 1000);
|
this.triggerRealizeSettingSyncMode = debounce(this.triggerRealizeSettingSyncMode.bind(this), 1000);
|
||||||
this.triggerCheckPluginUpdate = debounce(this.triggerCheckPluginUpdate.bind(this), 3000);
|
this.triggerCheckPluginUpdate = debounce(this.triggerCheckPluginUpdate.bind(this), 3000);
|
||||||
setLockNotifier(() => {
|
|
||||||
this.refreshStatusText();
|
|
||||||
});
|
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "livesync-plugin-dialog",
|
id: "livesync-plugin-dialog",
|
||||||
name: "Show Plugins and their settings",
|
name: "Show Plugins and their settings",
|
||||||
@@ -722,9 +723,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
const isMobile = this.app.isMobile;
|
const isMobile = this.app.isMobile;
|
||||||
this.localDatabase = new LocalPouchDB(this.settings, vaultName, isMobile);
|
this.localDatabase = new LocalPouchDB(this.settings, vaultName, isMobile);
|
||||||
this.localDatabase.updateInfo = () => {
|
this.observeForLogs();
|
||||||
this.refreshStatusText();
|
|
||||||
};
|
|
||||||
return await this.localDatabase.initializeDatabase();
|
return await this.localDatabase.initializeDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -866,6 +865,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
if (this.watchedFileEventQueue[i].type != type) break;
|
if (this.watchedFileEventQueue[i].type != type) break;
|
||||||
this.watchedFileEventQueue.remove(this.watchedFileEventQueue[i]);
|
this.watchedFileEventQueue.remove(this.watchedFileEventQueue[i]);
|
||||||
|
this.queuedFilesStore.set({ queuedItems: this.queuedFiles, fileEventItems: this.watchedFileEventQueue });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -878,7 +878,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
ctx
|
ctx
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
this.refreshStatusText();
|
this.queuedFilesStore.set({ queuedItems: this.queuedFiles, fileEventItems: this.watchedFileEventQueue });
|
||||||
if (this.isReady) {
|
if (this.isReady) {
|
||||||
await this.procFileEvent();
|
await this.procFileEvent();
|
||||||
}
|
}
|
||||||
@@ -917,8 +917,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
if (queue.type == "DELETE") {
|
if (queue.type == "DELETE") {
|
||||||
if (file instanceof TFile) {
|
if (file instanceof TFile) {
|
||||||
await this.deleteFromDB(file);
|
await this.deleteFromDB(file);
|
||||||
} else if (file instanceof TFolder) {
|
|
||||||
await this.deleteFolderOnDB(file);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (queue.type == "RENAME") {
|
if (queue.type == "RENAME") {
|
||||||
@@ -933,11 +931,9 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
await this.localDatabase.kvDB.set(key, file.stat.mtime);
|
await this.localDatabase.kvDB.set(key, file.stat.mtime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.refreshStatusText();
|
|
||||||
} while (this.watchedFileEventQueue.length != 0);
|
} while (this.watchedFileEventQueue.length != 0);
|
||||||
return true;
|
return true;
|
||||||
})
|
})
|
||||||
this.refreshStatusText();
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1052,30 +1048,13 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
return this.getFilePath(file.parent) + "/" + file.name;
|
return this.getFilePath(file.parent) + "/" + file.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
async watchVaultRenameAsync(file: TAbstractFile, oldFile: any, cache?: CacheData) {
|
async watchVaultRenameAsync(file: TFile, oldFile: any, cache?: CacheData) {
|
||||||
Logger(`${oldFile} renamed to ${file.path}`, LOG_LEVEL.VERBOSE);
|
Logger(`${oldFile} renamed to ${file.path}`, LOG_LEVEL.VERBOSE);
|
||||||
if (file instanceof TFolder) {
|
if (file instanceof TFile) {
|
||||||
const newFiles = this.GetAllFilesRecursively(file);
|
|
||||||
// for guard edge cases. this won't happen and each file's event will be raise.
|
|
||||||
for (const i of newFiles) {
|
|
||||||
try {
|
|
||||||
const newFilePath = normalizePath(this.getFilePath(i));
|
|
||||||
const newFile = getAbstractFileByPath(newFilePath);
|
|
||||||
if (newFile instanceof TFile) {
|
|
||||||
Logger(`save ${newFile.path} into db`);
|
|
||||||
await this.updateIntoDB(newFile);
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
Logger(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Logger(`delete below ${oldFile} from db`);
|
|
||||||
await this.deleteFromDBbyPath(oldFile);
|
|
||||||
} else if (file instanceof TFile) {
|
|
||||||
try {
|
try {
|
||||||
Logger(`file save ${file.path} into db`);
|
// Logger(`RENAMING.. ${file.path} into db`);
|
||||||
await this.updateIntoDB(file, false, cache);
|
await this.updateIntoDB(file, false, cache);
|
||||||
Logger(`deleted ${oldFile} from db`);
|
// Logger(`deleted ${oldFile} from db`);
|
||||||
await this.deleteFromDBbyPath(oldFile);
|
await this.deleteFromDBbyPath(oldFile);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
Logger(ex);
|
Logger(ex);
|
||||||
@@ -1196,7 +1175,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
ctime: doc.ctime,
|
ctime: doc.ctime,
|
||||||
mtime: doc.mtime,
|
mtime: doc.mtime,
|
||||||
});
|
});
|
||||||
// this.batchFileChange = this.batchFileChange.filter((e) => e != newFile.path);
|
|
||||||
Logger(msg + path);
|
Logger(msg + path);
|
||||||
touch(newFile);
|
touch(newFile);
|
||||||
this.app.vault.trigger("create", newFile);
|
this.app.vault.trigger("create", newFile);
|
||||||
@@ -1212,11 +1190,10 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
await this.ensureDirectory(path);
|
await this.ensureDirectory(path);
|
||||||
try {
|
try {
|
||||||
const newFile = await this.app.vault.create(normalizePath(path), doc.data, {
|
const newFile = await this.app.vault.create(normalizePath(path), getDocData(doc.data), {
|
||||||
ctime: doc.ctime,
|
ctime: doc.ctime,
|
||||||
mtime: doc.mtime,
|
mtime: doc.mtime,
|
||||||
});
|
});
|
||||||
// this.batchFileChange = this.batchFileChange.filter((e) => e != newFile.path);
|
|
||||||
Logger(msg + path);
|
Logger(msg + path);
|
||||||
touch(newFile);
|
touch(newFile);
|
||||||
this.app.vault.trigger("create", newFile);
|
this.app.vault.trigger("create", newFile);
|
||||||
@@ -1239,11 +1216,11 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
} else {
|
} else {
|
||||||
await this.app.vault.delete(file);
|
await this.app.vault.delete(file);
|
||||||
}
|
}
|
||||||
Logger(`deleted:${file.path}`);
|
Logger(`xxx <- STORAGE (deleted) ${file.path}`);
|
||||||
Logger(`other items:${dir.children.length}`);
|
Logger(`files: ${dir.children.length}`);
|
||||||
if (dir.children.length == 0) {
|
if (dir.children.length == 0) {
|
||||||
if (!this.settings.doNotDeleteFolder) {
|
if (!this.settings.doNotDeleteFolder) {
|
||||||
Logger(`all files deleted by replication, so delete dir`);
|
Logger(`All files under the parent directory (${dir}) have been deleted, so delete this one.`);
|
||||||
await this.deleteVaultItem(dir);
|
await this.deleteVaultItem(dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1302,7 +1279,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
await this.ensureDirectory(path);
|
await this.ensureDirectory(path);
|
||||||
try {
|
try {
|
||||||
await this.app.vault.modify(file, doc.data, { ctime: doc.ctime, mtime: doc.mtime });
|
await this.app.vault.modify(file, getDocData(doc.data), { ctime: doc.ctime, mtime: doc.mtime });
|
||||||
Logger(msg + path);
|
Logger(msg + path);
|
||||||
// this.batchFileChange = this.batchFileChange.filter((e) => e != file.path);
|
// this.batchFileChange = this.batchFileChange.filter((e) => e != file.path);
|
||||||
const xf = getAbstractFileByPath(file.path) as TFile;
|
const xf = getAbstractFileByPath(file.path) as TFile;
|
||||||
@@ -1351,7 +1328,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
this.refreshStatusText();
|
|
||||||
}
|
}
|
||||||
async handleDBChangedAsync(change: EntryBody) {
|
async handleDBChangedAsync(change: EntryBody) {
|
||||||
|
|
||||||
@@ -1396,13 +1372,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
queuedFiles: {
|
queuedFiles = [] as queueItem[];
|
||||||
entry: EntryBody;
|
queuedFilesStore = getGlobalStore("queuedFiles", { queuedItems: [] as queueItem[], fileEventItems: [] as FileEventItem[] });
|
||||||
missingChildren: string[];
|
|
||||||
timeout?: number;
|
|
||||||
done?: boolean;
|
|
||||||
warned?: boolean;
|
|
||||||
}[] = [];
|
|
||||||
chunkWaitTimeout = 60000;
|
chunkWaitTimeout = 60000;
|
||||||
|
|
||||||
saveQueuedFiles() {
|
saveQueuedFiles() {
|
||||||
@@ -1429,7 +1400,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
await this.syncInternalFilesAndDatabase("pull", false, false, w);
|
await this.syncInternalFilesAndDatabase("pull", false, false, w);
|
||||||
Logger(`Applying hidden ${w.length} files changed`);
|
Logger(`Applying hidden ${w.length} files changed`);
|
||||||
});
|
});
|
||||||
this.refreshStatusText();
|
|
||||||
}
|
}
|
||||||
procInternalFile(filename: string) {
|
procInternalFile(filename: string) {
|
||||||
this.procInternalFiles.push(filename);
|
this.procInternalFiles.push(filename);
|
||||||
@@ -1461,6 +1431,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.queuedFiles = this.queuedFiles.filter((e) => !e.done);
|
this.queuedFiles = this.queuedFiles.filter((e) => !e.done);
|
||||||
|
this.queuedFilesStore.set({ queuedItems: this.queuedFiles, fileEventItems: this.watchedFileEventQueue });
|
||||||
this.saveQueuedFiles();
|
this.saveQueuedFiles();
|
||||||
}
|
}
|
||||||
parseIncomingChunk(chunk: PouchDB.Core.ExistingDocument<EntryDoc>) {
|
parseIncomingChunk(chunk: PouchDB.Core.ExistingDocument<EntryDoc>) {
|
||||||
@@ -1524,7 +1495,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
|
|
||||||
//---> Sync
|
//---> Sync
|
||||||
async parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): Promise<void> {
|
async parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): Promise<void> {
|
||||||
this.refreshStatusText();
|
|
||||||
for (const change of docs) {
|
for (const change of docs) {
|
||||||
if (isPluginChunk(change._id)) {
|
if (isPluginChunk(change._id)) {
|
||||||
if (this.settings.notifyPluginOrSettingUpdated) {
|
if (this.settings.notifyPluginOrSettingUpdated) {
|
||||||
@@ -1596,8 +1566,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setPeriodicSync() {
|
setPeriodicSync() {
|
||||||
|
this.clearPeriodicSync();
|
||||||
if (this.settings.periodicReplication && this.settings.periodicReplicationInterval > 0) {
|
if (this.settings.periodicReplication && this.settings.periodicReplicationInterval > 0) {
|
||||||
this.clearPeriodicSync();
|
|
||||||
this.periodicSyncHandler = this.setInterval(async () => await this.periodicSync(), Math.max(this.settings.periodicReplicationInterval, 30) * 1000);
|
this.periodicSyncHandler = this.setInterval(async () => await this.periodicSync(), Math.max(this.settings.periodicReplicationInterval, 30) * 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1639,7 +1609,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
if (this.settings.liveSync) {
|
if (this.settings.liveSync) {
|
||||||
this.localDatabase.openReplication(this.settings, true, false, this.parseReplicationResult);
|
this.localDatabase.openReplication(this.settings, true, false, this.parseReplicationResult);
|
||||||
this.refreshStatusText();
|
|
||||||
}
|
}
|
||||||
if (this.settings.syncInternalFiles) {
|
if (this.settings.syncInternalFiles) {
|
||||||
await this.syncInternalFilesAndDatabase("safe", false);
|
await this.syncInternalFilesAndDatabase("safe", false);
|
||||||
@@ -1651,63 +1620,80 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
|
|
||||||
lastMessage = "";
|
lastMessage = "";
|
||||||
|
|
||||||
|
observeForLogs() {
|
||||||
|
const observer__ = observeStores(this.queuedFilesStore, lockStore);
|
||||||
|
const observer = observeStores(observer__, this.localDatabase.stat);
|
||||||
|
|
||||||
|
observer.observe(e => {
|
||||||
|
const sent = e.sent;
|
||||||
|
const arrived = e.arrived;
|
||||||
|
const maxPullSeq = e.maxPullSeq;
|
||||||
|
const maxPushSeq = e.maxPushSeq;
|
||||||
|
const lastSyncPullSeq = e.lastSyncPullSeq;
|
||||||
|
const lastSyncPushSeq = e.lastSyncPushSeq;
|
||||||
|
let pushLast = "";
|
||||||
|
let pullLast = "";
|
||||||
|
let w = "";
|
||||||
|
switch (e.syncStatus) {
|
||||||
|
case "CLOSED":
|
||||||
|
case "COMPLETED":
|
||||||
|
case "NOT_CONNECTED":
|
||||||
|
w = "⏹";
|
||||||
|
break;
|
||||||
|
case "STARTED":
|
||||||
|
w = "🌀";
|
||||||
|
break;
|
||||||
|
case "PAUSED":
|
||||||
|
w = "💤";
|
||||||
|
break;
|
||||||
|
case "CONNECTED":
|
||||||
|
w = "⚡";
|
||||||
|
pushLast = ((lastSyncPushSeq == 0) ? "" : (lastSyncPushSeq >= maxPushSeq ? " (LIVE)" : ` (${maxPushSeq - lastSyncPushSeq})`));
|
||||||
|
pullLast = ((lastSyncPullSeq == 0) ? "" : (lastSyncPullSeq >= maxPullSeq ? " (LIVE)" : ` (${maxPullSeq - lastSyncPullSeq})`));
|
||||||
|
break;
|
||||||
|
case "ERRORED":
|
||||||
|
w = "⚠";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
w = "?";
|
||||||
|
}
|
||||||
|
this.statusBar.title = e.syncStatus;
|
||||||
|
let waiting = "";
|
||||||
|
if (this.settings.batchSave) {
|
||||||
|
waiting = " " + this.watchedFileEventQueue.map((e) => "🛫").join("");
|
||||||
|
waiting = waiting.replace(/(🛫){10}/g, "🚀");
|
||||||
|
}
|
||||||
|
let queued = "";
|
||||||
|
const queue = Object.entries(e.queuedItems).filter((e) => !e[1].warned);
|
||||||
|
const queuedCount = queue.length;
|
||||||
|
|
||||||
|
if (queuedCount) {
|
||||||
|
const pieces = queue.map((e) => e[1].missingChildren).reduce((prev, cur) => prev + cur.length, 0);
|
||||||
|
queued = ` 🧩 ${queuedCount} (${pieces})`;
|
||||||
|
}
|
||||||
|
const processes = e.count;
|
||||||
|
const processesDisp = processes == 0 ? "" : ` ⏳${processes}`;
|
||||||
|
const message = `Sync: ${w} ↑${sent}${pushLast} ↓${arrived}${pullLast}${waiting}${processesDisp}${queued}`;
|
||||||
|
// const locks = getLocks();
|
||||||
|
const pendingTask = e.pending.length
|
||||||
|
? "\nPending: " +
|
||||||
|
Object.entries(e.pending.reduce((p, c) => ({ ...p, [c]: (p[c] ?? 0) + 1 }), {} as { [key: string]: number }))
|
||||||
|
.map((e) => `${e[0]}${e[1] == 1 ? "" : `(${e[1]})`}`)
|
||||||
|
.join(", ")
|
||||||
|
: "";
|
||||||
|
|
||||||
|
const runningTask = e.running.length
|
||||||
|
? "\nRunning: " +
|
||||||
|
Object.entries(e.running.reduce((p, c) => ({ ...p, [c]: (p[c] ?? 0) + 1 }), {} as { [key: string]: number }))
|
||||||
|
.map((e) => `${e[0]}${e[1] == 1 ? "" : `(${e[1]})`}`)
|
||||||
|
.join(", ")
|
||||||
|
: "";
|
||||||
|
this.setStatusBarText(message + pendingTask + runningTask);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
refreshStatusText() {
|
refreshStatusText() {
|
||||||
const sent = this.localDatabase.docSent;
|
return;
|
||||||
const arrived = this.localDatabase.docArrived;
|
|
||||||
let w = "";
|
|
||||||
switch (this.localDatabase.syncStatus) {
|
|
||||||
case "CLOSED":
|
|
||||||
case "COMPLETED":
|
|
||||||
case "NOT_CONNECTED":
|
|
||||||
w = "⏹";
|
|
||||||
break;
|
|
||||||
case "STARTED":
|
|
||||||
w = "🌀";
|
|
||||||
break;
|
|
||||||
case "PAUSED":
|
|
||||||
w = "💤";
|
|
||||||
break;
|
|
||||||
case "CONNECTED":
|
|
||||||
w = "⚡";
|
|
||||||
break;
|
|
||||||
case "ERRORED":
|
|
||||||
w = "⚠";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
w = "?";
|
|
||||||
}
|
|
||||||
this.statusBar.title = this.localDatabase.syncStatus;
|
|
||||||
let waiting = "";
|
|
||||||
if (this.settings.batchSave) {
|
|
||||||
waiting = " " + this.watchedFileEventQueue.map((e) => "🛫").join("");
|
|
||||||
waiting = waiting.replace(/(🛫){10}/g, "🚀");
|
|
||||||
}
|
|
||||||
let queued = "";
|
|
||||||
const queue = Object.entries(this.queuedFiles).filter((e) => !e[1].warned);
|
|
||||||
const queuedCount = queue.length;
|
|
||||||
|
|
||||||
if (queuedCount) {
|
|
||||||
const pieces = queue.map((e) => e[1].missingChildren).reduce((prev, cur) => prev + cur.length, 0);
|
|
||||||
queued = ` 🧩 ${queuedCount} (${pieces})`;
|
|
||||||
}
|
|
||||||
const processes = getProcessingCounts();
|
|
||||||
const processesDisp = processes == 0 ? "" : ` ⏳${processes}`;
|
|
||||||
const message = `Sync: ${w} ↑${sent} ↓${arrived}${waiting}${processesDisp}${queued}`;
|
|
||||||
const locks = getLocks();
|
|
||||||
const pendingTask = locks.pending.length
|
|
||||||
? "\nPending: " +
|
|
||||||
Object.entries(locks.pending.reduce((p, c) => ({ ...p, [c]: (p[c] ?? 0) + 1 }), {} as { [key: string]: number }))
|
|
||||||
.map((e) => `${e[0]}${e[1] == 1 ? "" : `(${e[1]})`}`)
|
|
||||||
.join(", ")
|
|
||||||
: "";
|
|
||||||
|
|
||||||
const runningTask = locks.running.length
|
|
||||||
? "\nRunning: " +
|
|
||||||
Object.entries(locks.running.reduce((p, c) => ({ ...p, [c]: (p[c] ?? 0) + 1 }), {} as { [key: string]: number }))
|
|
||||||
.map((e) => `${e[0]}${e[1] == 1 ? "" : `(${e[1]})`}`)
|
|
||||||
.join(", ")
|
|
||||||
: "";
|
|
||||||
this.setStatusBarText(message + pendingTask + runningTask);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logHideTimer: NodeJS.Timeout = null;
|
logHideTimer: NodeJS.Timeout = null;
|
||||||
@@ -1720,10 +1706,12 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
|
|
||||||
if (this.settings.showStatusOnEditor) {
|
if (this.settings.showStatusOnEditor) {
|
||||||
const root = activeDocument.documentElement;
|
const root = activeDocument.documentElement;
|
||||||
root.style.setProperty("--slsmessage", '"' + (newMsg + "\n" + newLog).split("\n").join("\\a ") + '"');
|
const q = root.querySelectorAll(`.CodeMirror-wrap,.cm-s-obsidian>.cm-editor,.canvas-wrapper`);
|
||||||
|
q.forEach(e => e.setAttr("data-log", '' + (newMsg + "\n" + newLog) + ''))
|
||||||
} else {
|
} else {
|
||||||
const root = activeDocument.documentElement;
|
const root = activeDocument.documentElement;
|
||||||
root.style.setProperty("--slsmessage", '""');
|
const q = root.querySelectorAll(`.CodeMirror-wrap,.cm-s-obsidian>.cm-editor,.canvas-wrapper`);
|
||||||
|
q.forEach(e => e.setAttr("data-log", ''))
|
||||||
}
|
}
|
||||||
if (this.logHideTimer != null) {
|
if (this.logHideTimer != null) {
|
||||||
clearTimeout(this.logHideTimer);
|
clearTimeout(this.logHideTimer);
|
||||||
@@ -1860,7 +1848,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
};
|
};
|
||||||
|
|
||||||
await runAll("UPDATE DATABASE", onlyInStorage, async (e) => {
|
await runAll("UPDATE DATABASE", onlyInStorage, async (e) => {
|
||||||
Logger(`Update into ${e.path}`);
|
Logger(`UPDATE DATABASE ${e.path}`);
|
||||||
await this.updateIntoDB(e, initialScan);
|
await this.updateIntoDB(e, initialScan);
|
||||||
});
|
});
|
||||||
if (!initialScan) {
|
if (!initialScan) {
|
||||||
@@ -1951,18 +1939,18 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
try {
|
try {
|
||||||
const doc = await this.localDatabase.getDBEntry(path, { rev: rev }, false, false, true);
|
const doc = await this.localDatabase.getDBEntry(path, { rev: rev }, false, false, true);
|
||||||
if (doc === false) return false;
|
if (doc === false) return false;
|
||||||
let data = doc.data;
|
let data = getDocData(doc.data)
|
||||||
if (doc.datatype == "newnote") {
|
if (doc.datatype == "newnote") {
|
||||||
data = base64ToString(doc.data);
|
data = base64ToString(data);
|
||||||
} else if (doc.datatype == "plain") {
|
} else if (doc.datatype == "plain") {
|
||||||
data = doc.data;
|
// NO OP.
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
deleted: doc.deleted || doc._deleted,
|
deleted: doc.deleted || doc._deleted,
|
||||||
ctime: doc.ctime,
|
ctime: doc.ctime,
|
||||||
mtime: doc.mtime,
|
mtime: doc.mtime,
|
||||||
rev: rev,
|
rev: rev,
|
||||||
data: data,
|
data: data
|
||||||
};
|
};
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex.status && ex.status == 404) {
|
if (ex.status && ex.status == 404) {
|
||||||
@@ -2297,6 +2285,9 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
await this.pullFile(filename);
|
await this.pullFile(filename);
|
||||||
Logger("concat both file");
|
Logger("concat both file");
|
||||||
|
if (this.settings.syncAfterMerge && !this.suspended) {
|
||||||
|
await this.replicate();
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
//resolved, check again.
|
//resolved, check again.
|
||||||
this.showIfConflicted(filename);
|
this.showIfConflicted(filename);
|
||||||
@@ -2304,9 +2295,12 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
} else if (toDelete == null) {
|
} else if (toDelete == null) {
|
||||||
Logger("Leave it still conflicted");
|
Logger("Leave it still conflicted");
|
||||||
} else {
|
} else {
|
||||||
Logger(`Conflict resolved:${filename}`);
|
|
||||||
await this.localDatabase.deleteDBEntry(filename, { rev: toDelete });
|
await this.localDatabase.deleteDBEntry(filename, { rev: toDelete });
|
||||||
await this.pullFile(filename, null, true, toKeep);
|
await this.pullFile(filename, null, true, toKeep);
|
||||||
|
Logger(`Conflict resolved:${filename}`);
|
||||||
|
if (this.settings.syncAfterMerge && !this.suspended) {
|
||||||
|
await this.replicate();
|
||||||
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
//resolved, check again.
|
//resolved, check again.
|
||||||
this.showIfConflicted(filename);
|
this.showIfConflicted(filename);
|
||||||
@@ -2354,6 +2348,9 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
if (conflictCheckResult === true) {
|
if (conflictCheckResult === true) {
|
||||||
//auto resolved, but need check again;
|
//auto resolved, but need check again;
|
||||||
|
if (this.settings.syncAfterMerge && !this.suspended) {
|
||||||
|
await this.replicate();
|
||||||
|
}
|
||||||
Logger("conflict:Automatically merged, but we have to check it again");
|
Logger("conflict:Automatically merged, but we have to check it again");
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.showIfConflicted(filename);
|
this.showIfConflicted(filename);
|
||||||
@@ -2447,11 +2444,13 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
if (shouldBeIgnored(file.path)) {
|
if (shouldBeIgnored(file.path)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let content = "";
|
let content: string | string[];
|
||||||
let datatype: "plain" | "newnote" = "newnote";
|
let datatype: "plain" | "newnote" = "newnote";
|
||||||
if (!cache) {
|
if (!cache) {
|
||||||
if (!isPlainText(file.name)) {
|
if (!isPlainText(file.name)) {
|
||||||
|
Logger(`Reading : ${file.path}`, LOG_LEVEL.VERBOSE);
|
||||||
const contentBin = await this.app.vault.readBinary(file);
|
const contentBin = await this.app.vault.readBinary(file);
|
||||||
|
Logger(`Processing: ${file.path}`, LOG_LEVEL.VERBOSE);
|
||||||
content = await arrayBufferToBase64(contentBin);
|
content = await arrayBufferToBase64(contentBin);
|
||||||
datatype = "newnote";
|
datatype = "newnote";
|
||||||
} else {
|
} else {
|
||||||
@@ -2487,12 +2486,12 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
try {
|
try {
|
||||||
const old = await this.localDatabase.getDBEntry(fullPath, null, false, false);
|
const old = await this.localDatabase.getDBEntry(fullPath, null, false, false);
|
||||||
if (old !== false) {
|
if (old !== false) {
|
||||||
const oldData = { data: old.data, deleted: old._deleted || old.deleted, };
|
const oldData = { data: old.data, deleted: old._deleted || old.deleted };
|
||||||
const newData = { data: d.data, deleted: d._deleted || d.deleted };
|
const newData = { data: d.data, deleted: d._deleted || d.deleted };
|
||||||
if (JSON.stringify(oldData) == JSON.stringify(newData)) {
|
if (oldData.deleted != newData.deleted) return false;
|
||||||
Logger(msg + "Skipped (not changed) " + fullPath + ((d._deleted || d.deleted) ? " (deleted)" : ""), LOG_LEVEL.VERBOSE);
|
if (!isDocContentSame(old.data, newData.data)) return false;
|
||||||
return true;
|
Logger(msg + "Skipped (not changed) " + fullPath + ((d._deleted || d.deleted) ? " (deleted)" : ""), LOG_LEVEL.VERBOSE);
|
||||||
}
|
return true;
|
||||||
// d._rev = old._rev;
|
// d._rev = old._rev;
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
@@ -2554,7 +2553,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
async getPluginList(): Promise<{ plugins: PluginList; allPlugins: DevicePluginList; thisDevicePlugins: DevicePluginList }> {
|
async getPluginList(): Promise<{ plugins: PluginList; allPlugins: DevicePluginList; thisDevicePlugins: DevicePluginList }> {
|
||||||
const db = this.localDatabase.localDatabase;
|
const db = this.localDatabase.localDatabase;
|
||||||
const docList = await db.allDocs<PluginDataEntry>({ startkey: PSCHeader, endkey: PSCHeaderEnd, include_docs: false });
|
const docList = await db.allDocs<PluginDataEntry>({ startkey: PSCHeader, endkey: PSCHeaderEnd, include_docs: false });
|
||||||
const oldDocs: PluginDataEntry[] = ((await Promise.all(docList.rows.map(async (e) => await this.localDatabase.getDBEntry(e.id)))).filter((e) => e !== false) as LoadedEntry[]).map((e) => JSON.parse(e.data));
|
const oldDocs: PluginDataEntry[] = ((await Promise.all(docList.rows.map(async (e) => await this.localDatabase.getDBEntry(e.id)))).filter((e) => e !== false) as LoadedEntry[]).map((e) => JSON.parse(getDocData(e.data)));
|
||||||
const plugins: { [key: string]: PluginDataEntry[] } = {};
|
const plugins: { [key: string]: PluginDataEntry[] } = {};
|
||||||
const allPlugins: { [key: string]: PluginDataEntry } = {};
|
const allPlugins: { [key: string]: PluginDataEntry } = {};
|
||||||
const thisDevicePlugins: { [key: string]: PluginDataEntry } = {};
|
const thisDevicePlugins: { [key: string]: PluginDataEntry } = {};
|
||||||
@@ -2962,33 +2961,33 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
if (doc._conflicts.length == 0) return false;
|
if (doc._conflicts.length == 0) return false;
|
||||||
Logger(`Hidden file conflicted:${id2filenameInternalChunk(id)}`);
|
Logger(`Hidden file conflicted:${id2filenameInternalChunk(id)}`);
|
||||||
const conflicts = doc._conflicts.sort((a, b) => Number(a.split("-")[0]) - Number(b.split("-")[0]));
|
const conflicts = doc._conflicts.sort((a, b) => Number(a.split("-")[0]) - Number(b.split("-")[0]));
|
||||||
|
|
||||||
const revA = doc._rev;
|
const revA = doc._rev;
|
||||||
const revB = conflicts[0];
|
const revB = conflicts[0];
|
||||||
|
|
||||||
const conflictedRev = conflicts[0];
|
if (doc._id.endsWith(".json")) {
|
||||||
const conflictedRevNo = Number(conflictedRev.split("-")[0]);
|
const conflictedRev = conflicts[0];
|
||||||
//Search
|
const conflictedRevNo = Number(conflictedRev.split("-")[0]);
|
||||||
const revFrom = (await this.localDatabase.localDatabase.get(id, { revs_info: true })) as unknown as LoadedEntry & PouchDB.Core.GetMeta;
|
//Search
|
||||||
const commonBase = revFrom._revs_info.filter(e => e.status == "available" && Number(e.rev.split("-")[0]) < conflictedRevNo).first().rev ?? "";
|
const revFrom = (await this.localDatabase.localDatabase.get(id, { revs_info: true })) as unknown as LoadedEntry & PouchDB.Core.GetMeta;
|
||||||
const result = await this.mergeObject(id, commonBase, doc._rev, conflictedRev);
|
const commonBase = revFrom._revs_info.filter(e => e.status == "available" && Number(e.rev.split("-")[0]) < conflictedRevNo).first()?.rev ?? "";
|
||||||
if (result) {
|
const result = await this.mergeObject(id, commonBase, doc._rev, conflictedRev);
|
||||||
Logger(`Object merge:${id}`, LOG_LEVEL.INFO);
|
if (result) {
|
||||||
const filename = id2filenameInternalChunk(id);
|
Logger(`Object merge:${id}`, LOG_LEVEL.INFO);
|
||||||
const isExists = await this.app.vault.adapter.exists(filename);
|
const filename = id2filenameInternalChunk(id);
|
||||||
if (!isExists) {
|
const isExists = await this.app.vault.adapter.exists(filename);
|
||||||
await this.ensureDirectoryEx(filename);
|
if (!isExists) {
|
||||||
|
await this.ensureDirectoryEx(filename);
|
||||||
|
}
|
||||||
|
await this.app.vault.adapter.write(filename, result);
|
||||||
|
const stat = await this.app.vault.adapter.stat(filename);
|
||||||
|
await this.storeInternalFileToDatabase({ path: filename, ...stat });
|
||||||
|
await this.extractInternalFileFromDatabase(filename);
|
||||||
|
await this.localDatabase.localDatabase.remove(id, revB);
|
||||||
|
return this.resolveConflictOnInternalFile(id);
|
||||||
|
} else {
|
||||||
|
Logger(`Object merge is not applicable.`, LOG_LEVEL.VERBOSE);
|
||||||
}
|
}
|
||||||
await this.app.vault.adapter.write(filename, result);
|
|
||||||
const stat = await this.app.vault.adapter.stat(filename);
|
|
||||||
await this.storeInternalFileToDatabase({ path: filename, ...stat });
|
|
||||||
await this.extractInternalFileFromDatabase(filename);
|
|
||||||
await this.localDatabase.localDatabase.remove(id, revB);
|
|
||||||
return this.resolveConflictOnInternalFile(id);
|
|
||||||
} else {
|
|
||||||
Logger(`Object merge is not applicable.`, LOG_LEVEL.VERBOSE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const revBDoc = await this.localDatabase.localDatabase.get(id, { rev: revB });
|
const revBDoc = await this.localDatabase.localDatabase.get(id, { rev: revB });
|
||||||
// determine which revision should been deleted.
|
// determine which revision should been deleted.
|
||||||
// simply check modified time
|
// simply check modified time
|
||||||
|
|||||||
+9
-1
@@ -1,5 +1,5 @@
|
|||||||
import { PluginManifest } from "obsidian";
|
import { PluginManifest } from "obsidian";
|
||||||
import { DatabaseEntry } from "./lib/src/types";
|
import { DatabaseEntry, EntryBody } from "./lib/src/types";
|
||||||
|
|
||||||
export interface PluginDataEntry extends DatabaseEntry {
|
export interface PluginDataEntry extends DatabaseEntry {
|
||||||
deviceVaultName: string;
|
deviceVaultName: string;
|
||||||
@@ -30,3 +30,11 @@ export interface InternalFileInfo {
|
|||||||
size: number;
|
size: number;
|
||||||
deleted?: boolean;
|
deleted?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type queueItem = {
|
||||||
|
entry: EntryBody;
|
||||||
|
missingChildren: string[];
|
||||||
|
timeout?: number;
|
||||||
|
done?: boolean;
|
||||||
|
warned?: boolean;
|
||||||
|
};
|
||||||
+1
-1
@@ -101,7 +101,7 @@
|
|||||||
.CodeMirror-wrap::before,
|
.CodeMirror-wrap::before,
|
||||||
.cm-s-obsidian>.cm-editor::before,
|
.cm-s-obsidian>.cm-editor::before,
|
||||||
.canvas-wrapper::before {
|
.canvas-wrapper::before {
|
||||||
content: var(--slsmessage);
|
content: attr(data-log);
|
||||||
text-align: right;
|
text-align: right;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
+16
@@ -35,6 +35,22 @@
|
|||||||
- Fixed button styling.
|
- Fixed button styling.
|
||||||
- Changed:
|
- Changed:
|
||||||
- Conflict checking on synchronising has been enabled for every note in default.
|
- Conflict checking on synchronising has been enabled for every note in default.
|
||||||
|
- 0.17.8
|
||||||
|
- Improved: Performance improved. Prebuilt PouchDB is no longer used.
|
||||||
|
- Fixed: Merging hidden files is also fixed.
|
||||||
|
- New Feature: Now we can synchronise automatically after merging conflicts.
|
||||||
|
- 0.17.9
|
||||||
|
- Fixed: Conflict merge of internal files is no longer broken.
|
||||||
|
- Improved: Smoother status display inside the editor.
|
||||||
|
- 0.17.10
|
||||||
|
- Fixed: Large file synchronising has been now addressed!
|
||||||
|
Note: When synchronising large files, we have to set `Chunk size` to lower than 50, disable `Read chunks online`, `Batch size` should be set 50-100, and `Batch limit` could be around 20.
|
||||||
|
- 0.17.11
|
||||||
|
- Fixed:
|
||||||
|
- Performance improvement
|
||||||
|
- Now `Chunk size` can be set to under one hundred.
|
||||||
|
- New feature:
|
||||||
|
- The number of transfers required before replication stabilises is now displayed.
|
||||||
|
|
||||||
### 0.16.0
|
### 0.16.0
|
||||||
- Now hidden files need not be scanned. Changes will be detected automatically.
|
- Now hidden files need not be scanned. Changes will be detected automatically.
|
||||||
|
|||||||
Reference in New Issue
Block a user