mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2026-05-06 07:41:51 +00:00
Compare commits
17 Commits
0.24.0.dev
...
0.24.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
115a0d2d8a | ||
|
|
2c97289ec8 | ||
|
|
8b45dd1d24 | ||
|
|
a2b36ccf31 | ||
|
|
25e30fa09d | ||
|
|
8f5bc387b4 | ||
|
|
5afe24c460 | ||
|
|
658a09f1cc | ||
|
|
293c731437 | ||
|
|
9e8d126259 | ||
|
|
1f0ad4eb1e | ||
|
|
5023d6da0b | ||
|
|
49160c7d57 | ||
|
|
4434224c29 | ||
|
|
cf3b9e5522 | ||
|
|
f778107727 | ||
|
|
12d825ea49 |
@@ -106,6 +106,8 @@ Now `https://tiles-photograph-routine-groundwater.trycloudflare.com` is our serv
|
|||||||
$ export hostname=https://tiles-photograph-routine-groundwater.trycloudflare.com #Point to your vault
|
$ export hostname=https://tiles-photograph-routine-groundwater.trycloudflare.com #Point to your vault
|
||||||
$ export database=obsidiannotes #Please change as you like
|
$ export database=obsidiannotes #Please change as you like
|
||||||
$ export passphrase=dfsapkdjaskdjasdas #Please change as you like
|
$ export passphrase=dfsapkdjaskdjasdas #Please change as you like
|
||||||
|
$ export username=johndoe
|
||||||
|
$ export password=abc123
|
||||||
$ deno run -A https://raw.githubusercontent.com/vrtmrz/obsidian-livesync/main/utils/flyio/generate_setupuri.ts
|
$ deno run -A https://raw.githubusercontent.com/vrtmrz/obsidian-livesync/main/utils/flyio/generate_setupuri.ts
|
||||||
obsidian://setuplivesync?settings=%5B%22tm2DpsOE74nJAryprZO2M93wF%2Fvg.......4b26ed33230729%22%5D
|
obsidian://setuplivesync?settings=%5B%22tm2DpsOE74nJAryprZO2M93wF%2Fvg.......4b26ed33230729%22%5D
|
||||||
|
|
||||||
@@ -206,4 +208,4 @@ entryPoints:
|
|||||||
address: ":443"
|
address: ":443"
|
||||||
|
|
||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.24.0.dev-rc7",
|
"version": "0.24.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",
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Self-hosted LiveSync",
|
"name": "Self-hosted LiveSync",
|
||||||
"version": "0.23.23",
|
"version": "0.24.1",
|
||||||
"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.24.0.dev-rc7",
|
"version": "0.24.1",
|
||||||
"lockfileVersion": 2,
|
"lockfileVersion": 2,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.24.0.dev-rc7",
|
"version": "0.24.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@aws-sdk/client-s3": "^3.645.0",
|
"@aws-sdk/client-s3": "^3.645.0",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.24.0.dev-rc7",
|
"version": "0.24.1",
|
||||||
"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",
|
||||||
|
|||||||
@@ -1,3 +1,7 @@
|
|||||||
|
import type { FilePathWithPrefix, ObsidianLiveSyncSettings } from "../lib/src/common/types";
|
||||||
|
import { eventHub } from "../lib/src/hub/hub";
|
||||||
|
import type ObsidianLiveSyncPlugin from "../main";
|
||||||
|
|
||||||
export const EVENT_LAYOUT_READY = "layout-ready";
|
export const EVENT_LAYOUT_READY = "layout-ready";
|
||||||
export const EVENT_PLUGIN_LOADED = "plugin-loaded";
|
export const EVENT_PLUGIN_LOADED = "plugin-loaded";
|
||||||
export const EVENT_PLUGIN_UNLOADED = "plugin-unloaded";
|
export const EVENT_PLUGIN_UNLOADED = "plugin-unloaded";
|
||||||
@@ -13,7 +17,7 @@ export const EVENT_REQUEST_OPEN_SETTING_WIZARD = "request-open-setting-wizard";
|
|||||||
export const EVENT_REQUEST_OPEN_SETUP_URI = "request-open-setup-uri";
|
export const EVENT_REQUEST_OPEN_SETUP_URI = "request-open-setup-uri";
|
||||||
export const EVENT_REQUEST_COPY_SETUP_URI = "request-copy-setup-uri";
|
export const EVENT_REQUEST_COPY_SETUP_URI = "request-copy-setup-uri";
|
||||||
|
|
||||||
export const EVENT_REQUEST_SHOW_HISTORY = "show-history";
|
|
||||||
|
|
||||||
export const EVENT_REQUEST_RELOAD_SETTING_TAB = "reload-setting-tab";
|
export const EVENT_REQUEST_RELOAD_SETTING_TAB = "reload-setting-tab";
|
||||||
|
|
||||||
@@ -22,8 +26,28 @@ export const EVENT_REQUEST_OPEN_PLUGIN_SYNC_DIALOG = "request-open-plugin-sync-d
|
|||||||
|
|
||||||
// export const EVENT_FILE_CHANGED = "file-changed";
|
// export const EVENT_FILE_CHANGED = "file-changed";
|
||||||
|
|
||||||
import { eventHub } from "../lib/src/hub/hub";
|
declare global {
|
||||||
// TODO: Add overloads for the emit method to allow for type checking
|
interface LSEvents {
|
||||||
|
[EVENT_REQUEST_OPEN_PLUGIN_SYNC_DIALOG]: undefined;
|
||||||
|
[EVENT_FILE_SAVED]: undefined;
|
||||||
|
[EVENT_REQUEST_OPEN_SETUP_URI]: undefined;
|
||||||
|
[EVENT_REQUEST_COPY_SETUP_URI]: undefined;
|
||||||
|
[EVENT_REQUEST_RELOAD_SETTING_TAB]: undefined;
|
||||||
|
[EVENT_PLUGIN_UNLOADED]: undefined;
|
||||||
|
[EVENT_SETTING_SAVED]: ObsidianLiveSyncSettings;
|
||||||
|
[EVENT_PLUGIN_LOADED]: ObsidianLiveSyncPlugin;
|
||||||
|
[EVENT_LAYOUT_READY]: undefined;
|
||||||
|
"event-file-changed": { file: FilePathWithPrefix, automated: boolean };
|
||||||
|
"document-stub-created":
|
||||||
|
{
|
||||||
|
toc: Set<string>, stub: { [key: string]: { [key: string]: Map<string, Record<string, string>> } }
|
||||||
|
},
|
||||||
|
[EVENT_REQUEST_OPEN_SETTINGS]: undefined;
|
||||||
|
[EVENT_REQUEST_OPEN_SETTING_WIZARD]: undefined;
|
||||||
|
[EVENT_FILE_RENAMED]: { newPath: FilePathWithPrefix, old: FilePathWithPrefix };
|
||||||
|
[EVENT_LEAF_ACTIVE_CHANGED]: undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export { eventHub };
|
export { eventHub };
|
||||||
|
|
||||||
|
|||||||
10
src/common/obsidianEvents.ts
Normal file
10
src/common/obsidianEvents.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import type { TFile } from "../deps";
|
||||||
|
import type { FilePathWithPrefix, LoadedEntry } from "../lib/src/common/types";
|
||||||
|
|
||||||
|
export const EVENT_REQUEST_SHOW_HISTORY = "show-history";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface LSEvents {
|
||||||
|
[EVENT_REQUEST_SHOW_HISTORY]: { file: TFile, fileOnDB: LoadedEntry } | { file: FilePathWithPrefix, fileOnDB: LoadedEntry };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -143,7 +143,7 @@ export class PeriodicProcessor {
|
|||||||
if (interval == 0) return;
|
if (interval == 0) return;
|
||||||
this._timer = window.setInterval(() => fireAndForget(async () => {
|
this._timer = window.setInterval(() => fireAndForget(async () => {
|
||||||
await this.process();
|
await this.process();
|
||||||
if (this._plugin._unloaded) {
|
if (this._plugin.$$isUnloaded()) {
|
||||||
this.disable();
|
this.disable();
|
||||||
}
|
}
|
||||||
}), interval);
|
}), interval);
|
||||||
|
|||||||
@@ -543,7 +543,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
|
|||||||
|
|
||||||
|
|
||||||
filenameToUnifiedKey(path: string, termOverRide?: string) {
|
filenameToUnifiedKey(path: string, termOverRide?: string) {
|
||||||
const term = termOverRide || this.plugin.deviceAndVaultName;
|
const term = termOverRide || this.plugin.$$getDeviceAndVaultName();
|
||||||
const category = this.getFileCategory(path);
|
const category = this.getFileCategory(path);
|
||||||
const name = (category == "CONFIG" || category == "SNIPPET") ?
|
const name = (category == "CONFIG" || category == "SNIPPET") ?
|
||||||
(path.split("/").slice(-1)[0]) :
|
(path.split("/").slice(-1)[0]) :
|
||||||
@@ -554,7 +554,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
filenameWithUnifiedKey(path: string, termOverRide?: string) {
|
filenameWithUnifiedKey(path: string, termOverRide?: string) {
|
||||||
const term = termOverRide || this.plugin.deviceAndVaultName;
|
const term = termOverRide || this.plugin.$$getDeviceAndVaultName();
|
||||||
const category = this.getFileCategory(path);
|
const category = this.getFileCategory(path);
|
||||||
const name = (category == "CONFIG" || category == "SNIPPET") ?
|
const name = (category == "CONFIG" || category == "SNIPPET") ?
|
||||||
(path.split("/").slice(-1)[0]) : path.split("/").slice(-2)[0];
|
(path.split("/").slice(-1)[0]) : path.split("/").slice(-2)[0];
|
||||||
@@ -563,7 +563,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
unifiedKeyPrefixOfTerminal(termOverRide?: string) {
|
unifiedKeyPrefixOfTerminal(termOverRide?: string) {
|
||||||
const term = termOverRide || this.plugin.deviceAndVaultName;
|
const term = termOverRide || this.plugin.$$getDeviceAndVaultName();
|
||||||
return `${ICXHeader}${term}/` as FilePathWithPrefix;
|
return `${ICXHeader}${term}/` as FilePathWithPrefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -870,7 +870,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
|
|||||||
await this.plugin.storageAccess.ensureDir(path);
|
await this.plugin.storageAccess.ensureDir(path);
|
||||||
// If the content has applied, modified time will be updated to the current time.
|
// If the content has applied, modified time will be updated to the current time.
|
||||||
await this.plugin.storageAccess.writeHiddenFileAuto(path, content);
|
await this.plugin.storageAccess.writeHiddenFileAuto(path, content);
|
||||||
await this.storeCustomisationFileV2(path, this.plugin.deviceAndVaultName);
|
await this.storeCustomisationFileV2(path, this.plugin.$$getDeviceAndVaultName());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const files = data.files;
|
const files = data.files;
|
||||||
@@ -914,7 +914,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
|
|||||||
await this.plugin.storageAccess.writeHiddenFileAuto(path, content, stat);
|
await this.plugin.storageAccess.writeHiddenFileAuto(path, content, stat);
|
||||||
}
|
}
|
||||||
this._log(`Applied ${f.filename} of ${data.displayName || data.name}..`);
|
this._log(`Applied ${f.filename} of ${data.displayName || data.name}..`);
|
||||||
await this.storeCustomisationFileV2(path, this.plugin.deviceAndVaultName);
|
await this.storeCustomisationFileV2(path, this.plugin.$$getDeviceAndVaultName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
@@ -1189,7 +1189,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
async storeCustomizationFiles(path: FilePath, termOverRide?: string) {
|
async storeCustomizationFiles(path: FilePath, termOverRide?: string) {
|
||||||
const term = termOverRide || this.plugin.deviceAndVaultName;
|
const term = termOverRide || this.plugin.$$getDeviceAndVaultName();
|
||||||
if (term == "") {
|
if (term == "") {
|
||||||
this._log("We have to configure the device name", LOG_LEVEL_NOTICE);
|
this._log("We have to configure the device name", LOG_LEVEL_NOTICE);
|
||||||
return;
|
return;
|
||||||
@@ -1362,7 +1362,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
|
|||||||
await shareRunningResult("scanAllConfigFiles", async () => {
|
await shareRunningResult("scanAllConfigFiles", async () => {
|
||||||
const logLevel = showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO;
|
const logLevel = showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO;
|
||||||
this._log("Scanning customizing files.", logLevel, "scan-all-config");
|
this._log("Scanning customizing files.", logLevel, "scan-all-config");
|
||||||
const term = this.plugin.deviceAndVaultName;
|
const term = this.plugin.$$getDeviceAndVaultName();
|
||||||
if (term == "") {
|
if (term == "") {
|
||||||
this._log("We have to configure the device name", LOG_LEVEL_NOTICE);
|
this._log("We have to configure the device name", LOG_LEVEL_NOTICE);
|
||||||
return;
|
return;
|
||||||
@@ -1505,7 +1505,10 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
|
|||||||
choices.push(CHOICE_DISABLE);
|
choices.push(CHOICE_DISABLE);
|
||||||
choices.push(CHOICE_DISMISS);
|
choices.push(CHOICE_DISMISS);
|
||||||
|
|
||||||
const ret = await this.plugin.confirm.confirmWithMessage("Customisation sync", message, choices, CHOICE_DISMISS, 40);
|
const ret = await this.plugin.confirm.askSelectStringDialogue(message, choices, {
|
||||||
|
defaultAction: CHOICE_DISMISS, timeout: 40,
|
||||||
|
title: "Customisation sync"
|
||||||
|
});
|
||||||
if (ret == CHOICE_CUSTOMIZE) {
|
if (ret == CHOICE_CUSTOMIZE) {
|
||||||
await this.configureHiddenFileSync("CUSTOMIZE");
|
await this.configureHiddenFileSync("CUSTOMIZE");
|
||||||
} else if (ret == CHOICE_DISABLE) {
|
} else if (ret == CHOICE_DISABLE) {
|
||||||
@@ -1544,7 +1547,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (mode == "CUSTOMIZE") {
|
if (mode == "CUSTOMIZE") {
|
||||||
if (!this.plugin.deviceAndVaultName) {
|
if (!this.plugin.$$getDeviceAndVaultName()) {
|
||||||
let name = await this.plugin.confirm.askString("Device name", "Please set this device name", `desktop`);
|
let name = await this.plugin.confirm.askString("Device name", "Please set this device name", `desktop`);
|
||||||
if (!name) {
|
if (!name) {
|
||||||
if (Platform.isAndroidApp) {
|
if (Platform.isAndroidApp) {
|
||||||
@@ -1568,7 +1571,7 @@ export class ConfigSync extends LiveSyncCommands implements IObsidianModule {
|
|||||||
}
|
}
|
||||||
name = name + Math.random().toString(36).slice(-4);
|
name = name + Math.random().toString(36).slice(-4);
|
||||||
}
|
}
|
||||||
this.plugin.deviceAndVaultName = name;
|
this.plugin.$$setDeviceAndVaultName(name);
|
||||||
}
|
}
|
||||||
this.plugin.settings.usePluginSync = true;
|
this.plugin.settings.usePluginSync = true;
|
||||||
this.plugin.settings.useAdvancedMode = true;
|
this.plugin.settings.useAdvancedMode = true;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ConfigSync, PluginDataExDisplayV2, type IPluginDataExDisplay } from "./CmdConfigSync.ts";
|
import { ConfigSync, PluginDataExDisplayV2, type IPluginDataExDisplay, type PluginDataExFile } from "./CmdConfigSync.ts";
|
||||||
import { Logger } from "../../lib/src/common/logger";
|
import { Logger } from "../../lib/src/common/logger";
|
||||||
import { type FilePath, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "../../lib/src/common/types";
|
import { type FilePath, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE } from "../../lib/src/common/types";
|
||||||
import { getDocData, timeDeltaToHumanReadable, unique } from "../../lib/src/common/utils";
|
import { getDocData, timeDeltaToHumanReadable, unique } from "../../lib/src/common/utils";
|
||||||
@@ -287,9 +287,17 @@
|
|||||||
menu.addItem((item) => item.setTitle("Compare file").setIsLabel(true));
|
menu.addItem((item) => item.setTitle("Compare file").setIsLabel(true));
|
||||||
menu.addSeparator();
|
menu.addSeparator();
|
||||||
const files = unique(local.files.map((e) => e.filename).concat(selectedItem.files.map((e) => e.filename)));
|
const files = unique(local.files.map((e) => e.filename).concat(selectedItem.files.map((e) => e.filename)));
|
||||||
|
const convDate = (dt: PluginDataExFile | undefined) => {
|
||||||
|
if (!dt) return "(Missing)";
|
||||||
|
const d = new Date(dt.mtime);
|
||||||
|
return d.toLocaleString();
|
||||||
|
};
|
||||||
for (const filename of files) {
|
for (const filename of files) {
|
||||||
menu.addItem((item) => {
|
menu.addItem((item) => {
|
||||||
item.setTitle(filename).onClick((e) => compareItems(local, selectedItem, filename));
|
const localFile = local.files.find((e) => e.filename == filename);
|
||||||
|
const remoteFile = selectedItem.files.find((e) => e.filename == filename);
|
||||||
|
const title = `${filename} (${convDate(localFile)} <--> ${convDate(remoteFile)})`;
|
||||||
|
item.setTitle(title).onClick((e) => compareItems(local, selectedItem, filename));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
menu.showAtMouseEvent(evt);
|
menu.showAtMouseEvent(evt);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
export let plugin: ObsidianLiveSyncPlugin;
|
export let plugin: ObsidianLiveSyncPlugin;
|
||||||
|
|
||||||
$: hideNotApplicable = false;
|
$: hideNotApplicable = false;
|
||||||
$: thisTerm = plugin.deviceAndVaultName;
|
$: thisTerm = plugin.$$getDeviceAndVaultName();
|
||||||
|
|
||||||
const addOn = plugin.getAddOn(ConfigSync.name) as ConfigSync;
|
const addOn = plugin.getAddOn(ConfigSync.name) as ConfigSync;
|
||||||
if (!addOn) {
|
if (!addOn) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { normalizePath, type PluginManifest, type ListedFiles } from "../../deps.ts";
|
import { normalizePath, type PluginManifest, type ListedFiles } from "../../deps.ts";
|
||||||
import { type EntryDoc, type LoadedEntry, type InternalFileEntry, type FilePathWithPrefix, type FilePath, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, MODE_SELECTIVE, MODE_PAUSED, type SavingEntry, type DocumentID, type UXStat, MODE_AUTOMATIC } from "../../lib/src/common/types.ts";
|
import { type EntryDoc, type LoadedEntry, type InternalFileEntry, type FilePathWithPrefix, type FilePath, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, MODE_SELECTIVE, MODE_PAUSED, type SavingEntry, type DocumentID, type UXStat, MODE_AUTOMATIC, type FilePathWithPrefixLC } from "../../lib/src/common/types.ts";
|
||||||
import { type InternalFileInfo, ICHeader, ICHeaderEnd } from "../../common/types.ts";
|
import { type InternalFileInfo, ICHeader, ICHeaderEnd } from "../../common/types.ts";
|
||||||
import { readAsBlob, isDocContentSame, sendSignal, readContent, createBlob, fireAndForget } from "../../lib/src/common/utils.ts";
|
import { readAsBlob, isDocContentSame, sendSignal, readContent, createBlob, fireAndForget } from "../../lib/src/common/utils.ts";
|
||||||
import { BASE_IS_NEW, compareMTime, EVEN, getPath, isInternalMetadata, isMarkedAsSameChanges, markChangesAreSame, PeriodicProcessor, TARGET_IS_NEW } from "../../common/utils.ts";
|
import { BASE_IS_NEW, compareMTime, EVEN, getPath, isInternalMetadata, isMarkedAsSameChanges, markChangesAreSame, PeriodicProcessor, TARGET_IS_NEW } from "../../common/utils.ts";
|
||||||
@@ -10,6 +10,7 @@ import { addPrefix, stripAllPrefixes } from "../../lib/src/string_and_binary/pat
|
|||||||
import { QueueProcessor } from "../../lib/src/concurrency/processor.ts";
|
import { QueueProcessor } from "../../lib/src/concurrency/processor.ts";
|
||||||
import { hiddenFilesEventCount, hiddenFilesProcessingCount } from "../../lib/src/mock_and_interop/stores.ts";
|
import { hiddenFilesEventCount, hiddenFilesProcessingCount } from "../../lib/src/mock_and_interop/stores.ts";
|
||||||
import type { IObsidianModule } from "../../modules/AbstractObsidianModule.ts";
|
import type { IObsidianModule } from "../../modules/AbstractObsidianModule.ts";
|
||||||
|
import { EVENT_SETTING_SAVED, eventHub } from "../../common/events.ts";
|
||||||
|
|
||||||
export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule {
|
export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule {
|
||||||
|
|
||||||
@@ -36,8 +37,13 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
|
|||||||
void this.syncInternalFilesAndDatabase("safe", true);
|
void this.syncInternalFilesAndDatabase("safe", true);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
eventHub.onEvent(EVENT_SETTING_SAVED, () => {
|
||||||
|
this.updateSettingCache();
|
||||||
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
async $everyOnDatabaseInitialized(showNotice: boolean) {
|
async $everyOnDatabaseInitialized(showNotice: boolean) {
|
||||||
|
this.knownChanges = await this.plugin.kvDB.get("knownChanges") ?? {};
|
||||||
if (this._isThisModuleEnabled()) {
|
if (this._isThisModuleEnabled()) {
|
||||||
try {
|
try {
|
||||||
this._log("Synchronizing hidden files...");
|
this._log("Synchronizing hidden files...");
|
||||||
@@ -58,12 +64,26 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
|
|||||||
}
|
}
|
||||||
|
|
||||||
$everyOnloadAfterLoadSettings(): Promise<boolean> {
|
$everyOnloadAfterLoadSettings(): Promise<boolean> {
|
||||||
|
this.updateSettingCache();
|
||||||
|
return Promise.resolve(true);
|
||||||
|
}
|
||||||
|
updateSettingCache() {
|
||||||
const ignorePatterns = this.settings.syncInternalFilesIgnorePatterns
|
const ignorePatterns = this.settings.syncInternalFilesIgnorePatterns
|
||||||
.replace(/\n| /g, "")
|
.replace(/\n| /g, "")
|
||||||
.split(",").filter(e => e).map(e => new RegExp(e, "i"));
|
.split(",").filter(e => e).map(e => new RegExp(e, "i"));
|
||||||
this.ignorePatterns = ignorePatterns;
|
this.ignorePatterns = ignorePatterns;
|
||||||
return Promise.resolve(true);
|
this.shouldSkipFile = [] as FilePathWithPrefixLC[];
|
||||||
|
// Exclude files handled by customization sync
|
||||||
|
const configDir = normalizePath(this.app.vault.configDir);
|
||||||
|
const shouldSKip = !this.settings.usePluginSync ? [] :
|
||||||
|
Object.values(this.settings.pluginSyncExtendedSetting).
|
||||||
|
filter(e => e.mode == MODE_SELECTIVE || e.mode == MODE_PAUSED).
|
||||||
|
map(e => e.files).flat().map(e => `${configDir}/${e}`.toLowerCase());
|
||||||
|
this.shouldSkipFile = shouldSKip as FilePathWithPrefixLC[];
|
||||||
|
this._log(`Hidden file will skip ${this.shouldSkipFile.length} files`, LOG_LEVEL_INFO);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
shouldSkipFile = [] as FilePathWithPrefixLC[];
|
||||||
|
|
||||||
async $everyOnResumeProcess(): Promise<boolean> {
|
async $everyOnResumeProcess(): Promise<boolean> {
|
||||||
this.periodicInternalFileScanProcessor?.disable();
|
this.periodicInternalFileScanProcessor?.disable();
|
||||||
@@ -80,7 +100,7 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
|
|||||||
this.periodicInternalFileScanProcessor?.disable();
|
this.periodicInternalFileScanProcessor?.disable();
|
||||||
if (this._isMainSuspended())
|
if (this._isMainSuspended())
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
if (!this.plugin.isReady)
|
if (!this.plugin.$$isReady())
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
this.periodicInternalFileScanProcessor.enable(this._isThisModuleEnabled() && this.settings.syncInternalFilesInterval ? (this.settings.syncInternalFilesInterval * 1000) : 0);
|
this.periodicInternalFileScanProcessor.enable(this._isThisModuleEnabled() && this.settings.syncInternalFilesInterval ? (this.settings.syncInternalFilesInterval * 1000) : 0);
|
||||||
const ignorePatterns = this.settings.syncInternalFilesIgnorePatterns
|
const ignorePatterns = this.settings.syncInternalFilesIgnorePatterns
|
||||||
@@ -110,13 +130,7 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
|
|||||||
if (this._isMainSuspended()) return false;
|
if (this._isMainSuspended()) return false;
|
||||||
if (!this._isThisModuleEnabled()) return false;
|
if (!this._isThisModuleEnabled()) return false;
|
||||||
|
|
||||||
// Exclude files handled by customization sync
|
if (this.shouldSkipFile.some(e => e.startsWith(path.toLowerCase()))) {
|
||||||
const configDir = normalizePath(this.app.vault.configDir);
|
|
||||||
const synchronisedInConfigSync = !this.settings.usePluginSync ? [] :
|
|
||||||
Object.values(this.settings.pluginSyncExtendedSetting).
|
|
||||||
filter(e => e.mode == MODE_SELECTIVE || e.mode == MODE_PAUSED).
|
|
||||||
map(e => e.files).flat().map(e => `${configDir}/${e}`.toLowerCase());
|
|
||||||
if (synchronisedInConfigSync.some(e => e.startsWith(path.toLowerCase()))) {
|
|
||||||
this._log(`Hidden file skipped: ${path} is synchronized in customization sync.`, LOG_LEVEL_VERBOSE);
|
this._log(`Hidden file skipped: ${path} is synchronized in customization sync.`, LOG_LEVEL_VERBOSE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -407,7 +421,8 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
|
|||||||
fileOnStorage: xFileOnStorage,
|
fileOnStorage: xFileOnStorage,
|
||||||
fileOnDatabase: xFileOnDatabase
|
fileOnDatabase: xFileOnDatabase
|
||||||
} = params[0];
|
} = params[0];
|
||||||
if (xFileOnStorage && xFileOnDatabase) {
|
const xFileOnDatabaseExists = xFileOnDatabase !== undefined && !(xFileOnDatabase.deleted || xFileOnDatabase._deleted);
|
||||||
|
if (xFileOnStorage && xFileOnDatabaseExists) {
|
||||||
// Both => Synchronize
|
// Both => Synchronize
|
||||||
if ((direction != "pullForce" && direction != "pushForce") && isMarkedAsSameChanges(filename, [xFileOnDatabase.mtime, xFileOnStorage.mtime]) == EVEN) {
|
if ((direction != "pullForce" && direction != "pushForce") && isMarkedAsSameChanges(filename, [xFileOnDatabase.mtime, xFileOnStorage.mtime]) == EVEN) {
|
||||||
this._log(`Hidden file skipped: ${filename} is marked as same`, LOG_LEVEL_VERBOSE);
|
this._log(`Hidden file skipped: ${filename} is marked as same`, LOG_LEVEL_VERBOSE);
|
||||||
@@ -426,7 +441,7 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
|
|||||||
} else {
|
} else {
|
||||||
// Even, or not forced. skip.
|
// Even, or not forced. skip.
|
||||||
}
|
}
|
||||||
} else if (!xFileOnStorage && xFileOnDatabase) {
|
} else if (!xFileOnStorage && xFileOnDatabaseExists) {
|
||||||
if (direction == "push" || direction == "pushForce") {
|
if (direction == "push" || direction == "pushForce") {
|
||||||
if (xFileOnDatabase.deleted)
|
if (xFileOnDatabase.deleted)
|
||||||
return;
|
return;
|
||||||
@@ -442,13 +457,14 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
|
|||||||
countUpdatedFolder(filename);
|
countUpdatedFolder(filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (xFileOnStorage && !xFileOnDatabase) {
|
} else if (xFileOnStorage && !xFileOnDatabaseExists) {
|
||||||
if (direction == "push" || direction == "pushForce" || direction == "safe") {
|
if (direction == "push" || direction == "pushForce" || direction == "safe") {
|
||||||
await this.storeInternalFileToDatabase(xFileOnStorage);
|
await this.storeInternalFileToDatabase(xFileOnStorage);
|
||||||
} else {
|
} else {
|
||||||
// if (await this.extractInternalFileFromDatabase(xFileOnStorage.path)) {
|
// Apply the deletion
|
||||||
// countUpdatedFolder(xFileOnStorage.path);
|
if (await this.extractInternalFileFromDatabase(xFileOnStorage.path)) {
|
||||||
// }
|
countUpdatedFolder(xFileOnStorage.path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw new Error("Invalid state on hidden file sync");
|
throw new Error("Invalid state on hidden file sync");
|
||||||
@@ -502,7 +518,7 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
|
|||||||
|
|
||||||
// If something changes left, notify for reloading Obsidian.
|
// If something changes left, notify for reloading Obsidian.
|
||||||
if (updatedCount != 0) {
|
if (updatedCount != 0) {
|
||||||
if (!this.plugin.isReloadingScheduled) {
|
if (!this.plugin.$$isReloadingScheduled()) {
|
||||||
this.plugin.confirm.askInPopup(`updated-any-hidden`, `Hidden files have been synchronised, Press {HERE} to schedule a reload of Obsidian, or press elsewhere to dismiss this message.`, (anchor) => {
|
this.plugin.confirm.askInPopup(`updated-any-hidden`, `Hidden files have been synchronised, Press {HERE} to schedule a reload of Obsidian, or press elsewhere to dismiss this message.`, (anchor) => {
|
||||||
anchor.text = "HERE";
|
anchor.text = "HERE";
|
||||||
anchor.addEventListener("click", () => {
|
anchor.addEventListener("click", () => {
|
||||||
@@ -674,14 +690,16 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
|
|||||||
this._log(`STORAGE <x- DB: ${displayFileName}: deleted (hidden) Deleted on DB, but the file is already not found on storage.`);
|
this._log(`STORAGE <x- DB: ${displayFileName}: deleted (hidden) Deleted on DB, but the file is already not found on storage.`);
|
||||||
} else {
|
} else {
|
||||||
this._log(`STORAGE <x- DB: ${displayFileName}: deleted (hidden).`);
|
this._log(`STORAGE <x- DB: ${displayFileName}: deleted (hidden).`);
|
||||||
await this.plugin.storageAccess.removeHidden(storageFilePath);
|
if (await this.plugin.storageAccess.removeHidden(storageFilePath)) {
|
||||||
try {
|
try {
|
||||||
// -- @ts-ignore internalAPI
|
await this.plugin.storageAccess.triggerHiddenFile(storageFilePath);
|
||||||
// await this.app.vault.adapter.reconcileInternalFile(filename);
|
} catch (ex) {
|
||||||
await this.plugin.storageAccess.triggerHiddenFile(storageFilePath);
|
this._log("Failed to call internal API(reconcileInternalFile)", LOG_LEVEL_VERBOSE);
|
||||||
} catch (ex) {
|
this._log(ex, LOG_LEVEL_VERBOSE);
|
||||||
this._log("Failed to call internal API(reconcileInternalFile)", LOG_LEVEL_VERBOSE);
|
}
|
||||||
this._log(ex, LOG_LEVEL_VERBOSE);
|
} else {
|
||||||
|
this._log(`STORAGE <x- DB: ${storageFilePath}: deleted (hidden) Failed`);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -690,8 +708,7 @@ export class HiddenFileSync extends LiveSyncCommands implements IObsidianModule
|
|||||||
await this.plugin.storageAccess.ensureDir(storageFilePath);
|
await this.plugin.storageAccess.ensureDir(storageFilePath);
|
||||||
await this.plugin.storageAccess.writeHiddenFileAuto(storageFilePath, readContent(fileOnDB), { mtime: fileOnDB.mtime, ctime: fileOnDB.ctime });
|
await this.plugin.storageAccess.writeHiddenFileAuto(storageFilePath, readContent(fileOnDB), { mtime: fileOnDB.mtime, ctime: fileOnDB.ctime });
|
||||||
try {
|
try {
|
||||||
//@ts-ignore internalAPI
|
await this.plugin.storageAccess.triggerHiddenFile(storageFilePath);
|
||||||
await this.app.vault.adapter.reconcileInternalFile(filename);
|
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
this._log("Failed to call internal API(reconcileInternalFile)", LOG_LEVEL_VERBOSE);
|
this._log("Failed to call internal API(reconcileInternalFile)", LOG_LEVEL_VERBOSE);
|
||||||
this._log(ex, LOG_LEVEL_VERBOSE);
|
this._log(ex, LOG_LEVEL_VERBOSE);
|
||||||
|
|||||||
@@ -33,13 +33,13 @@ export abstract class LiveSyncCommands {
|
|||||||
abstract onload(): void | Promise<void>;
|
abstract onload(): void | Promise<void>;
|
||||||
|
|
||||||
_isMainReady() {
|
_isMainReady() {
|
||||||
return this.plugin._isMainReady();
|
return this.plugin.$$isReady();
|
||||||
}
|
}
|
||||||
_isMainSuspended() {
|
_isMainSuspended() {
|
||||||
return this.plugin._isMainSuspended();
|
return this.plugin.$$isSuspended();
|
||||||
}
|
}
|
||||||
_isDatabaseReady() {
|
_isDatabaseReady() {
|
||||||
return this.plugin._isDatabaseReady();
|
return this.plugin.$$isDatabaseReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
_log = (msg: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key?: string) => {
|
_log = (msg: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key?: string) => {
|
||||||
@@ -49,4 +49,5 @@ export abstract class LiveSyncCommands {
|
|||||||
// console.log(msg);
|
// console.log(msg);
|
||||||
Logger(msg, level, key);
|
Logger(msg, level, key);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
2
src/lib
2
src/lib
Submodule src/lib updated: 5079b0bf79...a91bb47c90
278
src/main.ts
278
src/main.ts
@@ -1,25 +1,18 @@
|
|||||||
import { Plugin } from "./deps";
|
import { Plugin } from "./deps";
|
||||||
import { type EntryDoc, type LoadedEntry, type ObsidianLiveSyncSettings, type LOG_LEVEL, VER, DEFAULT_SETTINGS, type diff_result, type DatabaseConnectingStatus, type EntryHasPath, type DocumentID, type FilePathWithPrefix, type FilePath, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, type HasSettings, type MetaEntry, type UXFileInfoStub, type MISSING_OR_ERROR, type AUTO_MERGED, } from "./lib/src/common/types.ts";
|
import { type EntryDoc, type LoadedEntry, type ObsidianLiveSyncSettings, type LOG_LEVEL, type diff_result, type DatabaseConnectingStatus, type EntryHasPath, type DocumentID, type FilePathWithPrefix, type FilePath, LOG_LEVEL_INFO, type HasSettings, type MetaEntry, type UXFileInfoStub, type MISSING_OR_ERROR, type AUTO_MERGED, type RemoteDBSettings, type TweakValues, } from "./lib/src/common/types.ts";
|
||||||
import { type FileEventItem } from "./common/types.ts";
|
import { type FileEventItem } from "./common/types.ts";
|
||||||
import { fireAndForget, type SimpleStore } from "./lib/src/common/utils.ts";
|
import { type SimpleStore } from "./lib/src/common/utils.ts";
|
||||||
import { Logger } from "./lib/src/common/logger.ts";
|
|
||||||
import { cancelAllPeriodicTask, cancelAllTasks } from "./common/utils.ts";
|
|
||||||
import { versionNumberString2Number } from "./lib/src/string_and_binary/convert.ts";
|
|
||||||
import { LiveSyncLocalDB, type LiveSyncLocalDBEnv } from "./lib/src/pouchdb/LiveSyncLocalDB.ts";
|
import { LiveSyncLocalDB, type LiveSyncLocalDBEnv } from "./lib/src/pouchdb/LiveSyncLocalDB.ts";
|
||||||
import { LiveSyncAbstractReplicator, type LiveSyncReplicatorEnv } from "./lib/src/replication/LiveSyncAbstractReplicator.js";
|
import { LiveSyncAbstractReplicator, type LiveSyncReplicatorEnv } from "./lib/src/replication/LiveSyncAbstractReplicator.js";
|
||||||
import { type KeyValueDatabase } from "./common/KeyValueDB.ts";
|
import { type KeyValueDatabase } from "./common/KeyValueDB.ts";
|
||||||
import { LiveSyncCommands } from "./features/LiveSyncCommands.ts";
|
import { LiveSyncCommands } from "./features/LiveSyncCommands.ts";
|
||||||
import { HiddenFileSync } from "./features/HiddenFileSync/CmdHiddenFileSync.ts";
|
import { HiddenFileSync } from "./features/HiddenFileSync/CmdHiddenFileSync.ts";
|
||||||
import { ConfigSync } from "./features/ConfigSync/CmdConfigSync.ts";
|
import { ConfigSync } from "./features/ConfigSync/CmdConfigSync.ts";
|
||||||
import { stopAllRunningProcessors } from "./lib/src/concurrency/processor.js";
|
|
||||||
import { reactiveSource, type ReactiveValue } from "./lib/src/dataobject/reactive.js";
|
import { reactiveSource, type ReactiveValue } from "./lib/src/dataobject/reactive.js";
|
||||||
import { type LiveSyncJournalReplicatorEnv } from "./lib/src/replication/journal/LiveSyncJournalReplicator.js";
|
import { type LiveSyncJournalReplicatorEnv } from "./lib/src/replication/journal/LiveSyncJournalReplicator.js";
|
||||||
import { type LiveSyncCouchDBReplicatorEnv } from "./lib/src/replication/couchdb/LiveSyncReplicator.js";
|
import { type LiveSyncCouchDBReplicatorEnv } from "./lib/src/replication/couchdb/LiveSyncReplicator.js";
|
||||||
import type { CheckPointInfo } from "./lib/src/replication/journal/JournalSyncTypes.js";
|
import type { CheckPointInfo } from "./lib/src/replication/journal/JournalSyncTypes.js";
|
||||||
import { ObsHttpHandler } from "./modules/essentialObsidian/APILib/ObsHttpHandler.js";
|
import { ObsHttpHandler } from "./modules/essentialObsidian/APILib/ObsHttpHandler.js";
|
||||||
import { $f, setLang } from "./lib/src/common/i18n.ts";
|
|
||||||
import { eventHub } from "./lib/src/hub/hub.ts";
|
|
||||||
import { EVENT_LAYOUT_READY, EVENT_PLUGIN_LOADED, EVENT_PLUGIN_UNLOADED, EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED } from "./common/events.ts";
|
|
||||||
import type { IObsidianModule } from "./modules/AbstractObsidianModule.ts";
|
import type { IObsidianModule } from "./modules/AbstractObsidianModule.ts";
|
||||||
|
|
||||||
import { ModuleDev } from "./modules/extras/ModuleDev.ts";
|
import { ModuleDev } from "./modules/extras/ModuleDev.ts";
|
||||||
@@ -64,6 +57,8 @@ import { ModuleResolvingMismatchedTweaks } from "./modules/coreFeatures/ModuleRe
|
|||||||
import { ModuleIntegratedTest } from "./modules/extras/ModuleIntegratedTest.ts";
|
import { ModuleIntegratedTest } from "./modules/extras/ModuleIntegratedTest.ts";
|
||||||
import { ModuleRebuilder } from "./modules/core/ModuleRebuilder.ts";
|
import { ModuleRebuilder } from "./modules/core/ModuleRebuilder.ts";
|
||||||
import { ModuleReplicateTest } from "./modules/extras/ModuleReplicateTest.ts";
|
import { ModuleReplicateTest } from "./modules/extras/ModuleReplicateTest.ts";
|
||||||
|
import { ModuleLiveSyncMain } from "./modules/main/ModuleLiveSyncMain.ts";
|
||||||
|
import { ModuleExtraSyncObsidian } from "./modules/extraFeaturesObsidian/ModuleExtraSyncObsidian.ts";
|
||||||
|
|
||||||
|
|
||||||
function throwShouldBeOverridden(): never {
|
function throwShouldBeOverridden(): never {
|
||||||
@@ -85,31 +80,8 @@ const InterceptiveAny = Promise.resolve(undefined);
|
|||||||
|
|
||||||
export default class ObsidianLiveSyncPlugin extends Plugin implements LiveSyncLocalDBEnv, LiveSyncReplicatorEnv, LiveSyncJournalReplicatorEnv, LiveSyncCouchDBReplicatorEnv, HasSettings<ObsidianLiveSyncSettings> {
|
export default class ObsidianLiveSyncPlugin extends Plugin implements LiveSyncLocalDBEnv, LiveSyncReplicatorEnv, LiveSyncJournalReplicatorEnv, LiveSyncCouchDBReplicatorEnv, HasSettings<ObsidianLiveSyncSettings> {
|
||||||
|
|
||||||
_log = Logger;
|
|
||||||
settings!: ObsidianLiveSyncSettings;
|
|
||||||
localDatabase!: LiveSyncLocalDB;
|
|
||||||
replicator!: LiveSyncAbstractReplicator;
|
|
||||||
|
|
||||||
_suspended = false;
|
|
||||||
get suspended() {
|
|
||||||
return this._suspended || !this.settings?.isConfigured;
|
|
||||||
}
|
|
||||||
set suspended(value: boolean) {
|
|
||||||
this._suspended = value;
|
|
||||||
}
|
|
||||||
get shouldBatchSave() {
|
|
||||||
return this.settings?.batchSave && this.settings?.liveSync != true;
|
|
||||||
}
|
|
||||||
get batchSaveMinimumDelay(): number {
|
|
||||||
return this.settings?.batchSaveMinimumDelay ?? DEFAULT_SETTINGS.batchSaveMinimumDelay
|
|
||||||
}
|
|
||||||
get batchSaveMaximumDelay(): number {
|
|
||||||
return this.settings?.batchSaveMaximumDelay ?? DEFAULT_SETTINGS.batchSaveMaximumDelay
|
|
||||||
}
|
|
||||||
deviceAndVaultName = "";
|
|
||||||
isReady = false;
|
|
||||||
packageVersion = "";
|
|
||||||
manifestVersion = "";
|
|
||||||
|
|
||||||
// --> Module System
|
// --> Module System
|
||||||
getAddOn<T extends LiveSyncCommands>(cls: string) {
|
getAddOn<T extends LiveSyncCommands>(cls: string) {
|
||||||
@@ -123,6 +95,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin implements LiveSyncLo
|
|||||||
addOns = [new ConfigSync(this), new HiddenFileSync(this)] as LiveSyncCommands[];
|
addOns = [new ConfigSync(this), new HiddenFileSync(this)] as LiveSyncCommands[];
|
||||||
|
|
||||||
modules = [
|
modules = [
|
||||||
|
new ModuleLiveSyncMain(this),
|
||||||
|
new ModuleExtraSyncObsidian(this, this),
|
||||||
// Only on Obsidian
|
// Only on Obsidian
|
||||||
new ModuleDatabaseFileAccess(this),
|
new ModuleDatabaseFileAccess(this),
|
||||||
// Common
|
// Common
|
||||||
@@ -166,48 +140,53 @@ export default class ObsidianLiveSyncPlugin extends Plugin implements LiveSyncLo
|
|||||||
new ModuleIntegratedTest(this, this),
|
new ModuleIntegratedTest(this, this),
|
||||||
] as (IObsidianModule | AbstractModule)[];
|
] as (IObsidianModule | AbstractModule)[];
|
||||||
injected = injectModules(this, [...this.modules, ...this.addOns] as ICoreModule[]);
|
injected = injectModules(this, [...this.modules, ...this.addOns] as ICoreModule[]);
|
||||||
|
// <-- Module System
|
||||||
|
|
||||||
|
$$isSuspended(): boolean { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
|
$$setSuspended(value: boolean): void { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
|
$$isDatabaseReady(): boolean { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
|
$$getDeviceAndVaultName(): string { throwShouldBeOverridden(); }
|
||||||
|
$$setDeviceAndVaultName(name: string): void { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
$$addLog(message: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key = ""): void { throwShouldBeOverridden() }
|
$$addLog(message: any, level: LOG_LEVEL = LOG_LEVEL_INFO, key = ""): void { throwShouldBeOverridden() }
|
||||||
|
$$isReady(): boolean { throwShouldBeOverridden(); }
|
||||||
|
$$markIsReady(): void { throwShouldBeOverridden(); }
|
||||||
|
$$resetIsReady(): void { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
// Following are plugged by the modules.
|
// Following are plugged by the modules.
|
||||||
|
|
||||||
|
settings!: ObsidianLiveSyncSettings;
|
||||||
|
localDatabase!: LiveSyncLocalDB;
|
||||||
|
simpleStore!: SimpleStore<CheckPointInfo>
|
||||||
|
replicator!: LiveSyncAbstractReplicator;
|
||||||
confirm!: Confirm;
|
confirm!: Confirm;
|
||||||
storageAccess!: StorageAccess;
|
storageAccess!: StorageAccess;
|
||||||
databaseFileAccess!: DatabaseFileAccess;
|
databaseFileAccess!: DatabaseFileAccess;
|
||||||
fileHandler!: ModuleFileHandler;
|
fileHandler!: ModuleFileHandler;
|
||||||
rebuilder!: Rebuilder;
|
rebuilder!: Rebuilder;
|
||||||
|
|
||||||
// implementing interfaces
|
|
||||||
kvDB!: KeyValueDatabase;
|
kvDB!: KeyValueDatabase;
|
||||||
last_successful_post = false;
|
|
||||||
|
|
||||||
totalFileEventCount = 0;
|
|
||||||
|
|
||||||
$$customFetchHandler(): ObsHttpHandler {
|
|
||||||
throw new Error("This function should be overridden by the module.");
|
|
||||||
}
|
|
||||||
|
|
||||||
customFetchHandler() {
|
|
||||||
return this.$$customFetchHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
getLastPostFailedBySize() {
|
|
||||||
return !this.last_successful_post;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
getDatabase(): PouchDB.Database<EntryDoc> { return this.localDatabase.localDatabase; }
|
getDatabase(): PouchDB.Database<EntryDoc> { return this.localDatabase.localDatabase; }
|
||||||
getSettings(): ObsidianLiveSyncSettings { return this.settings; }
|
getSettings(): ObsidianLiveSyncSettings { return this.settings; }
|
||||||
getIsMobile(): boolean { return this.isMobile; }
|
|
||||||
|
|
||||||
|
|
||||||
|
$$markFileListPossiblyChanged(): void { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
|
$$customFetchHandler(): ObsHttpHandler { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
|
$$getLastPostFailedBySize(): boolean { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$$isStorageInsensitive(): boolean { throwShouldBeOverridden() }
|
$$isStorageInsensitive(): boolean { throwShouldBeOverridden() }
|
||||||
|
|
||||||
get shouldCheckCaseInsensitive() {
|
$$shouldCheckCaseInsensitive(): boolean { throwShouldBeOverridden(); }
|
||||||
if (this.$$isStorageInsensitive()) return false;
|
|
||||||
return !this.settings.handleFilenameCaseSensitive;
|
|
||||||
}
|
|
||||||
|
|
||||||
_unloaded = false;
|
$$isUnloaded(): boolean { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
requestCount = reactiveSource(0);
|
requestCount = reactiveSource(0);
|
||||||
responseCount = reactiveSource(0);
|
responseCount = reactiveSource(0);
|
||||||
@@ -222,9 +201,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin implements LiveSyncLo
|
|||||||
processingFileEventCount = reactiveSource(0);
|
processingFileEventCount = reactiveSource(0);
|
||||||
|
|
||||||
_totalProcessingCount?: ReactiveValue<number>;
|
_totalProcessingCount?: ReactiveValue<number>;
|
||||||
get isReloadingScheduled() {
|
|
||||||
return this._totalProcessingCount !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
replicationStat = reactiveSource({
|
replicationStat = reactiveSource({
|
||||||
sent: 0,
|
sent: 0,
|
||||||
@@ -236,14 +213,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin implements LiveSyncLo
|
|||||||
syncStatus: "CLOSED" as DatabaseConnectingStatus
|
syncStatus: "CLOSED" as DatabaseConnectingStatus
|
||||||
});
|
});
|
||||||
|
|
||||||
get isMobile() { return this.$$isMobile(); }
|
$$isReloadingScheduled(): boolean { throwShouldBeOverridden(); }
|
||||||
|
$$getReplicator(): LiveSyncAbstractReplicator { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
// Plug-in's overrideable functions
|
|
||||||
onload() { void this.onLiveSyncLoad(); }
|
|
||||||
async saveSettings() { await this.$$saveSettingData(); }
|
|
||||||
onunload() { return void this.onLiveSyncUnload(); }
|
|
||||||
// <-- Plug-in's overrideable functions
|
|
||||||
|
|
||||||
$$connectRemoteCouchDB(uri: string, auth: {
|
$$connectRemoteCouchDB(uri: string, auth: {
|
||||||
username: string;
|
username: string;
|
||||||
@@ -286,20 +257,16 @@ export default class ObsidianLiveSyncPlugin extends Plugin implements LiveSyncLo
|
|||||||
|
|
||||||
$everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> { return InterceptiveEvery; }
|
$everyOnResetDatabase(db: LiveSyncLocalDB): Promise<boolean> { return InterceptiveEvery; }
|
||||||
|
|
||||||
getReplicator() {
|
|
||||||
return this.replicator;
|
|
||||||
}
|
|
||||||
|
|
||||||
// end interfaces
|
// end interfaces
|
||||||
|
|
||||||
$$getVaultName(): string { throwShouldBeOverridden(); }
|
$$getVaultName(): string { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
simpleStore!: SimpleStore<CheckPointInfo>
|
|
||||||
|
|
||||||
$$getSimpleStore<T>(kind: string): SimpleStore<T> { throwShouldBeOverridden(); }
|
$$getSimpleStore<T>(kind: string): SimpleStore<T> { throwShouldBeOverridden(); }
|
||||||
// trench!: Trench;
|
// trench!: Trench;
|
||||||
|
|
||||||
|
|
||||||
// --> Events
|
// --> Events
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -344,139 +311,11 @@ export default class ObsidianLiveSyncPlugin extends Plugin implements LiveSyncLo
|
|||||||
$everyOnFirstInitialize(): Promise<boolean> { return InterceptiveEvery }
|
$everyOnFirstInitialize(): Promise<boolean> { return InterceptiveEvery }
|
||||||
|
|
||||||
// Some Module should call this function to start the plugin.
|
// Some Module should call this function to start the plugin.
|
||||||
async onLiveSyncReady() {
|
$$onLiveSyncReady(): Promise<false | undefined> { throwShouldBeOverridden(); }
|
||||||
if (!await this.$everyOnLayoutReady()) return;
|
$$wireUpEvents(): void { throwShouldBeOverridden(); }
|
||||||
eventHub.emitEvent(EVENT_LAYOUT_READY);
|
$$onLiveSyncLoad(): Promise<void> { throwShouldBeOverridden(); }
|
||||||
if (this.settings.suspendFileWatching || this.settings.suspendParseReplicationResult) {
|
|
||||||
const ANSWER_KEEP = "Keep this plug-in suspended";
|
|
||||||
const ANSWER_RESUME = "Resume and restart Obsidian";
|
|
||||||
const message = `Self-hosted LiveSync has been configured to ignore some events. Is this intentional for you?
|
|
||||||
|
|
||||||
| Type | Status | Note |
|
$$onLiveSyncUnload(): Promise<void> { throwShouldBeOverridden(); }
|
||||||
|:---:|:---:|---|
|
|
||||||
| Storage Events | ${this.settings.suspendFileWatching ? "suspended" : "active"} | Every modification will be ignored |
|
|
||||||
| Database Events | ${this.settings.suspendParseReplicationResult ? "suspended" : "active"} | Every synchronised change will be postponed |
|
|
||||||
|
|
||||||
Do you want to resume them and restart Obsidian?
|
|
||||||
|
|
||||||
> [!DETAILS]-
|
|
||||||
> These flags are set by the plug-in while rebuilding, or fetching. If the process ends abnormally, it may be kept unintended.
|
|
||||||
> If you are not sure, you can try to rerun these processes. Make sure to back your vault up.
|
|
||||||
`;
|
|
||||||
if (await this.confirm.askSelectStringDialogue(message, [ANSWER_KEEP, ANSWER_RESUME], { defaultAction: ANSWER_KEEP, title: "Scram Enabled" }) == ANSWER_RESUME) {
|
|
||||||
this.settings.suspendFileWatching = false;
|
|
||||||
this.settings.suspendParseReplicationResult = false;
|
|
||||||
await this.saveSettings();
|
|
||||||
await this.$$scheduleAppReload();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const isInitialized = await this.$$initializeDatabase(false, false);
|
|
||||||
if (!isInitialized) {
|
|
||||||
//TODO:stop all sync.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!await this.$everyOnFirstInitialize()) return;
|
|
||||||
await this.realizeSettingSyncMode();
|
|
||||||
fireAndForget(async () => {
|
|
||||||
Logger(`Additional safety scan..`, LOG_LEVEL_VERBOSE);
|
|
||||||
if (!await this.$allScanStat()) {
|
|
||||||
Logger(`Additional safety scan has been failed on some module`, LOG_LEVEL_NOTICE);
|
|
||||||
} else {
|
|
||||||
Logger(`Additional safety scan done`, LOG_LEVEL_VERBOSE);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
wireUpEvents() {
|
|
||||||
eventHub.onEvent(EVENT_SETTING_SAVED, (evt: CustomEvent<ObsidianLiveSyncSettings>) => {
|
|
||||||
const settings = evt.detail;
|
|
||||||
this.localDatabase.settings = settings;
|
|
||||||
setLang(settings.displayLanguage);
|
|
||||||
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
|
||||||
});
|
|
||||||
eventHub.onEvent(EVENT_SETTING_SAVED, (evt: CustomEvent<ObsidianLiveSyncSettings>) => {
|
|
||||||
fireAndForget(() => this.realizeSettingSyncMode());
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
async onLiveSyncLoad() {
|
|
||||||
this.wireUpEvents();
|
|
||||||
// debugger;
|
|
||||||
eventHub.emitEvent(EVENT_PLUGIN_LOADED, this);
|
|
||||||
Logger("loading plugin");
|
|
||||||
if (!await this.$everyOnloadStart()) {
|
|
||||||
Logger("Plugin initialising has been cancelled by some module", LOG_LEVEL_NOTICE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// this.addUIs();
|
|
||||||
//@ts-ignore
|
|
||||||
const manifestVersion: string = MANIFEST_VERSION || "0.0.0";
|
|
||||||
//@ts-ignore
|
|
||||||
const packageVersion: string = PACKAGE_VERSION || "0.0.0";
|
|
||||||
|
|
||||||
this.manifestVersion = manifestVersion;
|
|
||||||
this.packageVersion = packageVersion;
|
|
||||||
|
|
||||||
Logger($f`Self-hosted LiveSync${" v"}${manifestVersion} ${packageVersion}`);
|
|
||||||
await this.$$loadSettings();
|
|
||||||
if (!await this.$everyOnloadAfterLoadSettings()) {
|
|
||||||
Logger("Plugin initialising has been cancelled by some module", LOG_LEVEL_NOTICE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const lsKey = "obsidian-live-sync-ver" + this.$$getVaultName();
|
|
||||||
const last_version = localStorage.getItem(lsKey);
|
|
||||||
|
|
||||||
const lastVersion = ~~(versionNumberString2Number(manifestVersion) / 1000);
|
|
||||||
if (lastVersion > this.settings.lastReadUpdates && this.settings.isConfigured) {
|
|
||||||
Logger($f`You have some unread release notes! Please read them once!`, LOG_LEVEL_NOTICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
//@ts-ignore
|
|
||||||
if (this.isMobile) {
|
|
||||||
this.settings.disableRequestURI = true;
|
|
||||||
}
|
|
||||||
if (last_version && Number(last_version) < VER) {
|
|
||||||
this.settings.liveSync = false;
|
|
||||||
this.settings.syncOnSave = false;
|
|
||||||
this.settings.syncOnEditorSave = false;
|
|
||||||
this.settings.syncOnStart = false;
|
|
||||||
this.settings.syncOnFileOpen = false;
|
|
||||||
this.settings.syncAfterMerge = false;
|
|
||||||
this.settings.periodicReplication = false;
|
|
||||||
this.settings.versionUpFlash = $f`Self-hosted LiveSync has been upgraded and some behaviors have changed incompatibly. All automatic synchronization is now disabled temporary. Ensure that other devices are also upgraded, and enable synchronization again.`;
|
|
||||||
await this.saveSettings();
|
|
||||||
}
|
|
||||||
localStorage.setItem(lsKey, `${VER}`);
|
|
||||||
await this.$$openDatabase();
|
|
||||||
this.realizeSettingSyncMode = this.realizeSettingSyncMode.bind(this);
|
|
||||||
// this.$$parseReplicationResult = this.$$parseReplicationResult.bind(this);
|
|
||||||
// this.$$replicate = this.$$replicate.bind(this);
|
|
||||||
this.onLiveSyncReady = this.onLiveSyncReady.bind(this);
|
|
||||||
await this.$everyOnload();
|
|
||||||
await Promise.all(this.addOns.map(e => e.onload()));
|
|
||||||
}
|
|
||||||
|
|
||||||
async onLiveSyncUnload() {
|
|
||||||
eventHub.emitEvent(EVENT_PLUGIN_UNLOADED);
|
|
||||||
await this.$allStartOnUnload();
|
|
||||||
cancelAllPeriodicTask();
|
|
||||||
cancelAllTasks();
|
|
||||||
stopAllRunningProcessors();
|
|
||||||
await this.$allOnUnload();
|
|
||||||
this._unloaded = true;
|
|
||||||
for (const addOn of this.addOns) {
|
|
||||||
addOn.onunload();
|
|
||||||
}
|
|
||||||
if (this.localDatabase != null) {
|
|
||||||
this.localDatabase.onunload();
|
|
||||||
if (this.replicator) {
|
|
||||||
this.replicator?.closeReplication();
|
|
||||||
}
|
|
||||||
await this.localDatabase.close();
|
|
||||||
}
|
|
||||||
Logger($f`unloading plugin`);
|
|
||||||
}
|
|
||||||
|
|
||||||
$allScanStat(): Promise<boolean> {
|
$allScanStat(): Promise<boolean> {
|
||||||
return InterceptiveAll;
|
return InterceptiveAll;
|
||||||
@@ -496,18 +335,7 @@ Do you want to resume them and restart Obsidian?
|
|||||||
|
|
||||||
$$openDatabase(): Promise<boolean> { throwShouldBeOverridden() }
|
$$openDatabase(): Promise<boolean> { throwShouldBeOverridden() }
|
||||||
|
|
||||||
async realizeSettingSyncMode() {
|
$$realizeSettingSyncMode(): Promise<void> { throwShouldBeOverridden(); }
|
||||||
await this.$everyBeforeSuspendProcess();
|
|
||||||
await this.$everyBeforeRealizeSetting();
|
|
||||||
this.localDatabase.refreshSettings();
|
|
||||||
await this.$everyCommitPendingFileEvent();
|
|
||||||
await this.$everyRealizeSettingSyncMode();
|
|
||||||
// disable all sync temporary.
|
|
||||||
if (this.suspended) return;
|
|
||||||
await this.$everyOnResumeProcess();
|
|
||||||
await this.$everyAfterResumeProcess();
|
|
||||||
await this.$everyAfterRealizeSetting();
|
|
||||||
}
|
|
||||||
$$performRestart() { throwShouldBeOverridden(); }
|
$$performRestart() { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
$$clearUsedPassphrase(): void { throwShouldBeOverridden() }
|
$$clearUsedPassphrase(): void { throwShouldBeOverridden() }
|
||||||
@@ -568,6 +396,9 @@ Do you want to resume them and restart Obsidian?
|
|||||||
|
|
||||||
|
|
||||||
$$askResolvingMismatchedTweaks(): Promise<"OK" | "CHECKAGAIN" | "IGNORE"> { throwShouldBeOverridden(); }
|
$$askResolvingMismatchedTweaks(): Promise<"OK" | "CHECKAGAIN" | "IGNORE"> { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
|
$$checkAndAskUseRemoteConfiguration(settings: RemoteDBSettings): Promise<{ result: false | TweakValues, requireFetch: boolean }> { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
$everyBeforeReplicate(showMessage: boolean): Promise<boolean> { return InterceptiveEvery; }
|
$everyBeforeReplicate(showMessage: boolean): Promise<boolean> { return InterceptiveEvery; }
|
||||||
$$replicate(showMessage: boolean = false): Promise<boolean | void> { throwShouldBeOverridden() }
|
$$replicate(showMessage: boolean = false): Promise<boolean | void> { throwShouldBeOverridden() }
|
||||||
|
|
||||||
@@ -636,12 +467,17 @@ Do you want to resume them and restart Obsidian?
|
|||||||
$everyModuleTestMultiDevice(): Promise<boolean> { return InterceptiveEvery; }
|
$everyModuleTestMultiDevice(): Promise<boolean> { return InterceptiveEvery; }
|
||||||
$$addTestResult(name: string, key: string, result: boolean, summary?: string, message?: string): void { throwShouldBeOverridden(); }
|
$$addTestResult(name: string, key: string, result: boolean, summary?: string, message?: string): void { throwShouldBeOverridden(); }
|
||||||
|
|
||||||
_isMainReady(): boolean { return this.isReady; }
|
|
||||||
_isMainSuspended(): boolean { return this.suspended; }
|
|
||||||
_isThisModuleEnabled(): boolean { return true; }
|
_isThisModuleEnabled(): boolean { return true; }
|
||||||
_isDatabaseReady(): boolean { return this.localDatabase.isReady; }
|
|
||||||
|
|
||||||
$anyGetAppId(): Promise<string | undefined> { return InterceptiveAny; }
|
$anyGetAppId(): Promise<string | undefined> { return InterceptiveAny; }
|
||||||
|
|
||||||
|
|
||||||
|
// Plug-in's overrideable functions
|
||||||
|
onload() { void this.$$onLiveSyncLoad(); }
|
||||||
|
async saveSettings() { await this.$$saveSettingData(); }
|
||||||
|
onunload() { return void this.$$onLiveSyncUnload(); }
|
||||||
|
// <-- Plug-in's overrideable functions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -39,13 +39,13 @@ export abstract class AbstractObsidianModule extends AbstractModule {
|
|||||||
|
|
||||||
|
|
||||||
_isMainReady() {
|
_isMainReady() {
|
||||||
return this.core._isMainReady();
|
return this.core.$$isReady();
|
||||||
}
|
}
|
||||||
_isMainSuspended() {
|
_isMainSuspended() {
|
||||||
return this.core._isMainSuspended();
|
return this.core.$$isSuspended();
|
||||||
}
|
}
|
||||||
_isDatabaseReady() {
|
_isDatabaseReady() {
|
||||||
return this.core._isDatabaseReady();
|
return this.core.$$isDatabaseReady();
|
||||||
}
|
}
|
||||||
|
|
||||||
//should be overridden
|
//should be overridden
|
||||||
|
|||||||
@@ -133,6 +133,10 @@ export class ModuleDatabaseFileAccess extends AbstractModule implements IObsidia
|
|||||||
//upsert should locked
|
//upsert should locked
|
||||||
const msg = `STORAGE -> DB (${datatype}) `;
|
const msg = `STORAGE -> DB (${datatype}) `;
|
||||||
const isNotChanged = await serialized("file-" + fullPath, async () => {
|
const isNotChanged = await serialized("file-" + fullPath, async () => {
|
||||||
|
if (force) {
|
||||||
|
this._log(msg + "Force writing " + fullPath, LOG_LEVEL_VERBOSE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// Commented out temporarily: this checks that the file was made ourself.
|
// Commented out temporarily: this checks that the file was made ourself.
|
||||||
// if (this.core.storageAccess.recentlyTouched(file)) {
|
// if (this.core.storageAccess.recentlyTouched(file)) {
|
||||||
// return true;
|
// return true;
|
||||||
|
|||||||
@@ -223,12 +223,9 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
|
|||||||
}
|
}
|
||||||
const docData = readContent(docRead);
|
const docData = readContent(docRead);
|
||||||
|
|
||||||
if (!existOnStorage) {
|
if (existOnStorage && !force) {
|
||||||
// The file is not exist on the storage. We do not care about the differences.
|
// The file is exist on the storage. Let's check the difference between the file and the entry.
|
||||||
await this.storage.ensureDir(path);
|
// But, if force is true, then it should be updated.
|
||||||
return await this.storage.writeFileAuto(path, docData, { ctime: docRead.ctime, mtime: docRead.mtime });
|
|
||||||
}
|
|
||||||
if (!force) {
|
|
||||||
// Ok, we have to compare.
|
// Ok, we have to compare.
|
||||||
let shouldApplied = false;
|
let shouldApplied = false;
|
||||||
// 1. if the time stamp is far different, then it should be updated.
|
// 1. if the time stamp is far different, then it should be updated.
|
||||||
@@ -257,6 +254,8 @@ export class ModuleFileHandler extends AbstractModule implements ICoreModule {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
// Let's apply the changes.
|
// Let's apply the changes.
|
||||||
|
} else {
|
||||||
|
this._log(`File ${docRead.path} ${existOnStorage ? "(new) " : ""} ${force ? " (forced)" : ""}`, LOG_LEVEL_VERBOSE);
|
||||||
}
|
}
|
||||||
await this.storage.ensureDir(path);
|
await this.storage.ensureDir(path);
|
||||||
const ret = await this.storage.writeFileAuto(path, docData, { ctime: docRead.ctime, mtime: docRead.mtime });
|
const ret = await this.storage.writeFileAuto(path, docData, { ctime: docRead.ctime, mtime: docRead.mtime });
|
||||||
|
|||||||
@@ -20,4 +20,8 @@ export class ModuleLocalDatabaseObsidian extends AbstractModule implements ICore
|
|||||||
return await this.localDatabase.initializeDatabase();
|
return await this.localDatabase.initializeDatabase();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$$isDatabaseReady(): boolean {
|
||||||
|
return this.localDatabase != null && this.localDatabase.isReady;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,7 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
|||||||
await this.core.$allSuspendExtraSync();
|
await this.core.$allSuspendExtraSync();
|
||||||
this.core.settings.isConfigured = true;
|
this.core.settings.isConfigured = true;
|
||||||
|
|
||||||
await this.core.realizeSettingSyncMode();
|
await this.core.$$realizeSettingSyncMode();
|
||||||
await this.core.$$markRemoteLocked();
|
await this.core.$$markRemoteLocked();
|
||||||
await this.core.$$tryResetRemoteDatabase();
|
await this.core.$$tryResetRemoteDatabase();
|
||||||
await this.core.$$markRemoteLocked();
|
await this.core.$$markRemoteLocked();
|
||||||
@@ -60,7 +60,7 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
|||||||
await this.core.$allSuspendExtraSync();
|
await this.core.$allSuspendExtraSync();
|
||||||
await this.askUseNewAdapter();
|
await this.askUseNewAdapter();
|
||||||
this.core.settings.isConfigured = true;
|
this.core.settings.isConfigured = true;
|
||||||
await this.core.realizeSettingSyncMode();
|
await this.core.$$realizeSettingSyncMode();
|
||||||
await this.resetLocalDatabase();
|
await this.resetLocalDatabase();
|
||||||
await delay(1000);
|
await delay(1000);
|
||||||
await this.core.$$initializeDatabase(true);
|
await this.core.$$initializeDatabase(true);
|
||||||
@@ -164,11 +164,12 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
|||||||
await this.askUseNewAdapter();
|
await this.askUseNewAdapter();
|
||||||
this.core.settings.isConfigured = true;
|
this.core.settings.isConfigured = true;
|
||||||
await this.suspendReflectingDatabase();
|
await this.suspendReflectingDatabase();
|
||||||
await this.core.realizeSettingSyncMode();
|
await this.core.$$realizeSettingSyncMode();
|
||||||
await this.resetLocalDatabase();
|
await this.resetLocalDatabase();
|
||||||
await delay(1000);
|
await delay(1000);
|
||||||
await this.core.$$openDatabase();
|
await this.core.$$openDatabase();
|
||||||
this.core.isReady = true;
|
// this.core.isReady = true;
|
||||||
|
this.core.$$markIsReady();
|
||||||
if (makeLocalChunkBeforeSync) {
|
if (makeLocalChunkBeforeSync) {
|
||||||
await this.core.fileHandler.createAllChunks(true);
|
await this.core.fileHandler.createAllChunks(true);
|
||||||
}
|
}
|
||||||
@@ -201,8 +202,8 @@ export class ModuleRebuilder extends AbstractModule implements ICoreModule, Rebu
|
|||||||
async fetchRemoteChunks() {
|
async fetchRemoteChunks() {
|
||||||
if (!this.core.settings.doNotSuspendOnFetching && this.core.settings.readChunksOnline && this.core.settings.remoteType == REMOTE_COUCHDB) {
|
if (!this.core.settings.doNotSuspendOnFetching && this.core.settings.readChunksOnline && this.core.settings.remoteType == REMOTE_COUCHDB) {
|
||||||
this._log(`Fetching chunks`, LOG_LEVEL_NOTICE);
|
this._log(`Fetching chunks`, LOG_LEVEL_NOTICE);
|
||||||
const replicator = this.core.getReplicator() as LiveSyncCouchDBReplicator;
|
const replicator = this.core.$$getReplicator() as LiveSyncCouchDBReplicator;
|
||||||
const remoteDB = await replicator.connectRemoteCouchDBWithSetting(this.settings, this.core.getIsMobile(), true);
|
const remoteDB = await replicator.connectRemoteCouchDBWithSetting(this.settings, this.core.$$isMobile(), true);
|
||||||
if (typeof remoteDB == "string") {
|
if (typeof remoteDB == "string") {
|
||||||
this._log(remoteDB, LOG_LEVEL_NOTICE);
|
this._log(remoteDB, LOG_LEVEL_NOTICE);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -14,12 +14,13 @@ import { getPath, isChunk, isValidPath, scheduleTask } from "../../common/utils"
|
|||||||
import { sendValue } from "octagonal-wheels/messagepassing/signal";
|
import { sendValue } from "octagonal-wheels/messagepassing/signal";
|
||||||
import { isAnyNote } from "../../lib/src/common/utils";
|
import { isAnyNote } from "../../lib/src/common/utils";
|
||||||
import { EVENT_FILE_SAVED, eventHub } from "../../common/events";
|
import { EVENT_FILE_SAVED, eventHub } from "../../common/events";
|
||||||
|
import type { LiveSyncAbstractReplicator } from "../../lib/src/replication/LiveSyncAbstractReplicator";
|
||||||
|
|
||||||
export class ModuleReplicator extends AbstractModule implements ICoreModule {
|
export class ModuleReplicator extends AbstractModule implements ICoreModule {
|
||||||
|
|
||||||
$everyOnloadAfterLoadSettings(): Promise<boolean> {
|
$everyOnloadAfterLoadSettings(): Promise<boolean> {
|
||||||
eventHub.onEvent(EVENT_FILE_SAVED, () => {
|
eventHub.onEvent(EVENT_FILE_SAVED, () => {
|
||||||
if (this.settings.syncOnSave && !this.core.suspended) {
|
if (this.settings.syncOnSave && !this.core.$$isSuspended()) {
|
||||||
scheduleTask("perform-replicate-after-save", 250, () => this.core.$$waitForReplicationOnce());
|
scheduleTask("perform-replicate-after-save", 250, () => this.core.$$waitForReplicationOnce());
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -36,6 +37,11 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule {
|
|||||||
await yieldMicrotask();
|
await yieldMicrotask();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$$getReplicator(): LiveSyncAbstractReplicator {
|
||||||
|
return this.core.replicator;
|
||||||
|
}
|
||||||
|
|
||||||
$everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<boolean> {
|
$everyOnInitializeDatabase(db: LiveSyncLocalDB): Promise<boolean> {
|
||||||
return this.setReplicator();
|
return this.setReplicator();
|
||||||
}
|
}
|
||||||
@@ -51,7 +57,7 @@ export class ModuleReplicator extends AbstractModule implements ICoreModule {
|
|||||||
}
|
}
|
||||||
async $$replicate(showMessage: boolean = false): Promise<boolean | void> {
|
async $$replicate(showMessage: boolean = false): Promise<boolean | void> {
|
||||||
//--?
|
//--?
|
||||||
if (!this.core.isReady) return;
|
if (!this.core.$$isReady()) return;
|
||||||
if (isLockAcquired("cleanup")) {
|
if (isLockAcquired("cleanup")) {
|
||||||
Logger("Database cleaning up is in process. replication has been cancelled", LOG_LEVEL_NOTICE);
|
Logger("Database cleaning up is in process. replication has been cancelled", LOG_LEVEL_NOTICE);
|
||||||
return;
|
return;
|
||||||
@@ -97,9 +103,9 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
|||||||
await this.core.rebuilder.$performRebuildDB("localOnly");
|
await this.core.rebuilder.$performRebuildDB("localOnly");
|
||||||
}
|
}
|
||||||
if (ret == CHOICE_CLEAN) {
|
if (ret == CHOICE_CLEAN) {
|
||||||
const replicator = this.core.getReplicator();
|
const replicator = this.core.$$getReplicator();
|
||||||
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return;
|
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return;
|
||||||
const remoteDB = await replicator.connectRemoteCouchDBWithSetting(this.settings, this.core.getIsMobile(), true);
|
const remoteDB = await replicator.connectRemoteCouchDBWithSetting(this.settings, this.core.$$isMobile(), true);
|
||||||
if (typeof remoteDB == "string") {
|
if (typeof remoteDB == "string") {
|
||||||
Logger(remoteDB, LOG_LEVEL_NOTICE);
|
Logger(remoteDB, LOG_LEVEL_NOTICE);
|
||||||
return false;
|
return false;
|
||||||
@@ -112,7 +118,7 @@ Even if you choose to clean up, you will see this option again if you exit Obsid
|
|||||||
await balanceChunkPurgedDBs(this.localDatabase.localDatabase, remoteDB.db);
|
await balanceChunkPurgedDBs(this.localDatabase.localDatabase, remoteDB.db);
|
||||||
await purgeUnreferencedChunks(this.localDatabase.localDatabase, false);
|
await purgeUnreferencedChunks(this.localDatabase.localDatabase, false);
|
||||||
this.localDatabase.hashCaches.clear();
|
this.localDatabase.hashCaches.clear();
|
||||||
await this.core.getReplicator().markRemoteResolved(this.settings);
|
await this.core.$$getReplicator().markRemoteResolved(this.settings);
|
||||||
Logger("The local database has been cleaned up.", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO)
|
Logger("The local database has been cleaned up.", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO)
|
||||||
} else {
|
} else {
|
||||||
Logger("Replication has been cancelled. Please try it again.", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO)
|
Logger("Replication has been cancelled. Please try it again.", showMessage ? LOG_LEVEL_NOTICE : LOG_LEVEL_INFO)
|
||||||
@@ -315,7 +321,7 @@ Or if you are sure know what had been happened, we can unlock the database from
|
|||||||
}
|
}
|
||||||
|
|
||||||
async $$replicateAllToServer(showingNotice: boolean = false, sendChunksInBulkDisabled: boolean = false): Promise<boolean> {
|
async $$replicateAllToServer(showingNotice: boolean = false, sendChunksInBulkDisabled: boolean = false): Promise<boolean> {
|
||||||
if (!this.core.isReady) return false;
|
if (!this.core.$$isReady()) return false;
|
||||||
if (!await this.core.$everyBeforeReplicate(showingNotice)) {
|
if (!await this.core.$everyBeforeReplicate(showingNotice)) {
|
||||||
Logger(`Replication has been cancelled by some module failure`, LOG_LEVEL_NOTICE);
|
Logger(`Replication has been cancelled by some module failure`, LOG_LEVEL_NOTICE);
|
||||||
return false;
|
return false;
|
||||||
@@ -334,7 +340,7 @@ Or if you are sure know what had been happened, we can unlock the database from
|
|||||||
return !checkResult;
|
return !checkResult;
|
||||||
}
|
}
|
||||||
async $$replicateAllFromServer(showingNotice: boolean = false): Promise<boolean> {
|
async $$replicateAllFromServer(showingNotice: boolean = false): Promise<boolean> {
|
||||||
if (!this.core.isReady) return false;
|
if (!this.core.$$isReady()) return false;
|
||||||
const ret = await this.core.replicator.replicateAllFromServer(this.settings, showingNotice);
|
const ret = await this.core.replicator.replicateAllFromServer(this.settings, showingNotice);
|
||||||
if (ret) return true;
|
if (ret) return true;
|
||||||
const checkResult = await this.core.$anyAfterConnectCheckFailed();
|
const checkResult = await this.core.$anyAfterConnectCheckFailed();
|
||||||
|
|||||||
@@ -13,10 +13,10 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
|
|||||||
|
|
||||||
}
|
}
|
||||||
$everyOnload(): Promise<boolean> {
|
$everyOnload(): Promise<boolean> {
|
||||||
eventHub.onEvent(EVENT_SETTING_SAVED, (evt: CustomEvent<ObsidianLiveSyncSettings>) => {
|
eventHub.onEvent(EVENT_SETTING_SAVED, (evt: ObsidianLiveSyncSettings) => {
|
||||||
this.reloadIgnoreFiles();
|
this.reloadIgnoreFiles();
|
||||||
});
|
});
|
||||||
eventHub.onEvent(EVENT_REQUEST_RELOAD_SETTING_TAB, (evt: CustomEvent<ObsidianLiveSyncSettings>) => {
|
eventHub.onEvent(EVENT_REQUEST_RELOAD_SETTING_TAB, () => {
|
||||||
this.reloadIgnoreFiles();
|
this.reloadIgnoreFiles();
|
||||||
});
|
});
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
@@ -45,8 +45,13 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$$markFileListPossiblyChanged(): void {
|
||||||
|
this.totalFileEventCount++;
|
||||||
|
}
|
||||||
|
totalFileEventCount = 0;
|
||||||
get fileListPossiblyChanged() {
|
get fileListPossiblyChanged() {
|
||||||
if (isDirty("totalFileEventCount", this.core.totalFileEventCount)) {
|
if (isDirty("totalFileEventCount", this.totalFileEventCount)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -88,7 +93,7 @@ export class ModuleTargetFilter extends AbstractModule implements ICoreModule {
|
|||||||
|
|
||||||
const filepath = getPathFromUXFileInfo(file);
|
const filepath = getPathFromUXFileInfo(file);
|
||||||
const lc = filepath.toLowerCase();
|
const lc = filepath.toLowerCase();
|
||||||
if (this.core.shouldCheckCaseInsensitive) {
|
if (this.core.$$shouldCheckCaseInsensitive()) {
|
||||||
if (lc in fileCount && fileCount[lc] > 1) {
|
if (lc in fileCount && fileCount[lc] > 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ export class ModuleConflictResolver extends AbstractModule implements ICoreModul
|
|||||||
}
|
}
|
||||||
if (conflictCheckResult === AUTO_MERGED) {
|
if (conflictCheckResult === AUTO_MERGED) {
|
||||||
//auto resolved, but need check again;
|
//auto resolved, but need check again;
|
||||||
if (this.settings.syncAfterMerge && !this.core.suspended) {
|
if (this.settings.syncAfterMerge && !this.core.$$isSuspended()) {
|
||||||
//Wait for the running replication, if not running replication, run it once.
|
//Wait for the running replication, if not running replication, run it once.
|
||||||
await this.core.$$waitForReplicationOnce();
|
await this.core.$$waitForReplicationOnce();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Logger, LOG_LEVEL_NOTICE } from "octagonal-wheels/common/logger";
|
import { Logger, LOG_LEVEL_NOTICE } from "octagonal-wheels/common/logger";
|
||||||
import { extractObject } from "octagonal-wheels/object";
|
import { extractObject } from "octagonal-wheels/object";
|
||||||
import { TweakValuesShouldMatchedTemplate, CompatibilityBreakingTweakValues, confName, type TweakValues } from "../../lib/src/common/types.ts";
|
import { TweakValuesShouldMatchedTemplate, CompatibilityBreakingTweakValues, confName, type TweakValues, type RemoteDBSettings } from "../../lib/src/common/types.ts";
|
||||||
import { escapeMarkdownValue } from "../../lib/src/common/utils.ts";
|
import { escapeMarkdownValue } from "../../lib/src/common/utils.ts";
|
||||||
import { AbstractModule } from "../AbstractModule.ts";
|
import { AbstractModule } from "../AbstractModule.ts";
|
||||||
import type { ICoreModule } from "../ModuleTypes.ts";
|
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||||
@@ -94,4 +94,78 @@ Please select which one you want to use.
|
|||||||
return "IGNORE";
|
return "IGNORE";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async $$checkAndAskUseRemoteConfiguration(trialSetting: RemoteDBSettings): Promise<{ result: false | TweakValues, requireFetch: boolean }> {
|
||||||
|
const replicator = await this.core.$anyNewReplicator(trialSetting);
|
||||||
|
if (await replicator.tryConnectRemote(trialSetting)) {
|
||||||
|
const preferred = await replicator.getRemotePreferredTweakValues(trialSetting);
|
||||||
|
if (preferred) {
|
||||||
|
const items = Object.entries(TweakValuesShouldMatchedTemplate);
|
||||||
|
let rebuildRequired = false;
|
||||||
|
// Making tables:
|
||||||
|
let table = `| Value name | This device | Stored | \n` + `|: --- |: ---- :|: ---- :| \n`;
|
||||||
|
let differenceCount = 0;
|
||||||
|
// const items = [mine,preferred]
|
||||||
|
for (const v of items) {
|
||||||
|
const key = v[0] as keyof typeof TweakValuesShouldMatchedTemplate;
|
||||||
|
const valuePreferred = escapeMarkdownValue(preferred[key]);
|
||||||
|
const currentDisp = `${escapeMarkdownValue((trialSetting as TweakValues)?.[key])} |`;
|
||||||
|
if ((trialSetting as TweakValues)?.[key] !== preferred[key]) {
|
||||||
|
if (CompatibilityBreakingTweakValues.indexOf(key) !== -1) {
|
||||||
|
rebuildRequired = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
table += `| ${confName(key)} | ${currentDisp} ${valuePreferred} | \n`;
|
||||||
|
differenceCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (differenceCount === 0) {
|
||||||
|
this._log("The settings in the remote database are the same as the local database.", LOG_LEVEL_NOTICE);
|
||||||
|
return { result: false, requireFetch: false };
|
||||||
|
}
|
||||||
|
const additionalMessage = (rebuildRequired && this.core.settings.isConfigured) ? `
|
||||||
|
|
||||||
|
>[!WARNING]
|
||||||
|
> Some remote configurations are not compatible with the local database of this device. Rebuilding the local database will be required.
|
||||||
|
***Please ensure that you have time and are connected to a stable network to apply!***` : "";
|
||||||
|
|
||||||
|
const message = `
|
||||||
|
The settings in the remote database are as follows.
|
||||||
|
If you want to use these settings, please select "Use configured".
|
||||||
|
If you want to keep the settings of this device, please select "Dismiss".
|
||||||
|
|
||||||
|
${table}
|
||||||
|
|
||||||
|
>[!TIP]
|
||||||
|
> If you want to synchronise all settings, please use \`Sync settings via markdown\` after applying minimal configuration with this feature.
|
||||||
|
|
||||||
|
${additionalMessage}`;
|
||||||
|
|
||||||
|
const CHOICE_USE_REMOTE = "Use configured";
|
||||||
|
const CHOICE_DISMISS = "Dismiss";
|
||||||
|
// const CHOICE_AND_VALUES = [
|
||||||
|
// [CHOICE_USE_REMOTE, preferred],
|
||||||
|
// [CHOICE_DISMISS, false]]
|
||||||
|
const CHOICES = [CHOICE_USE_REMOTE, CHOICE_DISMISS];
|
||||||
|
const retKey = await this.core.confirm.askSelectStringDialogue(message, CHOICES, {
|
||||||
|
title: "Use Remote Configuration",
|
||||||
|
timeout: 0,
|
||||||
|
defaultAction: CHOICE_DISMISS
|
||||||
|
});
|
||||||
|
if (!retKey) return { result: false, requireFetch: false };
|
||||||
|
if (retKey === CHOICE_DISMISS) return { result: false, requireFetch: false };
|
||||||
|
if (retKey === CHOICE_USE_REMOTE) {
|
||||||
|
return { result: { ...trialSetting, ...preferred }, requireFetch: rebuildRequired };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._log("Failed to get the preferred tweak values from the remote server.", LOG_LEVEL_NOTICE);
|
||||||
|
}
|
||||||
|
return { result: false, requireFetch: false };
|
||||||
|
} else {
|
||||||
|
this._log("Failed to connect to the remote server.", LOG_LEVEL_NOTICE);
|
||||||
|
return { result: false, requireFetch: false };
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -43,6 +43,11 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
|
|||||||
return this.vaultAccess.isStorageInsensitive();
|
return this.vaultAccess.isStorageInsensitive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$$shouldCheckCaseInsensitive(): boolean {
|
||||||
|
if (this.$$isStorageInsensitive()) return false;
|
||||||
|
return !this.settings.handleFilenameCaseSensitive;
|
||||||
|
}
|
||||||
|
|
||||||
async writeFileAuto(path: string, data: string | ArrayBuffer, opt?: UXDataWriteOptions): Promise<boolean> {
|
async writeFileAuto(path: string, data: string | ArrayBuffer, opt?: UXDataWriteOptions): Promise<boolean> {
|
||||||
const file = this.vaultAccess.getAbstractFileByPath(path);
|
const file = this.vaultAccess.getAbstractFileByPath(path);
|
||||||
if (file instanceof TFile) {
|
if (file instanceof TFile) {
|
||||||
@@ -115,6 +120,9 @@ export class ModuleFileAccessObsidian extends AbstractObsidianModule implements
|
|||||||
async removeHidden(path: string): Promise<boolean> {
|
async removeHidden(path: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
await this.vaultAccess.adapterRemove(path);
|
await this.vaultAccess.adapterRemove(path);
|
||||||
|
if (this.vaultAccess.adapterStat(path) !== null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._log(`Could not remove hidden file: ${path}`, LOG_LEVEL_VERBOSE);
|
this._log(`Could not remove hidden file: ${path}`, LOG_LEVEL_VERBOSE);
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ class AutoClosableModal extends Modal {
|
|||||||
|
|
||||||
constructor(app: App) {
|
constructor(app: App) {
|
||||||
super(app);
|
super(app);
|
||||||
this.removeEvent = eventHub.on(EVENT_PLUGIN_UNLOADED, async () => {
|
this.removeEvent = eventHub.onEvent(EVENT_PLUGIN_UNLOADED, async () => {
|
||||||
await delay(100);
|
await delay(100);
|
||||||
if (!this.removeEvent) return;
|
if (!this.removeEvent) return;
|
||||||
this.close();
|
this.close();
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { TAbstractFile, TFile, TFolder } from "../../../deps.ts";
|
import { TAbstractFile, TFile, TFolder } from "../../../deps.ts";
|
||||||
import { Logger } from "../../../lib/src/common/logger.ts";
|
import { Logger } from "../../../lib/src/common/logger.ts";
|
||||||
import { shouldBeIgnored } from "../../../lib/src/string_and_binary/path.ts";
|
import { shouldBeIgnored } from "../../../lib/src/string_and_binary/path.ts";
|
||||||
import { LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, type FilePath, type FilePathWithPrefix, type UXFileInfoStub, type UXInternalFileInfoStub } from "../../../lib/src/common/types.ts";
|
import { DEFAULT_SETTINGS, LOG_LEVEL_DEBUG, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, type FilePath, type FilePathWithPrefix, type UXFileInfoStub, type UXInternalFileInfoStub } from "../../../lib/src/common/types.ts";
|
||||||
import { delay, fireAndForget } from "../../../lib/src/common/utils.ts";
|
import { delay, fireAndForget } from "../../../lib/src/common/utils.ts";
|
||||||
import { type FileEventItem, type FileEventType } from "../../../common/types.ts";
|
import { type FileEventItem, type FileEventType } from "../../../common/types.ts";
|
||||||
import { serialized, skipIfDuplicated } from "../../../lib/src/concurrency/lock.ts";
|
import { serialized, skipIfDuplicated } from "../../../lib/src/concurrency/lock.ts";
|
||||||
@@ -38,13 +38,13 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
|||||||
core: LiveSyncCore;
|
core: LiveSyncCore;
|
||||||
|
|
||||||
get shouldBatchSave() {
|
get shouldBatchSave() {
|
||||||
return this.plugin.shouldBatchSave;
|
return this.core.settings?.batchSave && this.core.settings?.liveSync != true;
|
||||||
}
|
}
|
||||||
get batchSaveMinimumDelay(): number {
|
get batchSaveMinimumDelay(): number {
|
||||||
return this.plugin.batchSaveMinimumDelay;
|
return this.core.settings?.batchSaveMinimumDelay ?? DEFAULT_SETTINGS.batchSaveMinimumDelay
|
||||||
}
|
}
|
||||||
get batchSaveMaximumDelay(): number {
|
get batchSaveMaximumDelay(): number {
|
||||||
return this.plugin.batchSaveMaximumDelay
|
return this.core.settings?.batchSaveMaximumDelay ?? DEFAULT_SETTINGS.batchSaveMaximumDelay
|
||||||
}
|
}
|
||||||
constructor(plugin: ObsidianLiveSyncPlugin, core: LiveSyncCore) {
|
constructor(plugin: ObsidianLiveSyncPlugin, core: LiveSyncCore) {
|
||||||
super();
|
super();
|
||||||
@@ -155,9 +155,9 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
|||||||
}
|
}
|
||||||
// Cache file and waiting to can be proceed.
|
// Cache file and waiting to can be proceed.
|
||||||
async appendQueue(params: FileEvent[], ctx?: any) {
|
async appendQueue(params: FileEvent[], ctx?: any) {
|
||||||
if (!this.plugin.settings.isConfigured) return;
|
if (!this.core.settings.isConfigured) return;
|
||||||
if (this.plugin.settings.suspendFileWatching) return;
|
if (this.core.settings.suspendFileWatching) return;
|
||||||
this.plugin.totalFileEventCount++;
|
this.core.$$markFileListPossiblyChanged();
|
||||||
// Flag up to be reload
|
// Flag up to be reload
|
||||||
const processFiles = new Set<FilePath>();
|
const processFiles = new Set<FilePath>();
|
||||||
for (const param of params) {
|
for (const param of params) {
|
||||||
@@ -176,13 +176,13 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
|||||||
const oldPath = param.oldPath;
|
const oldPath = param.oldPath;
|
||||||
if (type !== "INTERNAL") {
|
if (type !== "INTERNAL") {
|
||||||
const size = (file as UXFileInfoStub).stat.size;
|
const size = (file as UXFileInfoStub).stat.size;
|
||||||
if (this.plugin.$$isFileSizeExceeded(size) && (type == "CREATE" || type == "CHANGED")) {
|
if (this.core.$$isFileSizeExceeded(size) && (type == "CREATE" || type == "CHANGED")) {
|
||||||
Logger(`The storage file has been changed but exceeds the maximum size. Skipping: ${param.file.path}`, LOG_LEVEL_NOTICE);
|
Logger(`The storage file has been changed but exceeds the maximum size. Skipping: ${param.file.path}`, LOG_LEVEL_NOTICE);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (file instanceof TFolder) continue;
|
if (file instanceof TFolder) continue;
|
||||||
if (!await this.plugin.$$isTargetFile(file.path)) continue;
|
if (!await this.core.$$isTargetFile(file.path)) continue;
|
||||||
|
|
||||||
// Stop cache using to prevent the corruption;
|
// Stop cache using to prevent the corruption;
|
||||||
// let cache: null | string | ArrayBuffer;
|
// let cache: null | string | ArrayBuffer;
|
||||||
@@ -190,7 +190,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
|||||||
if (file instanceof TFile && (type == "CREATE" || type == "CHANGED")) {
|
if (file instanceof TFile && (type == "CREATE" || type == "CHANGED")) {
|
||||||
// Wait for a bit while to let the writer has marked `touched` at the file.
|
// Wait for a bit while to let the writer has marked `touched` at the file.
|
||||||
await delay(10);
|
await delay(10);
|
||||||
if (this.plugin.storageAccess.recentlyTouched(file)) {
|
if (this.core.storageAccess.recentlyTouched(file)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,11 +338,11 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
|||||||
const key = `file-last-proc-${queue.type}-${file.path}`;
|
const key = `file-last-proc-${queue.type}-${file.path}`;
|
||||||
const last = Number(await this.core.kvDB.get(key) || 0);
|
const last = Number(await this.core.kvDB.get(key) || 0);
|
||||||
if (queue.type == "INTERNAL" || file.isInternal) {
|
if (queue.type == "INTERNAL" || file.isInternal) {
|
||||||
await this.plugin.$anyProcessOptionalFileEvent(file.path as unknown as FilePath);
|
await this.core.$anyProcessOptionalFileEvent(file.path as unknown as FilePath);
|
||||||
} else {
|
} else {
|
||||||
// let mtime = file.stat.mtime;
|
// let mtime = file.stat.mtime;
|
||||||
if (queue.type == "DELETE") {
|
if (queue.type == "DELETE") {
|
||||||
await this.plugin.$anyHandlerProcessesFileEvent(queue);
|
await this.core.$anyHandlerProcessesFileEvent(queue);
|
||||||
} else {
|
} else {
|
||||||
if (file.stat.mtime == last) {
|
if (file.stat.mtime == last) {
|
||||||
Logger(`File has been already scanned on ${queue.type}, skip: ${file.path}`, LOG_LEVEL_VERBOSE);
|
Logger(`File has been already scanned on ${queue.type}, skip: ${file.path}`, LOG_LEVEL_VERBOSE);
|
||||||
@@ -350,7 +350,7 @@ export class StorageEventManagerObsidian extends StorageEventManager {
|
|||||||
// this.cancelRelativeEvent(queue);
|
// this.cancelRelativeEvent(queue);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!await this.plugin.$anyHandlerProcessesFileEvent(queue)) {
|
if (!await this.core.$anyHandlerProcessesFileEvent(queue)) {
|
||||||
Logger(`STORAGE -> DB: Handler failed, cancel the relative operations: ${file.path}`, LOG_LEVEL_INFO);
|
Logger(`STORAGE -> DB: Handler failed, cancel the relative operations: ${file.path}`, LOG_LEVEL_INFO);
|
||||||
// cancel running queues and remove one of atomic operation (e.g. rename)
|
// cancel running queues and remove one of atomic operation (e.g. rename)
|
||||||
this.cancelRelativeEvent(queue);
|
this.cancelRelativeEvent(queue);
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
|||||||
}
|
}
|
||||||
|
|
||||||
async $$initializeDatabase(showingNotice: boolean = false, reopenDatabase = true): Promise<boolean> {
|
async $$initializeDatabase(showingNotice: boolean = false, reopenDatabase = true): Promise<boolean> {
|
||||||
this.core.isReady = false;
|
this.core.$$resetIsReady();
|
||||||
if ((!reopenDatabase) || await this.core.$$openDatabase()) {
|
if ((!reopenDatabase) || await this.core.$$openDatabase()) {
|
||||||
if (this.localDatabase.isReady) {
|
if (this.localDatabase.isReady) {
|
||||||
await this.core.$$performFullScan(showingNotice);
|
await this.core.$$performFullScan(showingNotice);
|
||||||
@@ -316,12 +316,12 @@ export class ModuleInitializerFile extends AbstractModule implements ICoreModule
|
|||||||
this._log(`Initializing database has been failed on some module`, LOG_LEVEL_NOTICE);
|
this._log(`Initializing database has been failed on some module`, LOG_LEVEL_NOTICE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.core.isReady = true;
|
this.core.$$markIsReady();
|
||||||
// run queued event once.
|
// run queued event once.
|
||||||
await this.core.$everyCommitPendingFileEvent();
|
await this.core.$everyCommitPendingFileEvent();
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
this.core.isReady = false;
|
this.core.$$resetIsReady();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,10 +33,14 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
authHeader = reactive(() =>
|
authHeader = reactive(() =>
|
||||||
this.authHeaderSource.value == "" ? "" : "Basic " + window.btoa(this.authHeaderSource.value));
|
this.authHeaderSource.value == "" ? "" : "Basic " + window.btoa(this.authHeaderSource.value));
|
||||||
|
|
||||||
|
last_successful_post = false;
|
||||||
$$customFetchHandler(): ObsHttpHandler {
|
$$customFetchHandler(): ObsHttpHandler {
|
||||||
if (!this._customHandler) this._customHandler = new ObsHttpHandler(undefined, undefined);
|
if (!this._customHandler) this._customHandler = new ObsHttpHandler(undefined, undefined);
|
||||||
return this._customHandler;
|
return this._customHandler;
|
||||||
}
|
}
|
||||||
|
$$getLastPostFailedBySize(): boolean {
|
||||||
|
return !this.last_successful_post;
|
||||||
|
}
|
||||||
|
|
||||||
async $$connectRemoteCouchDB(uri: string, auth: { username: string; password: string }, disableRequestURI: boolean, passphrase: string | false, useDynamicIterationCount: boolean, performSetup: boolean, skipInfo: boolean, compression: boolean): Promise<string | { db: PouchDB.Database<EntryDoc>; info: PouchDB.Core.DatabaseInfo }> {
|
async $$connectRemoteCouchDB(uri: string, auth: { username: string; password: string }, disableRequestURI: boolean, passphrase: string | false, useDynamicIterationCount: boolean, performSetup: boolean, skipInfo: boolean, compression: boolean): Promise<string | { db: PouchDB.Database<EntryDoc>; info: PouchDB.Core.DatabaseInfo }> {
|
||||||
if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid";
|
if (!isValidRemoteCouchDBURI(uri)) return "Remote URI is not valid";
|
||||||
@@ -62,7 +66,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
if (opts_length > 1000 * 1000 * 10) {
|
if (opts_length > 1000 * 1000 * 10) {
|
||||||
// over 10MB
|
// over 10MB
|
||||||
if (isCloudantURI(uri)) {
|
if (isCloudantURI(uri)) {
|
||||||
this.plugin.last_successful_post = false;
|
this.last_successful_post = false;
|
||||||
this._log("This request should fail on IBM Cloudant.", LOG_LEVEL_VERBOSE);
|
this._log("This request should fail on IBM Cloudant.", LOG_LEVEL_VERBOSE);
|
||||||
throw new Error("This request should fail on IBM Cloudant.");
|
throw new Error("This request should fail on IBM Cloudant.");
|
||||||
}
|
}
|
||||||
@@ -91,9 +95,9 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
|
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
|
||||||
const r = await fetchByAPI(requestParam);
|
const r = await fetchByAPI(requestParam);
|
||||||
if (method == "POST" || method == "PUT") {
|
if (method == "POST" || method == "PUT") {
|
||||||
this.plugin.last_successful_post = r.status - (r.status % 100) == 200;
|
this.last_successful_post = r.status - (r.status % 100) == 200;
|
||||||
} else {
|
} else {
|
||||||
this.plugin.last_successful_post = true;
|
this.last_successful_post = true;
|
||||||
}
|
}
|
||||||
this._log(`HTTP:${method}${size} to:${localURL} -> ${r.status}`, LOG_LEVEL_DEBUG);
|
this._log(`HTTP:${method}${size} to:${localURL} -> ${r.status}`, LOG_LEVEL_DEBUG);
|
||||||
|
|
||||||
@@ -106,7 +110,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
this._log(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL_VERBOSE);
|
this._log(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL_VERBOSE);
|
||||||
// limit only in bulk_docs.
|
// limit only in bulk_docs.
|
||||||
if (url.toString().indexOf("_bulk_docs") !== -1) {
|
if (url.toString().indexOf("_bulk_docs") !== -1) {
|
||||||
this.plugin.last_successful_post = false;
|
this.last_successful_post = false;
|
||||||
}
|
}
|
||||||
this._log(ex);
|
this._log(ex);
|
||||||
throw ex;
|
throw ex;
|
||||||
@@ -123,9 +127,9 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
|
this.plugin.requestCount.value = this.plugin.requestCount.value + 1;
|
||||||
const response: Response = await fetch(url, opts);
|
const response: Response = await fetch(url, opts);
|
||||||
if (method == "POST" || method == "PUT") {
|
if (method == "POST" || method == "PUT") {
|
||||||
this.plugin.last_successful_post = response.ok;
|
this.last_successful_post = response.ok;
|
||||||
} else {
|
} else {
|
||||||
this.plugin.last_successful_post = true;
|
this.last_successful_post = true;
|
||||||
}
|
}
|
||||||
this._log(`HTTP:${method}${size} to:${localURL} -> ${response.status}`, LOG_LEVEL_DEBUG);
|
this._log(`HTTP:${method}${size} to:${localURL} -> ${response.status}`, LOG_LEVEL_DEBUG);
|
||||||
if (Math.floor(response.status / 100) !== 2) {
|
if (Math.floor(response.status / 100) !== 2) {
|
||||||
@@ -148,7 +152,7 @@ export class ModuleObsidianAPI extends AbstractObsidianModule implements IObsidi
|
|||||||
this._log(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL_VERBOSE);
|
this._log(`HTTP:${method}${size} to:${localURL} -> failed`, LOG_LEVEL_VERBOSE);
|
||||||
// limit only in bulk_docs.
|
// limit only in bulk_docs.
|
||||||
if (url.toString().indexOf("_bulk_docs") !== -1) {
|
if (url.toString().indexOf("_bulk_docs") !== -1) {
|
||||||
this.plugin.last_successful_post = false;
|
this.last_successful_post = false;
|
||||||
}
|
}
|
||||||
this._log(ex);
|
this._log(ex);
|
||||||
throw ex;
|
throw ex;
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
|||||||
$everyOnloadStart(): Promise<boolean> {
|
$everyOnloadStart(): Promise<boolean> {
|
||||||
// this.registerEvent(this.app.workspace.on("editor-change", ));
|
// this.registerEvent(this.app.workspace.on("editor-change", ));
|
||||||
this.plugin.registerEvent(this.app.vault.on("rename", (file, oldPath) => {
|
this.plugin.registerEvent(this.app.vault.on("rename", (file, oldPath) => {
|
||||||
eventHub.emitEvent(EVENT_FILE_RENAMED, { newPath: file.path, old: oldPath });
|
eventHub.emitEvent(EVENT_FILE_RENAMED, { newPath: file.path as FilePathWithPrefix, old: oldPath as FilePathWithPrefix });
|
||||||
}));
|
}));
|
||||||
this.plugin.registerEvent(this.app.workspace.on("active-leaf-change", () => eventHub.emitEvent(EVENT_LEAF_ACTIVE_CHANGED)));
|
this.plugin.registerEvent(this.app.workspace.on("active-leaf-change", () => eventHub.emitEvent(EVENT_LEAF_ACTIVE_CHANGED)));
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
@@ -41,7 +41,7 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
|||||||
this.initialCallback = save;
|
this.initialCallback = save;
|
||||||
saveCommandDefinition.callback = () => {
|
saveCommandDefinition.callback = () => {
|
||||||
scheduleTask("syncOnEditorSave", 250, () => {
|
scheduleTask("syncOnEditorSave", 250, () => {
|
||||||
if (this.plugin._unloaded) {
|
if (this.core.$$isUnloaded()) {
|
||||||
this._log("Unload and remove the handler.", LOG_LEVEL_VERBOSE);
|
this._log("Unload and remove the handler.", LOG_LEVEL_VERBOSE);
|
||||||
saveCommandDefinition.callback = this.initialCallback;
|
saveCommandDefinition.callback = this.initialCallback;
|
||||||
this.initialCallback = undefined;
|
this.initialCallback = undefined;
|
||||||
@@ -98,14 +98,14 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
|||||||
// TODO:FIXME AT V0.17.31, this logic has been disabled.
|
// TODO:FIXME AT V0.17.31, this logic has been disabled.
|
||||||
if (navigator.onLine && this.localDatabase.needScanning) {
|
if (navigator.onLine && this.localDatabase.needScanning) {
|
||||||
this.localDatabase.needScanning = false;
|
this.localDatabase.needScanning = false;
|
||||||
await this.plugin.$$performFullScan();
|
await this.core.$$performFullScan();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async watchWindowVisibilityAsync() {
|
async watchWindowVisibilityAsync() {
|
||||||
if (this.settings.suspendFileWatching) return;
|
if (this.settings.suspendFileWatching) return;
|
||||||
if (!this.settings.isConfigured) return;
|
if (!this.settings.isConfigured) return;
|
||||||
if (!this.plugin.isReady) return;
|
if (!this.core.$$isReady()) return;
|
||||||
|
|
||||||
if (this.isLastHidden && !this.hasFocus) {
|
if (this.isLastHidden && !this.hasFocus) {
|
||||||
// NO OP while non-focused after made hidden;
|
// NO OP while non-focused after made hidden;
|
||||||
@@ -124,7 +124,7 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
|||||||
await this.core.$everyBeforeSuspendProcess();
|
await this.core.$everyBeforeSuspendProcess();
|
||||||
} else {
|
} else {
|
||||||
// suspend all temporary.
|
// suspend all temporary.
|
||||||
if (this.plugin.suspended) return;
|
if (this.core.$$isSuspended()) return;
|
||||||
if (!this.hasFocus) return;
|
if (!this.hasFocus) return;
|
||||||
await this.core.$everyOnResumeProcess();
|
await this.core.$everyOnResumeProcess();
|
||||||
await this.core.$everyAfterResumeProcess();
|
await this.core.$everyAfterResumeProcess();
|
||||||
@@ -133,7 +133,7 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
|||||||
watchWorkspaceOpen(file: TFile | null) {
|
watchWorkspaceOpen(file: TFile | null) {
|
||||||
if (this.settings.suspendFileWatching) return;
|
if (this.settings.suspendFileWatching) return;
|
||||||
if (!this.settings.isConfigured) return;
|
if (!this.settings.isConfigured) return;
|
||||||
if (!this.plugin.isReady) return;
|
if (!this.core.$$isReady()) return;
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
scheduleTask("watch-workspace-open", 500, () => fireAndForget(() => this.watchWorkspaceOpenAsync(file)));
|
scheduleTask("watch-workspace-open", 500, () => fireAndForget(() => this.watchWorkspaceOpenAsync(file)));
|
||||||
}
|
}
|
||||||
@@ -141,12 +141,12 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
|||||||
async watchWorkspaceOpenAsync(file: TFile) {
|
async watchWorkspaceOpenAsync(file: TFile) {
|
||||||
if (this.settings.suspendFileWatching) return;
|
if (this.settings.suspendFileWatching) return;
|
||||||
if (!this.settings.isConfigured) return;
|
if (!this.settings.isConfigured) return;
|
||||||
if (!this.plugin.isReady) return;
|
if (!this.core.$$isReady()) return;
|
||||||
await this.core.$everyCommitPendingFileEvent();
|
await this.core.$everyCommitPendingFileEvent();
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this.settings.syncOnFileOpen && !this.plugin.suspended) {
|
if (this.settings.syncOnFileOpen && !this.core.$$isSuspended()) {
|
||||||
await this.core.$$replicate();
|
await this.core.$$replicate();
|
||||||
}
|
}
|
||||||
await this.core.$$queueConflictCheckIfOpen(file.path as FilePathWithPrefix);
|
await this.core.$$queueConflictCheckIfOpen(file.path as FilePathWithPrefix);
|
||||||
@@ -160,7 +160,7 @@ export class ModuleObsidianEvents extends AbstractObsidianModule implements IObs
|
|||||||
|
|
||||||
|
|
||||||
$$askReload(message?: string) {
|
$$askReload(message?: string) {
|
||||||
if (this.core.isReloadingScheduled) {
|
if (this.core.$$isReloadingScheduled()) {
|
||||||
this._log(`Reloading is already scheduled`, LOG_LEVEL_VERBOSE);
|
this._log(`Reloading is already scheduled`, LOG_LEVEL_VERBOSE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.addRibbonIcon("replicate", "Replicate", async () => {
|
this.addRibbonIcon("replicate", "Replicate", async () => {
|
||||||
await this.plugin.$$replicate(true);
|
await this.core.$$replicate(true);
|
||||||
}).addClass("livesync-ribbon-replicate");
|
}).addClass("livesync-ribbon-replicate");
|
||||||
|
|
||||||
|
|
||||||
@@ -27,14 +27,14 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
|
|||||||
id: "livesync-replicate",
|
id: "livesync-replicate",
|
||||||
name: "Replicate now",
|
name: "Replicate now",
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
await this.plugin.$$replicate();
|
await this.core.$$replicate();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "livesync-dump",
|
id: "livesync-dump",
|
||||||
name: "Dump information of this doc ",
|
name: "Dump information of this doc ",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
const file = this.plugin.$$getActiveFilePath();
|
const file = this.core.$$getActiveFilePath();
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
fireAndForget(() => this.localDatabase.getDBEntry(file, {}, true, false));
|
fireAndForget(() => this.localDatabase.getDBEntry(file, {}, true, false));
|
||||||
},
|
},
|
||||||
@@ -45,7 +45,7 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
|
|||||||
editorCallback: (editor: Editor, view: MarkdownView | MarkdownFileInfo) => {
|
editorCallback: (editor: Editor, view: MarkdownView | MarkdownFileInfo) => {
|
||||||
const file = view.file;
|
const file = view.file;
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
void this.plugin.$$queueConflictCheckIfOpen(file.path as FilePathWithPrefix);
|
void this.core.$$queueConflictCheckIfOpen(file.path as FilePathWithPrefix);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -60,23 +60,23 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
|
|||||||
this.settings.liveSync = true;
|
this.settings.liveSync = true;
|
||||||
this._log("LiveSync Enabled.", LOG_LEVEL_NOTICE);
|
this._log("LiveSync Enabled.", LOG_LEVEL_NOTICE);
|
||||||
}
|
}
|
||||||
await this.plugin.realizeSettingSyncMode();
|
await this.core.$$realizeSettingSyncMode();
|
||||||
await this.plugin.saveSettings();
|
await this.core.$$saveSettingData();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "livesync-suspendall",
|
id: "livesync-suspendall",
|
||||||
name: "Toggle All Sync.",
|
name: "Toggle All Sync.",
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
if (this.plugin.suspended) {
|
if (this.core.$$isSuspended()) {
|
||||||
this.plugin.suspended = false;
|
this.core.$$setSuspended(false);
|
||||||
this._log("Self-hosted LiveSync resumed", LOG_LEVEL_NOTICE);
|
this._log("Self-hosted LiveSync resumed", LOG_LEVEL_NOTICE);
|
||||||
} else {
|
} else {
|
||||||
this.plugin.suspended = true;
|
this.core.$$setSuspended(true);
|
||||||
this._log("Self-hosted LiveSync suspended", LOG_LEVEL_NOTICE);
|
this._log("Self-hosted LiveSync suspended", LOG_LEVEL_NOTICE);
|
||||||
}
|
}
|
||||||
await this.plugin.realizeSettingSyncMode();
|
await this.core.$$realizeSettingSyncMode();
|
||||||
await this.plugin.saveSettings();
|
await this.core.$$saveSettingData();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -84,7 +84,7 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
|
|||||||
id: "livesync-scan-files",
|
id: "livesync-scan-files",
|
||||||
name: "Scan storage and database again",
|
name: "Scan storage and database again",
|
||||||
callback: async () => {
|
callback: async () => {
|
||||||
await this.plugin.$$performFullScan(true)
|
await this.core.$$performFullScan(true)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -101,14 +101,14 @@ export class ModuleObsidianMenu extends AbstractObsidianModule implements IObsid
|
|||||||
id: "livesync-abortsync",
|
id: "livesync-abortsync",
|
||||||
name: "Abort synchronization immediately",
|
name: "Abort synchronization immediately",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
this.plugin.replicator.terminateSync();
|
this.core.replicator.terminateSync();
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
|
|
||||||
}
|
}
|
||||||
$everyOnload(): Promise<boolean> {
|
$everyOnload(): Promise<boolean> {
|
||||||
this.app.workspace.onLayoutReady(this.plugin.onLiveSyncReady.bind(this.plugin));
|
this.app.workspace.onLayoutReady(this.core.$$onLiveSyncReady.bind(this.core));
|
||||||
// eslint-disable-next-line no-unused-labels
|
// eslint-disable-next-line no-unused-labels
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
}
|
}
|
||||||
|
|||||||
13
src/modules/extraFeaturesObsidian/ModuleExtraSyncObsidian.ts
Normal file
13
src/modules/extraFeaturesObsidian/ModuleExtraSyncObsidian.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { AbstractObsidianModule, type IObsidianModule } from '../AbstractObsidianModule.ts';
|
||||||
|
|
||||||
|
export class ModuleExtraSyncObsidian extends AbstractObsidianModule implements IObsidianModule {
|
||||||
|
deviceAndVaultName: string = "";
|
||||||
|
|
||||||
|
$$getDeviceAndVaultName(): string {
|
||||||
|
return this.deviceAndVaultName;
|
||||||
|
}
|
||||||
|
$$setDeviceAndVaultName(name: string): void {
|
||||||
|
this.deviceAndVaultName = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -42,10 +42,10 @@ export class ModuleDev extends AbstractObsidianModule implements IObsidianModule
|
|||||||
toc: Set<string>,
|
toc: Set<string>,
|
||||||
stub: { [key: string]: { [key: string]: Map<string, Record<string, string>> } }
|
stub: { [key: string]: { [key: string]: Map<string, Record<string, string>> } }
|
||||||
};
|
};
|
||||||
eventHub.onEvent("document-stub-created", (e: CustomEvent<STUB>) => {
|
eventHub.onEvent("document-stub-created", (detail: STUB) => {
|
||||||
fireAndForget(async () => {
|
fireAndForget(async () => {
|
||||||
const stub = e.detail.stub;
|
const stub = detail.stub;
|
||||||
const toc = e.detail.toc;
|
const toc = detail.toc;
|
||||||
|
|
||||||
const stubDocX =
|
const stubDocX =
|
||||||
Object.entries(stub).map(([key, value]) => {
|
Object.entries(stub).map(([key, value]) => {
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ import { parseYaml, requestUrl, stringifyYaml } from "obsidian";
|
|||||||
import type { FilePath } from "../../lib/src/common/types.ts";
|
import type { FilePath } from "../../lib/src/common/types.ts";
|
||||||
import { scheduleTask } from "octagonal-wheels/concurrency/task";
|
import { scheduleTask } from "octagonal-wheels/concurrency/task";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface LSEvents {
|
||||||
|
"debug-sync-status": string[];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class ModuleReplicateTest extends AbstractObsidianModule implements IObsidianModule {
|
export class ModuleReplicateTest extends AbstractObsidianModule implements IObsidianModule {
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
isReady = true;
|
isReady = true;
|
||||||
// performTest();
|
// performTest();
|
||||||
|
|
||||||
eventHub.once(EVENT_LAYOUT_READY, async () => {
|
eventHub.onceEvent(EVENT_LAYOUT_READY, async () => {
|
||||||
if (await plugin.storageAccess.isExistsIncludeHidden("_AUTO_TEST.md")) {
|
if (await plugin.storageAccess.isExistsIncludeHidden("_AUTO_TEST.md")) {
|
||||||
new Notice("Auto test file found, running tests...");
|
new Notice("Auto test file found, running tests...");
|
||||||
fireAndForget(async () => {
|
fireAndForget(async () => {
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
$: resultLines = $results;
|
$: resultLines = $results;
|
||||||
|
|
||||||
let syncStatus = [] as string[];
|
let syncStatus = [] as string[];
|
||||||
eventHub.on<string[]>("debug-sync-status", (status) => {
|
eventHub.onEvent("debug-sync-status", (status) => {
|
||||||
syncStatus = [...status];
|
syncStatus = [...status];
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
import { diff_match_patch } from "../../../deps.ts";
|
import { diff_match_patch } from "../../../deps.ts";
|
||||||
import { DocumentHistoryModal } from "../DocumentHistory/DocumentHistoryModal.ts";
|
import { DocumentHistoryModal } from "../DocumentHistory/DocumentHistoryModal.ts";
|
||||||
import { isPlainText, stripAllPrefixes } from "../../../lib/src/string_and_binary/path.ts";
|
import { isPlainText, stripAllPrefixes } from "../../../lib/src/string_and_binary/path.ts";
|
||||||
import { TFile } from "../../../deps.ts";
|
|
||||||
import { getPath } from "../../../common/utils.ts";
|
import { getPath } from "../../../common/utils.ts";
|
||||||
export let plugin: ObsidianLiveSyncPlugin;
|
export let plugin: ObsidianLiveSyncPlugin;
|
||||||
|
|
||||||
@@ -105,9 +104,9 @@
|
|||||||
}
|
}
|
||||||
if (rev == docA._rev) {
|
if (rev == docA._rev) {
|
||||||
if (checkStorageDiff) {
|
if (checkStorageDiff) {
|
||||||
const isExist = await plugin.storageAccess.isExists(stripAllPrefixes(getPath(docA)));
|
const isExist = await plugin.storageAccess.isExistsIncludeHidden(stripAllPrefixes(getPath(docA)));
|
||||||
if (isExist) {
|
if (isExist) {
|
||||||
const data = await plugin.storageAccess.readFileAuto(stripAllPrefixes(getPath(docA)));
|
const data = await plugin.storageAccess.readHiddenFileBinary(stripAllPrefixes(getPath(docA)));
|
||||||
const d = readAsBlob(doc);
|
const d = readAsBlob(doc);
|
||||||
const result = await isDocContentSame(data, d);
|
const result = await isDocContentSame(data, d);
|
||||||
if (result) {
|
if (result) {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
|||||||
// In here, some merge has been processed.
|
// In here, some merge has been processed.
|
||||||
// So we have to run replication if configured.
|
// So we have to run replication if configured.
|
||||||
// TODO: Make this is as a event request
|
// TODO: Make this is as a event request
|
||||||
if (this.settings.syncAfterMerge && !this.plugin.suspended) {
|
if (this.settings.syncAfterMerge && !this.core.$$isSuspended()) {
|
||||||
await this.core.$$waitForReplicationOnce();
|
await this.core.$$waitForReplicationOnce();
|
||||||
}
|
}
|
||||||
// And, check it again.
|
// And, check it again.
|
||||||
@@ -96,7 +96,7 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
|||||||
this._log("There are no conflicted documents", LOG_LEVEL_NOTICE);
|
this._log("There are no conflicted documents", LOG_LEVEL_NOTICE);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const target = await this.plugin.confirm.askSelectString("File to resolve conflict", notesList);
|
const target = await this.core.confirm.askSelectString("File to resolve conflict", notesList);
|
||||||
if (target) {
|
if (target) {
|
||||||
const targetItem = notes.find(e => e.dispPath == target)!;
|
const targetItem = notes.find(e => e.dispPath == target)!;
|
||||||
await this.core.$$queueConflictCheck(targetItem.path);
|
await this.core.$$queueConflictCheck(targetItem.path);
|
||||||
@@ -114,7 +114,7 @@ export class ModuleInteractiveConflictResolver extends AbstractObsidianModule im
|
|||||||
notes.push({ path: getPath(doc), mtime: doc.mtime });
|
notes.push({ path: getPath(doc), mtime: doc.mtime });
|
||||||
}
|
}
|
||||||
if (notes.length > 0) {
|
if (notes.length > 0) {
|
||||||
this.plugin.confirm.askInPopup(`conflicting-detected-on-safety`, `Some files have been left conflicted! Press {HERE} to resolve them, or you can do it later by "Pick a file to resolve conflict`, (anchor) => {
|
this.core.confirm.askInPopup(`conflicting-detected-on-safety`, `Some files have been left conflicted! Press {HERE} to resolve them, or you can do it later by "Pick a file to resolve conflict`, (anchor) => {
|
||||||
anchor.text = "HERE";
|
anchor.text = "HERE";
|
||||||
anchor.addEventListener("click", () => {
|
anchor.addEventListener("click", () => {
|
||||||
fireAndForget(() => this.allConflictCheck())
|
fireAndForget(() => this.allConflictCheck())
|
||||||
|
|||||||
@@ -67,12 +67,12 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
|||||||
})
|
})
|
||||||
return computed(() => formatted.value);
|
return computed(() => formatted.value);
|
||||||
}
|
}
|
||||||
const labelReplication = padLeftSpComputed(this.plugin.replicationResultCount, `📥`);
|
const labelReplication = padLeftSpComputed(this.core.replicationResultCount, `📥`);
|
||||||
const labelDBCount = padLeftSpComputed(this.plugin.databaseQueueCount, `📄`);
|
const labelDBCount = padLeftSpComputed(this.core.databaseQueueCount, `📄`);
|
||||||
const labelStorageCount = padLeftSpComputed(this.plugin.storageApplyingCount, `💾`);
|
const labelStorageCount = padLeftSpComputed(this.core.storageApplyingCount, `💾`);
|
||||||
const labelChunkCount = padLeftSpComputed(collectingChunks, `🧩`);
|
const labelChunkCount = padLeftSpComputed(collectingChunks, `🧩`);
|
||||||
const labelPluginScanCount = padLeftSpComputed(pluginScanningCount, `🔌`);
|
const labelPluginScanCount = padLeftSpComputed(pluginScanningCount, `🔌`);
|
||||||
const labelConflictProcessCount = padLeftSpComputed(this.plugin.conflictProcessQueueCount, `🔩`);
|
const labelConflictProcessCount = padLeftSpComputed(this.core.conflictProcessQueueCount, `🔩`);
|
||||||
const hiddenFilesCount = reactive(() => hiddenFilesEventCount.value + hiddenFilesProcessingCount.value);
|
const hiddenFilesCount = reactive(() => hiddenFilesEventCount.value + hiddenFilesProcessingCount.value);
|
||||||
const labelHiddenFilesCount = padLeftSpComputed(hiddenFilesCount, `⚙️`)
|
const labelHiddenFilesCount = padLeftSpComputed(hiddenFilesCount, `⚙️`)
|
||||||
const queueCountLabelX = reactive(() => {
|
const queueCountLabelX = reactive(() => {
|
||||||
@@ -81,12 +81,12 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
|||||||
const queueCountLabel = () => queueCountLabelX.value;
|
const queueCountLabel = () => queueCountLabelX.value;
|
||||||
|
|
||||||
const requestingStatLabel = computed(() => {
|
const requestingStatLabel = computed(() => {
|
||||||
const diff = this.plugin.requestCount.value - this.plugin.responseCount.value;
|
const diff = this.core.requestCount.value - this.core.responseCount.value;
|
||||||
return diff != 0 ? "📲 " : "";
|
return diff != 0 ? "📲 " : "";
|
||||||
})
|
})
|
||||||
|
|
||||||
const replicationStatLabel = computed(() => {
|
const replicationStatLabel = computed(() => {
|
||||||
const e = this.plugin.replicationStat.value;
|
const e = this.core.replicationStat.value;
|
||||||
const sent = e.sent;
|
const sent = e.sent;
|
||||||
const arrived = e.arrived;
|
const arrived = e.arrived;
|
||||||
const maxPullSeq = e.maxPullSeq;
|
const maxPullSeq = e.maxPullSeq;
|
||||||
@@ -128,9 +128,9 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
|||||||
}
|
}
|
||||||
return { w, sent, pushLast, arrived, pullLast };
|
return { w, sent, pushLast, arrived, pullLast };
|
||||||
})
|
})
|
||||||
const labelProc = padLeftSpComputed(this.plugin.processing, `⏳`);
|
const labelProc = padLeftSpComputed(this.core.processing, `⏳`);
|
||||||
const labelPend = padLeftSpComputed(this.plugin.totalQueued, `🛫`);
|
const labelPend = padLeftSpComputed(this.core.totalQueued, `🛫`);
|
||||||
const labelInBatchDelay = padLeftSpComputed(this.plugin.batched, `📬`);
|
const labelInBatchDelay = padLeftSpComputed(this.core.batched, `📬`);
|
||||||
const waitingLabel = computed(() => {
|
const waitingLabel = computed(() => {
|
||||||
return `${labelProc()}${labelPend()}${labelInBatchDelay()}`;
|
return `${labelProc()}${labelPend()}${labelInBatchDelay()}`;
|
||||||
})
|
})
|
||||||
@@ -144,7 +144,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
|||||||
};
|
};
|
||||||
})
|
})
|
||||||
const statusBarLabels = reactive(() => {
|
const statusBarLabels = reactive(() => {
|
||||||
const scheduleMessage = this.plugin.isReloadingScheduled ? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n` : "";
|
const scheduleMessage = this.core.$$isReloadingScheduled() ? `WARNING! RESTARTING OBSIDIAN IS SCHEDULED!\n` : "";
|
||||||
const { message } = statusLineLabel();
|
const { message } = statusLineLabel();
|
||||||
const status = scheduleMessage + this.statusLog.value;
|
const status = scheduleMessage + this.statusLog.value;
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
|||||||
const thisFile = this.app.workspace.getActiveFile();
|
const thisFile = this.app.workspace.getActiveFile();
|
||||||
if (!thisFile) return "";
|
if (!thisFile) return "";
|
||||||
// Case Sensitivity
|
// Case Sensitivity
|
||||||
if (this.core.shouldCheckCaseInsensitive) {
|
if (this.core.$$shouldCheckCaseInsensitive()) {
|
||||||
const f = this.core.storageAccess.getFiles().map(e => e.path).filter(e => e.toLowerCase() == thisFile.path.toLowerCase());
|
const f = this.core.storageAccess.getFiles().map(e => e.path).filter(e => e.toLowerCase() == thisFile.path.toLowerCase());
|
||||||
if (f.length > 1) return "Not synchronised: There are multiple files with the same name";
|
if (f.length > 1) return "Not synchronised: There are multiple files with the same name";
|
||||||
}
|
}
|
||||||
@@ -274,7 +274,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
|||||||
}
|
}
|
||||||
$everyOnloadAfterLoadSettings(): Promise<boolean> {
|
$everyOnloadAfterLoadSettings(): Promise<boolean> {
|
||||||
logStore.pipeTo(new QueueProcessor(logs => logs.forEach(e => this.core.$$addLog(e.message, e.level, e.key)), { suspended: false, batchSize: 20, concurrentLimit: 1, delay: 0 })).startPipeline();
|
logStore.pipeTo(new QueueProcessor(logs => logs.forEach(e => this.core.$$addLog(e.message, e.level, e.key)), { suspended: false, batchSize: 20, concurrentLimit: 1, delay: 0 })).startPipeline();
|
||||||
eventHub.onEvent(EVENT_FILE_RENAMED, (evt: CustomEvent<{ oldPath: string, newPath: string }>) => {
|
eventHub.onEvent(EVENT_FILE_RENAMED, (data) => {
|
||||||
void this.setFileStatus();
|
void this.setFileStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -290,7 +290,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
|||||||
this.logHistory = this.statusDiv.createDiv({ cls: "livesync-status-loghistory" });
|
this.logHistory = this.statusDiv.createDiv({ cls: "livesync-status-loghistory" });
|
||||||
eventHub.onEvent(EVENT_LAYOUT_READY, () => this.adjustStatusDivPosition());
|
eventHub.onEvent(EVENT_LAYOUT_READY, () => this.adjustStatusDivPosition());
|
||||||
if (this.settings?.showStatusOnStatusbar) {
|
if (this.settings?.showStatusOnStatusbar) {
|
||||||
this.statusBar = this.plugin.addStatusBarItem();
|
this.statusBar = this.core.addStatusBarItem();
|
||||||
this.statusBar.addClass("syncstatusbar");
|
this.statusBar.addClass("syncstatusbar");
|
||||||
}
|
}
|
||||||
this.adjustStatusDivPosition();
|
this.adjustStatusDivPosition();
|
||||||
@@ -318,7 +318,7 @@ export class ModuleLog extends AbstractObsidianModule implements IObsidianModule
|
|||||||
if (this.settings && !this.settings.showVerboseLog && level == LOG_LEVEL_VERBOSE) {
|
if (this.settings && !this.settings.showVerboseLog && level == LOG_LEVEL_VERBOSE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const vaultName = this.plugin.$$getVaultName();
|
const vaultName = this.core.$$getVaultName();
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
const timestamp = now.toLocaleString();
|
const timestamp = now.toLocaleString();
|
||||||
const messageContent = typeof message == "string" ? message : message instanceof Error ? `${message.name}:${message.message}` : JSON.stringify(message, null, 2);
|
const messageContent = typeof message == "string" ? message : message instanceof Error ? `${message.name}:${message.message}` : JSON.stringify(message, null, 2);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { type TFile } from "obsidian";
|
import { type TFile } from "obsidian";
|
||||||
import { eventHub, EVENT_REQUEST_SHOW_HISTORY } from "../../common/events.ts";
|
import { eventHub } from "../../common/events.ts";
|
||||||
|
import { EVENT_REQUEST_SHOW_HISTORY } from "../../common/obsidianEvents.ts";
|
||||||
import type { FilePathWithPrefix, LoadedEntry, DocumentID } from "../../lib/src/common/types.ts";
|
import type { FilePathWithPrefix, LoadedEntry, DocumentID } from "../../lib/src/common/types.ts";
|
||||||
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
import { AbstractObsidianModule, type IObsidianModule } from "../AbstractObsidianModule.ts";
|
||||||
import { DocumentHistoryModal } from "./DocumentHistory/DocumentHistoryModal.ts";
|
import { DocumentHistoryModal } from "./DocumentHistory/DocumentHistoryModal.ts";
|
||||||
@@ -29,7 +30,7 @@ export class ModuleObsidianDocumentHistory extends AbstractObsidianModule implem
|
|||||||
fireAndForget(async () => await this.fileHistory());
|
fireAndForget(async () => await this.fileHistory());
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
eventHub.on(EVENT_REQUEST_SHOW_HISTORY, ({ file, fileOnDB }: { file: TFile, fileOnDB: LoadedEntry }) => {
|
eventHub.onEvent(EVENT_REQUEST_SHOW_HISTORY, ({ file, fileOnDB }: { file: TFile | FilePathWithPrefix, fileOnDB: LoadedEntry }) => {
|
||||||
this.showHistory(file, fileOnDB._id);
|
this.showHistory(file, fileOnDB._id);
|
||||||
})
|
})
|
||||||
return Promise.resolve(true);
|
return Promise.resolve(true);
|
||||||
@@ -46,7 +47,7 @@ export class ModuleObsidianDocumentHistory extends AbstractObsidianModule implem
|
|||||||
}
|
}
|
||||||
notes.sort((a, b) => b.mtime - a.mtime);
|
notes.sort((a, b) => b.mtime - a.mtime);
|
||||||
const notesList = notes.map(e => e.dispPath);
|
const notesList = notes.map(e => e.dispPath);
|
||||||
const target = await this.plugin.confirm.askSelectString("File to view History", notesList);
|
const target = await this.core.confirm.askSelectString("File to view History", notesList);
|
||||||
if (target) {
|
if (target) {
|
||||||
const targetId = notes.find(e => e.dispPath == target)!;
|
const targetId = notes.find(e => e.dispPath == target)!;
|
||||||
this.showHistory(targetId.path, targetId.id);
|
this.showHistory(targetId.path, targetId.id);
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
|||||||
const methods: Record<ConfigPassphraseStore, (() => Promise<string | false>)> = {
|
const methods: Record<ConfigPassphraseStore, (() => Promise<string | false>)> = {
|
||||||
"": () => Promise.resolve("*"),
|
"": () => Promise.resolve("*"),
|
||||||
"LOCALSTORAGE": () => Promise.resolve(localStorage.getItem("ls-setting-passphrase") ?? false),
|
"LOCALSTORAGE": () => Promise.resolve(localStorage.getItem("ls-setting-passphrase") ?? false),
|
||||||
"ASK_AT_LAUNCH": () => this.plugin.confirm.askString("Passphrase", "passphrase", "")
|
"ASK_AT_LAUNCH": () => this.core.confirm.askString("Passphrase", "passphrase", "")
|
||||||
}
|
}
|
||||||
const method = settings.configPassphraseStore;
|
const method = settings.configPassphraseStore;
|
||||||
const methodFunc = method in methods ? methods[method] : methods[""];
|
const methodFunc = method in methods ? methods[method] : methods[""];
|
||||||
@@ -20,8 +20,8 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
$$saveDeviceAndVaultName(): void {
|
$$saveDeviceAndVaultName(): void {
|
||||||
const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.plugin.$$getVaultName();
|
const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.core.$$getVaultName();
|
||||||
localStorage.setItem(lsKey, this.plugin.deviceAndVaultName || "");
|
localStorage.setItem(lsKey, this.core.$$getDeviceAndVaultName() || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
usedPassphrase = "";
|
usedPassphrase = "";
|
||||||
@@ -64,7 +64,7 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
|||||||
}
|
}
|
||||||
|
|
||||||
async $$saveSettingData() {
|
async $$saveSettingData() {
|
||||||
this.plugin.$$saveDeviceAndVaultName();
|
this.core.$$saveDeviceAndVaultName();
|
||||||
const settings = { ...this.settings };
|
const settings = { ...this.settings };
|
||||||
settings.deviceAndVaultName = "";
|
settings.deviceAndVaultName = "";
|
||||||
if (this.usedPassphrase == "" && !await this.getPassphrase(settings)) {
|
if (this.usedPassphrase == "" && !await this.getPassphrase(settings)) {
|
||||||
@@ -182,11 +182,11 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
|||||||
// So, use history is always enabled.
|
// So, use history is always enabled.
|
||||||
this.settings.useHistory = true;
|
this.settings.useHistory = true;
|
||||||
|
|
||||||
const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.plugin.$$getVaultName();
|
const lsKey = "obsidian-live-sync-vaultanddevicename-" + this.core.$$getVaultName();
|
||||||
if (this.settings.deviceAndVaultName != "") {
|
if (this.settings.deviceAndVaultName != "") {
|
||||||
if (!localStorage.getItem(lsKey)) {
|
if (!localStorage.getItem(lsKey)) {
|
||||||
this.core.deviceAndVaultName = this.settings.deviceAndVaultName;
|
this.core.$$setDeviceAndVaultName(this.settings.deviceAndVaultName);
|
||||||
localStorage.setItem(lsKey, this.core.deviceAndVaultName);
|
this.$$saveDeviceAndVaultName();
|
||||||
this.settings.deviceAndVaultName = "";
|
this.settings.deviceAndVaultName = "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,8 +194,8 @@ export class ModuleObsidianSettings extends AbstractObsidianModule implements IO
|
|||||||
this._log("Configuration verification founds problems with your configuration. This has been fixed automatically. But you may already have data that cannot be synchronised. If this is the case, please rebuild everything.", LOG_LEVEL_NOTICE)
|
this._log("Configuration verification founds problems with your configuration. This has been fixed automatically. But you may already have data that cannot be synchronised. If this is the case, please rebuild everything.", LOG_LEVEL_NOTICE)
|
||||||
this.settings.customChunkSize = 0;
|
this.settings.customChunkSize = 0;
|
||||||
}
|
}
|
||||||
this.core.deviceAndVaultName = localStorage.getItem(lsKey) || "";
|
this.core.$$setDeviceAndVaultName(localStorage.getItem(lsKey) || "");
|
||||||
if (this.core.deviceAndVaultName == "") {
|
if (this.core.$$getDeviceAndVaultName() == "") {
|
||||||
if (this.settings.usePluginSync) {
|
if (this.settings.usePluginSync) {
|
||||||
this._log("Device name is not set. Plug-in sync has been disabled.", LOG_LEVEL_NOTICE);
|
this._log("Device name is not set. Plug-in sync has been disabled.", LOG_LEVEL_NOTICE);
|
||||||
this.settings.usePluginSync = false;
|
this.settings.usePluginSync = false;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
|
|||||||
return this.settings.settingSyncFile != "";
|
return this.settings.settingSyncFile != "";
|
||||||
}
|
}
|
||||||
fireAndForget(async () => {
|
fireAndForget(async () => {
|
||||||
await this.plugin.$$saveSettingData();
|
await this.core.$$saveSettingData();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -38,13 +38,12 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
eventHub.on("event-file-changed", (info: {
|
eventHub.onEvent("event-file-changed", (info: {
|
||||||
file: FilePathWithPrefix, automated: boolean
|
file: FilePathWithPrefix, automated: boolean
|
||||||
}) => {
|
}) => {
|
||||||
fireAndForget(() => this.checkAndApplySettingFromMarkdown(info.file, info.automated));
|
fireAndForget(() => this.checkAndApplySettingFromMarkdown(info.file, info.automated));
|
||||||
});
|
});
|
||||||
eventHub.onEvent(EVENT_SETTING_SAVED, (evt: CustomEvent<ObsidianLiveSyncSettings>) => {
|
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
|
||||||
const settings = evt.detail;
|
|
||||||
if (settings.settingSyncFile != "") {
|
if (settings.settingSyncFile != "") {
|
||||||
fireAndForget(() => this.saveSettingToMarkdown(settings.settingSyncFile));
|
fireAndForget(() => this.saveSettingToMarkdown(settings.settingSyncFile));
|
||||||
}
|
}
|
||||||
@@ -123,7 +122,7 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
const addMsg = this.settings.settingSyncFile != filename ? " (This is not-active file)" : "";
|
const addMsg = this.settings.settingSyncFile != filename ? " (This is not-active file)" : "";
|
||||||
this.plugin.confirm.askInPopup("apply-setting-from-md", `Setting markdown ${filename}${addMsg} has been detected. Apply this from {HERE}.`, (anchor) => {
|
this.core.confirm.askInPopup("apply-setting-from-md", `Setting markdown ${filename}${addMsg} has been detected. Apply this from {HERE}.`, (anchor) => {
|
||||||
anchor.text = "HERE";
|
anchor.text = "HERE";
|
||||||
anchor.addEventListener("click", () => {
|
anchor.addEventListener("click", () => {
|
||||||
fireAndForget(async () => {
|
fireAndForget(async () => {
|
||||||
@@ -132,26 +131,26 @@ export class ModuleObsidianSettingsAsMarkdown extends AbstractObsidianModule imp
|
|||||||
const APPLY_AND_REBUILD = "Apply settings and restart obsidian with red_flag_rebuild.md";
|
const APPLY_AND_REBUILD = "Apply settings and restart obsidian with red_flag_rebuild.md";
|
||||||
const APPLY_AND_FETCH = "Apply settings and restart obsidian with red_flag_fetch.md";
|
const APPLY_AND_FETCH = "Apply settings and restart obsidian with red_flag_fetch.md";
|
||||||
const CANCEL = "Cancel";
|
const CANCEL = "Cancel";
|
||||||
const result = await this.plugin.confirm.askSelectStringDialogue("Ready for apply the setting.", [
|
const result = await this.core.confirm.askSelectStringDialogue("Ready for apply the setting.", [
|
||||||
APPLY_AND_RESTART,
|
APPLY_AND_RESTART,
|
||||||
APPLY_ONLY,
|
APPLY_ONLY,
|
||||||
APPLY_AND_FETCH,
|
APPLY_AND_FETCH,
|
||||||
APPLY_AND_REBUILD,
|
APPLY_AND_REBUILD,
|
||||||
CANCEL], { defaultAction: APPLY_AND_RESTART });
|
CANCEL], { defaultAction: APPLY_AND_RESTART });
|
||||||
if (result == APPLY_ONLY || result == APPLY_AND_RESTART || result == APPLY_AND_REBUILD || result == APPLY_AND_FETCH) {
|
if (result == APPLY_ONLY || result == APPLY_AND_RESTART || result == APPLY_AND_REBUILD || result == APPLY_AND_FETCH) {
|
||||||
this.plugin.settings = settingToApply;
|
this.core.settings = settingToApply;
|
||||||
await this.plugin.$$saveSettingData();
|
await this.core.$$saveSettingData();
|
||||||
if (result == APPLY_ONLY) {
|
if (result == APPLY_ONLY) {
|
||||||
this._log("Loaded settings have been applied!", LOG_LEVEL_NOTICE);
|
this._log("Loaded settings have been applied!", LOG_LEVEL_NOTICE);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (result == APPLY_AND_REBUILD) {
|
if (result == APPLY_AND_REBUILD) {
|
||||||
await this.plugin.rebuilder.scheduleRebuild();
|
await this.core.rebuilder.scheduleRebuild();
|
||||||
}
|
}
|
||||||
if (result == APPLY_AND_FETCH) {
|
if (result == APPLY_AND_FETCH) {
|
||||||
await this.plugin.rebuilder.scheduleFetch();
|
await this.core.rebuilder.scheduleFetch();
|
||||||
}
|
}
|
||||||
this.plugin.$$performRestart();
|
this.core.$$performRestart();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -20,11 +20,12 @@ import { Semaphore } from "octagonal-wheels/concurrency/semaphore";
|
|||||||
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
|
import { LiveSyncSetting as Setting } from "./LiveSyncSetting.ts";
|
||||||
import { fireAndForget, yieldNextAnimationFrame } from "octagonal-wheels/promises";
|
import { fireAndForget, yieldNextAnimationFrame } from "octagonal-wheels/promises";
|
||||||
import { confirmWithMessage } from "../../coreObsidian/UILib/dialogs.ts";
|
import { confirmWithMessage } from "../../coreObsidian/UILib/dialogs.ts";
|
||||||
import { EVENT_REQUEST_COPY_SETUP_URI, EVENT_REQUEST_OPEN_PLUGIN_SYNC_DIALOG, EVENT_REQUEST_OPEN_SETUP_URI, EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_REQUEST_SHOW_HISTORY, eventHub } from "../../../common/events.ts";
|
import { EVENT_REQUEST_COPY_SETUP_URI, EVENT_REQUEST_OPEN_PLUGIN_SYNC_DIALOG, EVENT_REQUEST_OPEN_SETUP_URI, EVENT_REQUEST_RELOAD_SETTING_TAB, eventHub } from "../../../common/events.ts";
|
||||||
import { skipIfDuplicated } from "octagonal-wheels/concurrency/lock";
|
import { skipIfDuplicated } from "octagonal-wheels/concurrency/lock";
|
||||||
import { JournalSyncMinio } from "../../../lib/src/replication/journal/objectstore/JournalSyncMinio.ts";
|
import { JournalSyncMinio } from "../../../lib/src/replication/journal/objectstore/JournalSyncMinio.ts";
|
||||||
import { ICHeader, ICXHeader, PSCHeader } from "../../../common/types.ts";
|
import { ICHeader, ICXHeader, PSCHeader } from "../../../common/types.ts";
|
||||||
import { HiddenFileSync } from "../../../features/HiddenFileSync/CmdHiddenFileSync.ts";
|
import { HiddenFileSync } from "../../../features/HiddenFileSync/CmdHiddenFileSync.ts";
|
||||||
|
import { EVENT_REQUEST_SHOW_HISTORY } from "../../../common/obsidianEvents.ts";
|
||||||
|
|
||||||
export type OnUpdateResult = {
|
export type OnUpdateResult = {
|
||||||
visibility?: boolean,
|
visibility?: boolean,
|
||||||
@@ -162,7 +163,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
return await Promise.resolve();
|
return await Promise.resolve();
|
||||||
}
|
}
|
||||||
if (key == "deviceAndVaultName") {
|
if (key == "deviceAndVaultName") {
|
||||||
this.plugin.deviceAndVaultName = this.editingSettings?.[key] ?? "";
|
this.plugin.$$setDeviceAndVaultName(this.editingSettings?.[key] ?? "");
|
||||||
this.plugin.$$saveDeviceAndVaultName();
|
this.plugin.$$saveDeviceAndVaultName();
|
||||||
return await Promise.resolve();
|
return await Promise.resolve();
|
||||||
}
|
}
|
||||||
@@ -230,7 +231,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
const ret = { ...OnDialogSettingsDefault };
|
const ret = { ...OnDialogSettingsDefault };
|
||||||
ret.configPassphrase = localStorage.getItem("ls-setting-passphrase") || "";
|
ret.configPassphrase = localStorage.getItem("ls-setting-passphrase") || "";
|
||||||
ret.preset = ""
|
ret.preset = ""
|
||||||
ret.deviceAndVaultName = this.plugin.deviceAndVaultName;
|
ret.deviceAndVaultName = this.plugin.$$getDeviceAndVaultName();
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
computeAllLocalSettings(): Partial<OnDialogSettings> {
|
computeAllLocalSettings(): Partial<OnDialogSettings> {
|
||||||
@@ -304,7 +305,7 @@ export class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
super(app, plugin);
|
super(app, plugin);
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
Setting.env = this;
|
Setting.env = this;
|
||||||
eventHub.on(EVENT_REQUEST_RELOAD_SETTING_TAB, () => {
|
eventHub.onEvent(EVENT_REQUEST_RELOAD_SETTING_TAB, () => {
|
||||||
this.requestReload();
|
this.requestReload();
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -710,7 +711,7 @@ Store only the settings. **Caution: This may lead to data corruption**; database
|
|||||||
const replicator = this.plugin.$anyNewReplicator(settingForCheck);
|
const replicator = this.plugin.$anyNewReplicator(settingForCheck);
|
||||||
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return true;
|
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return true;
|
||||||
|
|
||||||
const db = await replicator.connectRemoteCouchDBWithSetting(settingForCheck, this.plugin.isMobile, true);
|
const db = await replicator.connectRemoteCouchDBWithSetting(settingForCheck, this.plugin.$$isMobile(), true);
|
||||||
if (typeof db === "string") {
|
if (typeof db === "string") {
|
||||||
Logger(`ERROR: Failed to check passphrase with the remote server: \n${db}.`, LOG_LEVEL_NOTICE);
|
Logger(`ERROR: Failed to check passphrase with the remote server: \n${db}.`, LOG_LEVEL_NOTICE);
|
||||||
return false;
|
return false;
|
||||||
@@ -1187,7 +1188,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
|
|
||||||
|
|
||||||
void addPanel(paneEl, "CouchDB", undefined, onlyOnCouchDB).then(paneEl => {
|
void addPanel(paneEl, "CouchDB", undefined, onlyOnCouchDB).then(paneEl => {
|
||||||
if (this.plugin.isMobile) {
|
if (this.plugin.$$isMobile()) {
|
||||||
this.createEl(paneEl, "div", {
|
this.createEl(paneEl, "div", {
|
||||||
text: `Configured as using non-HTTPS. We cannot connect to the remote. Please set up the credentials and use HTTPS for the remote URI.`,
|
text: `Configured as using non-HTTPS. We cannot connect to the remote. Please set up the credentials and use HTTPS for the remote URI.`,
|
||||||
}, undefined, visibleOnly(() => !this.editingSettings.couchDB_URI.startsWith("https://")))
|
}, undefined, visibleOnly(() => !this.editingSettings.couchDB_URI.startsWith("https://")))
|
||||||
@@ -1280,6 +1281,23 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
}).setClass("wizardHidden");
|
}).setClass("wizardHidden");
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
void addPanel(paneEl, "Fetch settings").then((paneEl) => {
|
||||||
|
new Setting(paneEl)
|
||||||
|
.setName("Fetch tweaks from the remote")
|
||||||
|
.setDesc("Fetch other necessary settings from already configured remote.")
|
||||||
|
.addButton((button) => button
|
||||||
|
.setButtonText("Fetch")
|
||||||
|
.setDisabled(false)
|
||||||
|
.onClick(async () => {
|
||||||
|
const trialSetting = { ...this.initialSettings, ...this.editingSettings, };
|
||||||
|
const newTweaks = await this.plugin.$$checkAndAskUseRemoteConfiguration(trialSetting);
|
||||||
|
if (newTweaks.result !== false) {
|
||||||
|
this.editingSettings = { ...this.editingSettings, ...newTweaks.result };
|
||||||
|
this.requestUpdate();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
new Setting(paneEl)
|
new Setting(paneEl)
|
||||||
.setClass("wizardOnly")
|
.setClass("wizardOnly")
|
||||||
.addButton((button) => button
|
.addButton((button) => button
|
||||||
@@ -1313,6 +1331,16 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
} else {
|
} else {
|
||||||
this.editingSettings = { ...this.editingSettings, ...PREFERRED_SETTING_SELF_HOSTED };
|
this.editingSettings = { ...this.editingSettings, ...PREFERRED_SETTING_SELF_HOSTED };
|
||||||
}
|
}
|
||||||
|
if (await this.plugin.confirm.askYesNoDialog("Do you want to fetch the tweaks from the remote?", { defaultOption: "Yes", title: "Fetch tweaks" }) == "yes") {
|
||||||
|
const trialSetting = { ...this.initialSettings, ...this.editingSettings, };
|
||||||
|
const newTweaks = await this.plugin.$$checkAndAskUseRemoteConfiguration(trialSetting);
|
||||||
|
if (newTweaks.result !== false) {
|
||||||
|
this.editingSettings = { ...this.editingSettings, ...newTweaks.result };
|
||||||
|
this.requestUpdate();
|
||||||
|
} else {
|
||||||
|
// Messages should be already shown.
|
||||||
|
}
|
||||||
|
}
|
||||||
changeDisplay("30")
|
changeDisplay("30")
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
@@ -1360,7 +1388,8 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
}).addButton(button => {
|
}).addButton(button => {
|
||||||
button.setButtonText("Apply");
|
button.setButtonText("Apply");
|
||||||
button.onClick(async () => {
|
button.onClick(async () => {
|
||||||
await this.saveSettings(["preset"]);
|
// await this.saveSettings(["preset"]);
|
||||||
|
await this.saveAllDirtySettings();
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1416,7 +1445,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
if (!this.editingSettings.isConfigured) {
|
if (!this.editingSettings.isConfigured) {
|
||||||
this.editingSettings.isConfigured = true;
|
this.editingSettings.isConfigured = true;
|
||||||
await this.saveAllDirtySettings();
|
await this.saveAllDirtySettings();
|
||||||
await this.plugin.realizeSettingSyncMode();
|
await this.plugin.$$realizeSettingSyncMode();
|
||||||
await rebuildDB("localOnly");
|
await rebuildDB("localOnly");
|
||||||
// this.resetEditingSettings();
|
// this.resetEditingSettings();
|
||||||
if (await this.plugin.confirm.askYesNoDialog(
|
if (await this.plugin.confirm.askYesNoDialog(
|
||||||
@@ -1430,13 +1459,13 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
await confirmRebuild();
|
await confirmRebuild();
|
||||||
} else {
|
} else {
|
||||||
await this.saveAllDirtySettings();
|
await this.saveAllDirtySettings();
|
||||||
await this.plugin.realizeSettingSyncMode();
|
await this.plugin.$$realizeSettingSyncMode();
|
||||||
this.plugin.$$askReload();
|
this.plugin.$$askReload();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await this.saveAllDirtySettings();
|
await this.saveAllDirtySettings();
|
||||||
await this.plugin.realizeSettingSyncMode();
|
await this.plugin.$$realizeSettingSyncMode();
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -1471,7 +1500,7 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
}
|
}
|
||||||
await this.saveSettings(["liveSync", "periodicReplication"]);
|
await this.saveSettings(["liveSync", "periodicReplication"]);
|
||||||
|
|
||||||
await this.plugin.realizeSettingSyncMode();
|
await this.plugin.$$realizeSettingSyncMode();
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -1546,6 +1575,8 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
});
|
});
|
||||||
|
|
||||||
void addPanel(paneEl, "Sync settings via markdown", undefined, undefined, LEVEL_ADVANCED).then((paneEl) => {
|
void addPanel(paneEl, "Sync settings via markdown", undefined, undefined, LEVEL_ADVANCED).then((paneEl) => {
|
||||||
|
paneEl.addClass("wizardHidden");
|
||||||
|
|
||||||
new Setting(paneEl)
|
new Setting(paneEl)
|
||||||
.autoWireText("settingSyncFile", { holdValue: true })
|
.autoWireText("settingSyncFile", { holdValue: true })
|
||||||
.addApplyButton(["settingSyncFile"])
|
.addApplyButton(["settingSyncFile"])
|
||||||
@@ -1569,7 +1600,6 @@ However, your report is needed to stabilise this. I appreciate you for your grea
|
|||||||
const hiddenFileSyncSettingEl = hiddenFileSyncSetting.settingEl
|
const hiddenFileSyncSettingEl = hiddenFileSyncSetting.settingEl
|
||||||
const hiddenFileSyncSettingDiv = hiddenFileSyncSettingEl.createDiv("");
|
const hiddenFileSyncSettingDiv = hiddenFileSyncSettingEl.createDiv("");
|
||||||
hiddenFileSyncSettingDiv.innerText = this.editingSettings.syncInternalFiles ? LABEL_ENABLED : LABEL_DISABLED;
|
hiddenFileSyncSettingDiv.innerText = this.editingSettings.syncInternalFiles ? LABEL_ENABLED : LABEL_DISABLED;
|
||||||
|
|
||||||
if (this.editingSettings.syncInternalFiles) {
|
if (this.editingSettings.syncInternalFiles) {
|
||||||
new Setting(paneEl)
|
new Setting(paneEl)
|
||||||
.setName("Disable Hidden files sync")
|
.setName("Disable Hidden files sync")
|
||||||
@@ -1979,7 +2009,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
.split(",").filter(e => e).map(e => new RegExp(e, "i"));
|
.split(",").filter(e => e).map(e => new RegExp(e, "i"));
|
||||||
this.plugin.localDatabase.hashCaches.clear();
|
this.plugin.localDatabase.hashCaches.clear();
|
||||||
Logger("Start verifying all files", LOG_LEVEL_NOTICE, "verify");
|
Logger("Start verifying all files", LOG_LEVEL_NOTICE, "verify");
|
||||||
const files = await this.plugin.storageAccess.getFilesIncludeHidden("/", undefined, ignorePatterns)
|
const files = this.plugin.settings.syncInternalFiles ? (await this.plugin.storageAccess.getFilesIncludeHidden("/", undefined, ignorePatterns)) : (await this.plugin.storageAccess.getFileNames());
|
||||||
const documents = [] as FilePath[];
|
const documents = [] as FilePath[];
|
||||||
|
|
||||||
const adn = this.plugin.localDatabase.findAllDocs()
|
const adn = this.plugin.localDatabase.findAllDocs()
|
||||||
@@ -1987,6 +2017,7 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
const path = getPath(i);
|
const path = getPath(i);
|
||||||
if (path.startsWith(ICXHeader)) continue;
|
if (path.startsWith(ICXHeader)) continue;
|
||||||
if (path.startsWith(PSCHeader)) continue;
|
if (path.startsWith(PSCHeader)) continue;
|
||||||
|
if (!this.plugin.settings.syncInternalFiles && path.startsWith(ICHeader)) continue;
|
||||||
documents.push(stripAllPrefixes(path));
|
documents.push(stripAllPrefixes(path));
|
||||||
}
|
}
|
||||||
const allPaths = [
|
const allPaths = [
|
||||||
@@ -2648,9 +2679,9 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
|
|
||||||
async dryRunGC() {
|
async dryRunGC() {
|
||||||
await skipIfDuplicated("cleanup", async () => {
|
await skipIfDuplicated("cleanup", async () => {
|
||||||
const replicator = this.plugin.getReplicator();
|
const replicator = this.plugin.$$getReplicator();
|
||||||
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return;
|
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return;
|
||||||
const remoteDBConn = await replicator.connectRemoteCouchDBWithSetting(this.plugin.settings, this.plugin.isMobile)
|
const remoteDBConn = await replicator.connectRemoteCouchDBWithSetting(this.plugin.settings, this.plugin.$$isMobile())
|
||||||
if (typeof (remoteDBConn) == "string") {
|
if (typeof (remoteDBConn) == "string") {
|
||||||
Logger(remoteDBConn);
|
Logger(remoteDBConn);
|
||||||
return;
|
return;
|
||||||
@@ -2664,10 +2695,10 @@ ${stringifyYaml(pluginConfig)}`;
|
|||||||
async dbGC() {
|
async dbGC() {
|
||||||
// Lock the remote completely once.
|
// Lock the remote completely once.
|
||||||
await skipIfDuplicated("cleanup", async () => {
|
await skipIfDuplicated("cleanup", async () => {
|
||||||
const replicator = this.plugin.getReplicator();
|
const replicator = this.plugin.$$getReplicator();
|
||||||
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return;
|
if (!(replicator instanceof LiveSyncCouchDBReplicator)) return;
|
||||||
await this.plugin.getReplicator().markRemoteLocked(this.plugin.settings, true, true);
|
await this.plugin.$$getReplicator().markRemoteLocked(this.plugin.settings, true, true);
|
||||||
const remoteDBConnection = await replicator.connectRemoteCouchDBWithSetting(this.plugin.settings, this.plugin.isMobile)
|
const remoteDBConnection = await replicator.connectRemoteCouchDBWithSetting(this.plugin.settings, this.plugin.$$isMobile())
|
||||||
if (typeof (remoteDBConnection) == "string") {
|
if (typeof (remoteDBConnection) == "string") {
|
||||||
Logger(remoteDBConnection);
|
Logger(remoteDBConnection);
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -327,8 +327,8 @@ export const SettingInformation: Partial<Record<keyof AllSettings, Configuration
|
|||||||
desc: "MB (0 to disable)."
|
desc: "MB (0 to disable)."
|
||||||
},
|
},
|
||||||
"usePluginSyncV2": {
|
"usePluginSyncV2": {
|
||||||
name: "Enable per-file-saved customization sync",
|
name: "Enable per-file customization sync",
|
||||||
desc: "If enabled per-filed efficient customization sync will be used. We need a small migration when enabling this. And all devices should be updated to v0.23.18. Once we enabled this, we lost a compatibility with old versions."
|
desc: "If enabled, efficient per-file customization sync will be used. A minor migration is required when enabling this feature, and all devices must be updated to v0.23.18. Enabling this feature will result in losing compatibility with older versions."
|
||||||
},
|
},
|
||||||
"handleFilenameCaseSensitive": {
|
"handleFilenameCaseSensitive": {
|
||||||
name: "Handle files as Case-Sensitive",
|
name: "Handle files as Case-Sensitive",
|
||||||
|
|||||||
182
src/modules/main/ModuleLiveSyncMain.ts
Normal file
182
src/modules/main/ModuleLiveSyncMain.ts
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import { fireAndForget } from "octagonal-wheels/promises";
|
||||||
|
import { LOG_LEVEL_NOTICE, LOG_LEVEL_VERBOSE, VER, type ObsidianLiveSyncSettings } from "../../lib/src/common/types.ts";
|
||||||
|
import { EVENT_LAYOUT_READY, EVENT_PLUGIN_LOADED, EVENT_PLUGIN_UNLOADED, EVENT_REQUEST_RELOAD_SETTING_TAB, EVENT_SETTING_SAVED, eventHub } from "../../common/events.ts";
|
||||||
|
import { $f, setLang } from "../../lib/src/common/i18n.ts";
|
||||||
|
import { versionNumberString2Number } from "../../lib/src/string_and_binary/convert.ts";
|
||||||
|
import { cancelAllPeriodicTask, cancelAllTasks } from "octagonal-wheels/concurrency/task";
|
||||||
|
import { stopAllRunningProcessors } from "octagonal-wheels/concurrency/processor";
|
||||||
|
import { AbstractModule } from "../AbstractModule.ts";
|
||||||
|
import type { ICoreModule } from "../ModuleTypes.ts";
|
||||||
|
|
||||||
|
export class ModuleLiveSyncMain extends AbstractModule implements ICoreModule {
|
||||||
|
|
||||||
|
async $$onLiveSyncReady() {
|
||||||
|
if (!await this.core.$everyOnLayoutReady()) return;
|
||||||
|
eventHub.emitEvent(EVENT_LAYOUT_READY);
|
||||||
|
if (this.settings.suspendFileWatching || this.settings.suspendParseReplicationResult) {
|
||||||
|
const ANSWER_KEEP = "Keep this plug-in suspended";
|
||||||
|
const ANSWER_RESUME = "Resume and restart Obsidian";
|
||||||
|
const message = `Self-hosted LiveSync has been configured to ignore some events. Is this intentional for you?
|
||||||
|
|
||||||
|
| Type | Status | Note |
|
||||||
|
|:---:|:---:|---|
|
||||||
|
| Storage Events | ${this.settings.suspendFileWatching ? "suspended" : "active"} | Every modification will be ignored |
|
||||||
|
| Database Events | ${this.settings.suspendParseReplicationResult ? "suspended" : "active"} | Every synchronised change will be postponed |
|
||||||
|
|
||||||
|
Do you want to resume them and restart Obsidian?
|
||||||
|
|
||||||
|
> [!DETAILS]-
|
||||||
|
> These flags are set by the plug-in while rebuilding, or fetching. If the process ends abnormally, it may be kept unintended.
|
||||||
|
> If you are not sure, you can try to rerun these processes. Make sure to back your vault up.
|
||||||
|
`;
|
||||||
|
if (await this.core.confirm.askSelectStringDialogue(message, [ANSWER_KEEP, ANSWER_RESUME], { defaultAction: ANSWER_KEEP, title: "Scram Enabled" }) == ANSWER_RESUME) {
|
||||||
|
this.settings.suspendFileWatching = false;
|
||||||
|
this.settings.suspendParseReplicationResult = false;
|
||||||
|
await this.saveSettings();
|
||||||
|
await this.core.$$scheduleAppReload();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const isInitialized = await this.core.$$initializeDatabase(false, false);
|
||||||
|
if (!isInitialized) {
|
||||||
|
//TODO:stop all sync.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!await this.core.$everyOnFirstInitialize()) return;
|
||||||
|
await this.core.$$realizeSettingSyncMode();
|
||||||
|
fireAndForget(async () => {
|
||||||
|
this._log(`Additional safety scan..`, LOG_LEVEL_VERBOSE);
|
||||||
|
if (!await this.core.$allScanStat()) {
|
||||||
|
this._log(`Additional safety scan has been failed on some module`, LOG_LEVEL_NOTICE);
|
||||||
|
} else {
|
||||||
|
this._log(`Additional safety scan done`, LOG_LEVEL_VERBOSE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$$wireUpEvents(): void {
|
||||||
|
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
|
||||||
|
this.localDatabase.settings = settings;
|
||||||
|
setLang(settings.displayLanguage);
|
||||||
|
eventHub.emitEvent(EVENT_REQUEST_RELOAD_SETTING_TAB);
|
||||||
|
});
|
||||||
|
eventHub.onEvent(EVENT_SETTING_SAVED, (settings: ObsidianLiveSyncSettings) => {
|
||||||
|
fireAndForget(() => this.core.$$realizeSettingSyncMode());
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async $$onLiveSyncLoad(): Promise<void> {
|
||||||
|
this.$$wireUpEvents();
|
||||||
|
// debugger;
|
||||||
|
eventHub.emitEvent(EVENT_PLUGIN_LOADED, this.core);
|
||||||
|
this._log("loading plugin");
|
||||||
|
if (!await this.core.$everyOnloadStart()) {
|
||||||
|
this._log("Plugin initialising has been cancelled by some module", LOG_LEVEL_NOTICE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// this.addUIs();
|
||||||
|
//@ts-ignore
|
||||||
|
const manifestVersion: string = MANIFEST_VERSION || "0.0.0";
|
||||||
|
//@ts-ignore
|
||||||
|
const packageVersion: string = PACKAGE_VERSION || "0.0.0";
|
||||||
|
|
||||||
|
this._log($f`Self-hosted LiveSync${" v"}${manifestVersion} ${packageVersion}`);
|
||||||
|
await this.core.$$loadSettings();
|
||||||
|
if (!await this.core.$everyOnloadAfterLoadSettings()) {
|
||||||
|
this._log("Plugin initialising has been cancelled by some module", LOG_LEVEL_NOTICE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const lsKey = "obsidian-live-sync-ver" + this.core.$$getVaultName();
|
||||||
|
const last_version = localStorage.getItem(lsKey);
|
||||||
|
|
||||||
|
const lastVersion = ~~(versionNumberString2Number(manifestVersion) / 1000);
|
||||||
|
if (lastVersion > this.settings.lastReadUpdates && this.settings.isConfigured) {
|
||||||
|
this._log($f`You have some unread release notes! Please read them once!`, LOG_LEVEL_NOTICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
//@ts-ignore
|
||||||
|
if (this.isMobile) {
|
||||||
|
this.settings.disableRequestURI = true;
|
||||||
|
}
|
||||||
|
if (last_version && Number(last_version) < VER) {
|
||||||
|
this.settings.liveSync = false;
|
||||||
|
this.settings.syncOnSave = false;
|
||||||
|
this.settings.syncOnEditorSave = false;
|
||||||
|
this.settings.syncOnStart = false;
|
||||||
|
this.settings.syncOnFileOpen = false;
|
||||||
|
this.settings.syncAfterMerge = false;
|
||||||
|
this.settings.periodicReplication = false;
|
||||||
|
this.settings.versionUpFlash = $f`Self-hosted LiveSync has been upgraded and some behaviors have changed incompatibly. All automatic synchronization is now disabled temporary. Ensure that other devices are also upgraded, and enable synchronization again.`;
|
||||||
|
await this.saveSettings();
|
||||||
|
}
|
||||||
|
localStorage.setItem(lsKey, `${VER}`);
|
||||||
|
await this.core.$$openDatabase();
|
||||||
|
this.core.$$realizeSettingSyncMode = this.core.$$realizeSettingSyncMode.bind(this);
|
||||||
|
// this.$$parseReplicationResult = this.$$parseReplicationResult.bind(this);
|
||||||
|
// this.$$replicate = this.$$replicate.bind(this);
|
||||||
|
this.core.$$onLiveSyncReady = this.core.$$onLiveSyncReady.bind(this);
|
||||||
|
await this.core.$everyOnload();
|
||||||
|
await Promise.all(this.core.addOns.map(e => e.onload()));
|
||||||
|
}
|
||||||
|
|
||||||
|
async $$onLiveSyncUnload(): Promise<void> {
|
||||||
|
eventHub.emitEvent(EVENT_PLUGIN_UNLOADED);
|
||||||
|
await this.core.$allStartOnUnload();
|
||||||
|
cancelAllPeriodicTask();
|
||||||
|
cancelAllTasks();
|
||||||
|
stopAllRunningProcessors();
|
||||||
|
await this.core.$allOnUnload();
|
||||||
|
this._unloaded = true;
|
||||||
|
for (const addOn of this.core.addOns) {
|
||||||
|
addOn.onunload();
|
||||||
|
}
|
||||||
|
if (this.localDatabase != null) {
|
||||||
|
this.localDatabase.onunload();
|
||||||
|
if (this.core.replicator) {
|
||||||
|
this.core.replicator?.closeReplication();
|
||||||
|
}
|
||||||
|
await this.localDatabase.close();
|
||||||
|
}
|
||||||
|
this._log($f`unloading plugin`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async $$realizeSettingSyncMode(): Promise<void> {
|
||||||
|
await this.core.$everyBeforeSuspendProcess();
|
||||||
|
await this.core.$everyBeforeRealizeSetting();
|
||||||
|
this.localDatabase.refreshSettings();
|
||||||
|
await this.core.$everyCommitPendingFileEvent();
|
||||||
|
await this.core.$everyRealizeSettingSyncMode();
|
||||||
|
// disable all sync temporary.
|
||||||
|
if (this.core.$$isSuspended()) return;
|
||||||
|
await this.core.$everyOnResumeProcess();
|
||||||
|
await this.core.$everyAfterResumeProcess();
|
||||||
|
await this.core.$everyAfterRealizeSetting();
|
||||||
|
}
|
||||||
|
|
||||||
|
$$isReloadingScheduled(): boolean {
|
||||||
|
return this.core._totalProcessingCount !== undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
isReady = false;
|
||||||
|
|
||||||
|
$$isReady(): boolean { return this.isReady; }
|
||||||
|
|
||||||
|
$$markIsReady(): void { this.isReady = true; }
|
||||||
|
|
||||||
|
$$resetIsReady(): void { this.isReady = false; }
|
||||||
|
|
||||||
|
|
||||||
|
_suspended = false;
|
||||||
|
$$isSuspended(): boolean {
|
||||||
|
return this._suspended || !this.settings?.isConfigured;
|
||||||
|
}
|
||||||
|
$$setSuspended(value: boolean) {
|
||||||
|
this._suspended = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_unloaded = false;
|
||||||
|
$$isUnloaded(): boolean {
|
||||||
|
return this._unloaded;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
10
styles.css
10
styles.css
@@ -426,4 +426,14 @@ span.ls-mark-cr::after {
|
|||||||
background-color: rgba(var(--background-primary), 0.3);
|
background-color: rgba(var(--background-primary), 0.3);
|
||||||
backdrop-filter: blur(4px);
|
backdrop-filter: blur(4px);
|
||||||
border-radius: 30%;
|
border-radius: 30%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sls-dialogue-note-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sls-dialogue-note-countdown {
|
||||||
|
font-size: 0.8em;
|
||||||
}
|
}
|
||||||
97
updates.md
97
updates.md
@@ -1,68 +1,47 @@
|
|||||||
## 0.24.0 RC Release Note
|
## 0.24.0
|
||||||
|
|
||||||
**Note:** This will be rewritten with the stable release. I confess, before you take the time, this is quite long.
|
I know that we have been waiting for a long time. It is finally released!
|
||||||
|
|
||||||
Over the past three years since the inception of the plugin, various features have been implemented to address diverse user needs. This is so honourable and I am grateful for your years of support.
|
Over the past three years since the inception of the plugin, various features have been implemented to address diverse user needs. This is truly honourable, and I am grateful for your years of support. However, this process has led to an increasingly disorganised codebase, with features becoming entangled. Consequently, this has led to a situation where bugs can go unnoticed and resolving one issue may inadvertently introduce another.
|
||||||
However, However, this process has resulted in a codebase that has become increasingly disorganised, with features becoming entangled.
|
|
||||||
|
|
||||||
Consequently, this has led to a situation where bugs can go unnoticed or resolving one issue may inadvertently introduce another.
|
In 0.24.0, I reorganised the previously jumbled main codebase into clearly defined modules. Although I had assumed that the total size of the code would not increase, I discovered that it has in fact increased. While the complexity is still considerable, the refactoring has improved the clarity of the code's structure. Additionally, while testing the release candidates, we still found many bugs to fix, which helped to make this plug-in robust and stable. Therefore, we are now ready to use the updated plug-in, and in addition to that, proceed to the next step.
|
||||||
|
|
||||||
In 0.24.0, I reorganised the previously disjointed main codebase into clearly defined modules. Although I anticipated that the overall volume of code would not increase, I discovered that it has, in fact, expanded. While the complexity may still be considerable, the refactoring has enhanced clarity regarding the current structure of the code. (The next focus may involve a review of dependencies).
|
This is also the first step towards a fully-fledged-fancy LiveSync, not just a plug-in from Obsidian. Of course, it will still be a plug-in primarily and foremost, but this development marks a significant step towards the self-hosting concept.
|
||||||
|
|
||||||
Throughout this process, a significant number of bugs have been resolved. And it may be worth mentioning that these bugs may had given rise to other bugs. I kindly request that you verify whether your issues have been addressed. At least conflict resolution and related issues have improved significantly.
|
Finally, I would like to once again express my respect and gratitude to all of you. My gratitude extends to all of the dev testers! Your contributions have certainly made the plug-in robust and stable!
|
||||||
|
|
||||||
It is also the first step towards a fully-fledged-fancy LiveSync, not just a plug-in from Obsidian. Of course, it will still be a plug-in as a first class and foremost, but this development marks a significant step towards the self-hosting concept.
|
|
||||||
|
|
||||||
This dev release is very close to the beta version that I had previously indicated would not be released. As a result, I have faced challenges in maintaining the main branch while working on this dev release. Regrettably, I have not been able to make any commits to the main branch in the last three weeks. Thus, the dev branch will remain reserved for major changes only.
|
|
||||||
|
|
||||||
The Release Candidate will be available for a few days and will only be officially released once users, including myself, have confirmed that there are no issues.
|
|
||||||
|
|
||||||
Finally, I would like to once again express my respect and gratitude to all of you once again. Thank you for your interest in the development version. Your contributions and dedication are greatly appreciated through testing.
|
|
||||||
|
|
||||||
Thank you, and I hope your troubles will be resolved!
|
Thank you, and I hope your troubles will be resolved!
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 0.24.0.dev-rc7
|
## 0.24.1
|
||||||
|
|
||||||
### Fixed
|
#### Fixed
|
||||||
|
|
||||||
- Verifying files between the local database and storage is now working correctly.
|
- Vault History can show the correct information of match-or-not for each file and database even if it is a binary file.
|
||||||
|
- `Sync settings via markdown` is now hidden during the setup wizard.
|
||||||
|
- Verify and Fix will ignore the hidden files if the hidden file sync is disabled.
|
||||||
|
|
||||||
### New Features
|
#### New feature
|
||||||
|
|
||||||
- We can verify and resolve also the hidden files now.
|
- Now we can fetch the tweaks from the remote database while the setting dialogue and wizard are processing.
|
||||||
|
|
||||||
## 0.24.0.dev-rc6
|
#### Improved
|
||||||
|
|
||||||
### Fixed
|
- More things are moved to the modules.
|
||||||
|
- Includes the Main codebase. Now `main.ts` is almost stub.
|
||||||
|
- EventHub is now more robust and typesafe.
|
||||||
|
|
||||||
- We can resolve the conflict of the JSON file correctly now.
|
## 0.24.0
|
||||||
- This would be the final Release Candidate.
|
|
||||||
|
|
||||||
## 0.24.0.dev-rc5
|
|
||||||
|
|
||||||
### Improved
|
|
||||||
|
|
||||||
- A note relating to device names has been added to Customisation Sync on the setting dialogue.
|
|
||||||
- Logs of Hidden File Sync and Customisation Sync have been prefixed with the respective feature names.
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Hidden file sync is now working correctly.
|
|
||||||
- Customisation Sync is now working correctly together with hidden file sync
|
|
||||||
- No longer database suffix is stored in the setting sharing markdown.
|
|
||||||
|
|
||||||
## 0.24.0.dev-rc4
|
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- The welcome message is now more simple to encourage the use of the Setup-URI.
|
- The welcome message is now more simple to encourage the use of the Setup-URI.
|
||||||
- And the secondary message is also simpler to guide users to Minimal Setup.
|
- The secondary message is also simpler to guide users to Minimal Setup.
|
||||||
- But Setup-URI will be recommended again, due to its importance.
|
- But Setup-URI will be recommended again, due to its importance.
|
||||||
- These dialogues contain a link to the documentation which can be clicked.
|
- These dialogues contain a link to the documentation which can be clicked.
|
||||||
- The minimal setup is more minimal now. And, the setup is more user-friendly.
|
- The minimal setup is more minimal now. And, the setup is more user-friendly.
|
||||||
- Now the Configuration of the remote database is checked more robust, but we can ignore the warning and proceed with the setup.
|
- Now the Configuration of the remote database is checked more robustly, but we can ignore the warning and proceed with the setup.
|
||||||
- Before we are asked about each feature, we are asked if we want to use optional features in the first place.
|
- Before we are asked about each feature, we are asked if we want to use optional features in the first place.
|
||||||
- This is to prevent the user from being overwhelmed by the features.
|
- This is to prevent the user from being overwhelmed by the features.
|
||||||
- And made it clear that it is not recommended for new users.
|
- And made it clear that it is not recommended for new users.
|
||||||
@@ -73,45 +52,31 @@ Thank you, and I hope your troubles will be resolved!
|
|||||||
- Especially auto-closing dialogues are now explicitly labelled: `To stop the countdown, tap anywhere on the dialogue`.
|
- Especially auto-closing dialogues are now explicitly labelled: `To stop the countdown, tap anywhere on the dialogue`.
|
||||||
- Now if the is plugin configured to ignore some events, we will get a chance to fix it, in addition to the warning.
|
- Now if the is plugin configured to ignore some events, we will get a chance to fix it, in addition to the warning.
|
||||||
- And why that has happened is also explained in the dialogue.
|
- And why that has happened is also explained in the dialogue.
|
||||||
|
- A note relating to device names has been added to Customisation Sync on the setting dialogue.
|
||||||
|
- We can verify and resolve also the hidden files now.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- We can resolve the conflict of the JSON file correctly now.
|
||||||
|
- Verifying files between the local database and storage is now working correctly.
|
||||||
- While restarting the plug-in, the shown dialogues will be automatically closed to avoid unexpected behaviour.
|
- While restarting the plug-in, the shown dialogues will be automatically closed to avoid unexpected behaviour.
|
||||||
- Replicated documents that the local device has configured to ignore are now correctly ignored.
|
- Replicated documents that the local device has configured to ignore are now correctly ignored.
|
||||||
- The chunks of the document on the local device during the first transfer will be created correctly.
|
- The chunks of the document on the local device during the first transfer will be created correctly.
|
||||||
- And why we should create them is now explained in the dialogue.
|
- And why we should create them is now explained in the dialogue.
|
||||||
- If optional features have been enabled in the wizard, `Enable advanced features` will be toggled correctly.
|
- If optional features have been enabled in the wizard, `Enable advanced features` will be toggled correctly.
|
||||||
|
The hidden file sync is now working correctly. - Now the deletion of hidden files is correctly synchronised.
|
||||||
|
- Customisation Sync is now working correctly together with hidden file sync.
|
||||||
|
- No longer database suffix is stored in the setting sharing markdown.
|
||||||
|
- A fair number of bugs have been fixed.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Some default settings have been changed for easier new user experience.
|
- Some default settings have been changed for an easier new user experience.
|
||||||
- Preventing the meaningless migration of the settings.
|
- Preventing the meaningless migration of the settings.
|
||||||
|
|
||||||
### Tidied
|
|
||||||
|
|
||||||
- Commented-out codes have been gradually removed.
|
|
||||||
|
|
||||||
## 0.24.0.dev-rc3
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- No longer Missing Translation Warning is shown in the console.
|
|
||||||
- Fixed the issue where some functions were not working properly (`_` started functions).
|
|
||||||
|
|
||||||
## 0.24.0.dev-rc2
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- Some status icons is now shown correctly.
|
|
||||||
|
|
||||||
## 0.24.0-rc1
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- A fair numbers of bugs have been fixed.
|
|
||||||
|
|
||||||
### Tiding
|
### Tiding
|
||||||
|
|
||||||
- The codebase has been reorganised into clearly defined modules.
|
- The codebase has been reorganised into clearly defined modules.
|
||||||
|
- Commented-out codes have been gradually removed.
|
||||||
|
|
||||||
Older notes is in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md).
|
Older notes are in [updates_old.md](https://github.com/vrtmrz/obsidian-livesync/blob/main/updates_old.md).
|
||||||
|
|||||||
Reference in New Issue
Block a user