diff --git a/README.md b/README.md index 755d96d..698872d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Self-hosted LiveSync + **Renamed from: obsidian-livesync** This is the obsidian plugin that enables livesync between multi-devices with self-hosted database. @@ -11,7 +12,7 @@ Community implementation, not compatible with official "Sync". **It's getting almost stable now, But Please make sure to back your vault up!** -Limitations: Folder deletion handling is not completed. +Limitations: ~~Folder deletion handling is not completed.~~ **It would work now.** ## This plugin enables.. @@ -77,7 +78,6 @@ Note: The figure is drawn as single-directional, between two devices. But everyt ![dedupe](images/2.png) - ## Cloudant Setup ### Creating an Instance @@ -105,7 +105,7 @@ Select Multitenant(it's the default) and the region as you like. ![step 8](instruction_images/cloudant_8.png) 7. In resource details, there's information to connect from self-hosted-livesync. - Copy the "External Endpoint(preferred)" address. (\*1) + Copy the "External Endpoint(preferred)" address. (\*1). We use this address later, with the database name. ![step 9](instruction_images/cloudant_9.png) ### CouchDB setup @@ -120,11 +120,13 @@ Select Multitenant(it's the default) and the region as you like. _NOTE: of course We want to set "app://obsidian.md" but it's not acceptable on Cloudant._ ![step 2](instruction_images/couchdb_2.png) -1. And open the "Databases" tab and hit the "Create Database" button. +1. And open the "Databases" tab and hit the "Create Database" button. Enter the name as you like (\*2) and Hit the "Create" button below. ![step 3](instruction_images/couchdb_3.png) -1. If the database was shown with joyful messages, then you can close this browser tab now. +1. If the database was shown with joyful messages, setup is almost done. + And, once you have confirmed that you can create a database, usullay there is no need to open this screen. + You can create a database from Self-hosted LiveSync. ![step 4](instruction_images/couchdb_4.png) ### Credentials Setup @@ -135,7 +137,7 @@ Select Multitenant(it's the default) and the region as you like. 1. The dialog to create a credential will be shown. type any name or leave it default, hit the "Add" button. ![step 2](instruction_images/credentials_2.png) - _NOTE: This "name" is not related to your username that uses in self-hosted-livesync._ + _NOTE: This "name" is not related to your username that uses in Self-hosted LiveSync._ 1. Back to "Service credentials", the new credential should be created. open details. @@ -145,16 +147,16 @@ Select Multitenant(it's the default) and the region as you like. follow the figure, it's "apikey-v2-2unu15184f7o8emr90xlqgkm2ncwhbltml6tgnjl9sd5"(\*3) and "c2c11651d75497fa3d3c486e4c8bdf27"(\*4) -### self-hosted-livesync setting +### Self-hosted LiveSync setting ![xx](instruction_images/obsidian_sync_1.png) example values. -| Items | Value | example | -| ------------------- | ----------- | --------------------------------------------------------------------------- | -| CouchDB Remote URI: | (\*1)/(\*2) | https://xxxxxxxxxxxxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud/sync-test | -| CouchDB Username | (\*3) | apikey-v2-2unu15184f7o8emr90xlqgkm2ncwhbltml6tgnjl9sd5 | -| CouchDB Password | (\*4) | c2c11651d75497fa3d3c486e4c8bdf27 | +| Items | Value | example | +| ------------------- | -------------------------------- | --------------------------------------------------------------------------- | +| CouchDB Remote URI: | (\*1)/(\*2) or any favorite name | https://xxxxxxxxxxxxxxxxx-bluemix.cloudantnosqldb.appdomain.cloud/sync-test | +| CouchDB Username | (\*3) | apikey-v2-2unu15184f7o8emr90xlqgkm2ncwhbltml6tgnjl9sd5 | +| CouchDB Password | (\*4) | c2c11651d75497fa3d3c486e4c8bdf27 | # License diff --git a/main.ts b/main.ts index f0538d2..f17738a 100644 --- a/main.ts +++ b/main.ts @@ -1,4 +1,4 @@ -import { App, debounce, Modal, Notice, Plugin, PluginSettingTab, Setting, TFile, addIcon, TFolder, normalizePath } from "obsidian"; +import { App, debounce, Modal, Notice, Plugin, PluginSettingTab, Setting, TFile, addIcon, TFolder, normalizePath, TAbstractFile } from "obsidian"; import { PouchDB } from "./pouchdb-browser-webpack/dist/pouchdb-browser"; import { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch"; import xxhash from "xxhash-wasm"; @@ -663,7 +663,7 @@ class LocalPouchDB { console.log("!" + v.id); } else { if (!v.id.startsWith("h:")) { - console.log("?" + v.id); + // console.log("?" + v.id); } } } @@ -1285,9 +1285,14 @@ export default class ObsidianLiveSyncPlugin extends Plugin { if (delay < 200) delay = 200; if (delay > 5000) delay = 5000; this.watchVaultChange = debounce(this.watchVaultChange.bind(this), delay, false); - this.watchVaultDelete = debounce(this.watchVaultDelete.bind(this), delay, false); - this.watchVaultRename = debounce(this.watchVaultRename.bind(this), delay, false); + // this.watchVaultDelete = debounce(this.watchVaultDelete.bind(this), delay, false); + // this.watchVaultRename = debounce(this.watchVaultRename.bind(this), delay, false); + + // this.watchVaultChange = this.watchVaultChange.bind(this); + this.watchVaultDelete = this.watchVaultDelete.bind(this); + this.watchVaultRename = this.watchVaultRename.bind(this); this.watchWorkspaceOpen = debounce(this.watchWorkspaceOpen.bind(this), delay, false); + this.watchWindowVisiblity = debounce(this.watchWindowVisiblity.bind(this), delay, false); this.registerWatchEvents(); this.parseReplicationResult = this.parseReplicationResult.bind(this); @@ -1409,16 +1414,19 @@ export default class ObsidianLiveSyncPlugin extends Plugin { } watchWindowVisiblity() { + this.watchWindowVisiblityAsync(); + } + async watchWindowVisiblityAsync() { if (this.settings.suspendFileWatching) return; let isHidden = document.hidden; if (isHidden) { this.localDatabase.closeReplication(); } else { if (this.settings.liveSync) { - this.localDatabase.openReplication(this.settings, true, false, this.parseReplicationResult); + await this.localDatabase.openReplication(this.settings, true, false, this.parseReplicationResult); } if (this.settings.syncOnStart) { - this.localDatabase.openReplication(this.settings, false, false, this.parseReplicationResult); + await this.localDatabase.openReplication(this.settings, false, false, this.parseReplicationResult); } } this.gcHook(); @@ -1426,35 +1434,82 @@ export default class ObsidianLiveSyncPlugin extends Plugin { watchWorkspaceOpen(file: TFile) { if (this.settings.suspendFileWatching) return; + this.watchWorkspaceOpenAsync(file); + } + async watchWorkspaceOpenAsync(file: TFile) { if (file == null) return; this.localDatabase.disposeHashCache(); - this.showIfConflicted(file); + await this.showIfConflicted(file); this.gcHook(); } watchVaultChange(file: TFile, ...args: any[]) { if (this.settings.suspendFileWatching) return; - this.updateIntoDB(file); + this.watchVaultChangeAsync(file, ...args); + } + batchFileChange: string[] = []; + async watchVaultChangeAsync(file: TFile, ...args: any[]) { + await this.updateIntoDB(file); this.gcHook(); } - watchVaultDelete(file: TFile & TFolder) { + watchVaultDelete(file: TFile | TFolder) { if (this.settings.suspendFileWatching) return; - if (file.children) { - //folder - this.deleteFolderOnDB(file); - // this.app.vault.delete(file); - } else { - this.deleteFromDB(file); + this.watchVaultDeleteAsync(file); + } + async watchVaultDeleteAsync(file: TFile | TFolder) { + if (file instanceof TFile) { + await this.deleteFromDB(file); + } else if (file instanceof TFolder) { + await this.deleteFolderOnDB(file); } this.gcHook(); } - watchVaultRename(file: TFile & TFolder, oldFile: any) { - if (this.settings.suspendFileWatching) return; - if (file.children) { - // this.renameFolder(file,oldFile); - Logger(`folder name changed:(this operation is not supported) ${file.path}`, LOG_LEVEL.NOTICE); + GetAllFilesRecursively(file: TAbstractFile): TFile[] { + if (file instanceof TFile) { + return [file]; + } else if (file instanceof TFolder) { + let result: TFile[] = []; + for (var v of file.children) { + result.push(...this.GetAllFilesRecursively(v)); + } + return result; } else { - this.updateIntoDB(file); - this.deleteFromDBbyPath(oldFile); + Logger(`Filetype error:${file.path}`, LOG_LEVEL.NOTICE); + throw new Error(`Filetype error:${file.path}`); + } + } + watchVaultRename(file: TFile | TFolder, oldFile: any) { + if (this.settings.suspendFileWatching) return; + this.watchVaultRenameAsync(file, oldFile); + } + getFilePath(file: TAbstractFile): string { + if (file instanceof TFolder) { + if (file.isRoot()) return ""; + return this.getFilePath(file.parent) + "/" + file.name; + } + if (file instanceof TFile) { + return this.getFilePath(file.parent) + "/" + file.name; + } + } + async watchVaultRenameAsync(file: TFile | TFolder, oldFile: any) { + Logger(`${oldFile} renamed to ${file.path}`, LOG_LEVEL.VERBOSE); + if (file instanceof TFolder) { + const newFiles = this.GetAllFilesRecursively(file); + // for guard edge cases. this won't happen and each file's event will be raise. + for (const i of newFiles) { + let newFilePath = normalizePath(this.getFilePath(i)); + let newFile = this.app.vault.getAbstractFileByPath(newFilePath); + if (newFile instanceof TFile) { + Logger(`save ${newFile.path} into db`); + await this.updateIntoDB(newFile); + } + } + Logger(`delete below ${oldFile} from db`); + await this.deleteFromDBbyPath(oldFile); + } else if (file instanceof TFile) { + Logger(`file save ${file.path} into db`); + await this.updateIntoDB(file); + Logger(`deleted ${oldFile} into db`); + await this.deleteFromDBbyPath(oldFile); } this.gcHook(); } diff --git a/manifest.json b/manifest.json index 0ecb60e..a1b44c6 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "obsidian-livesync", "name": "Self-hosted LiveSync", - "version": "0.1.11", + "version": "0.1.12", "minAppVersion": "0.9.12", "description": "Community implementation of self-hosted livesync. Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "author": "vorotamoroz", diff --git a/package-lock.json b/package-lock.json index 337612e..16f2479 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "obsidian-livesync", - "version": "0.1.11", + "version": "0.1.12", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "obsidian-livesync", - "version": "0.1.11", + "version": "0.1.12", "license": "MIT", "dependencies": { "diff-match-patch": "^1.0.5", diff --git a/package.json b/package.json index 20866f7..4e089c9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-livesync", - "version": "0.1.11", + "version": "0.1.12", "description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.", "main": "main.js", "scripts": {