You've already forked obsidian-livesync
mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2025-07-13 00:10:20 +02:00
Implemented: New "plugins and their settings"
Fixed: some plugin synchronization bugs.
This commit is contained in:
37
esbuild.config.mjs
Normal file
37
esbuild.config.mjs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import esbuild from "esbuild";
|
||||||
|
import process from "process";
|
||||||
|
import builtins from "builtin-modules";
|
||||||
|
import sveltePlugin from "esbuild-svelte";
|
||||||
|
import sveltePreprocess from "svelte-preprocess";
|
||||||
|
|
||||||
|
const banner = `/*
|
||||||
|
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
||||||
|
if you want to view the source, please visit the github repository of this plugin
|
||||||
|
*/
|
||||||
|
`;
|
||||||
|
|
||||||
|
const prod = process.argv[2] === "production";
|
||||||
|
|
||||||
|
esbuild
|
||||||
|
.build({
|
||||||
|
banner: {
|
||||||
|
js: banner,
|
||||||
|
},
|
||||||
|
entryPoints: ["src/main.ts"],
|
||||||
|
bundle: true,
|
||||||
|
external: ["obsidian", "electron", ...builtins],
|
||||||
|
format: "cjs",
|
||||||
|
watch: !prod,
|
||||||
|
target: "es2015",
|
||||||
|
logLevel: "info",
|
||||||
|
sourcemap: prod ? false : "inline",
|
||||||
|
treeShaking: true,
|
||||||
|
plugins: [
|
||||||
|
sveltePlugin({
|
||||||
|
preprocess: sveltePreprocess(),
|
||||||
|
compilerOptions: { css: true },
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
outfile: "main.js",
|
||||||
|
})
|
||||||
|
.catch(() => process.exit(1));
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.6.1",
|
"version": "0.7.0",
|
||||||
"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",
|
||||||
|
939
package-lock.json
generated
939
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.6.1",
|
"version": "0.7.0",
|
||||||
"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",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "rollup --config rollup.config.js -w",
|
"dev": "node esbuild.config.mjs",
|
||||||
"build": "rollup --config rollup.config.js --environment BUILD:production",
|
"build": "node esbuild.config.mjs production",
|
||||||
"lint": "eslint src"
|
"lint": "eslint src"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
@ -16,6 +17,7 @@
|
|||||||
"@rollup/plugin-node-resolve": "^11.2.1",
|
"@rollup/plugin-node-resolve": "^11.2.1",
|
||||||
"@rollup/plugin-typescript": "^8.2.1",
|
"@rollup/plugin-typescript": "^8.2.1",
|
||||||
"@types/diff-match-patch": "^1.0.32",
|
"@types/diff-match-patch": "^1.0.32",
|
||||||
|
"@types/pouchdb": "^6.4.0",
|
||||||
"@types/pouchdb-browser": "^6.1.3",
|
"@types/pouchdb-browser": "^6.1.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.7.0",
|
"@typescript-eslint/eslint-plugin": "^5.7.0",
|
||||||
"@typescript-eslint/parser": "^5.0.0",
|
"@typescript-eslint/parser": "^5.0.0",
|
||||||
@ -25,7 +27,11 @@
|
|||||||
"obsidian": "^0.13.11",
|
"obsidian": "^0.13.11",
|
||||||
"rollup": "^2.32.1",
|
"rollup": "^2.32.1",
|
||||||
"tslib": "^2.2.0",
|
"tslib": "^2.2.0",
|
||||||
"typescript": "^4.2.4"
|
"typescript": "^4.2.4",
|
||||||
|
"builtin-modules": "^3.2.0",
|
||||||
|
"esbuild": "0.13.12",
|
||||||
|
"esbuild-svelte": "^0.6.0",
|
||||||
|
"svelte-preprocess": "^4.10.2"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"diff-match-patch": "^1.0.5",
|
"diff-match-patch": "^1.0.5",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { App, Notice, PluginSettingTab, Setting, sanitizeHTMLToDom } from "obsidian";
|
import { App, Notice, PluginSettingTab, Setting, sanitizeHTMLToDom } from "obsidian";
|
||||||
import { EntryDoc, LOG_LEVEL } from "./types";
|
import { EntryDoc, LOG_LEVEL } from "./types";
|
||||||
import { escapeStringToHTML, versionNumberString2Number, path2id, id2path, runWithLock } from "./utils";
|
import { path2id, id2path, runWithLock } from "./utils";
|
||||||
import { Logger } from "./logger";
|
import { Logger } from "./logger";
|
||||||
import { connectRemoteCouchDB } from "./utils_couchdb";
|
import { connectRemoteCouchDB } from "./utils_couchdb";
|
||||||
import { testCrypt } from "./e2ee";
|
import { testCrypt } from "./e2ee";
|
||||||
@ -830,14 +830,12 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
toggle.setValue(this.plugin.settings.usePluginSync).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.usePluginSync).onChange(async (value) => {
|
||||||
this.plugin.settings.usePluginSync = value;
|
this.plugin.settings.usePluginSync = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
updatePluginPane();
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
new Setting(containerPluginSettings).setName("Show own plugins and settings").addToggle((toggle) =>
|
new Setting(containerPluginSettings).setName("Show own plugins and settings").addToggle((toggle) =>
|
||||||
toggle.setValue(this.plugin.settings.showOwnPlugins).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.showOwnPlugins).onChange(async (value) => {
|
||||||
this.plugin.settings.showOwnPlugins = value;
|
this.plugin.settings.showOwnPlugins = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
updatePluginPane();
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -884,233 +882,19 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
});
|
});
|
||||||
// text.inputEl.setAttribute("type", "password");
|
// text.inputEl.setAttribute("type", "password");
|
||||||
});
|
});
|
||||||
|
new Setting(containerPluginSettings)
|
||||||
|
.setName("Open")
|
||||||
|
.setDesc("Open the plugin dialog")
|
||||||
|
.addButton((button) => {
|
||||||
|
button
|
||||||
|
.setButtonText("Open")
|
||||||
|
.setDisabled(false)
|
||||||
|
.onClick(() => {
|
||||||
|
this.plugin.showPluginSyncModal();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
updateDisabledOfDeviceAndVaultName();
|
updateDisabledOfDeviceAndVaultName();
|
||||||
const sweepPlugin = async (showMessage: boolean) => {
|
|
||||||
if (!this.plugin.settings.usePluginSync) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await this.plugin.sweepPlugin(showMessage);
|
|
||||||
updatePluginPane();
|
|
||||||
};
|
|
||||||
const updatePluginPane = async () => {
|
|
||||||
pluginConfig.innerHTML = "<div class='sls-plugins-wrap'>Retrieving...</div>";
|
|
||||||
const { plugins, allPlugins, thisDevicePlugins } = await this.plugin.getPluginList();
|
|
||||||
let html = `
|
|
||||||
<div class='sls-plugins-wrap'>
|
|
||||||
<table class='sls-plugins-tbl'>
|
|
||||||
`;
|
|
||||||
for (const vaults in plugins) {
|
|
||||||
if (!this.plugin.settings.showOwnPlugins && vaults == this.plugin.settings.deviceAndVaultName) continue;
|
|
||||||
html += `
|
|
||||||
<tr>
|
|
||||||
<th colspan=1 class='sls-plugins-tbl-device-head'>${escapeStringToHTML(vaults)}</th>
|
|
||||||
<td class='sls-plugins-tbl-device-head sls-plugins-tbl-buttons'>
|
|
||||||
<button class='sls-plugin-apply-all-newer-plugin mod-cta' data-key="${vaults}" aria-label="Apply all newer (without setting)">⚡</button>
|
|
||||||
<button class='sls-plugin-apply-all-newer-setting mod-cta' data-key="${vaults}" aria-label="Apply all newer settings">📚</button>
|
|
||||||
<button class='sls-plugin-delete mod-warning' data-key="${vaults}" aria-label="Delete">❌</button>
|
|
||||||
</td>
|
|
||||||
</tr>`;
|
|
||||||
for (const v of plugins[vaults]) {
|
|
||||||
const mtime = v.mtime == 0 ? "-" : new Date(v.mtime).toLocaleString();
|
|
||||||
let settingApplyable: boolean | string = "-";
|
|
||||||
let settingFleshness = "";
|
|
||||||
let isSameVersion = false;
|
|
||||||
let isSameContents = false;
|
|
||||||
if (thisDevicePlugins[v.manifest.id]) {
|
|
||||||
if (thisDevicePlugins[v.manifest.id].manifest.version == v.manifest.version) {
|
|
||||||
isSameVersion = true;
|
|
||||||
}
|
|
||||||
if (thisDevicePlugins[v.manifest.id].styleCss == v.styleCss && thisDevicePlugins[v.manifest.id].mainJs == v.mainJs && thisDevicePlugins[v.manifest.id].manifestJson == v.manifestJson) {
|
|
||||||
isSameContents = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (thisDevicePlugins[v.manifest.id] && v.dataJson) {
|
|
||||||
// have this plugin.
|
|
||||||
const localSetting = thisDevicePlugins[v.manifest.id].dataJson || null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const remoteSetting = v.dataJson;
|
|
||||||
if (!localSetting) {
|
|
||||||
settingFleshness = "newer";
|
|
||||||
settingApplyable = true;
|
|
||||||
} else if (localSetting == remoteSetting) {
|
|
||||||
settingApplyable = "even";
|
|
||||||
} else {
|
|
||||||
if (v.mtime > thisDevicePlugins[v.manifest.id].mtime) {
|
|
||||||
settingFleshness = "newer";
|
|
||||||
} else {
|
|
||||||
settingFleshness = "older";
|
|
||||||
}
|
|
||||||
settingApplyable = true;
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
settingApplyable = "could not decrypt";
|
|
||||||
}
|
|
||||||
} else if (!v.dataJson) {
|
|
||||||
settingApplyable = "N/A";
|
|
||||||
}
|
|
||||||
// very ugly way.
|
|
||||||
const piece = `
|
|
||||||
<tr class='divider'>
|
|
||||||
<th colspan=2></th>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<th class='sls-table-head'>${escapeStringToHTML(v.manifest.name)}</th>
|
|
||||||
<td class="sls-table-tail tcenter">${isSameContents ? "even" : `<button data-key='${v._id}' class='apply-plugin-version mod-cta'>Use (${isSameVersion ? "=" : ""}${v.manifest.version}) </button>`}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td class="sls-table-head tcenter">${escapeStringToHTML(mtime)}</td>
|
|
||||||
<td class="sls-table-tail tcenter">${settingApplyable === true ? "<button data-key='" + v._id + "' class='apply-plugin-data mod-cta'>Apply (" + settingFleshness + ")</button>" : settingApplyable}</td>
|
|
||||||
</tr>
|
|
||||||
`;
|
|
||||||
html += piece;
|
|
||||||
}
|
|
||||||
html += `
|
|
||||||
<tr class='divider'>
|
|
||||||
<th colspan=2></th>
|
|
||||||
</tr>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
html += "</table></div>";
|
|
||||||
pluginConfig.innerHTML = html;
|
|
||||||
pluginConfig.querySelectorAll(".apply-plugin-data").forEach((e) =>
|
|
||||||
e.addEventListener("click", async (evt) => {
|
|
||||||
const plugin = allPlugins[e.attributes.getNamedItem("data-key").value];
|
|
||||||
Logger(`Updating plugin:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
|
||||||
await this.plugin.applyPluginData(plugin);
|
|
||||||
Logger(`Setting done:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
|
||||||
await sweepPlugin(true);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
pluginConfig.querySelectorAll(".apply-plugin-version").forEach((e) =>
|
|
||||||
e.addEventListener("click", async (evt) => {
|
|
||||||
const plugin = allPlugins[e.attributes.getNamedItem("data-key").value];
|
|
||||||
Logger(`Setting plugin:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
|
||||||
await this.plugin.applyPlugin(plugin);
|
|
||||||
Logger(`Updated plugin:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
|
||||||
await sweepPlugin(true);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
pluginConfig.querySelectorAll(".sls-plugin-apply-all-newer-plugin").forEach((e) =>
|
|
||||||
e.addEventListener("click", async (evt) => {
|
|
||||||
Logger("Apply all newer plugins.", LOG_LEVEL.NOTICE);
|
|
||||||
const vaultname = e.attributes.getNamedItem("data-key").value;
|
|
||||||
const plugins = Object.values(allPlugins).filter((e) => e.deviceVaultName == vaultname && e.manifest.id != "obsidian-livesync");
|
|
||||||
for (const plugin of plugins) {
|
|
||||||
const currentPlugin = thisDevicePlugins[plugin.manifest.id];
|
|
||||||
if (currentPlugin) {
|
|
||||||
const thisVersion = versionNumberString2Number(plugin.manifest.version);
|
|
||||||
const currentVersion = versionNumberString2Number(currentPlugin.manifest.version);
|
|
||||||
if (thisVersion > currentVersion) {
|
|
||||||
Logger(`Updating plugin:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
|
||||||
await this.plugin.applyPlugin(plugin);
|
|
||||||
Logger(`Updated plugin:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
|
||||||
} else {
|
|
||||||
Logger(`Plugin ${plugin.manifest.name} is not new`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Logger(`Updating plugin:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
|
||||||
await this.plugin.applyPlugin(plugin);
|
|
||||||
Logger(`Updated plugin:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await sweepPlugin(true);
|
|
||||||
Logger("Done", LOG_LEVEL.NOTICE);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
pluginConfig.querySelectorAll(".sls-plugin-apply-all-newer-setting").forEach((e) =>
|
|
||||||
e.addEventListener("click", async (evt) => {
|
|
||||||
Logger("Apply all newer settings.", LOG_LEVEL.NOTICE);
|
|
||||||
const vaultname = e.attributes.getNamedItem("data-key").value;
|
|
||||||
const plugins = Object.values(allPlugins).filter((e) => e.deviceVaultName == vaultname && e.manifest.id != "obsidian-livesync");
|
|
||||||
for (const plugin of plugins) {
|
|
||||||
const currentPlugin = thisDevicePlugins[plugin.manifest.id];
|
|
||||||
if (currentPlugin) {
|
|
||||||
const thisVersion = plugin.mtime;
|
|
||||||
const currentVersion = currentPlugin.mtime;
|
|
||||||
if (thisVersion > currentVersion) {
|
|
||||||
Logger(`Setting plugin:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
|
||||||
await this.plugin.applyPluginData(plugin);
|
|
||||||
Logger(`Setting done:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
|
||||||
} else {
|
|
||||||
Logger(`Setting ${plugin.manifest.name} is not new`);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Logger(`Setting plugin:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
|
||||||
await this.plugin.applyPluginData(plugin);
|
|
||||||
Logger(`Setting done:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
await sweepPlugin(true);
|
|
||||||
Logger("Done", LOG_LEVEL.NOTICE);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
pluginConfig.querySelectorAll(".sls-plugin-delete").forEach((e) =>
|
|
||||||
e.addEventListener("click", async (evt) => {
|
|
||||||
const db = this.plugin.localDatabase.localDatabase;
|
|
||||||
const vaultname = e.attributes.getNamedItem("data-key").value;
|
|
||||||
const oldDocs = await db.allDocs({ startkey: `ps:${vaultname}-`, endkey: `ps:${vaultname}.`, include_docs: true });
|
|
||||||
Logger(`Deleting ${vaultname}`, LOG_LEVEL.NOTICE);
|
|
||||||
const delDocs = oldDocs.rows.map((e) => {
|
|
||||||
e.doc._deleted = true;
|
|
||||||
return e.doc;
|
|
||||||
});
|
|
||||||
await db.bulkDocs(delDocs);
|
|
||||||
Logger(`Deleted ${vaultname}`, LOG_LEVEL.NOTICE);
|
|
||||||
await this.plugin.replicate(true);
|
|
||||||
await updatePluginPane();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const pluginConfig = containerPluginSettings.createEl("div");
|
|
||||||
|
|
||||||
new Setting(containerPluginSettings)
|
|
||||||
.setName("Reload")
|
|
||||||
.setDesc("Replicate once and reload the list")
|
|
||||||
.addButton((button) =>
|
|
||||||
button
|
|
||||||
.setButtonText("Reload")
|
|
||||||
.setDisabled(false)
|
|
||||||
.onClick(async () => {
|
|
||||||
if (!this.plugin.settings.usePluginSync) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
await this.plugin.replicate(true);
|
|
||||||
await updatePluginPane();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
new Setting(containerPluginSettings)
|
|
||||||
.setName("Save plugins into the database")
|
|
||||||
.setDesc("")
|
|
||||||
.addButton((button) =>
|
|
||||||
button
|
|
||||||
.setButtonText("Save plugins")
|
|
||||||
.setDisabled(false)
|
|
||||||
.onClick(async () => {
|
|
||||||
if (!this.plugin.settings.usePluginSync) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Logger("Save plugins.", LOG_LEVEL.NOTICE);
|
|
||||||
await sweepPlugin(true);
|
|
||||||
Logger("All plugins have been saved.", LOG_LEVEL.NOTICE);
|
|
||||||
await this.plugin.replicate(true);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
new Setting(containerPluginSettings)
|
|
||||||
.setName("Check updates")
|
|
||||||
.setDesc("")
|
|
||||||
.addButton((button) =>
|
|
||||||
button
|
|
||||||
.setButtonText("Check")
|
|
||||||
.setDisabled(false)
|
|
||||||
.onClick(async () => {
|
|
||||||
Logger("Checking plugins.", LOG_LEVEL.NOTICE);
|
|
||||||
await this.plugin.checkPluginUpdate();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
updatePluginPane();
|
|
||||||
|
|
||||||
addScreenElement("60", containerPluginSettings);
|
addScreenElement("60", containerPluginSettings);
|
||||||
|
|
||||||
|
290
src/PluginPane.svelte
Normal file
290
src/PluginPane.svelte
Normal file
@ -0,0 +1,290 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import ObsidianLiveSyncPlugin from "./main";
|
||||||
|
import { onMount } from "svelte";
|
||||||
|
import { DevicePluginList, PluginDataEntry } from "./types";
|
||||||
|
import { versionNumberString2Number } from "./utils";
|
||||||
|
|
||||||
|
type JudgeResult = "" | "NEWER" | "EVEN" | "EVEN_BUT_DIFFERENT" | "OLDER" | "REMOTE_ONLY";
|
||||||
|
|
||||||
|
interface PluginDataEntryDisp extends PluginDataEntry {
|
||||||
|
versionInfo: string;
|
||||||
|
mtimeInfo: string;
|
||||||
|
mtimeFlag: JudgeResult;
|
||||||
|
versionFlag: JudgeResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
export let plugin: ObsidianLiveSyncPlugin;
|
||||||
|
let plugins: PluginDataEntry[] = [];
|
||||||
|
let deviceAndPlugins: { [key: string]: PluginDataEntryDisp[] } = {};
|
||||||
|
let devicePluginList: [string, PluginDataEntryDisp[]][] = [];
|
||||||
|
let ownPlugins: DevicePluginList = null;
|
||||||
|
let showOwnPlugins = false;
|
||||||
|
let targetList: { [key: string]: boolean } = {};
|
||||||
|
|
||||||
|
function saveTargetList() {
|
||||||
|
window.localStorage.setItem("ols-plugin-targetlist", JSON.stringify(targetList));
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTargetList() {
|
||||||
|
let e = window.localStorage.getItem("ols-plugin-targetlist") || "{}";
|
||||||
|
try {
|
||||||
|
targetList = JSON.parse(e);
|
||||||
|
} catch (_) {
|
||||||
|
// NO OP.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSelection() {
|
||||||
|
targetList = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateList() {
|
||||||
|
let x = await plugin.getPluginList();
|
||||||
|
ownPlugins = x.thisDevicePlugins;
|
||||||
|
plugins = Object.values(x.allPlugins);
|
||||||
|
let targetListItems = Array.from(new Set(plugins.map((e) => e.deviceVaultName + "---" + e.manifest.id)));
|
||||||
|
let newTargetList: { [key: string]: boolean } = {};
|
||||||
|
for (const id of targetListItems) {
|
||||||
|
for (const tag of ["---plugin", "---setting"]) {
|
||||||
|
newTargetList[id + tag] = id + tag in targetList && targetList[id + tag];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
targetList = newTargetList;
|
||||||
|
saveTargetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
$: {
|
||||||
|
deviceAndPlugins = {};
|
||||||
|
for (const p of plugins) {
|
||||||
|
if (p.deviceVaultName == plugin.settings.deviceAndVaultName && !showOwnPlugins) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (!(p.deviceVaultName in deviceAndPlugins)) {
|
||||||
|
deviceAndPlugins[p.deviceVaultName] = [];
|
||||||
|
}
|
||||||
|
let dispInfo: PluginDataEntryDisp = { ...p, versionInfo: "", mtimeInfo: "", versionFlag: "", mtimeFlag: "" };
|
||||||
|
dispInfo.versionInfo = p.manifest.version;
|
||||||
|
let x = new Date().getTime() / 1000;
|
||||||
|
let mtime = p.mtime / 1000;
|
||||||
|
let diff = (x - mtime) / 60;
|
||||||
|
if (p.mtime == 0) {
|
||||||
|
dispInfo.mtimeInfo = `-`;
|
||||||
|
} else if (diff < 60) {
|
||||||
|
dispInfo.mtimeInfo = `${diff | 0} Mins ago`;
|
||||||
|
} else if (diff < 60 * 24) {
|
||||||
|
dispInfo.mtimeInfo = `${(diff / 60) | 0} Hours ago`;
|
||||||
|
} else if (diff < 60 * 24 * 10) {
|
||||||
|
dispInfo.mtimeInfo = `${(diff / (60 * 24)) | 0} Days ago`;
|
||||||
|
} else {
|
||||||
|
dispInfo.mtimeInfo = new Date(dispInfo.mtime).toLocaleString();
|
||||||
|
}
|
||||||
|
// compare with own plugin
|
||||||
|
let id = p.manifest.id;
|
||||||
|
|
||||||
|
if (id in ownPlugins) {
|
||||||
|
// Which we have.
|
||||||
|
const ownPlugin = ownPlugins[id];
|
||||||
|
let localVer = versionNumberString2Number(ownPlugin.manifest.version);
|
||||||
|
let pluginVer = versionNumberString2Number(p.manifest.version);
|
||||||
|
if (localVer > pluginVer) {
|
||||||
|
dispInfo.versionFlag = "OLDER";
|
||||||
|
} else if (localVer == pluginVer) {
|
||||||
|
if (ownPlugin.manifestJson + (ownPlugin.styleCss ?? "") + ownPlugin.mainJs != p.manifestJson + (p.styleCss ?? "") + p.mainJs) {
|
||||||
|
dispInfo.versionFlag = "EVEN_BUT_DIFFERENT";
|
||||||
|
} else {
|
||||||
|
dispInfo.versionFlag = "EVEN";
|
||||||
|
}
|
||||||
|
} else if (localVer < pluginVer) {
|
||||||
|
dispInfo.versionFlag = "NEWER";
|
||||||
|
}
|
||||||
|
if ((ownPlugin.dataJson ?? "") == (p.dataJson ?? "")) {
|
||||||
|
if (ownPlugin.mtime == 0 && p.mtime == 0) {
|
||||||
|
dispInfo.mtimeFlag = "";
|
||||||
|
} else {
|
||||||
|
dispInfo.mtimeFlag = "EVEN";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (((ownPlugin.mtime / 1000) | 0) > ((p.mtime / 1000) | 0)) {
|
||||||
|
dispInfo.mtimeFlag = "OLDER";
|
||||||
|
} else if (((ownPlugin.mtime / 1000) | 0) == ((p.mtime / 1000) | 0)) {
|
||||||
|
dispInfo.mtimeFlag = "EVEN_BUT_DIFFERENT";
|
||||||
|
} else if (((ownPlugin.mtime / 1000) | 0) < ((p.mtime / 1000) | 0)) {
|
||||||
|
dispInfo.mtimeFlag = "NEWER";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
dispInfo.versionFlag = "REMOTE_ONLY";
|
||||||
|
dispInfo.mtimeFlag = "REMOTE_ONLY";
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceAndPlugins[p.deviceVaultName].push(dispInfo);
|
||||||
|
}
|
||||||
|
devicePluginList = Object.entries(deviceAndPlugins);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDispString(stat: JudgeResult): string {
|
||||||
|
if (stat == "") return "";
|
||||||
|
if (stat == "NEWER") return " (Newer)";
|
||||||
|
if (stat == "OLDER") return " (Older)";
|
||||||
|
if (stat == "EVEN") return " (Even)";
|
||||||
|
if (stat == "EVEN_BUT_DIFFERENT") return " (Even but different)";
|
||||||
|
if (stat == "REMOTE_ONLY") return " (Remote Only)";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(async () => {
|
||||||
|
loadTargetList();
|
||||||
|
await updateList();
|
||||||
|
});
|
||||||
|
|
||||||
|
function toggleShowOwnPlugins() {
|
||||||
|
showOwnPlugins = !showOwnPlugins;
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleTarget(key: string) {
|
||||||
|
targetList[key] = !targetList[key];
|
||||||
|
saveTargetList();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleAll(devicename: string) {
|
||||||
|
for (const c in targetList) {
|
||||||
|
if (c.startsWith(devicename)) {
|
||||||
|
targetList[c] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function sweepPlugins() {
|
||||||
|
//@ts-ignore
|
||||||
|
await plugin.app.plugins.loadManifests();
|
||||||
|
await plugin.sweepPlugin(true);
|
||||||
|
updateList();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function applyPlugins() {
|
||||||
|
for (const c in targetList) {
|
||||||
|
if (targetList[c] == true) {
|
||||||
|
const [deviceAndVault, id, opt] = c.split("---");
|
||||||
|
if (deviceAndVault in deviceAndPlugins) {
|
||||||
|
const entry = deviceAndPlugins[deviceAndVault].find((e) => e.manifest.id == id);
|
||||||
|
if (entry) {
|
||||||
|
if (opt == "plugin") {
|
||||||
|
if (entry.versionFlag != "EVEN") await plugin.applyPlugin(entry);
|
||||||
|
} else if (opt == "setting") {
|
||||||
|
if (entry.mtimeFlag != "EVEN") await plugin.applyPluginData(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//@ts-ignore
|
||||||
|
await plugin.app.plugins.loadManifests();
|
||||||
|
await plugin.sweepPlugin(true);
|
||||||
|
updateList();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkUpdates() {
|
||||||
|
await plugin.checkPluginUpdate();
|
||||||
|
}
|
||||||
|
async function replicateAndRefresh() {
|
||||||
|
await plugin.replicate(true);
|
||||||
|
updateList();
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h1>Plugins and their settings</h1>
|
||||||
|
<div class="ols-plugins-div-buttons">
|
||||||
|
Show own items
|
||||||
|
<div class="checkbox-container" class:is-enabled={showOwnPlugins} on:click={toggleShowOwnPlugins} />
|
||||||
|
</div>
|
||||||
|
<div class="sls-plugins-wrap">
|
||||||
|
<table class="sls-plugins-tbl">
|
||||||
|
<tr style="position:sticky">
|
||||||
|
<th class="sls-plugins-tbl-device-head">Name</th>
|
||||||
|
<th class="sls-plugins-tbl-device-head">Info</th>
|
||||||
|
<th class="sls-plugins-tbl-device-head">Target</th>
|
||||||
|
</tr>
|
||||||
|
{#if devicePluginList.length == 0}
|
||||||
|
<tr>
|
||||||
|
<td colspan="3" class="sls-table-tail tcenter"> Retrieving... </td>
|
||||||
|
</tr>
|
||||||
|
{/if}
|
||||||
|
{#each devicePluginList as [deviceName, devicePlugins]}
|
||||||
|
<tr>
|
||||||
|
<th colspan="2" class="sls-plugins-tbl-device-head">{deviceName}</th>
|
||||||
|
<th class="sls-plugins-tbl-device-head">
|
||||||
|
<button class="mod-cta" on:click={() => toggleAll(deviceName)}>✔</button>
|
||||||
|
</th>
|
||||||
|
</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}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div class="ols-plugins-div-buttons">
|
||||||
|
<button class="" on:click={replicateAndRefresh}>Replicate and refresh</button>
|
||||||
|
<button class="" on:click={clearSelection}>Clear Selection</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ols-plugins-div-buttons">
|
||||||
|
<button class="mod-cta" on:click={checkUpdates}>Check Updates</button>
|
||||||
|
<button class="mod-cta" on:click={sweepPlugins}>Sweep installed</button>
|
||||||
|
<button class="mod-cta" on:click={applyPlugins}>Apply all</button>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="ols-plugins-div-buttons">-->
|
||||||
|
<!-- <button class="mod-warning" on:click={applyPlugins}>Delete all selected</button>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.ols-plugins-div-buttons {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapToggle {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
181
src/main.ts
181
src/main.ts
@ -1,4 +1,4 @@
|
|||||||
import { debounce, Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, PluginManifest } from "obsidian";
|
import { debounce, Notice, Plugin, TFile, addIcon, TFolder, normalizePath, TAbstractFile, Editor, MarkdownView, PluginManifest, Modal, App } from "obsidian";
|
||||||
import { diff_match_patch } from "diff-match-patch";
|
import { diff_match_patch } from "diff-match-patch";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -26,6 +26,36 @@ import { ConflictResolveModal } from "./ConflictResolveModal";
|
|||||||
import { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab";
|
import { ObsidianLiveSyncSettingTab } from "./ObsidianLiveSyncSettingTab";
|
||||||
import { DocumentHistoryModal } from "./DocumentHistoryModal";
|
import { DocumentHistoryModal } from "./DocumentHistoryModal";
|
||||||
|
|
||||||
|
import PluginPane from "./PluginPane.svelte";
|
||||||
|
|
||||||
|
class PluginDialogModal extends Modal {
|
||||||
|
plugin: ObsidianLiveSyncPlugin;
|
||||||
|
logEl: HTMLDivElement;
|
||||||
|
component: PluginPane = null;
|
||||||
|
|
||||||
|
constructor(app: App, plugin: ObsidianLiveSyncPlugin) {
|
||||||
|
super(app);
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpen() {
|
||||||
|
const { contentEl } = this;
|
||||||
|
if (this.component == null) {
|
||||||
|
this.component = new PluginPane({
|
||||||
|
target: contentEl,
|
||||||
|
props: { plugin: this.plugin },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onClose() {
|
||||||
|
if (this.component != null) {
|
||||||
|
this.component.$destroy();
|
||||||
|
this.component = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default class ObsidianLiveSyncPlugin extends Plugin {
|
export default class ObsidianLiveSyncPlugin extends Plugin {
|
||||||
settings: ObsidianLiveSyncSettings;
|
settings: ObsidianLiveSyncSettings;
|
||||||
localDatabase: LocalPouchDB;
|
localDatabase: LocalPouchDB;
|
||||||
@ -39,6 +69,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.registerInterval(timer);
|
this.registerInterval(timer);
|
||||||
return timer;
|
return timer;
|
||||||
}
|
}
|
||||||
|
|
||||||
isRedFlagRaised(): boolean {
|
isRedFlagRaised(): boolean {
|
||||||
const redflag = this.app.vault.getAbstractFileByPath(normalizePath(FLAGMD_REDFLAG));
|
const redflag = this.app.vault.getAbstractFileByPath(normalizePath(FLAGMD_REDFLAG));
|
||||||
if (redflag != null) {
|
if (redflag != null) {
|
||||||
@ -46,6 +77,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
showHistory(file: TFile) {
|
showHistory(file: TFile) {
|
||||||
if (!this.settings.useHistory) {
|
if (!this.settings.useHistory) {
|
||||||
Logger("You have to enable Use History in misc.", LOG_LEVEL.NOTICE);
|
Logger("You have to enable Use History in misc.", LOG_LEVEL.NOTICE);
|
||||||
@ -53,6 +85,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
new DocumentHistoryModal(this.app, this, file).open();
|
new DocumentHistoryModal(this.app, this, file).open();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async onload() {
|
async onload() {
|
||||||
setLogger(this.addLog.bind(this)); // Logger moved to global.
|
setLogger(this.addLog.bind(this)); // Logger moved to global.
|
||||||
Logger("loading plugin");
|
Logger("loading plugin");
|
||||||
@ -116,6 +149,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.periodicSync = this.periodicSync.bind(this);
|
this.periodicSync = this.periodicSync.bind(this);
|
||||||
this.setPeriodicSync = this.setPeriodicSync.bind(this);
|
this.setPeriodicSync = this.setPeriodicSync.bind(this);
|
||||||
|
|
||||||
|
this.getPluginList = this.getPluginList.bind(this);
|
||||||
// this.registerWatchEvents();
|
// this.registerWatchEvents();
|
||||||
this.addSettingTab(new ObsidianLiveSyncSettingTab(this.app, this));
|
this.addSettingTab(new ObsidianLiveSyncSettingTab(this.app, this));
|
||||||
|
|
||||||
@ -223,8 +257,35 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
setLockNotifier(() => {
|
setLockNotifier(() => {
|
||||||
this.refreshStatusText();
|
this.refreshStatusText();
|
||||||
});
|
});
|
||||||
|
this.addCommand({
|
||||||
|
id: "livesync-plugin-dialog",
|
||||||
|
name: "Show Plugins and their settings",
|
||||||
|
callback: () => {
|
||||||
|
this.showPluginSyncModal();
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pluginDialog: PluginDialogModal = null;
|
||||||
|
|
||||||
|
showPluginSyncModal() {
|
||||||
|
if (this.pluginDialog != null) {
|
||||||
|
this.pluginDialog.open();
|
||||||
|
} else {
|
||||||
|
this.pluginDialog = new PluginDialogModal(this.app, this);
|
||||||
|
this.pluginDialog.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hidePluginSyncModal() {
|
||||||
|
if (this.pluginDialog != null) {
|
||||||
|
this.pluginDialog.close();
|
||||||
|
this.pluginDialog = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onunload() {
|
onunload() {
|
||||||
|
this.hidePluginSyncModal();
|
||||||
this.localDatabase.onunload();
|
this.localDatabase.onunload();
|
||||||
if (this.gcTimerHandler != null) {
|
if (this.gcTimerHandler != null) {
|
||||||
clearTimeout(this.gcTimerHandler);
|
clearTimeout(this.gcTimerHandler);
|
||||||
@ -250,6 +311,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
};
|
};
|
||||||
await this.localDatabase.initializeDatabase();
|
await this.localDatabase.initializeDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
async garbageCollect() {
|
async garbageCollect() {
|
||||||
await this.localDatabase.garbageCollect();
|
await this.localDatabase.garbageCollect();
|
||||||
}
|
}
|
||||||
@ -263,12 +325,15 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
triggerRealizeSettingSyncMode() {
|
triggerRealizeSettingSyncMode() {
|
||||||
(async () => await this.realizeSettingSyncMode())();
|
(async () => await this.realizeSettingSyncMode())();
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveSettings() {
|
async saveSettings() {
|
||||||
await this.saveData(this.settings);
|
await this.saveData(this.settings);
|
||||||
this.localDatabase.settings = this.settings;
|
this.localDatabase.settings = this.settings;
|
||||||
this.triggerRealizeSettingSyncMode();
|
this.triggerRealizeSettingSyncMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
gcTimerHandler: any = null;
|
gcTimerHandler: any = null;
|
||||||
|
|
||||||
gcHook() {
|
gcHook() {
|
||||||
if (this.settings.gcDelay == 0) return;
|
if (this.settings.gcDelay == 0) return;
|
||||||
if (this.settings.useHistory) return;
|
if (this.settings.useHistory) return;
|
||||||
@ -282,6 +347,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.garbageCollect();
|
this.garbageCollect();
|
||||||
}, GC_DELAY);
|
}, GC_DELAY);
|
||||||
}
|
}
|
||||||
|
|
||||||
registerWatchEvents() {
|
registerWatchEvents() {
|
||||||
this.registerEvent(this.app.vault.on("modify", this.watchVaultChange));
|
this.registerEvent(this.app.vault.on("modify", this.watchVaultChange));
|
||||||
this.registerEvent(this.app.vault.on("delete", this.watchVaultDelete));
|
this.registerEvent(this.app.vault.on("delete", this.watchVaultDelete));
|
||||||
@ -294,6 +360,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
watchWindowVisiblity() {
|
watchWindowVisiblity() {
|
||||||
this.watchWindowVisiblityAsync();
|
this.watchWindowVisiblityAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
async watchWindowVisiblityAsync() {
|
async watchWindowVisiblityAsync() {
|
||||||
if (this.settings.suspendFileWatching) return;
|
if (this.settings.suspendFileWatching) return;
|
||||||
// if (this.suspended) return;
|
// if (this.suspended) return;
|
||||||
@ -325,6 +392,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
if (this.settings.suspendFileWatching) return;
|
if (this.settings.suspendFileWatching) return;
|
||||||
this.watchWorkspaceOpenAsync(file);
|
this.watchWorkspaceOpenAsync(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
async watchWorkspaceOpenAsync(file: TFile) {
|
async watchWorkspaceOpenAsync(file: TFile) {
|
||||||
await this.applyBatchChange();
|
await this.applyBatchChange();
|
||||||
if (file == null) return;
|
if (file == null) return;
|
||||||
@ -335,10 +403,12 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
await this.showIfConflicted(file);
|
await this.showIfConflicted(file);
|
||||||
this.gcHook();
|
this.gcHook();
|
||||||
}
|
}
|
||||||
|
|
||||||
watchVaultCreate(file: TFile, ...args: any[]) {
|
watchVaultCreate(file: TFile, ...args: any[]) {
|
||||||
if (this.settings.suspendFileWatching) return;
|
if (this.settings.suspendFileWatching) return;
|
||||||
this.watchVaultChangeAsync(file, ...args);
|
this.watchVaultChangeAsync(file, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
watchVaultChange(file: TAbstractFile, ...args: any[]) {
|
watchVaultChange(file: TAbstractFile, ...args: any[]) {
|
||||||
if (!(file instanceof TFile)) {
|
if (!(file instanceof TFile)) {
|
||||||
return;
|
return;
|
||||||
@ -352,6 +422,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
this.watchVaultChangeAsync(file, ...args);
|
this.watchVaultChangeAsync(file, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
async applyBatchChange() {
|
async applyBatchChange() {
|
||||||
if (!this.settings.batchSave || this.batchFileChange.length == 0) {
|
if (!this.settings.batchSave || this.batchFileChange.length == 0) {
|
||||||
return [];
|
return [];
|
||||||
@ -375,19 +446,23 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
return await Promise.all(promises);
|
return await Promise.all(promises);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
batchFileChange: string[] = [];
|
batchFileChange: string[] = [];
|
||||||
|
|
||||||
async watchVaultChangeAsync(file: TFile, ...args: any[]) {
|
async watchVaultChangeAsync(file: TFile, ...args: any[]) {
|
||||||
if (file instanceof TFile) {
|
if (file instanceof TFile) {
|
||||||
await this.updateIntoDB(file);
|
await this.updateIntoDB(file);
|
||||||
this.gcHook();
|
this.gcHook();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watchVaultDelete(file: TAbstractFile) {
|
watchVaultDelete(file: TAbstractFile) {
|
||||||
// When save is delayed, it should be cancelled.
|
// When save is delayed, it should be cancelled.
|
||||||
this.batchFileChange = this.batchFileChange.filter((e) => e == file.path);
|
this.batchFileChange = this.batchFileChange.filter((e) => e == file.path);
|
||||||
if (this.settings.suspendFileWatching) return;
|
if (this.settings.suspendFileWatching) return;
|
||||||
this.watchVaultDeleteAsync(file);
|
this.watchVaultDeleteAsync(file).then(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
async watchVaultDeleteAsync(file: TAbstractFile) {
|
async watchVaultDeleteAsync(file: TAbstractFile) {
|
||||||
if (file instanceof TFile) {
|
if (file instanceof TFile) {
|
||||||
await this.deleteFromDB(file);
|
await this.deleteFromDB(file);
|
||||||
@ -396,6 +471,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
this.gcHook();
|
this.gcHook();
|
||||||
}
|
}
|
||||||
|
|
||||||
GetAllFilesRecursively(file: TAbstractFile): TFile[] {
|
GetAllFilesRecursively(file: TAbstractFile): TFile[] {
|
||||||
if (file instanceof TFile) {
|
if (file instanceof TFile) {
|
||||||
return [file];
|
return [file];
|
||||||
@ -410,10 +486,12 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
throw new Error(`Filetype error:${file.path}`);
|
throw new Error(`Filetype error:${file.path}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watchVaultRename(file: TAbstractFile, oldFile: any) {
|
watchVaultRename(file: TAbstractFile, oldFile: any) {
|
||||||
if (this.settings.suspendFileWatching) return;
|
if (this.settings.suspendFileWatching) return;
|
||||||
this.watchVaultRenameAsync(file, oldFile);
|
this.watchVaultRenameAsync(file, oldFile).then(() => {});
|
||||||
}
|
}
|
||||||
|
|
||||||
getFilePath(file: TAbstractFile): string {
|
getFilePath(file: TAbstractFile): string {
|
||||||
if (file instanceof TFolder) {
|
if (file instanceof TFolder) {
|
||||||
if (file.isRoot()) return "";
|
if (file.isRoot()) return "";
|
||||||
@ -425,6 +503,7 @@ 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) {
|
async watchVaultRenameAsync(file: TAbstractFile, oldFile: any) {
|
||||||
Logger(`${oldFile} renamed to ${file.path}`, LOG_LEVEL.VERBOSE);
|
Logger(`${oldFile} renamed to ${file.path}`, LOG_LEVEL.VERBOSE);
|
||||||
try {
|
try {
|
||||||
@ -461,9 +540,11 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
this.gcHook();
|
this.gcHook();
|
||||||
}
|
}
|
||||||
|
|
||||||
addLogHook: () => void = null;
|
addLogHook: () => void = null;
|
||||||
//--> 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
|
// 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.INFO && this.settings && this.settings.lessInformationInLog) {
|
if (level < LOG_LEVEL.INFO && this.settings && this.settings.lessInformationInLog) {
|
||||||
@ -547,7 +628,10 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
await this.ensureDirectory(path);
|
await this.ensureDirectory(path);
|
||||||
try {
|
try {
|
||||||
const newfile = await this.app.vault.createBinary(normalizePath(path), bin, { ctime: doc.ctime, mtime: doc.mtime });
|
const newfile = await this.app.vault.createBinary(normalizePath(path), bin, {
|
||||||
|
ctime: doc.ctime,
|
||||||
|
mtime: doc.mtime,
|
||||||
|
});
|
||||||
Logger("live : write to local (newfile:b) " + path);
|
Logger("live : write to local (newfile:b) " + path);
|
||||||
this.app.vault.trigger("create", newfile);
|
this.app.vault.trigger("create", newfile);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
@ -562,7 +646,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, { ctime: doc.ctime, mtime: doc.mtime });
|
const newfile = await this.app.vault.create(normalizePath(path), doc.data, {
|
||||||
|
ctime: doc.ctime,
|
||||||
|
mtime: doc.mtime,
|
||||||
|
});
|
||||||
Logger("live : write to local (newfile:p) " + path);
|
Logger("live : write to local (newfile:p) " + path);
|
||||||
this.app.vault.trigger("create", newfile);
|
this.app.vault.trigger("create", newfile);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
@ -590,6 +677,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async doc2storate_modify(docEntry: EntryBody, file: TFile, force?: boolean) {
|
async doc2storate_modify(docEntry: EntryBody, file: TFile, force?: boolean) {
|
||||||
const pathSrc = id2path(docEntry._id);
|
const pathSrc = id2path(docEntry._id);
|
||||||
if (shouldBeIgnored(pathSrc)) {
|
if (shouldBeIgnored(pathSrc)) {
|
||||||
@ -597,7 +685,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
if (docEntry._deleted) {
|
if (docEntry._deleted) {
|
||||||
//basically pass.
|
//basically pass.
|
||||||
//but if there're no docs left, delete file.
|
//but if there are no docs left, delete file.
|
||||||
const lastDocs = await this.localDatabase.getDBEntry(pathSrc);
|
const lastDocs = await this.localDatabase.getDBEntry(pathSrc);
|
||||||
if (lastDocs === false) {
|
if (lastDocs === false) {
|
||||||
await this.deleteVaultItem(file);
|
await this.deleteVaultItem(file);
|
||||||
@ -657,6 +745,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
//eq.case
|
//eq.case
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async handleDBChanged(change: EntryBody) {
|
async handleDBChanged(change: EntryBody) {
|
||||||
const targetFile = this.app.vault.getAbstractFileByPath(id2path(change._id));
|
const targetFile = this.app.vault.getAbstractFileByPath(id2path(change._id));
|
||||||
if (targetFile == null) {
|
if (targetFile == null) {
|
||||||
@ -676,6 +765,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
periodicSyncHandler: number = null;
|
periodicSyncHandler: number = null;
|
||||||
|
|
||||||
//---> Sync
|
//---> Sync
|
||||||
async parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): Promise<void> {
|
async parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): Promise<void> {
|
||||||
this.refreshStatusText();
|
this.refreshStatusText();
|
||||||
@ -702,61 +792,81 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.gcHook();
|
this.gcHook();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
triggerCheckPluginUpdate() {
|
triggerCheckPluginUpdate() {
|
||||||
(async () => await this.checkPluginUpdate())();
|
(async () => await this.checkPluginUpdate())();
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkPluginUpdate() {
|
async checkPluginUpdate() {
|
||||||
if (!this.settings.usePluginSync) return;
|
if (!this.settings.usePluginSync) return;
|
||||||
await this.sweepPlugin(false);
|
await this.sweepPlugin(false);
|
||||||
const { allPlugins, thisDevicePlugins } = await this.getPluginList();
|
const { allPlugins, thisDevicePlugins } = await this.getPluginList();
|
||||||
const arrPlugins = Object.values(allPlugins);
|
const arrPlugins = Object.values(allPlugins);
|
||||||
|
let updateFound = false;
|
||||||
for (const plugin of arrPlugins) {
|
for (const plugin of arrPlugins) {
|
||||||
const currentPlugin = thisDevicePlugins[plugin.manifest.id];
|
const ownPlugin = thisDevicePlugins[plugin.manifest.id];
|
||||||
if (currentPlugin) {
|
if (ownPlugin) {
|
||||||
const thisVersion = versionNumberString2Number(plugin.manifest.version);
|
const remoteVersion = versionNumberString2Number(plugin.manifest.version);
|
||||||
const currentVersion = versionNumberString2Number(currentPlugin.manifest.version);
|
const ownVersion = versionNumberString2Number(ownPlugin.manifest.version);
|
||||||
if (thisVersion > currentVersion) {
|
if (remoteVersion > ownVersion) {
|
||||||
Logger(`the device ${plugin.deviceVaultName} has the newer plugin:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
updateFound = true;
|
||||||
}
|
}
|
||||||
if (plugin.mtime > currentPlugin.mtime) {
|
if (((plugin.mtime / 1000) | 0) > ((ownPlugin.mtime / 1000) | 0) && (plugin.dataJson ?? "") != (ownPlugin.dataJson ?? "")) {
|
||||||
Logger(`the device ${plugin.deviceVaultName} has the newer settings of the plugin:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
updateFound = true;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (updateFound) {
|
||||||
|
const fragment = createFragment((doc) => {
|
||||||
|
doc.createEl("a", null, (a) => {
|
||||||
|
a.text = "There're some new plugins or their settings";
|
||||||
|
a.addEventListener("click", () => this.showPluginSyncModal());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
new Notice(fragment, 10000);
|
||||||
} else {
|
} else {
|
||||||
Logger(`the device ${plugin.deviceVaultName} has the new plugin:${plugin.manifest.name}`, LOG_LEVEL.NOTICE);
|
Logger("Everything is up to date.", LOG_LEVEL.NOTICE);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clearPeriodicSync() {
|
clearPeriodicSync() {
|
||||||
if (this.periodicSyncHandler != null) {
|
if (this.periodicSyncHandler != null) {
|
||||||
clearInterval(this.periodicSyncHandler);
|
clearInterval(this.periodicSyncHandler);
|
||||||
this.periodicSyncHandler = null;
|
this.periodicSyncHandler = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setPeriodicSync() {
|
setPeriodicSync() {
|
||||||
if (this.settings.periodicReplication && this.settings.periodicReplicationInterval > 0) {
|
if (this.settings.periodicReplication && this.settings.periodicReplicationInterval > 0) {
|
||||||
this.clearPeriodicSync();
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async periodicSync() {
|
async periodicSync() {
|
||||||
await this.replicate();
|
await this.replicate();
|
||||||
}
|
}
|
||||||
|
|
||||||
periodicPluginSweepHandler: number = null;
|
periodicPluginSweepHandler: number = null;
|
||||||
|
|
||||||
clearPluginSweep() {
|
clearPluginSweep() {
|
||||||
if (this.periodicPluginSweepHandler != null) {
|
if (this.periodicPluginSweepHandler != null) {
|
||||||
clearInterval(this.periodicPluginSweepHandler);
|
clearInterval(this.periodicPluginSweepHandler);
|
||||||
this.periodicPluginSweepHandler = null;
|
this.periodicPluginSweepHandler = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setPluginSweep() {
|
setPluginSweep() {
|
||||||
if (this.settings.autoSweepPluginsPeriodic) {
|
if (this.settings.autoSweepPluginsPeriodic) {
|
||||||
this.clearPluginSweep();
|
this.clearPluginSweep();
|
||||||
this.periodicPluginSweepHandler = this.setInterval(async () => await this.periodicPluginSweep(), PERIODIC_PLUGIN_SWEEP * 1000);
|
this.periodicPluginSweepHandler = this.setInterval(async () => await this.periodicPluginSweep(), PERIODIC_PLUGIN_SWEEP * 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async periodicPluginSweep() {
|
async periodicPluginSweep() {
|
||||||
await this.sweepPlugin(false);
|
await this.sweepPlugin(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async realizeSettingSyncMode() {
|
async realizeSettingSyncMode() {
|
||||||
this.localDatabase.closeReplication();
|
this.localDatabase.closeReplication();
|
||||||
this.clearPeriodicSync();
|
this.clearPeriodicSync();
|
||||||
@ -774,7 +884,9 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.setPeriodicSync();
|
this.setPeriodicSync();
|
||||||
this.setPluginSweep();
|
this.setPluginSweep();
|
||||||
}
|
}
|
||||||
|
|
||||||
lastMessage = "";
|
lastMessage = "";
|
||||||
|
|
||||||
refreshStatusText() {
|
refreshStatusText() {
|
||||||
const sent = this.localDatabase.docSent;
|
const sent = this.localDatabase.docSent;
|
||||||
const arrived = this.localDatabase.docArrived;
|
const arrived = this.localDatabase.docArrived;
|
||||||
@ -811,6 +923,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
const message = `Sync:${w} ↑${sent} ↓${arrived}${waiting}${procsDisp}`;
|
const message = `Sync:${w} ↑${sent} ↓${arrived}${waiting}${procsDisp}`;
|
||||||
this.setStatusBarText(message);
|
this.setStatusBarText(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatusBarText(message: string) {
|
setStatusBarText(message: string) {
|
||||||
if (this.lastMessage != message) {
|
if (this.lastMessage != message) {
|
||||||
this.statusBar.setText(message);
|
this.statusBar.setText(message);
|
||||||
@ -824,6 +937,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.lastMessage = message;
|
this.lastMessage = message;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async replicate(showMessage?: boolean) {
|
async replicate(showMessage?: boolean) {
|
||||||
if (this.settings.versionUpFlash != "") {
|
if (this.settings.versionUpFlash != "") {
|
||||||
new Notice("Open settings and check message, please.");
|
new Notice("Open settings and check message, please.");
|
||||||
@ -840,21 +954,26 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
await this.openDatabase();
|
await this.openDatabase();
|
||||||
await this.syncAllFiles(showingNotice);
|
await this.syncAllFiles(showingNotice);
|
||||||
}
|
}
|
||||||
|
|
||||||
async replicateAllToServer(showingNotice?: boolean) {
|
async replicateAllToServer(showingNotice?: boolean) {
|
||||||
if (this.settings.autoSweepPlugins) {
|
if (this.settings.autoSweepPlugins) {
|
||||||
await this.sweepPlugin(showingNotice);
|
await this.sweepPlugin(showingNotice);
|
||||||
}
|
}
|
||||||
return await this.localDatabase.replicateAllToServer(this.settings, showingNotice);
|
return await this.localDatabase.replicateAllToServer(this.settings, showingNotice);
|
||||||
}
|
}
|
||||||
|
|
||||||
async markRemoteLocked() {
|
async markRemoteLocked() {
|
||||||
return await this.localDatabase.markRemoteLocked(this.settings, true);
|
return await this.localDatabase.markRemoteLocked(this.settings, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
async markRemoteUnlocked() {
|
async markRemoteUnlocked() {
|
||||||
return await this.localDatabase.markRemoteLocked(this.settings, false);
|
return await this.localDatabase.markRemoteLocked(this.settings, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
async markRemoteResolved() {
|
async markRemoteResolved() {
|
||||||
return await this.localDatabase.markRemoteResolved(this.settings);
|
return await this.localDatabase.markRemoteResolved(this.settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncAllFiles(showingNotice?: boolean) {
|
async syncAllFiles(showingNotice?: boolean) {
|
||||||
// synchronize all files between database and storage.
|
// synchronize all files between database and storage.
|
||||||
let notice: Notice = null;
|
let notice: Notice = null;
|
||||||
@ -896,6 +1015,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
Logger(ex);
|
Logger(ex);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// @ts-ignore
|
||||||
if (!Promise.allSettled) {
|
if (!Promise.allSettled) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
procs.map((p) =>
|
procs.map((p) =>
|
||||||
@ -911,6 +1031,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
|
// @ts-ignore
|
||||||
await Promise.allSettled(procs);
|
await Promise.allSettled(procs);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -932,6 +1053,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
Logger("Initialize done!", LOG_LEVEL.NOTICE);
|
Logger("Initialize done!", LOG_LEVEL.NOTICE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteFolderOnDB(folder: TFolder) {
|
async deleteFolderOnDB(folder: TFolder) {
|
||||||
Logger(`delete folder:${folder.path}`);
|
Logger(`delete folder:${folder.path}`);
|
||||||
await this.localDatabase.deleteDBEntryPrefix(folder.path + "/");
|
await this.localDatabase.deleteDBEntryPrefix(folder.path + "/");
|
||||||
@ -1016,6 +1138,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Getting file conflicted status.
|
* Getting file conflicted status.
|
||||||
* @param path the file location
|
* @param path the file location
|
||||||
@ -1076,6 +1199,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
diff: diff,
|
diff: diff,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
showMergeDialog(file: TFile, conflictCheckResult: diff_result): Promise<boolean> {
|
showMergeDialog(file: TFile, conflictCheckResult: diff_result): Promise<boolean> {
|
||||||
return new Promise((res, rej) => {
|
return new Promise((res, rej) => {
|
||||||
Logger("open conflict dialog", LOG_LEVEL.VERBOSE);
|
Logger("open conflict dialog", LOG_LEVEL.VERBOSE);
|
||||||
@ -1121,10 +1245,12 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}).open();
|
}).open();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
conflictedCheckFiles: string[] = [];
|
conflictedCheckFiles: string[] = [];
|
||||||
|
|
||||||
// queueing the conflicted file check
|
// queueing the conflicted file check
|
||||||
conflictedCheckTimer: number;
|
conflictedCheckTimer: number;
|
||||||
|
|
||||||
queueConflictedCheck(file: TFile) {
|
queueConflictedCheck(file: TFile) {
|
||||||
this.conflictedCheckFiles = this.conflictedCheckFiles.filter((e) => e != file.path);
|
this.conflictedCheckFiles = this.conflictedCheckFiles.filter((e) => e != file.path);
|
||||||
this.conflictedCheckFiles.push(file.path);
|
this.conflictedCheckFiles.push(file.path);
|
||||||
@ -1146,6 +1272,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
async showIfConflicted(file: TFile) {
|
async showIfConflicted(file: TFile) {
|
||||||
await runWithLock("conflicted", false, async () => {
|
await runWithLock("conflicted", false, async () => {
|
||||||
const conflictCheckResult = await this.getConflictedStatus(file.path);
|
const conflictCheckResult = await this.getConflictedStatus(file.path);
|
||||||
@ -1165,6 +1292,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
await this.showMergeDialog(file, conflictCheckResult);
|
await this.showMergeDialog(file, conflictCheckResult);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async pullFile(filename: string, fileList?: TFile[], force?: boolean, rev?: string, waitForReady = true) {
|
async pullFile(filename: string, fileList?: TFile[], force?: boolean, rev?: string, waitForReady = true) {
|
||||||
const targetFile = this.app.vault.getAbstractFileByPath(id2path(filename));
|
const targetFile = this.app.vault.getAbstractFileByPath(id2path(filename));
|
||||||
if (targetFile == null) {
|
if (targetFile == null) {
|
||||||
@ -1184,6 +1312,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
//when to opened file;
|
//when to opened file;
|
||||||
}
|
}
|
||||||
|
|
||||||
async syncFileBetweenDBandStorage(file: TFile, fileList?: TFile[]) {
|
async syncFileBetweenDBandStorage(file: TFile, fileList?: TFile[]) {
|
||||||
const doc = await this.localDatabase.getDBEntryMeta(file.path);
|
const doc = await this.localDatabase.getDBEntryMeta(file.path);
|
||||||
if (doc === false) return;
|
if (doc === false) return;
|
||||||
@ -1257,6 +1386,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
await this.replicate();
|
await this.replicate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteFromDB(file: TFile) {
|
async deleteFromDB(file: TFile) {
|
||||||
const fullpath = file.path;
|
const fullpath = file.path;
|
||||||
Logger(`deleteDB By path:${fullpath}`);
|
Logger(`deleteDB By path:${fullpath}`);
|
||||||
@ -1265,6 +1395,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
await this.replicate();
|
await this.replicate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async deleteFromDBbyPath(fullpath: string) {
|
async deleteFromDBbyPath(fullpath: string) {
|
||||||
await this.localDatabase.deleteDBEntry(fullpath);
|
await this.localDatabase.deleteDBEntry(fullpath);
|
||||||
if (this.settings.syncOnSave && !this.suspended) {
|
if (this.settings.syncOnSave && !this.suspended) {
|
||||||
@ -1275,12 +1406,15 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
async resetLocalDatabase() {
|
async resetLocalDatabase() {
|
||||||
await this.localDatabase.resetDatabase();
|
await this.localDatabase.resetDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
async tryResetRemoteDatabase() {
|
async tryResetRemoteDatabase() {
|
||||||
await this.localDatabase.tryResetRemoteDatabase(this.settings);
|
await this.localDatabase.tryResetRemoteDatabase(this.settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
async tryCreateRemoteDatabase() {
|
async tryCreateRemoteDatabase() {
|
||||||
await this.localDatabase.tryCreateRemoteDatabase(this.settings);
|
await this.localDatabase.tryCreateRemoteDatabase(this.settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
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: `ps:`, endkey: `ps;`, include_docs: false });
|
const docList = await db.allDocs<PluginDataEntry>({ startkey: `ps:`, endkey: `ps;`, include_docs: false });
|
||||||
@ -1300,6 +1434,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
return { plugins, allPlugins, thisDevicePlugins };
|
return { plugins, allPlugins, thisDevicePlugins };
|
||||||
}
|
}
|
||||||
|
|
||||||
async sweepPlugin(showMessage = false) {
|
async sweepPlugin(showMessage = false) {
|
||||||
if (!this.settings.usePluginSync) return;
|
if (!this.settings.usePluginSync) return;
|
||||||
await runWithLock("sweepplugin", false, async () => {
|
await runWithLock("sweepplugin", false, async () => {
|
||||||
@ -1314,7 +1449,11 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
Logger("Sweeping plugins", logLevel);
|
Logger("Sweeping plugins", logLevel);
|
||||||
const db = this.localDatabase.localDatabase;
|
const db = this.localDatabase.localDatabase;
|
||||||
const oldDocs = await db.allDocs({ startkey: `ps:${this.settings.deviceAndVaultName}-`, endkey: `ps:${this.settings.deviceAndVaultName}.`, include_docs: true });
|
const oldDocs = await db.allDocs({
|
||||||
|
startkey: `ps:${this.settings.deviceAndVaultName}-`,
|
||||||
|
endkey: `ps:${this.settings.deviceAndVaultName}.`,
|
||||||
|
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
|
||||||
@ -1383,12 +1522,13 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
Logger(`Sweep plugin done.`, logLevel);
|
Logger(`Sweep plugin done.`, logLevel);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async applyPluginData(plugin: PluginDataEntry) {
|
async applyPluginData(plugin: PluginDataEntry) {
|
||||||
await runWithLock("plugin-" + plugin.manifest.id, false, async () => {
|
await runWithLock("plugin-" + plugin.manifest.id, false, async () => {
|
||||||
const pluginTargetFolderPath = normalizePath(plugin.manifest.dir) + "/";
|
const pluginTargetFolderPath = normalizePath(plugin.manifest.dir) + "/";
|
||||||
const adapter = this.app.vault.adapter;
|
const adapter = this.app.vault.adapter;
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const stat = this.app.plugins.enabledPlugins[plugin.manifest.id];
|
const stat = this.app.plugins.enabledPlugins.has(plugin.manifest.id) == true;
|
||||||
if (stat) {
|
if (stat) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await this.app.plugins.unloadPlugin(plugin.manifest.id);
|
await this.app.plugins.unloadPlugin(plugin.manifest.id);
|
||||||
@ -1396,7 +1536,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
if (plugin.dataJson) await adapter.write(pluginTargetFolderPath + "data.json", plugin.dataJson);
|
if (plugin.dataJson) await adapter.write(pluginTargetFolderPath + "data.json", plugin.dataJson);
|
||||||
Logger("wrote:" + pluginTargetFolderPath + "data.json", LOG_LEVEL.NOTICE);
|
Logger("wrote:" + pluginTargetFolderPath + "data.json", LOG_LEVEL.NOTICE);
|
||||||
// @ts-ignore
|
|
||||||
if (stat) {
|
if (stat) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await this.app.plugins.loadPlugin(plugin.manifest.id);
|
await this.app.plugins.loadPlugin(plugin.manifest.id);
|
||||||
@ -1404,10 +1543,11 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async applyPlugin(plugin: PluginDataEntry) {
|
async applyPlugin(plugin: PluginDataEntry) {
|
||||||
await runWithLock("plugin-" + plugin.manifest.id, false, async () => {
|
await runWithLock("plugin-" + plugin.manifest.id, false, async () => {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const stat = this.app.plugins.enabledPlugins[plugin.manifest.id];
|
const stat = this.app.plugins.enabledPlugins.has(plugin.manifest.id) == true;
|
||||||
if (stat) {
|
if (stat) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await this.app.plugins.unloadPlugin(plugin.manifest.id);
|
await this.app.plugins.unloadPlugin(plugin.manifest.id);
|
||||||
@ -1422,7 +1562,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
await adapter.write(pluginTargetFolderPath + "main.js", plugin.mainJs);
|
await adapter.write(pluginTargetFolderPath + "main.js", plugin.mainJs);
|
||||||
await adapter.write(pluginTargetFolderPath + "manifest.json", plugin.manifestJson);
|
await adapter.write(pluginTargetFolderPath + "manifest.json", plugin.manifestJson);
|
||||||
if (plugin.styleCss) await adapter.write(pluginTargetFolderPath + "styles.css", plugin.styleCss);
|
if (plugin.styleCss) await adapter.write(pluginTargetFolderPath + "styles.css", plugin.styleCss);
|
||||||
// if (plugin.dataJson) await adapter.write(pluginTargetFolderPath + "data.json", plugin.dataJson);
|
|
||||||
if (stat) {
|
if (stat) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
await this.app.plugins.loadPlugin(plugin.manifest.id);
|
await this.app.plugins.loadPlugin(plugin.manifest.id);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
// and cloudant limitation is 1MB , we use 900kb;
|
// and cloudant limitation is 1MB , we use 900kb;
|
||||||
|
|
||||||
import { PluginManifest } from "obsidian";
|
import { PluginManifest } from "obsidian";
|
||||||
|
import * as PouchDB from "pouchdb";
|
||||||
|
|
||||||
export const MAX_DOC_SIZE = 1000; // for .md file, but if delimiters exists. use that before.
|
export const MAX_DOC_SIZE = 1000; // for .md file, but if delimiters exists. use that before.
|
||||||
export const MAX_DOC_SIZE_BIN = 102400; // 100kb
|
export const MAX_DOC_SIZE_BIN = 102400; // 100kb
|
||||||
@ -58,7 +59,7 @@ export interface ObsidianLiveSyncSettings {
|
|||||||
checkIntegrityOnSave: boolean;
|
checkIntegrityOnSave: boolean;
|
||||||
batch_size: number;
|
batch_size: number;
|
||||||
batches_limit: number;
|
batches_limit: number;
|
||||||
useHistory:boolean;
|
useHistory: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_SETTINGS: ObsidianLiveSyncSettings = {
|
export const DEFAULT_SETTINGS: ObsidianLiveSyncSettings = {
|
||||||
@ -99,7 +100,7 @@ export const DEFAULT_SETTINGS: ObsidianLiveSyncSettings = {
|
|||||||
checkIntegrityOnSave: false,
|
checkIntegrityOnSave: false,
|
||||||
batch_size: 250,
|
batch_size: 250,
|
||||||
batches_limit: 40,
|
batches_limit: 40,
|
||||||
useHistory:false,
|
useHistory: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PERIODIC_PLUGIN_SWEEP = 60;
|
export const PERIODIC_PLUGIN_SWEEP = 60;
|
||||||
|
@ -34,11 +34,13 @@
|
|||||||
.sls-plugins-wrap {
|
.sls-plugins-wrap {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
/* overflow: scroll; */
|
max-height: 50vh;
|
||||||
|
overflow-y: scroll;
|
||||||
}
|
}
|
||||||
.sls-plugins-tbl {
|
.sls-plugins-tbl {
|
||||||
border: 1px solid var(--background-modifier-border);
|
border: 1px solid var(--background-modifier-border);
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-height: 80%;
|
||||||
}
|
}
|
||||||
.divider th {
|
.divider th {
|
||||||
border-top: 1px solid var(--background-modifier-border);
|
border-top: 1px solid var(--background-modifier-border);
|
||||||
|
@ -1,21 +1,17 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
"inlineSourceMap": true,
|
|
||||||
"inlineSources": true,
|
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"target": "es6",
|
"target": "ES6",
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"noImplicitAny": true,
|
"noImplicitAny": true,
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
|
"types": ["svelte", "node"],
|
||||||
|
// "importsNotUsedAsValues": "error",
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
"noImplicitReturns": true,
|
|
||||||
"noImplicitThis": true,
|
|
||||||
"strictFunctionTypes": true,
|
|
||||||
"alwaysStrict": true,
|
"alwaysStrict": true,
|
||||||
"lib": ["dom", "es5", "ES6", "ES7", "es2020"]
|
"lib": ["es2018", "DOM", "ES5", "ES6", "ES7"]
|
||||||
},
|
},
|
||||||
"include": ["./src/*.ts"],
|
"include": ["**/*.ts"],
|
||||||
// "files": ["./src/main.ts"],
|
|
||||||
"exclude": ["pouchdb-browser-webpack"]
|
"exclude": ["pouchdb-browser-webpack"]
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user