mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-12 10:35:25 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dcfb9867f2 | ||
|
|
46ff17fdf3 | ||
|
|
728dabce60 | ||
|
|
3783fc6926 |
1
lib
1
lib
Submodule lib deleted from 315ef99845
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.10.1",
|
"version": "0.11.2",
|
||||||
"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",
|
||||||
|
|||||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.10.1",
|
"version": "0.11.2",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.10.1",
|
"version": "0.11.2",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.10.1",
|
"version": "0.11.2",
|
||||||
"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",
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ import {
|
|||||||
MAX_DOC_SIZE,
|
MAX_DOC_SIZE,
|
||||||
MAX_DOC_SIZE_BIN,
|
MAX_DOC_SIZE_BIN,
|
||||||
NODEINFO_DOCID,
|
NODEINFO_DOCID,
|
||||||
RECENT_MOFIDIED_DOCS_QTY,
|
|
||||||
VER,
|
VER,
|
||||||
MILSTONE_DOCID,
|
MILSTONE_DOCID,
|
||||||
DatabaseConnectingStatus,
|
DatabaseConnectingStatus,
|
||||||
@@ -142,19 +141,6 @@ export class LocalPouchDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateRecentModifiedDocs(id: string, rev: string, deleted: boolean) {
|
|
||||||
const idrev = id + rev;
|
|
||||||
if (deleted) {
|
|
||||||
this.recentModifiedDocs = this.recentModifiedDocs.filter((e) => e != idrev);
|
|
||||||
} else {
|
|
||||||
this.recentModifiedDocs.push(idrev);
|
|
||||||
this.recentModifiedDocs = this.recentModifiedDocs.slice(0 - RECENT_MOFIDIED_DOCS_QTY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
isSelfModified(id: string, rev: string): boolean {
|
|
||||||
const idrev = id + rev;
|
|
||||||
return this.recentModifiedDocs.indexOf(idrev) !== -1;
|
|
||||||
}
|
|
||||||
async isOldDatabaseExists() {
|
async isOldDatabaseExists() {
|
||||||
const db = new PouchDB<EntryDoc>(this.dbname + "-livesync", {
|
const db = new PouchDB<EntryDoc>(this.dbname + "-livesync", {
|
||||||
auto_compaction: this.settings.useHistory ? false : true,
|
auto_compaction: this.settings.useHistory ? false : true,
|
||||||
@@ -267,7 +253,7 @@ export class LocalPouchDB {
|
|||||||
Logger("Conversion completed!", LOG_LEVEL.NOTICE);
|
Logger("Conversion completed!", LOG_LEVEL.NOTICE);
|
||||||
old.destroy(); // delete the old database.
|
old.destroy(); // delete the old database.
|
||||||
this.isReady = true;
|
this.isReady = true;
|
||||||
return nextSeq();
|
return await nextSeq();
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Conversion failed!");
|
throw new Error("Conversion failed!");
|
||||||
}
|
}
|
||||||
@@ -279,7 +265,7 @@ export class LocalPouchDB {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return nextSeq();
|
return await nextSeq();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,7 +291,7 @@ export class LocalPouchDB {
|
|||||||
waitForLeafReady(id: string): Promise<boolean> {
|
waitForLeafReady(id: string): Promise<boolean> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
// Set timeout.
|
// Set timeout.
|
||||||
const timer = setTimeout(() => rej(new Error(`Leaf timed out:${id}`)), LEAF_WAIT_TIMEOUT);
|
const timer = setTimeout(() => rej(new Error(`Chunk reading timed out:${id}`)), LEAF_WAIT_TIMEOUT);
|
||||||
if (typeof this.leafArrivedCallbacks[id] == "undefined") {
|
if (typeof this.leafArrivedCallbacks[id] == "undefined") {
|
||||||
this.leafArrivedCallbacks[id] = [];
|
this.leafArrivedCallbacks[id] = [];
|
||||||
}
|
}
|
||||||
@@ -329,21 +315,21 @@ export class LocalPouchDB {
|
|||||||
this.hashCaches.set(id, w.data);
|
this.hashCaches.set(id, w.data);
|
||||||
return w.data;
|
return w.data;
|
||||||
}
|
}
|
||||||
throw new Error(`retrive leaf, but it was not leaf.`);
|
throw new Error(`Corrupted chunk detected: ${id}`);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex.status && ex.status == 404) {
|
if (ex.status && ex.status == 404) {
|
||||||
if (waitForReady) {
|
if (waitForReady) {
|
||||||
// just leaf is not ready.
|
// just leaf is not ready.
|
||||||
// wait for on
|
// wait for on
|
||||||
if ((await this.waitForLeafReady(id)) === false) {
|
if ((await this.waitForLeafReady(id)) === false) {
|
||||||
throw new Error(`time out (waiting leaf)`);
|
throw new Error(`time out (waiting chunk)`);
|
||||||
}
|
}
|
||||||
return this.getDBLeaf(id, false);
|
return this.getDBLeaf(id, false);
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Leaf was not found");
|
throw new Error(`Chunk was not found: ${id}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Logger(`Something went wrong on retriving leaf`);
|
Logger(`Something went wrong on retriving chunk`);
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,11 +433,11 @@ export class LocalPouchDB {
|
|||||||
try {
|
try {
|
||||||
childrens = await Promise.all(obj.children.map((e) => this.getDBLeaf(e, waitForReady)));
|
childrens = await Promise.all(obj.children.map((e) => this.getDBLeaf(e, waitForReady)));
|
||||||
if (dump) {
|
if (dump) {
|
||||||
Logger(`childrens:`);
|
Logger(`Chunks:`);
|
||||||
Logger(childrens);
|
Logger(childrens);
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
Logger(`Something went wrong on reading elements of ${obj._id} from database:`, LOG_LEVEL.NOTICE);
|
Logger(`Something went wrong on reading chunks of ${obj._id} from database, see verbose info for detail.`, LOG_LEVEL.NOTICE);
|
||||||
Logger(ex, LOG_LEVEL.VERBOSE);
|
Logger(ex, LOG_LEVEL.VERBOSE);
|
||||||
this.corruptedEntries[obj._id] = obj;
|
this.corruptedEntries[obj._id] = obj;
|
||||||
return false;
|
return false;
|
||||||
@@ -515,7 +501,7 @@ export class LocalPouchDB {
|
|||||||
if (!obj.type || (obj.type && obj.type == "notes")) {
|
if (!obj.type || (obj.type && obj.type == "notes")) {
|
||||||
obj._deleted = true;
|
obj._deleted = true;
|
||||||
const r = await this.localDatabase.put(obj);
|
const r = await this.localDatabase.put(obj);
|
||||||
this.updateRecentModifiedDocs(r.id, r.rev, true);
|
Logger(`entry removed:${obj._id}-${r.rev}`);
|
||||||
if (typeof this.corruptedEntries[obj._id] != "undefined") {
|
if (typeof this.corruptedEntries[obj._id] != "undefined") {
|
||||||
delete this.corruptedEntries[obj._id];
|
delete this.corruptedEntries[obj._id];
|
||||||
}
|
}
|
||||||
@@ -526,7 +512,6 @@ export class LocalPouchDB {
|
|||||||
obj._deleted = true;
|
obj._deleted = true;
|
||||||
const r = await this.localDatabase.put(obj);
|
const r = await this.localDatabase.put(obj);
|
||||||
Logger(`entry removed:${obj._id}-${r.rev}`);
|
Logger(`entry removed:${obj._id}-${r.rev}`);
|
||||||
this.updateRecentModifiedDocs(r.id, r.rev, true);
|
|
||||||
if (typeof this.corruptedEntries[obj._id] != "undefined") {
|
if (typeof this.corruptedEntries[obj._id] != "undefined") {
|
||||||
delete this.corruptedEntries[obj._id];
|
delete this.corruptedEntries[obj._id];
|
||||||
}
|
}
|
||||||
@@ -579,7 +564,6 @@ export class LocalPouchDB {
|
|||||||
const item = await this.localDatabase.get(v);
|
const item = await this.localDatabase.get(v);
|
||||||
item._deleted = true;
|
item._deleted = true;
|
||||||
await this.localDatabase.put(item);
|
await this.localDatabase.put(item);
|
||||||
this.updateRecentModifiedDocs(item._id, item._rev, true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
deleteCount++;
|
deleteCount++;
|
||||||
@@ -702,21 +686,21 @@ export class LocalPouchDB {
|
|||||||
try {
|
try {
|
||||||
const result = await this.localDatabase.bulkDocs(newLeafs);
|
const result = await this.localDatabase.bulkDocs(newLeafs);
|
||||||
for (const item of result) {
|
for (const item of result) {
|
||||||
if ((item as any).ok) {
|
if (!(item as any).ok) {
|
||||||
this.updateRecentModifiedDocs(item.id, item.rev, false);
|
|
||||||
Logger(`save ok:id:${item.id} rev:${item.rev}`, LOG_LEVEL.VERBOSE);
|
|
||||||
} else {
|
|
||||||
if ((item as any).status && (item as any).status == 409) {
|
if ((item as any).status && (item as any).status == 409) {
|
||||||
// conflicted, but it would be ok in childrens.
|
// conflicted, but it would be ok in childrens.
|
||||||
} else {
|
} else {
|
||||||
Logger(`save failed:id:${item.id} rev:${item.rev}`, LOG_LEVEL.NOTICE);
|
Logger(`Save failed:id:${item.id} rev:${item.rev}`, LOG_LEVEL.NOTICE);
|
||||||
Logger(item);
|
Logger(item);
|
||||||
saved = false;
|
saved = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (saved) {
|
||||||
|
Logger(`Chunk saved:${newLeafs.length} chunks`);
|
||||||
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
Logger("ERROR ON SAVING LEAVES:", LOG_LEVEL.NOTICE);
|
Logger("Chunk save failed:", LOG_LEVEL.NOTICE);
|
||||||
Logger(ex, LOG_LEVEL.NOTICE);
|
Logger(ex, LOG_LEVEL.NOTICE);
|
||||||
saved = false;
|
saved = false;
|
||||||
}
|
}
|
||||||
@@ -748,7 +732,6 @@ export class LocalPouchDB {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const r = await this.localDatabase.put(newDoc, { force: true });
|
const r = await this.localDatabase.put(newDoc, { force: true });
|
||||||
this.updateRecentModifiedDocs(r.id, r.rev, newDoc._deleted);
|
|
||||||
if (typeof this.corruptedEntries[note._id] != "undefined") {
|
if (typeof this.corruptedEntries[note._id] != "undefined") {
|
||||||
delete this.corruptedEntries[note._id];
|
delete this.corruptedEntries[note._id];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -710,6 +710,24 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
new Setting(containerSyncSettingEl)
|
||||||
|
.setName("Skip old files on sync")
|
||||||
|
.setDesc("Skip old incoming if incoming changes older than storage.")
|
||||||
|
.addToggle((toggle) =>
|
||||||
|
toggle.setValue(this.plugin.settings.skipOlderFilesOnSync).onChange(async (value) => {
|
||||||
|
this.plugin.settings.skipOlderFilesOnSync = value;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
new Setting(containerSyncSettingEl)
|
||||||
|
.setName("Check conflict only on opening file.")
|
||||||
|
.setDesc("Do not check conflict while replication")
|
||||||
|
.addToggle((toggle) =>
|
||||||
|
toggle.setValue(this.plugin.settings.checkConflictOnlyOnOpen).onChange(async (value) => {
|
||||||
|
this.plugin.settings.checkConflictOnlyOnOpen = value;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
})
|
||||||
|
);
|
||||||
containerSyncSettingEl.createEl("div", {
|
containerSyncSettingEl.createEl("div", {
|
||||||
text: sanitizeHTMLToDom(`Advanced settings<br>
|
text: sanitizeHTMLToDom(`Advanced settings<br>
|
||||||
If you reached the payload size limit when using IBM Cloudant, please set batch size and batch limit to a lower value.`),
|
If you reached the payload size limit when using IBM Cloudant, please set batch size and batch limit to a lower value.`),
|
||||||
@@ -1119,8 +1137,8 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
|
|
||||||
const containerCorruptedDataEl = containerEl.createDiv();
|
const containerCorruptedDataEl = containerEl.createDiv();
|
||||||
|
|
||||||
containerCorruptedDataEl.createEl("h3", { text: "Corrupted data" });
|
containerCorruptedDataEl.createEl("h3", { text: "Corrupted or missing data" });
|
||||||
|
containerCorruptedDataEl.createEl("h4", { text: "Corrupted" });
|
||||||
if (Object.keys(this.plugin.localDatabase.corruptedEntries).length > 0) {
|
if (Object.keys(this.plugin.localDatabase.corruptedEntries).length > 0) {
|
||||||
const cx = containerCorruptedDataEl.createEl("div", { text: "If you have copy of these items on any device, simply edit once or twice. Or not, delete this. sorry.." });
|
const cx = containerCorruptedDataEl.createEl("div", { text: "If you have copy of these items on any device, simply edit once or twice. Or not, delete this. sorry.." });
|
||||||
for (const k in this.plugin.localDatabase.corruptedEntries) {
|
for (const k in this.plugin.localDatabase.corruptedEntries) {
|
||||||
@@ -1149,6 +1167,38 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
} else {
|
} else {
|
||||||
containerCorruptedDataEl.createEl("div", { text: "There is no corrupted data." });
|
containerCorruptedDataEl.createEl("div", { text: "There is no corrupted data." });
|
||||||
}
|
}
|
||||||
|
containerCorruptedDataEl.createEl("h4", { text: "Missing or waiting" });
|
||||||
|
if (Object.keys(this.plugin.queuedFiles).length > 0) {
|
||||||
|
const cx = containerCorruptedDataEl.createEl("div", {
|
||||||
|
text: "These files have missing or waiting chunks. Perhaps almost chunks will be found in a while after replication. But if there're no chunk, you have to restore database entry from existed file by hitting the button below.",
|
||||||
|
});
|
||||||
|
const files = [...new Set([...this.plugin.queuedFiles.map((e) => e.entry._id)])];
|
||||||
|
for (const k of files) {
|
||||||
|
const xx = cx.createEl("div", { text: `${id2path(k)}` });
|
||||||
|
|
||||||
|
const ba = xx.createEl("button", { text: `Delete this` }, (e) => {
|
||||||
|
e.addEventListener("click", async () => {
|
||||||
|
await this.plugin.localDatabase.deleteDBEntry(k);
|
||||||
|
xx.remove();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ba.addClass("mod-warning");
|
||||||
|
xx.createEl("button", { text: `Restore from file` }, (e) => {
|
||||||
|
e.addEventListener("click", async () => {
|
||||||
|
const f = await this.app.vault.getFiles().filter((e) => path2id(e.path) == k);
|
||||||
|
if (f.length == 0) {
|
||||||
|
Logger("Not found in vault", LOG_LEVEL.NOTICE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await this.plugin.updateIntoDB(f[0]);
|
||||||
|
xx.remove();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
xx.addClass("mod-warning");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
containerCorruptedDataEl.createEl("div", { text: "There is no missing or waiting chunk." });
|
||||||
|
}
|
||||||
applyDisplayEnabled();
|
applyDisplayEnabled();
|
||||||
addScreenElement("70", containerCorruptedDataEl);
|
addScreenElement("70", containerCorruptedDataEl);
|
||||||
changeDisplay("0");
|
changeDisplay("0");
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
export let plugin: ObsidianLiveSyncPlugin;
|
export let plugin: ObsidianLiveSyncPlugin;
|
||||||
let plugins: PluginDataEntry[] = [];
|
let plugins: PluginDataEntry[] = [];
|
||||||
let deviceAndPlugins: { [key: string]: PluginDataEntryDisp[] } = {};
|
let deviceAndPlugins: { [key: string]: PluginDataEntryDisp[] } = {};
|
||||||
let devicePluginList: [string, PluginDataEntryDisp[]][] = [];
|
let devicePluginList: [string, PluginDataEntryDisp[]][] = null;
|
||||||
let ownPlugins: DevicePluginList = null;
|
let ownPlugins: DevicePluginList = null;
|
||||||
let showOwnPlugins = false;
|
let showOwnPlugins = false;
|
||||||
let targetList: { [key: string]: boolean } = {};
|
let targetList: { [key: string]: boolean } = {};
|
||||||
@@ -205,58 +205,63 @@
|
|||||||
<th class="sls-plugins-tbl-device-head">Info</th>
|
<th class="sls-plugins-tbl-device-head">Info</th>
|
||||||
<th class="sls-plugins-tbl-device-head">Target</th>
|
<th class="sls-plugins-tbl-device-head">Target</th>
|
||||||
</tr>
|
</tr>
|
||||||
{#if devicePluginList.length == 0}
|
{#if !devicePluginList}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="3" class="sls-table-tail tcenter"> Retrieving... </td>
|
<td colspan="3" class="sls-table-tail tcenter"> Retrieving... </td>
|
||||||
</tr>
|
</tr>
|
||||||
{/if}
|
{:else if devicePluginList.length == 0}
|
||||||
{#each devicePluginList as [deviceName, devicePlugins]}
|
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="2" class="sls-plugins-tbl-device-head">{deviceName}</th>
|
<td colspan="3" class="sls-table-tail tcenter"> No plugins found. </td>
|
||||||
<th class="sls-plugins-tbl-device-head">
|
|
||||||
<button class="mod-cta" on:click={() => toggleAll(deviceName)}>✔</button>
|
|
||||||
</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
{#each devicePlugins as plugin}
|
{:else}
|
||||||
|
{#each devicePluginList as [deviceName, devicePlugins]}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="sls-table-head">{plugin.manifest.name}</td>
|
<th colspan="2" class="sls-plugins-tbl-device-head">{deviceName}</th>
|
||||||
<td class="sls-table-tail tcenter">{plugin.versionInfo}{getDispString(plugin.versionFlag)}</td>
|
<th class="sls-plugins-tbl-device-head">
|
||||||
<td class="sls-table-tail tcenter">
|
<button class="mod-cta" on:click={() => toggleAll(deviceName)}>✔</button>
|
||||||
{#if plugin.versionFlag === "EVEN" || plugin.versionFlag === ""}
|
</th>
|
||||||
-
|
|
||||||
{:else}
|
|
||||||
<div class="wrapToggle">
|
|
||||||
<div
|
|
||||||
class="checkbox-container"
|
|
||||||
class:is-enabled={targetList[plugin.deviceVaultName + "---" + plugin.manifest.id + "---plugin"]}
|
|
||||||
on:click={() => toggleTarget(plugin.deviceVaultName + "---" + plugin.manifest.id + "---plugin")}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="sls-table-head">Settings</td>
|
|
||||||
<td class="sls-table-tail tcenter">{plugin.mtimeInfo}{getDispString(plugin.mtimeFlag)}</td>
|
|
||||||
<td class="sls-table-tail tcenter">
|
|
||||||
{#if plugin.mtimeFlag === "EVEN" || plugin.mtimeFlag === ""}
|
|
||||||
-
|
|
||||||
{:else}
|
|
||||||
<div class="wrapToggle">
|
|
||||||
<div
|
|
||||||
class="checkbox-container"
|
|
||||||
class:is-enabled={targetList[plugin.deviceVaultName + "---" + plugin.manifest.id + "---setting"]}
|
|
||||||
on:click={() => toggleTarget(plugin.deviceVaultName + "---" + plugin.manifest.id + "---setting")}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr class="divider">
|
|
||||||
<th colspan="3" />
|
|
||||||
</tr>
|
</tr>
|
||||||
|
{#each devicePlugins as plugin}
|
||||||
|
<tr>
|
||||||
|
<td class="sls-table-head">{plugin.manifest.name}</td>
|
||||||
|
<td class="sls-table-tail tcenter">{plugin.versionInfo}{getDispString(plugin.versionFlag)}</td>
|
||||||
|
<td class="sls-table-tail tcenter">
|
||||||
|
{#if plugin.versionFlag === "EVEN" || plugin.versionFlag === ""}
|
||||||
|
-
|
||||||
|
{:else}
|
||||||
|
<div class="wrapToggle">
|
||||||
|
<div
|
||||||
|
class="checkbox-container"
|
||||||
|
class:is-enabled={targetList[plugin.deviceVaultName + "---" + plugin.manifest.id + "---plugin"]}
|
||||||
|
on:click={() => toggleTarget(plugin.deviceVaultName + "---" + plugin.manifest.id + "---plugin")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td class="sls-table-head">Settings</td>
|
||||||
|
<td class="sls-table-tail tcenter">{plugin.mtimeInfo}{getDispString(plugin.mtimeFlag)}</td>
|
||||||
|
<td class="sls-table-tail tcenter">
|
||||||
|
{#if plugin.mtimeFlag === "EVEN" || plugin.mtimeFlag === ""}
|
||||||
|
-
|
||||||
|
{:else}
|
||||||
|
<div class="wrapToggle">
|
||||||
|
<div
|
||||||
|
class="checkbox-container"
|
||||||
|
class:is-enabled={targetList[plugin.deviceVaultName + "---" + plugin.manifest.id + "---setting"]}
|
||||||
|
on:click={() => toggleTarget(plugin.deviceVaultName + "---" + plugin.manifest.id + "---setting")}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr class="divider">
|
||||||
|
<th colspan="3" />
|
||||||
|
</tr>
|
||||||
|
{/each}
|
||||||
{/each}
|
{/each}
|
||||||
{/each}
|
{/if}
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="ols-plugins-div-buttons">
|
<div class="ols-plugins-div-buttons">
|
||||||
|
|||||||
2
src/lib
2
src/lib
Submodule src/lib updated: b031e4e69d...6451afd112
252
src/main.ts
252
src/main.ts
@@ -1,4 +1,4 @@
|
|||||||
import { debounce, Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, PluginManifest, Modal, App } from "obsidian";
|
import { debounce, Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, PluginManifest, Modal, App, FuzzySuggestModal } from "obsidian";
|
||||||
import { diff_match_patch } from "diff-match-patch";
|
import { 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 } 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 } from "./lib/src/types";
|
||||||
@@ -28,6 +28,7 @@ import { DocumentHistoryModal } from "./DocumentHistoryModal";
|
|||||||
|
|
||||||
import PluginPane from "./PluginPane.svelte";
|
import PluginPane from "./PluginPane.svelte";
|
||||||
import { id2path, path2id } from "./utils";
|
import { id2path, path2id } from "./utils";
|
||||||
|
import { decrypt, encrypt } from "./lib/src/e2ee";
|
||||||
const isDebug = false;
|
const isDebug = false;
|
||||||
setNoticeClass(Notice);
|
setNoticeClass(Notice);
|
||||||
class PluginDialogModal extends Modal {
|
class PluginDialogModal extends Modal {
|
||||||
@@ -57,6 +58,45 @@ class PluginDialogModal extends Modal {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
class PopoverYesNo extends FuzzySuggestModal<string> {
|
||||||
|
app: App;
|
||||||
|
callback: (e: string) => void = () => {};
|
||||||
|
|
||||||
|
constructor(app: App, note: string, callback: (e: string) => void) {
|
||||||
|
super(app);
|
||||||
|
this.app = app;
|
||||||
|
this.setPlaceholder("y/n) " + note);
|
||||||
|
this.callback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
getItems(): string[] {
|
||||||
|
return ["yes", "no"];
|
||||||
|
}
|
||||||
|
|
||||||
|
getItemText(item: string): string {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
onChooseItem(item: string, evt: MouseEvent | KeyboardEvent): void {
|
||||||
|
// debugger;
|
||||||
|
this.callback(item);
|
||||||
|
this.callback = null;
|
||||||
|
}
|
||||||
|
onClose(): void {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (this.callback != null) {
|
||||||
|
this.callback("");
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const askYesNo = (app: App, message: string): Promise<"yes" | "no"> => {
|
||||||
|
return new Promise((res) => {
|
||||||
|
const popover = new PopoverYesNo(app, message, (result) => res(result as "yes" | "no"));
|
||||||
|
popover.open();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export default class ObsidianLiveSyncPlugin extends Plugin {
|
export default class ObsidianLiveSyncPlugin extends Plugin {
|
||||||
settings: ObsidianLiveSyncSettings;
|
settings: ObsidianLiveSyncSettings;
|
||||||
@@ -200,6 +240,81 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
Logger(ex, LOG_LEVEL.VERBOSE);
|
Logger(ex, LOG_LEVEL.VERBOSE);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
this.addCommand({
|
||||||
|
id: "livesync-exportconfig",
|
||||||
|
name: "Copy setup uri (beta)",
|
||||||
|
callback: async () => {
|
||||||
|
const encryptedSetting = encodeURIComponent(await encrypt(JSON.stringify(this.settings), "---"));
|
||||||
|
const uri = `obsidian://setuplivesync?settings=${encryptedSetting}`;
|
||||||
|
await navigator.clipboard.writeText(uri);
|
||||||
|
Logger("Setup uri copied to clipboard", LOG_LEVEL.NOTICE);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
this.registerObsidianProtocolHandler("setuplivesync", async (conf: any) => {
|
||||||
|
try {
|
||||||
|
const oldConf = JSON.parse(JSON.stringify(this.settings));
|
||||||
|
const newconf = await JSON.parse(await decrypt(conf.settings, "---"));
|
||||||
|
if (newconf) {
|
||||||
|
const result = await askYesNo(this.app, "Importing LiveSync's conf, OK?");
|
||||||
|
if (result == "yes") {
|
||||||
|
const newSettingW = Object.assign({}, this.settings, newconf);
|
||||||
|
// stopping once.
|
||||||
|
this.localDatabase.closeReplication();
|
||||||
|
this.settings.suspendFileWatching = true;
|
||||||
|
console.dir(newSettingW);
|
||||||
|
const keepLocalDB = await askYesNo(this.app, "Keep local DB?");
|
||||||
|
const keepRemoteDB = await askYesNo(this.app, "Keep remote DB?");
|
||||||
|
if (keepLocalDB == "yes" && keepRemoteDB == "yes") {
|
||||||
|
// nothing to do. so peaceful.
|
||||||
|
this.settings = newSettingW;
|
||||||
|
await this.saveSettings();
|
||||||
|
Logger("Configuration loaded.", LOG_LEVEL.NOTICE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (keepLocalDB == "no" && keepRemoteDB == "no") {
|
||||||
|
const reset = await askYesNo(this.app, "Drop everything?");
|
||||||
|
if (reset != "yes") {
|
||||||
|
Logger("Cancelled", LOG_LEVEL.NOTICE);
|
||||||
|
this.settings = oldConf;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let initDB;
|
||||||
|
await this.saveSettings();
|
||||||
|
if (keepLocalDB == "no") {
|
||||||
|
this.resetLocalOldDatabase();
|
||||||
|
this.resetLocalDatabase();
|
||||||
|
this.localDatabase.initializeDatabase();
|
||||||
|
const rebuild = await askYesNo(this.app, "Rebuild the database?");
|
||||||
|
if (rebuild == "yes") {
|
||||||
|
initDB = this.initializeDatabase(true);
|
||||||
|
} else {
|
||||||
|
this.markRemoteResolved();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (keepRemoteDB == "no") {
|
||||||
|
await this.tryResetRemoteDatabase();
|
||||||
|
await this.markRemoteLocked();
|
||||||
|
}
|
||||||
|
if (keepLocalDB == "no" || keepRemoteDB == "no") {
|
||||||
|
const replicate = await askYesNo(this.app, "Replicate once?");
|
||||||
|
if (replicate == "yes") {
|
||||||
|
if (initDB != null) {
|
||||||
|
await initDB;
|
||||||
|
}
|
||||||
|
await this.replicate(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger("Configuration loaded.", LOG_LEVEL.NOTICE);
|
||||||
|
} else {
|
||||||
|
Logger("Cancelled.", LOG_LEVEL.NOTICE);
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
Logger("Couldn't parse configuration uri.", LOG_LEVEL.NOTICE);
|
||||||
|
}
|
||||||
|
});
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "livesync-replicate",
|
id: "livesync-replicate",
|
||||||
name: "Replicate now",
|
name: "Replicate now",
|
||||||
@@ -587,8 +702,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
//--> Basic document Functions
|
//--> Basic document Functions
|
||||||
notifies: { [key: string]: { notice: Notice; timer: NodeJS.Timeout; count: number } } = {};
|
notifies: { [key: string]: { notice: Notice; timer: NodeJS.Timeout; count: number } } = {};
|
||||||
|
|
||||||
// eslint-disable-next-line require-await
|
|
||||||
lastLog = "";
|
lastLog = "";
|
||||||
|
// eslint-disable-next-line require-await
|
||||||
async addLog(message: any, level: LOG_LEVEL = LOG_LEVEL.INFO) {
|
async addLog(message: any, level: LOG_LEVEL = LOG_LEVEL.INFO) {
|
||||||
if (level == LOG_LEVEL.DEBUG && !isDebug) {
|
if (level == LOG_LEVEL.DEBUG && !isDebug) {
|
||||||
return;
|
return;
|
||||||
@@ -808,12 +923,120 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
const doc = change;
|
const doc = change;
|
||||||
const file = targetFile;
|
const file = targetFile;
|
||||||
await this.doc2storage_modify(doc, file);
|
await this.doc2storage_modify(doc, file);
|
||||||
this.queueConflictedCheck(file);
|
if (!this.settings.checkConflictOnlyOnOpen) {
|
||||||
|
this.queueConflictedCheck(file);
|
||||||
|
} else {
|
||||||
|
const af = app.workspace.getActiveFile();
|
||||||
|
if (af && af.path == file.path) {
|
||||||
|
this.queueConflictedCheck(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Logger(`${id2path(change._id)} is already exist as the folder`);
|
Logger(`${id2path(change._id)} is already exist as the folder`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
queuedFiles: {
|
||||||
|
entry: EntryBody;
|
||||||
|
missingChildren: string[];
|
||||||
|
timeout?: number;
|
||||||
|
done?: boolean;
|
||||||
|
warned?: boolean;
|
||||||
|
}[] = [];
|
||||||
|
chunkWaitTimeout = 60000;
|
||||||
|
|
||||||
|
async saveQueuedFiles() {
|
||||||
|
const saveData = JSON.stringify(this.queuedFiles.filter((e) => !e.done).map((e) => e.entry._id));
|
||||||
|
const lsname = "obsidian-livesync-queuefiles-" + this.app.vault.getName();
|
||||||
|
localStorage.setItem(lsname, saveData);
|
||||||
|
}
|
||||||
|
async loadQueuedFiles() {
|
||||||
|
const lsname = "obsidian-livesync-queuefiles-" + this.app.vault.getName();
|
||||||
|
const ids = JSON.parse(localStorage.getItem(lsname) || "[]") as string[];
|
||||||
|
const ret = await this.localDatabase.localDatabase.allDocs({ keys: ids, include_docs: true });
|
||||||
|
for (const doc of ret.rows) {
|
||||||
|
if (doc.doc && !this.queuedFiles.some((e) => e.entry._id == doc.doc._id)) {
|
||||||
|
await this.parseIncomingDoc(doc.doc as PouchDB.Core.ExistingDocument<EntryBody & PouchDB.Core.AllDocsMeta>);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async procQueuedFiles() {
|
||||||
|
await runWithLock("procQueue", true, async () => {
|
||||||
|
this.saveQueuedFiles();
|
||||||
|
for (const queue of this.queuedFiles) {
|
||||||
|
if (queue.done) continue;
|
||||||
|
const now = new Date().getTime();
|
||||||
|
if (queue.missingChildren.length == 0) {
|
||||||
|
queue.done = true;
|
||||||
|
if (isValidPath(id2path(queue.entry._id))) {
|
||||||
|
Logger(`Applying ${queue.entry._id} (${queue.entry._rev}) change...`);
|
||||||
|
await this.handleDBChanged(queue.entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (now > queue.timeout) {
|
||||||
|
if (!queue.warned) Logger(`Timed out: ${queue.entry._id} could not collect ${queue.missingChildren.length} chunks. plugin keeps watching, but you have to check the file after the replication.`, LOG_LEVEL.NOTICE);
|
||||||
|
queue.warned = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.queuedFiles = this.queuedFiles.filter((e) => !e.done);
|
||||||
|
this.saveQueuedFiles();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
parseIncomingChunk(chunk: PouchDB.Core.ExistingDocument<EntryDoc>) {
|
||||||
|
const now = new Date().getTime();
|
||||||
|
let isNewFileCompleted = false;
|
||||||
|
|
||||||
|
for (const queue of this.queuedFiles) {
|
||||||
|
if (queue.done) continue;
|
||||||
|
if (queue.missingChildren.indexOf(chunk._id) !== -1) {
|
||||||
|
queue.missingChildren = queue.missingChildren.filter((e) => e != chunk._id);
|
||||||
|
queue.timeout = now + this.chunkWaitTimeout;
|
||||||
|
}
|
||||||
|
if (queue.missingChildren.length == 0) {
|
||||||
|
for (const e of this.queuedFiles) {
|
||||||
|
if (e.entry._id == queue.entry._id && e.entry.mtime < queue.entry.mtime) {
|
||||||
|
e.done = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isNewFileCompleted = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isNewFileCompleted) this.procQueuedFiles();
|
||||||
|
}
|
||||||
|
async parseIncomingDoc(doc: PouchDB.Core.ExistingDocument<EntryBody>) {
|
||||||
|
const skipOldFile = this.settings.skipOlderFilesOnSync;
|
||||||
|
if (skipOldFile) {
|
||||||
|
const info = this.app.vault.getAbstractFileByPath(id2path(doc._id));
|
||||||
|
|
||||||
|
if (info && info instanceof TFile) {
|
||||||
|
const localMtime = ~~((info as TFile).stat.mtime / 1000);
|
||||||
|
const docMtime = ~~(doc.mtime / 1000);
|
||||||
|
if (localMtime >= docMtime) {
|
||||||
|
Logger(`${doc._id} Skipped, older than storage.`, LOG_LEVEL.VERBOSE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const now = new Date().getTime();
|
||||||
|
const newQueue = {
|
||||||
|
entry: doc,
|
||||||
|
missingChildren: [] as string[],
|
||||||
|
timeout: now + this.chunkWaitTimeout,
|
||||||
|
};
|
||||||
|
if ("children" in doc) {
|
||||||
|
const c = await this.localDatabase.localDatabase.allDocs({ keys: doc.children, include_docs: false });
|
||||||
|
const missing = c.rows.filter((e) => "error" in e).map((e) => e.key);
|
||||||
|
if (missing.length) Logger(`${doc._id}(${doc._rev}) Queued (waiting ${missing.length} items)`, LOG_LEVEL.VERBOSE);
|
||||||
|
newQueue.missingChildren = missing;
|
||||||
|
this.queuedFiles.push(newQueue);
|
||||||
|
this.saveQueuedFiles();
|
||||||
|
} else {
|
||||||
|
this.queuedFiles.push(newQueue);
|
||||||
|
this.saveQueuedFiles();
|
||||||
|
this.procQueuedFiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
periodicSyncHandler: number = null;
|
periodicSyncHandler: number = null;
|
||||||
|
|
||||||
//---> Sync
|
//---> Sync
|
||||||
@@ -827,14 +1050,15 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (change._id.startsWith("h:")) {
|
if (change._id.startsWith("h:")) {
|
||||||
|
await this.parseIncomingChunk(change);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (change._id == SYNCINFO_ID) {
|
if (change._id == SYNCINFO_ID) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (change.type != "leaf" && change.type != "versioninfo" && change.type != "milestoneinfo" && change.type != "nodeinfo") {
|
if (change.type != "leaf" && change.type != "versioninfo" && change.type != "milestoneinfo" && change.type != "nodeinfo") {
|
||||||
Logger("replication change arrived", LOG_LEVEL.VERBOSE);
|
await this.parseIncomingDoc(change);
|
||||||
await this.handleDBChanged(change);
|
continue;
|
||||||
}
|
}
|
||||||
if (change.type == "versioninfo") {
|
if (change.type == "versioninfo") {
|
||||||
if (change.version > VER) {
|
if (change.version > VER) {
|
||||||
@@ -971,9 +1195,17 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
waiting = " " + this.batchFileChange.map((e) => "🛫").join("");
|
waiting = " " + this.batchFileChange.map((e) => "🛫").join("");
|
||||||
waiting = waiting.replace(/(🛫){10}/g, "🚀");
|
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 procs = getProcessingCounts();
|
const procs = getProcessingCounts();
|
||||||
const procsDisp = procs == 0 ? "" : ` ⏳${procs}`;
|
const procsDisp = procs == 0 ? "" : ` ⏳${procs}`;
|
||||||
const message = `Sync:${w} ↑${sent} ↓${arrived}${waiting}${procsDisp}`;
|
const message = `Sync:${w} ↑${sent} ↓${arrived}${waiting}${procsDisp}${queued}`;
|
||||||
const locks = getLocks();
|
const locks = getLocks();
|
||||||
const pendingTask = locks.pending.length ? `\nPending:${locks.pending.join(", ")}` : "";
|
const pendingTask = locks.pending.length ? `\nPending:${locks.pending.join(", ")}` : "";
|
||||||
const runningTask = locks.running.length ? `\nRunning:${locks.running.join(", ")}` : "";
|
const runningTask = locks.running.length ? `\nRunning:${locks.running.join(", ")}` : "";
|
||||||
@@ -1014,6 +1246,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
if (this.settings.autoSweepPlugins) {
|
if (this.settings.autoSweepPlugins) {
|
||||||
await this.sweepPlugin(false);
|
await this.sweepPlugin(false);
|
||||||
}
|
}
|
||||||
|
await this.loadQueuedFiles();
|
||||||
this.localDatabase.openReplication(this.settings, false, showMessage, this.parseReplicationResult);
|
this.localDatabase.openReplication(this.settings, false, showMessage, this.parseReplicationResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1053,6 +1286,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
if (showingNotice) {
|
if (showingNotice) {
|
||||||
notice = NewNotice("Initializing", 0);
|
notice = NewNotice("Initializing", 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
const filesStorage = this.app.vault.getFiles();
|
const filesStorage = this.app.vault.getFiles();
|
||||||
const filesStorageName = filesStorage.map((e) => e.path);
|
const filesStorageName = filesStorage.map((e) => e.path);
|
||||||
const wf = await this.localDatabase.localDatabase.allDocs();
|
const wf = await this.localDatabase.localDatabase.allDocs();
|
||||||
@@ -1440,6 +1674,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
});
|
});
|
||||||
if (isNotChanged) return;
|
if (isNotChanged) return;
|
||||||
await this.localDatabase.putDBEntry(d);
|
await this.localDatabase.putDBEntry(d);
|
||||||
|
this.queuedFiles = this.queuedFiles.map((e) => ({ ...e, ...(e.entry._id == d._id ? { done: true } : {}) }));
|
||||||
|
|
||||||
Logger("put database:" + fullpath + "(" + datatype + ") ");
|
Logger("put database:" + fullpath + "(" + datatype + ") ");
|
||||||
if (this.settings.syncOnSave && !this.suspended) {
|
if (this.settings.syncOnSave && !this.suspended) {
|
||||||
@@ -1500,7 +1735,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
|
|
||||||
async sweepPlugin(showMessage = false) {
|
async sweepPlugin(showMessage = false) {
|
||||||
if (!this.settings.usePluginSync) return;
|
if (!this.settings.usePluginSync) return;
|
||||||
await runWithLock("sweepplugin", false, async () => {
|
if (!this.localDatabase.isReady) return;
|
||||||
|
await runWithLock("sweepplugin", true, async () => {
|
||||||
const logLevel = showMessage ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO;
|
const logLevel = showMessage ? LOG_LEVEL.NOTICE : LOG_LEVEL.INFO;
|
||||||
if (!this.settings.encrypt) {
|
if (!this.settings.encrypt) {
|
||||||
Logger("You have to encrypt the database to use plugin setting sync.", LOG_LEVEL.NOTICE);
|
Logger("You have to encrypt the database to use plugin setting sync.", LOG_LEVEL.NOTICE);
|
||||||
@@ -1517,7 +1753,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
endkey: `ps:${this.deviceAndVaultName}.`,
|
endkey: `ps:${this.deviceAndVaultName}.`,
|
||||||
include_docs: true,
|
include_docs: true,
|
||||||
});
|
});
|
||||||
Logger("OLD DOCS.", LOG_LEVEL.VERBOSE);
|
// Logger("OLD DOCS.", LOG_LEVEL.VERBOSE);
|
||||||
// sweep current plugin.
|
// sweep current plugin.
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const pl = this.app.plugins;
|
const pl = this.app.plugins;
|
||||||
|
|||||||
@@ -134,6 +134,7 @@ const connectRemoteCouchDB = async (uri: string, auth: { username: string; passw
|
|||||||
// return await fetch(url, opts);
|
// return await fetch(url, opts);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const db: PouchDB.Database<EntryDoc> = new PouchDB<EntryDoc>(uri, conf);
|
const db: PouchDB.Database<EntryDoc> = new PouchDB<EntryDoc>(uri, conf);
|
||||||
if (passphrase && typeof passphrase === "string") {
|
if (passphrase && typeof passphrase === "string") {
|
||||||
enableEncryption(db, passphrase);
|
enableEncryption(db, passphrase);
|
||||||
|
|||||||
Reference in New Issue
Block a user