mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2025-12-23 14:41:28 +00:00
Fixed
- 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 feature
- Now we can fetch the tweaks from the remote database while the setting dialogue and wizard are processing.
Improved
- More things are moved to the modules.
- Includes the Main codebase. Now `main.ts` is almost stub.
- EventHub is now more robust and typesafe.
This commit is contained in:
@@ -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 };
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
@@ -504,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", () => {
|
||||||
@@ -676,13 +690,17 @@ 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 {
|
||||||
await this.plugin.storageAccess.triggerHiddenFile(storageFilePath);
|
await this.plugin.storageAccess.triggerHiddenFile(storageFilePath);
|
||||||
} 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);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this._log(`STORAGE <x- DB: ${storageFilePath}: deleted (hidden) Failed`);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
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
@@ -427,3 +427,13 @@ span.ls-mark-cr::after {
|
|||||||
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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user