brushup readme
add delete handling icon fix
No files matched your search
@@ -1,24 +1,100 @@
|
|||||||
# obsidian-livesync
|
# obsidian-livesync
|
||||||
|
|
||||||
This is the obsidian plugin that enables livesync between multi terminals.
|
This is the obsidian plugin that enables livesync between multi terminals.
|
||||||
|
|
||||||
<!-- screenshot/movie will coming. -->
|
<!-- screenshot/movie will coming. -->
|
||||||
|
|
||||||
**It's on the bleeding edge. Do not use for your "precious" Vault.**
|
**It's on the bleeding edge. Do not use for your "precious" Vault.**
|
||||||
|
|
||||||
## This plugin enables..
|
## This plugin enables..
|
||||||
- Live sync
|
|
||||||
- Self-Hosted data synchronization with conflict detection and resolving in Obsidian.
|
|
||||||
|
|
||||||
## how to use the experimental build
|
- Live sync
|
||||||
|
- Self-Hosted data synchronization with conflict detection and resolving in Obsidian.
|
||||||
|
|
||||||
1. download this repo and expand `[your-vault]/.obsidian/plugins/` (PC, Mac and Android will work)
|
## how to use the experimental build
|
||||||
1. enable obsidian livesync in the settings dialog.
|
|
||||||
1. If you use your self-hosted CouchDB, set your server's info.
|
|
||||||
1. or Use [IBM Cloudant](https://www.ibm.com/cloud/cloudant), take an account and enable **Cloudant** in [Catalog](https://cloud.ibm.com/catalog#services)
|
|
||||||
Note please choose "IAM and legacy credentials" for the Authentication method
|
|
||||||
Setup details are in Couldant Setup Section.
|
|
||||||
1. Setup LiveSync or SyncOnSave or SyncOnStart as you like.
|
|
||||||
|
|
||||||
### Cloudant Setup
|
1. download this repo and expand `[your-vault]/.obsidian/plugins/` (PC, Mac and Android will work)
|
||||||
**WIP**
|
1. enable obsidian livesync in the settings dialog.
|
||||||
|
1. If you use your self-hosted CouchDB, set your server's info.
|
||||||
|
1. or Use [IBM Cloudant](https://www.ibm.com/cloud/cloudant), take an account and enable **Cloudant** in [Catalog](https://cloud.ibm.com/catalog#services)
|
||||||
|
Note please choose "IAM and legacy credentials" for the Authentication method
|
||||||
|
Setup details are in Couldant Setup Section.
|
||||||
|
1. Setup LiveSync or SyncOnSave or SyncOnStart as you like.
|
||||||
|
|
||||||
|
## Cloudant Setup
|
||||||
|
|
||||||
|
### Creating an Instance
|
||||||
|
|
||||||
|
1. Hit the "Create Resource" button.
|
||||||
|

|
||||||
|
|
||||||
|
1. In IBM Cloud Catalog, search "Cloudant".
|
||||||
|

|
||||||
|
|
||||||
|
1. You can choise "Lite plan" in free.
|
||||||
|

|
||||||
|
|
||||||
|
Select Multitenant(it's the default) and the region as you like.
|
||||||
|
 3. Be sure to select "IAM and Legacy credentials" for "Authentication Method".
|
||||||
|

|
||||||
|
|
||||||
|
4. Select Lite and be sure to check the capacity.
|
||||||
|

|
||||||
|
|
||||||
|
5. And hit "Create" on the right panel.
|
||||||
|

|
||||||
|
|
||||||
|
6. When all of the above steps have been done, Open "Resource list" on the left pane. you can see the Cloudant instance in the "Service and software". Click it.
|
||||||
|

|
||||||
|
|
||||||
|
7. In resource details, there's information to connect from obsidian-livesync.
|
||||||
|
Copy the "External Endpoint(preferred)" address. <sup>(\*1)</sup>
|
||||||
|

|
||||||
|
|
||||||
|
### CouchDB setup
|
||||||
|
|
||||||
|
1. Hit the "Launch Dashboard" button, Cloudant dashboard will be shown.
|
||||||
|
Yes, it's almost CouchDB's fauxton.
|
||||||
|

|
||||||
|
|
||||||
|
1. First, you have to enable the CORS option.
|
||||||
|
Hit the Account menu and open the "CORS" tab.
|
||||||
|
Initially, "Origin Domains" is set to "Restrict to specific domains"., so set to "All domains(\*)"
|
||||||
|
_NOTE: of course We want to set "app://obsidian.md" but it's not acceptable on Cloudant._
|
||||||
|

|
||||||
|
|
||||||
|
1. And open the "Databases" tab and hit the "Create Database" button.
|
||||||
|
Enter the name as you like <sup>(\*2)</sup> and Hit the "Create" button below.
|
||||||
|

|
||||||
|
|
||||||
|
1. If the database was shown with joyful messages, then you can close this browser tab now.
|
||||||
|

|
||||||
|
|
||||||
|
### Credentials Setup
|
||||||
|
|
||||||
|
1. Back into IBM Cloud, Open the "Service credentials". You'll get an empty list, hit the "New credential" button.
|
||||||
|

|
||||||
|
|
||||||
|
1. The dialog to create a credential will be shown.
|
||||||
|
type any name or leave it default, hit the "Add" button.
|
||||||
|

|
||||||
|
_NOTE: This "name" is not related to your username that uses in Obsidian-livesync._
|
||||||
|
|
||||||
|
1. Back to "Service credentials", the new credential should be created.
|
||||||
|
open details.
|
||||||
|

|
||||||
|
The username and password pair is inside this JSON.
|
||||||
|
"username" and "password" are so.
|
||||||
|
follow the figure, it's
|
||||||
|
"apikey-v2-2unu15184f7o8emr90xlqgkm2ncwhbltml6tgnjl9sd5"<sup>(\*3)</sup> and "c2c11651d75497fa3d3c486e4c8bdf27"<sup>(\*4)</sup>
|
||||||
|
|
||||||
|
### obsidian-livesync setting
|
||||||
|
|
||||||
|

|
||||||
|
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 |
|
||||||
|
After Width: | Height: | Size: 90 KiB |
|
After Width: | Height: | Size: 36 KiB |
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
After Width: | Height: | Size: 37 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -2319,6 +2319,27 @@ const escapeStringToHTML = (str) => {
|
|||||||
return escape[match];
|
return escape[match];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const isValidRemoteCouchDBURI = (uri) => {
|
||||||
|
if (uri.startsWith("https://"))
|
||||||
|
return true;
|
||||||
|
if (uri.startsWith("http://"))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
const connectRemoteCouchDB = (uri, auth) => __awaiter(void 0, void 0, void 0, function* () {
|
||||||
|
if (!isValidRemoteCouchDBURI(uri))
|
||||||
|
;
|
||||||
|
let db = new o(uri, {
|
||||||
|
auth,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
let info = yield db.info();
|
||||||
|
return { db: db, info: info };
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
});
|
||||||
//<--Functions
|
//<--Functions
|
||||||
class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -2327,15 +2348,22 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
//---> Sync
|
//---> Sync
|
||||||
this.syncHandler = null;
|
this.syncHandler = null;
|
||||||
}
|
}
|
||||||
addLog(message) {
|
addLog(message, isNotify) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
let timestamp = new Date().toLocaleString();
|
let timestamp = new Date().toLocaleString();
|
||||||
let newmessage = timestamp + "->" + (typeof message == "string" ? message : JSON.stringify(message, null, 2));
|
let messagecontent = typeof message == "string" ? message : JSON.stringify(message, null, 2);
|
||||||
|
let newmessage = timestamp + "->" + messagecontent;
|
||||||
this.logMessage = [].concat(this.logMessage).concat([newmessage]).slice(-100);
|
this.logMessage = [].concat(this.logMessage).concat([newmessage]).slice(-100);
|
||||||
// this.logMessage = [...this.logMessage, timestamp + ":" + newmessage].slice(-100);
|
// this.logMessage = [...this.logMessage, timestamp + ":" + newmessage].slice(-100);
|
||||||
console.log(newmessage);
|
console.log(newmessage);
|
||||||
if (this.statusBar2 != null) {
|
if (this.statusBar2 != null) {
|
||||||
this.statusBar2.setText(newmessage);
|
this.statusBar2.setText(newmessage.substring(0, 60));
|
||||||
|
}
|
||||||
|
if (this.onLogChanged != null) {
|
||||||
|
this.onLogChanged();
|
||||||
|
}
|
||||||
|
if (isNotify) {
|
||||||
|
new obsidian.Notice(messagecontent);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2350,8 +2378,12 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
yield this.app.vault.createFolder(c);
|
yield this.app.vault.createFolder(c);
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
this.addLog("ensure excep");
|
// basically skip exceptions.
|
||||||
this.addLog(ex);
|
if (ex.message && ex.message == "Folder already exists.") ;
|
||||||
|
else {
|
||||||
|
this.addLog("Folder Create Error");
|
||||||
|
this.addLog(ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c += "/";
|
c += "/";
|
||||||
}
|
}
|
||||||
@@ -2368,19 +2400,31 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
deleteVaultItem(file) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
let dir = file.parent;
|
||||||
|
yield this.app.vault.delete(file);
|
||||||
|
this.addLog(`deleted:${file.path}`);
|
||||||
|
this.addLog(`other items:${dir.children.length}`);
|
||||||
|
if (dir.children.length == 0) {
|
||||||
|
this.addLog(`all files deleted by replication, so delete dir`);
|
||||||
|
yield this.deleteVaultItem(dir);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
doc2storate_modify(doc, file, force) {
|
doc2storate_modify(doc, file, force) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
if (doc._deleted) {
|
if (doc._deleted) {
|
||||||
//basically pass.
|
//basically pass.
|
||||||
//but if there're no docs left, delete file.
|
//but if there're no docs left, delete file.
|
||||||
try {
|
try {
|
||||||
let lastDocs = this.localDatabase.get(doc._id);
|
let lastDocs = yield this.localDatabase.get(doc._id);
|
||||||
this.addLog(`delete skipped:${doc._id}`);
|
this.addLog(`delete skipped:${lastDocs._id}`);
|
||||||
}
|
}
|
||||||
catch (ex) {
|
catch (ex) {
|
||||||
if (ex.status || ex.status == 404) {
|
if (ex.status || ex.status == 404) {
|
||||||
//no op.
|
//no op.
|
||||||
yield this.app.vault.delete(file);
|
yield this.deleteVaultItem(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -2421,18 +2465,15 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
if (this.syncHandler != null) {
|
if (this.syncHandler != null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let db = new o(this.settings.couchDB_URI, {
|
let dbret = yield connectRemoteCouchDB(this.settings.couchDB_URI, {
|
||||||
auth: {
|
username: this.settings.couchDB_USER,
|
||||||
username: this.settings.couchDB_USER,
|
password: this.settings.couchDB_PASSWORD,
|
||||||
password: this.settings.couchDB_PASSWORD,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
try {
|
if (dbret === false) {
|
||||||
let info = yield db.info();
|
this.addLog(`could not connect to ${this.settings.couchDB_URI}`, true);
|
||||||
}
|
|
||||||
catch (ex) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let db = dbret.db;
|
||||||
this.syncHandler = this.localDatabase.sync(db, { live: true, retry: true });
|
this.syncHandler = this.localDatabase.sync(db, { live: true, retry: true });
|
||||||
this.syncHandler
|
this.syncHandler
|
||||||
.on("change", (e) => __awaiter(this, void 0, void 0, function* () {
|
.on("change", (e) => __awaiter(this, void 0, void 0, function* () {
|
||||||
@@ -2440,20 +2481,22 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
for (var change of docs) {
|
for (var change of docs) {
|
||||||
yield this.pouchdbChanged(change);
|
yield this.pouchdbChanged(change);
|
||||||
}
|
}
|
||||||
|
this.addLog(`replicated ${docs.length} doc(s)`);
|
||||||
localStorage.setItem("last-sync-no", e.change.last_seq + "");
|
localStorage.setItem("last-sync-no", e.change.last_seq + "");
|
||||||
// new Notice(`changed:${JSON.stringify(e)}`);
|
// new Notice(`changed:${JSON.stringify(e)}`);
|
||||||
}))
|
}))
|
||||||
.on("active", () => {
|
.on("active", () => {
|
||||||
// new Notice(`Replication activated`);
|
// new Notice(`Replication activated`);
|
||||||
|
this.addLog("Replication activated");
|
||||||
})
|
})
|
||||||
.on("complete", (e) => {
|
.on("complete", (e) => {
|
||||||
new obsidian.Notice(`Replication completed`);
|
this.addLog("Replication completed", true);
|
||||||
})
|
})
|
||||||
.on("denied", (e) => {
|
.on("denied", (e) => {
|
||||||
new obsidian.Notice(`Replication denied`);
|
this.addLog("Replication denied", true);
|
||||||
})
|
})
|
||||||
.on("error", (e) => {
|
.on("error", (e) => {
|
||||||
new obsidian.Notice(`Replication error`);
|
this.addLog("Replication error", true);
|
||||||
})
|
})
|
||||||
.on("paused", (e) => {
|
.on("paused", (e) => {
|
||||||
// new Notice(`Replication paused`);
|
// new Notice(`Replication paused`);
|
||||||
@@ -2500,7 +2543,6 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
}
|
}
|
||||||
initializeDatabase() {
|
initializeDatabase() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
// debugger;
|
|
||||||
let vaultName = this.app.vault.getName();
|
let vaultName = this.app.vault.getName();
|
||||||
this.localDatabase = new o(vaultName + "-livesync", {
|
this.localDatabase = new o(vaultName + "-livesync", {
|
||||||
auto_compaction: true,
|
auto_compaction: true,
|
||||||
@@ -2513,14 +2555,17 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
//<-- Sync
|
//<-- Sync
|
||||||
replicate() {
|
replicate() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
let db = new o(this.settings.couchDB_URI, {
|
let dbret = yield connectRemoteCouchDB(this.settings.couchDB_URI, {
|
||||||
auth: {
|
username: this.settings.couchDB_USER,
|
||||||
username: this.settings.couchDB_USER,
|
password: this.settings.couchDB_PASSWORD,
|
||||||
password: this.settings.couchDB_PASSWORD,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
if (dbret === false) {
|
||||||
|
this.addLog("could not connect to database");
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
var info = yield db.info();
|
let db = dbret.db;
|
||||||
|
var info = dbret.info;
|
||||||
new obsidian.Notice(`Connected to ${info.db_name}`);
|
new obsidian.Notice(`Connected to ${info.db_name}`);
|
||||||
this.localDatabase
|
this.localDatabase
|
||||||
.sync(db, {
|
.sync(db, {
|
||||||
@@ -2528,7 +2573,6 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
// since: "0",
|
// since: "0",
|
||||||
})
|
})
|
||||||
.on("change", (e) => __awaiter(this, void 0, void 0, function* () {
|
.on("change", (e) => __awaiter(this, void 0, void 0, function* () {
|
||||||
// debugger;
|
|
||||||
let docs = e.change.docs;
|
let docs = e.change.docs;
|
||||||
for (var change of docs) {
|
for (var change of docs) {
|
||||||
this.addLog("replication change arrived");
|
this.addLog("replication change arrived");
|
||||||
@@ -2536,8 +2580,6 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
yield this.pouchdbChanged(change);
|
yield this.pouchdbChanged(change);
|
||||||
}
|
}
|
||||||
localStorage.setItem("last-sync-no", e.change.last_seq + "");
|
localStorage.setItem("last-sync-no", e.change.last_seq + "");
|
||||||
// this.addLog(e.change.last_seq);
|
|
||||||
// new Notice(`changed:${JSON.stringify(e)}`);
|
|
||||||
}))
|
}))
|
||||||
.on("active", () => {
|
.on("active", () => {
|
||||||
new obsidian.Notice(`Replication activated`);
|
new obsidian.Notice(`Replication activated`);
|
||||||
@@ -2564,27 +2606,36 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
this.addLog("loading plugin");
|
this.addLog("loading plugin");
|
||||||
yield this.loadSettings();
|
yield this.loadSettings();
|
||||||
this.addRibbonIcon("dice", "Replicate", () => __awaiter(this, void 0, void 0, function* () {
|
obsidian.addIcon("replicate", `<g transform="matrix(1.15 0 0 1.15 -8.31 -9.52)" fill="currentColor" fill-rule="evenodd">
|
||||||
|
<path d="m85 22.2c-0.799-4.74-4.99-8.37-9.88-8.37-0.499 0-1.1 0.101-1.6 0.101-2.4-3.03-6.09-4.94-10.3-4.94-6.09 0-11.2 4.14-12.8 9.79-5.59 1.11-9.78 6.05-9.78 12 0 6.76 5.39 12.2 12 12.2h29.9c5.79 0 10.1-4.74 10.1-10.6 0-4.84-3.29-8.88-7.68-10.2zm-2.99 14.7h-29.5c-2.3-0.202-4.29-1.51-5.29-3.53-0.899-2.12-0.699-4.54 0.698-6.46 1.2-1.61 2.99-2.52 4.89-2.52 0.299 0 0.698 0 0.998 0.101l1.8 0.303v-2.02c0-3.63 2.4-6.76 5.89-7.57 0.599-0.101 1.2-0.202 1.8-0.202 2.89 0 5.49 1.62 6.79 4.24l0.598 1.21 1.3-0.504c0.599-0.202 1.3-0.303 2-0.303 1.3 0 2.5 0.404 3.59 1.11 1.6 1.21 2.6 3.13 2.6 5.15v1.61h2c2.6 0 4.69 2.12 4.69 4.74-0.099 2.52-2.2 4.64-4.79 4.64z"/>
|
||||||
|
<path d="m53.2 49.2h-41.6c-1.8 0-3.2 1.4-3.2 3.2v28.6c0 1.8 1.4 3.2 3.2 3.2h15.8v4h-7v6h24v-6h-7v-4h15.8c1.8 0 3.2-1.4 3.2-3.2v-28.6c0-1.8-1.4-3.2-3.2-3.2zm-2.8 29h-36v-23h36z"/>
|
||||||
|
<path d="m73 49.2c1.02 1.29 1.53 2.97 1.53 4.56 0 2.97-1.74 5.65-4.39 7.04v-4.06l-7.46 7.33 7.46 7.14v-4.06c7.66-1.98 12.2-9.61 10-17-0.102-0.297-0.205-0.595-0.307-0.892z"/>
|
||||||
|
<path d="m24.1 43c-0.817-0.991-1.53-2.97-1.53-4.56 0-2.97 1.74-5.65 4.39-7.04v4.06l7.46-7.33-7.46-7.14v4.06c-7.66 1.98-12.2 9.61-10 17 0.102 0.297 0.205 0.595 0.307 0.892z"/>
|
||||||
|
</g>`);
|
||||||
|
obsidian.addIcon("view-log", `<g transform="matrix(1.28 0 0 1.28 -131 -411)" fill="currentColor" fill-rule="evenodd">
|
||||||
|
<path d="m103 330h76v12h-76z"/>
|
||||||
|
<path d="m106 346v44h70v-44zm45 16h-20v-8h20z"/>
|
||||||
|
</g>`);
|
||||||
|
this.addRibbonIcon("replicate", "Replicate", () => __awaiter(this, void 0, void 0, function* () {
|
||||||
yield this.replicate();
|
yield this.replicate();
|
||||||
}));
|
}));
|
||||||
this.addRibbonIcon("dice", "pull file force", () => __awaiter(this, void 0, void 0, function* () {
|
// this.addRibbonIcon("dice", "pull file force", async () => {
|
||||||
this.localDatabase
|
// this.localDatabase
|
||||||
.changes({
|
// .changes({
|
||||||
since: 0,
|
// since: 0,
|
||||||
include_docs: true,
|
// include_docs: true,
|
||||||
})
|
// })
|
||||||
.on("change", (change) => __awaiter(this, void 0, void 0, function* () {
|
// .on("change", async (change) => {
|
||||||
yield this.pouchdbChanged(change.doc);
|
// await this.pouchdbChanged(change.doc);
|
||||||
localStorage.setItem("last-sync-no", change.seq + "");
|
// localStorage.setItem("last-sync-no", change.seq + "");
|
||||||
// received a change
|
// // received a change
|
||||||
}))
|
// })
|
||||||
.on("error", function (err) {
|
// .on("error", function (err) {
|
||||||
// handle errors
|
// // handle errors
|
||||||
});
|
// });
|
||||||
}));
|
// });
|
||||||
this.addRibbonIcon("dice", "Toggle Sync", () => {
|
this.addRibbonIcon("view-log", "Show log", () => {
|
||||||
this.toggleSync();
|
new LogDisplayModal(this.app, this).open();
|
||||||
// Vault.recurseChildren();
|
|
||||||
});
|
});
|
||||||
this.statusBar = this.addStatusBarItem();
|
this.statusBar = this.addStatusBarItem();
|
||||||
this.statusBar2 = this.addStatusBarItem();
|
this.statusBar2 = this.addStatusBarItem();
|
||||||
@@ -2639,17 +2690,66 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
this.registerEvent(this.app.workspace.on("file-open", this.watchWorkspaceOpen));
|
this.registerEvent(this.app.workspace.on("file-open", this.watchWorkspaceOpen));
|
||||||
}
|
}
|
||||||
watchWorkspaceOpen(file) {
|
watchWorkspaceOpen(file) {
|
||||||
|
if (file == null)
|
||||||
|
return;
|
||||||
this.showIfConflicted(file);
|
this.showIfConflicted(file);
|
||||||
}
|
}
|
||||||
watchVaultChange(file, ...args) {
|
watchVaultChange(file, ...args) {
|
||||||
this.updateDB(file);
|
this.updateDB(file);
|
||||||
}
|
}
|
||||||
watchVaultDelete(file) {
|
watchVaultDelete(file) {
|
||||||
this.deleteDB(file);
|
// debugger;
|
||||||
|
if (file.children) {
|
||||||
|
//folder
|
||||||
|
this.deleteFolderOnDB(file);
|
||||||
|
// this.app.vault.delete(file);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.deleteDB(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
deleteFolderOnDB(folder) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
this.addLog(`delete folder:${folder.path}`);
|
||||||
|
for (var v of folder.children) {
|
||||||
|
let entry = v;
|
||||||
|
this.addLog(`->entry:${entry.path}`);
|
||||||
|
if (entry.children) {
|
||||||
|
this.addLog(`->is dir`);
|
||||||
|
yield this.deleteFolderOnDB(entry);
|
||||||
|
yield this.app.vault.delete(entry);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.addLog(`->is file`);
|
||||||
|
yield this.deleteDB(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
yield this.app.vault.delete(folder);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
watchVaultRename(file, oldFile) {
|
watchVaultRename(file, oldFile) {
|
||||||
this.updateDB(file);
|
if (file.children) {
|
||||||
this.deleteDBbyPath(oldFile);
|
// this.renameFolder(file,oldFile);
|
||||||
|
this.addLog(`folder name changed:(this operation is not supported) ${file.path}`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.updateDB(file);
|
||||||
|
this.deleteDBbyPath(oldFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
renameFolder(folder, oldFile) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
for (var v of folder.children) {
|
||||||
|
let entry = v;
|
||||||
|
if (entry.children) {
|
||||||
|
yield this.deleteFolderOnDB(entry);
|
||||||
|
this.app.vault.delete(entry);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
yield this.deleteDB(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
// --> conflict resolving
|
// --> conflict resolving
|
||||||
getConflictedDoc(path, rev) {
|
getConflictedDoc(path, rev) {
|
||||||
@@ -2673,7 +2773,21 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
}
|
}
|
||||||
getConflictedStatus(path) {
|
getConflictedStatus(path) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
let test = yield this.localDatabase.get(path, { conflicts: true });
|
let test = null;
|
||||||
|
try {
|
||||||
|
test = yield this.localDatabase.get(path, { conflicts: true });
|
||||||
|
}
|
||||||
|
catch (ex) {
|
||||||
|
if (ex.status && ex.status == 404) {
|
||||||
|
this.addLog(`Getting conflicted status, but there was not ${path}`);
|
||||||
|
// NO OP.
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (test == null)
|
||||||
|
return false;
|
||||||
if (!test._conflicts)
|
if (!test._conflicts)
|
||||||
return false;
|
return false;
|
||||||
if (test._conflicts.length == 0)
|
if (test._conflicts.length == 0)
|
||||||
@@ -2756,7 +2870,10 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
let doc = yield this.localDatabase.get(filename);
|
let doc = yield this.localDatabase.get(filename);
|
||||||
yield this.doc2storate_modify(doc, file, force);
|
yield this.doc2storate_modify(doc, file, force);
|
||||||
}
|
}
|
||||||
else ;
|
else {
|
||||||
|
this.addLog(`target files:${filename} is two or more files in your vault`);
|
||||||
|
//something went wrong..
|
||||||
|
}
|
||||||
//when to opened file;
|
//when to opened file;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -2790,8 +2907,8 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
let old = yield this.localDatabase.get(fullpath);
|
let old = yield this.localDatabase.get(fullpath);
|
||||||
let oldData = { data: old.data };
|
let oldData = { data: old.data, deleted: old._deleted };
|
||||||
let newData = { data: d.data };
|
let newData = { data: d.data, deleted: d._deleted };
|
||||||
if (JSON.stringify(oldData) == JSON.stringify(newData)) {
|
if (JSON.stringify(oldData) == JSON.stringify(newData)) {
|
||||||
this.addLog("no changed" + fullpath);
|
this.addLog("no changed" + fullpath);
|
||||||
return;
|
return;
|
||||||
@@ -2805,7 +2922,7 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let ret = yield this.localDatabase.put(d);
|
let ret = yield this.localDatabase.put(d);
|
||||||
this.addLog("put database " + fullpath);
|
this.addLog("put database:" + fullpath);
|
||||||
this.addLog(ret);
|
this.addLog(ret);
|
||||||
if (this.settings.syncOnSave) {
|
if (this.settings.syncOnSave) {
|
||||||
yield this.replicate();
|
yield this.replicate();
|
||||||
@@ -2862,6 +2979,36 @@ class ObsidianLiveSyncPlugin extends obsidian.Plugin {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
class LogDisplayModal extends obsidian.Modal {
|
||||||
|
constructor(app, plugin) {
|
||||||
|
super(app);
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
updateLog() {
|
||||||
|
let logs = [...this.plugin.logMessage];
|
||||||
|
let msg = "";
|
||||||
|
for (var v of logs) {
|
||||||
|
msg += escapeStringToHTML(v) + "<br>";
|
||||||
|
}
|
||||||
|
this.logEl.innerHTML = msg;
|
||||||
|
}
|
||||||
|
onOpen() {
|
||||||
|
let { contentEl } = this;
|
||||||
|
contentEl.empty();
|
||||||
|
contentEl.createEl("h2", { text: "Sync Status" });
|
||||||
|
let div = contentEl.createDiv("");
|
||||||
|
div.addClass("op-scrollable");
|
||||||
|
div.addClass("op-pre");
|
||||||
|
this.logEl = div;
|
||||||
|
this.plugin.onLogChanged = this.updateLog;
|
||||||
|
this.updateLog();
|
||||||
|
}
|
||||||
|
onClose() {
|
||||||
|
let { contentEl } = this;
|
||||||
|
contentEl.empty();
|
||||||
|
this.plugin.onLogChanged = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
class ConflictResolveModal extends obsidian.Modal {
|
class ConflictResolveModal extends obsidian.Modal {
|
||||||
constructor(app, diff, callback) {
|
constructor(app, diff, callback) {
|
||||||
super(app);
|
super(app);
|
||||||
@@ -2927,29 +3074,24 @@ class ObsidianLiveSyncSettingTab extends obsidian.PluginSettingTab {
|
|||||||
}
|
}
|
||||||
test() {
|
test() {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
let db = new o(this.plugin.settings.couchDB_URI, {
|
let db = yield connectRemoteCouchDB(this.plugin.settings.couchDB_URI, {
|
||||||
auth: {
|
username: this.plugin.settings.couchDB_USER,
|
||||||
username: this.plugin.settings.couchDB_USER,
|
password: this.plugin.settings.couchDB_PASSWORD,
|
||||||
password: this.plugin.settings.couchDB_PASSWORD,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
try {
|
if (db === false) {
|
||||||
var info = yield db.info();
|
this.plugin.addLog(`could not connect to ${this.plugin.settings.couchDB_URI}`);
|
||||||
new obsidian.Notice(`Connected to ${info.db_name}`);
|
new obsidian.Notice(`could not connect to ${this.plugin.settings.couchDB_URI}`);
|
||||||
}
|
return;
|
||||||
catch (ex) {
|
|
||||||
new obsidian.Notice("Could not connect to db:" + ex);
|
|
||||||
}
|
}
|
||||||
|
new obsidian.Notice(`Connected to ${db.info.db_name}`);
|
||||||
|
this.plugin.addLog(`Connected to ${db.info.db_name}`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
display() {
|
display() {
|
||||||
let { containerEl } = this;
|
let { containerEl } = this;
|
||||||
containerEl.empty();
|
containerEl.empty();
|
||||||
containerEl.createEl("h2", { text: "Settings for obsidian-livesync." });
|
containerEl.createEl("h2", { text: "Settings for obsidian-livesync." });
|
||||||
new obsidian.Setting(containerEl)
|
new obsidian.Setting(containerEl).setName("CouchDB Remote URI").addText((text) => text
|
||||||
.setName("CouchDB Remote URI")
|
|
||||||
.setDesc("It's a secret")
|
|
||||||
.addText((text) => text
|
|
||||||
.setPlaceholder("https://........")
|
.setPlaceholder("https://........")
|
||||||
.setValue(this.plugin.settings.couchDB_URI)
|
.setValue(this.plugin.settings.couchDB_URI)
|
||||||
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
.onChange((value) => __awaiter(this, void 0, void 0, function* () {
|
||||||
@@ -3007,7 +3149,7 @@ class ObsidianLiveSyncSettingTab extends obsidian.PluginSettingTab {
|
|||||||
yield this.plugin.saveSettings();
|
yield this.plugin.saveSettings();
|
||||||
})));
|
})));
|
||||||
new obsidian.Setting(containerEl)
|
new obsidian.Setting(containerEl)
|
||||||
.setName("Database Operations")
|
.setName("Local Database Operations")
|
||||||
.addButton((button) => button
|
.addButton((button) => button
|
||||||
.setButtonText("Reset local database")
|
.setButtonText("Reset local database")
|
||||||
.setDisabled(false)
|
.setDisabled(false)
|
||||||
@@ -3025,4 +3167,4 @@ class ObsidianLiveSyncSettingTab extends obsidian.PluginSettingTab {
|
|||||||
}
|
}
|
||||||
|
|
||||||
module.exports = ObsidianLiveSyncPlugin;
|
module.exports = ObsidianLiveSyncPlugin;
|
||||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsInBvdWNoZGItYnJvd3Nlci13ZWJwYWNrL2Rpc3QvcG91Y2hkYi1icm93c2VyLmpzIiwibm9kZV9tb2R1bGVzL2RpZmYtbWF0Y2gtcGF0Y2gvaW5kZXguanMiLCJtYWluLnRzIl0sInNvdXJjZXNDb250ZW50IjpudWxsLCJuYW1lcyI6WyJQbHVnaW4iLCJQb3VjaERCIiwiTm90aWNlIiwiZGVib3VuY2UiLCJkaWZmX21hdGNoX3BhdGNoIiwiTW9kYWwiLCJESUZGX0RFTEVURSIsIkRJRkZfRVFVQUwiLCJESUZGX0lOU0VSVCIsIlBsdWdpblNldHRpbmdUYWIiLCJTZXR0aW5nIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7QUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBdURBO0FBQ08sU0FBUyxTQUFTLENBQUMsT0FBTyxFQUFFLFVBQVUsRUFBRSxDQUFDLEVBQUUsU0FBUyxFQUFFO0FBQzdELElBQUksU0FBUyxLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsT0FBTyxLQUFLLFlBQVksQ0FBQyxHQUFHLEtBQUssR0FBRyxJQUFJLENBQUMsQ0FBQyxVQUFVLE9BQU8sRUFBRSxFQUFFLE9BQU8sQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxFQUFFO0FBQ2hILElBQUksT0FBTyxLQUFLLENBQUMsS0FBSyxDQUFDLEdBQUcsT0FBTyxDQUFDLEVBQUUsVUFBVSxPQUFPLEVBQUUsTUFBTSxFQUFFO0FBQy9ELFFBQVEsU0FBUyxTQUFTLENBQUMsS0FBSyxFQUFFLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRTtBQUNuRyxRQUFRLFNBQVMsUUFBUSxDQUFDLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsRUFBRTtBQUN0RyxRQUFRLFNBQVMsSUFBSSxDQUFDLE1BQU0sRUFBRSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEdBQUcsT0FBTyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxLQUFLLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQyxTQUFTLEVBQUUsUUFBUSxDQUFDLENBQUMsRUFBRTtBQUN0SCxRQUFRLElBQUksQ0FBQyxDQUFDLFNBQVMsR0FBRyxTQUFTLENBQUMsS0FBSyxDQUFDLE9BQU8sRUFBRSxVQUFVLElBQUksRUFBRSxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztBQUM5RSxLQUFLLENBQUMsQ0FBQztBQUNQOztBQzdFQSxJQUFJLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsT0FBTyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUMsU0FBUyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBQyxDQUFDLENBQUMsR0FBRyxDQUFDLENBQUMsRUFBRSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxRQUFRLEVBQUUsT0FBTyxPQUFPLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxRQUFRLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxxQkFBcUIsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLE9BQU8sTUFBTSxDQUFDLG1CQUFtQixDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMscUJBQXFCLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsT0FBTyxNQUFNLENBQUMsbUJBQW1CLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxNQUFNLENBQUMsS0FBSyxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLElBQUksT0FBTyxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxjQUFjLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUMsQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQyxjQUFjLEVBQUUsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLEdBQUcsQ0FBQyxFQUFFLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUMsRUFBRSxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxHQUFHLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQyxNQUFNLElBQUksU0FBUyxDQUFDLGtFQUFrRSxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsYUFBYSxDQUFDLENBQUMsQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDLENBQUMsYUFBYSxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUF
Line truncated
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWFpbi5qcyIsInNvdXJjZXMiOlsibm9kZV9tb2R1bGVzL3RzbGliL3RzbGliLmVzNi5qcyIsInBvdWNoZGItYnJvd3Nlci13ZWJwYWNrL2Rpc3QvcG91Y2hkYi1icm93c2VyLmpzIiwibm9kZV9tb2R1bGVzL2RpZmYtbWF0Y2gtcGF0Y2gvaW5kZXguanMiLCJtYWluLnRzIl0sInNvdXJjZXNDb250ZW50IjpudWxsLCJuYW1lcyI6WyJQb3VjaERCIiwiUGx1Z2luIiwiTm90aWNlIiwiYWRkSWNvbiIsImRlYm91bmNlIiwiZGlmZl9tYXRjaF9wYXRjaCIsIk1vZGFsIiwiRElGRl9ERUxFVEUiLCJESUZGX0VRVUFMIiwiRElGRl9JTlNFUlQiLCJQbHVnaW5TZXR0aW5nVGFiIiwiU2V0dGluZyJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7O0FBQUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQXVEQTtBQUNPLFNBQVMsU0FBUyxDQUFDLE9BQU8sRUFBRSxVQUFVLEVBQUUsQ0FBQyxFQUFFLFNBQVMsRUFBRTtBQUM3RCxJQUFJLFNBQVMsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLE9BQU8sS0FBSyxZQUFZLENBQUMsR0FBRyxLQUFLLEdBQUcsSUFBSSxDQUFDLENBQUMsVUFBVSxPQUFPLEVBQUUsRUFBRSxPQUFPLENBQUMsS0FBSyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsRUFBRTtBQUNoSCxJQUFJLE9BQU8sS0FBSyxDQUFDLEtBQUssQ0FBQyxHQUFHLE9BQU8sQ0FBQyxFQUFFLFVBQVUsT0FBTyxFQUFFLE1BQU0sRUFBRTtBQUMvRCxRQUFRLFNBQVMsU0FBUyxDQUFDLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUU7QUFDbkcsUUFBUSxTQUFTLFFBQVEsQ0FBQyxLQUFLLEVBQUUsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRSxFQUFFLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLEVBQUU7QUFDdEcsUUFBUSxTQUFTLElBQUksQ0FBQyxNQUFNLEVBQUUsRUFBRSxNQUFNLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsS0FBSyxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsU0FBUyxFQUFFLFFBQVEsQ0FBQyxDQUFDLEVBQUU7QUFDdEgsUUFBUSxJQUFJLENBQUMsQ0FBQyxTQUFTLEdBQUcsU0FBUyxDQUFDLEtBQUssQ0FBQyxPQUFPLEVBQUUsVUFBVSxJQUFJLEVBQUUsQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7QUFDOUUsS0FBSyxDQUFDLENBQUM7QUFDUDs7QUM3RUEsSUFBSSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLE9BQU8sVUFBVSxDQUFDLElBQUksQ0FBQyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEVBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsUUFBUSxFQUFFLE9BQU8sT0FBTyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sUUFBUSxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLFVBQVUsRUFBRSxPQUFPLENBQUMsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMscUJBQXFCLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxPQUFPLE1BQU0sQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLHFCQUFxQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLE9BQU8sTUFBTSxDQUFDLG1CQUFtQixDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsTUFBTSxDQUFDLEtBQUssRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxJQUFJLE9BQU8sRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsY0FBYyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFDLENBQUMsU0FBUyxDQUFDLEVBQUUsQ0FBQyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUMsY0FBYyxFQUFFLENBQUMsQ0FBQyxjQUFjLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsT0FBTyxHQUFHLENBQUMsRUFBRSxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsVUFBVSxFQUFFLE9BQU8sQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsRUFBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsWUFBWSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsU0FBUyxDQUFDLGFBQWEsQ0FBQyxLQUFLLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxFQUFFLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsR0FBRyxVQUFVLEVBQUUsT0FBTyxDQUFDLENBQUMsTUFBTSxJQUFJLFNBQVMsQ0FBQyxrRUFBa0UsQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLFNBQVMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE9BQU8sS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUMsbUJBQW1CLENBQUMsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUF
Line truncated
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { App, debounce, Modal, Notice, Plugin, PluginSettingTab, Setting, TFile, Vault, DataWriteOptions, View } from "obsidian";
|
import { App, debounce, Modal, Notice, Plugin, PluginSettingTab, Setting, TFile, addIcon, TFolder } from "obsidian";
|
||||||
import { PouchDB } from "./pouchdb-browser-webpack/dist/pouchdb-browser";
|
import { PouchDB } from "./pouchdb-browser-webpack/dist/pouchdb-browser";
|
||||||
import { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
|
import { DIFF_DELETE, DIFF_EQUAL, DIFF_INSERT, diff_match_patch } from "diff-match-patch";
|
||||||
|
|
||||||
@@ -104,22 +104,47 @@ const escapeStringToHTML = (str: string) => {
|
|||||||
return escape[match];
|
return escape[match];
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
const isValidRemoteCouchDBURI = (uri: string): boolean => {
|
||||||
|
if (uri.startsWith("https://")) return true;
|
||||||
|
if (uri.startsWith("http://")) return true;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
const connectRemoteCouchDB = async (uri: string, auth: { username: string; password: string }): Promise<false | { db: PouchDB.Database; info: any }> => {
|
||||||
|
if (!isValidRemoteCouchDBURI(uri)) false;
|
||||||
|
let db = new PouchDB(uri, {
|
||||||
|
auth,
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
let info = await db.info();
|
||||||
|
return { db: db, info: info };
|
||||||
|
} catch (ex) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
//<--Functions
|
//<--Functions
|
||||||
|
|
||||||
export default class ObsidianLiveSyncPlugin extends Plugin {
|
export default class ObsidianLiveSyncPlugin extends Plugin {
|
||||||
settings: ObsidianLiveSyncSettings;
|
settings: ObsidianLiveSyncSettings;
|
||||||
localDatabase: PouchDB.Database<Notes>;
|
localDatabase: PouchDB.Database<Notes>;
|
||||||
logMessage: string[] = [];
|
logMessage: string[] = [];
|
||||||
|
onLogChanged: () => void;
|
||||||
|
|
||||||
async addLog(message: any) {
|
async addLog(message: any, isNotify?: boolean) {
|
||||||
let timestamp = new Date().toLocaleString();
|
let timestamp = new Date().toLocaleString();
|
||||||
let newmessage = timestamp + "->" + (typeof message == "string" ? message : JSON.stringify(message, null, 2));
|
let messagecontent = typeof message == "string" ? message : JSON.stringify(message, null, 2);
|
||||||
|
let newmessage = timestamp + "->" + messagecontent;
|
||||||
|
|
||||||
this.logMessage = [].concat(this.logMessage).concat([newmessage]).slice(-100);
|
this.logMessage = [].concat(this.logMessage).concat([newmessage]).slice(-100);
|
||||||
// this.logMessage = [...this.logMessage, timestamp + ":" + newmessage].slice(-100);
|
// this.logMessage = [...this.logMessage, timestamp + ":" + newmessage].slice(-100);
|
||||||
console.log(newmessage);
|
console.log(newmessage);
|
||||||
if (this.statusBar2 != null) {
|
if (this.statusBar2 != null) {
|
||||||
this.statusBar2.setText(newmessage);
|
this.statusBar2.setText(newmessage.substring(0, 60));
|
||||||
|
}
|
||||||
|
if (this.onLogChanged != null) {
|
||||||
|
this.onLogChanged();
|
||||||
|
}
|
||||||
|
if (isNotify) {
|
||||||
|
new Notice(messagecontent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,8 +157,13 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
try {
|
try {
|
||||||
await this.app.vault.createFolder(c);
|
await this.app.vault.createFolder(c);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
this.addLog("ensure excep");
|
// basically skip exceptions.
|
||||||
this.addLog(ex);
|
if (ex.message && ex.message == "Folder already exists.") {
|
||||||
|
// especialy this message is.
|
||||||
|
} else {
|
||||||
|
this.addLog("Folder Create Error");
|
||||||
|
this.addLog(ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
c += "/";
|
c += "/";
|
||||||
}
|
}
|
||||||
@@ -148,17 +178,27 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
await this.app.vault.trigger("create", newfile);
|
await this.app.vault.trigger("create", newfile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
async deleteVaultItem(file: TFile | TFolder) {
|
||||||
|
let dir = file.parent;
|
||||||
|
await this.app.vault.delete(file);
|
||||||
|
this.addLog(`deleted:${file.path}`);
|
||||||
|
this.addLog(`other items:${dir.children.length}`);
|
||||||
|
if (dir.children.length == 0) {
|
||||||
|
this.addLog(`all files deleted by replication, so delete dir`);
|
||||||
|
await this.deleteVaultItem(dir);
|
||||||
|
}
|
||||||
|
}
|
||||||
async doc2storate_modify(doc: Notes, file: TFile, force?: boolean) {
|
async doc2storate_modify(doc: Notes, file: TFile, force?: boolean) {
|
||||||
if (doc._deleted) {
|
if (doc._deleted) {
|
||||||
//basically pass.
|
//basically pass.
|
||||||
//but if there're no docs left, delete file.
|
//but if there're no docs left, delete file.
|
||||||
try {
|
try {
|
||||||
let lastDocs = this.localDatabase.get(doc._id);
|
let lastDocs = await this.localDatabase.get(doc._id);
|
||||||
this.addLog(`delete skipped:${doc._id}`);
|
this.addLog(`delete skipped:${lastDocs._id}`);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex.status || ex.status == 404) {
|
if (ex.status || ex.status == 404) {
|
||||||
//no op.
|
//no op.
|
||||||
await this.app.vault.delete(file);
|
await this.deleteVaultItem(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -203,17 +243,17 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
if (this.syncHandler != null) {
|
if (this.syncHandler != null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let db = new PouchDB(this.settings.couchDB_URI, {
|
|
||||||
auth: {
|
let dbret = await connectRemoteCouchDB(this.settings.couchDB_URI, {
|
||||||
username: this.settings.couchDB_USER,
|
username: this.settings.couchDB_USER,
|
||||||
password: this.settings.couchDB_PASSWORD,
|
password: this.settings.couchDB_PASSWORD,
|
||||||
},
|
|
||||||
});
|
});
|
||||||
try {
|
if (dbret === false) {
|
||||||
let info = await db.info();
|
this.addLog(`could not connect to ${this.settings.couchDB_URI}`, true);
|
||||||
} catch (ex) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
let db = dbret.db;
|
||||||
|
|
||||||
this.syncHandler = this.localDatabase.sync(db, { live: true, retry: true });
|
this.syncHandler = this.localDatabase.sync(db, { live: true, retry: true });
|
||||||
|
|
||||||
this.syncHandler
|
this.syncHandler
|
||||||
@@ -222,20 +262,22 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
for (var change of docs) {
|
for (var change of docs) {
|
||||||
await this.pouchdbChanged(change as Notes);
|
await this.pouchdbChanged(change as Notes);
|
||||||
}
|
}
|
||||||
|
this.addLog(`replicated ${docs.length} doc(s)`);
|
||||||
localStorage.setItem("last-sync-no", e.change.last_seq + "");
|
localStorage.setItem("last-sync-no", e.change.last_seq + "");
|
||||||
// new Notice(`changed:${JSON.stringify(e)}`);
|
// new Notice(`changed:${JSON.stringify(e)}`);
|
||||||
})
|
})
|
||||||
.on("active", () => {
|
.on("active", () => {
|
||||||
// new Notice(`Replication activated`);
|
// new Notice(`Replication activated`);
|
||||||
|
this.addLog("Replication activated");
|
||||||
})
|
})
|
||||||
.on("complete", (e) => {
|
.on("complete", (e) => {
|
||||||
new Notice(`Replication completed`);
|
this.addLog("Replication completed", true);
|
||||||
})
|
})
|
||||||
.on("denied", (e) => {
|
.on("denied", (e) => {
|
||||||
new Notice(`Replication denied`);
|
this.addLog("Replication denied", true);
|
||||||
})
|
})
|
||||||
.on("error", (e) => {
|
.on("error", (e) => {
|
||||||
new Notice(`Replication error`);
|
this.addLog("Replication error", true);
|
||||||
})
|
})
|
||||||
.on("paused", (e) => {
|
.on("paused", (e) => {
|
||||||
// new Notice(`Replication paused`);
|
// new Notice(`Replication paused`);
|
||||||
@@ -274,7 +316,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.statusBar.setText("Sync:" + statusStr);
|
this.statusBar.setText("Sync:" + statusStr);
|
||||||
}
|
}
|
||||||
async initializeDatabase() {
|
async initializeDatabase() {
|
||||||
// debugger;
|
|
||||||
let vaultName = this.app.vault.getName();
|
let vaultName = this.app.vault.getName();
|
||||||
|
|
||||||
this.localDatabase = new PouchDB<Notes>(vaultName + "-livesync", {
|
this.localDatabase = new PouchDB<Notes>(vaultName + "-livesync", {
|
||||||
@@ -289,14 +330,17 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
statusBar2: HTMLElement;
|
statusBar2: HTMLElement;
|
||||||
//<-- Sync
|
//<-- Sync
|
||||||
async replicate() {
|
async replicate() {
|
||||||
let db = new PouchDB(this.settings.couchDB_URI, {
|
let dbret = await connectRemoteCouchDB(this.settings.couchDB_URI, {
|
||||||
auth: {
|
username: this.settings.couchDB_USER,
|
||||||
username: this.settings.couchDB_USER,
|
password: this.settings.couchDB_PASSWORD,
|
||||||
password: this.settings.couchDB_PASSWORD,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
if (dbret === false) {
|
||||||
|
this.addLog("could not connect to database");
|
||||||
|
return;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
var info = await db.info();
|
let db = dbret.db;
|
||||||
|
var info = dbret.info;
|
||||||
new Notice(`Connected to ${info.db_name}`);
|
new Notice(`Connected to ${info.db_name}`);
|
||||||
this.localDatabase
|
this.localDatabase
|
||||||
.sync(db, {
|
.sync(db, {
|
||||||
@@ -304,8 +348,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
// since: "0",
|
// since: "0",
|
||||||
})
|
})
|
||||||
.on("change", async (e) => {
|
.on("change", async (e) => {
|
||||||
// debugger;
|
|
||||||
|
|
||||||
let docs = e.change.docs;
|
let docs = e.change.docs;
|
||||||
for (var change of docs) {
|
for (var change of docs) {
|
||||||
this.addLog("replication change arrived");
|
this.addLog("replication change arrived");
|
||||||
@@ -313,8 +355,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
await this.pouchdbChanged(change as Notes);
|
await this.pouchdbChanged(change as Notes);
|
||||||
}
|
}
|
||||||
localStorage.setItem("last-sync-no", e.change.last_seq + "");
|
localStorage.setItem("last-sync-no", e.change.last_seq + "");
|
||||||
// this.addLog(e.change.last_seq);
|
|
||||||
// new Notice(`changed:${JSON.stringify(e)}`);
|
|
||||||
})
|
})
|
||||||
.on("active", () => {
|
.on("active", () => {
|
||||||
new Notice(`Replication activated`);
|
new Notice(`Replication activated`);
|
||||||
@@ -339,31 +379,47 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.addLog("loading plugin");
|
this.addLog("loading plugin");
|
||||||
|
|
||||||
await this.loadSettings();
|
await this.loadSettings();
|
||||||
|
addIcon(
|
||||||
this.addRibbonIcon("dice", "Replicate", async () => {
|
"replicate",
|
||||||
|
`<g transform="matrix(1.15 0 0 1.15 -8.31 -9.52)" fill="currentColor" fill-rule="evenodd">
|
||||||
|
<path d="m85 22.2c-0.799-4.74-4.99-8.37-9.88-8.37-0.499 0-1.1 0.101-1.6 0.101-2.4-3.03-6.09-4.94-10.3-4.94-6.09 0-11.2 4.14-12.8 9.79-5.59 1.11-9.78 6.05-9.78 12 0 6.76 5.39 12.2 12 12.2h29.9c5.79 0 10.1-4.74 10.1-10.6 0-4.84-3.29-8.88-7.68-10.2zm-2.99 14.7h-29.5c-2.3-0.202-4.29-1.51-5.29-3.53-0.899-2.12-0.699-4.54 0.698-6.46 1.2-1.61 2.99-2.52 4.89-2.52 0.299 0 0.698 0 0.998 0.101l1.8 0.303v-2.02c0-3.63 2.4-6.76 5.89-7.57 0.599-0.101 1.2-0.202 1.8-0.202 2.89 0 5.49 1.62 6.79 4.24l0.598 1.21 1.3-0.504c0.599-0.202 1.3-0.303 2-0.303 1.3 0 2.5 0.404 3.59 1.11 1.6 1.21 2.6 3.13 2.6 5.15v1.61h2c2.6 0 4.69 2.12 4.69 4.74-0.099 2.52-2.2 4.64-4.79 4.64z"/>
|
||||||
|
<path d="m53.2 49.2h-41.6c-1.8 0-3.2 1.4-3.2 3.2v28.6c0 1.8 1.4 3.2 3.2 3.2h15.8v4h-7v6h24v-6h-7v-4h15.8c1.8 0 3.2-1.4 3.2-3.2v-28.6c0-1.8-1.4-3.2-3.2-3.2zm-2.8 29h-36v-23h36z"/>
|
||||||
|
<path d="m73 49.2c1.02 1.29 1.53 2.97 1.53 4.56 0 2.97-1.74 5.65-4.39 7.04v-4.06l-7.46 7.33 7.46 7.14v-4.06c7.66-1.98 12.2-9.61 10-17-0.102-0.297-0.205-0.595-0.307-0.892z"/>
|
||||||
|
<path d="m24.1 43c-0.817-0.991-1.53-2.97-1.53-4.56 0-2.97 1.74-5.65 4.39-7.04v4.06l7.46-7.33-7.46-7.14v4.06c-7.66 1.98-12.2 9.61-10 17 0.102 0.297 0.205 0.595 0.307 0.892z"/>
|
||||||
|
</g>`
|
||||||
|
);
|
||||||
|
addIcon(
|
||||||
|
"view-log",
|
||||||
|
`<g transform="matrix(1.28 0 0 1.28 -131 -411)" fill="currentColor" fill-rule="evenodd">
|
||||||
|
<path d="m103 330h76v12h-76z"/>
|
||||||
|
<path d="m106 346v44h70v-44zm45 16h-20v-8h20z"/>
|
||||||
|
</g>`
|
||||||
|
);
|
||||||
|
this.addRibbonIcon("replicate", "Replicate", async () => {
|
||||||
await this.replicate();
|
await this.replicate();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.addRibbonIcon("dice", "pull file force", async () => {
|
// this.addRibbonIcon("dice", "pull file force", async () => {
|
||||||
this.localDatabase
|
// this.localDatabase
|
||||||
.changes({
|
// .changes({
|
||||||
since: 0,
|
// since: 0,
|
||||||
include_docs: true,
|
// include_docs: true,
|
||||||
})
|
// })
|
||||||
.on("change", async (change) => {
|
// .on("change", async (change) => {
|
||||||
await this.pouchdbChanged(change.doc);
|
// await this.pouchdbChanged(change.doc);
|
||||||
localStorage.setItem("last-sync-no", change.seq + "");
|
// localStorage.setItem("last-sync-no", change.seq + "");
|
||||||
// received a change
|
// // received a change
|
||||||
})
|
// })
|
||||||
.on("error", function (err) {
|
// .on("error", function (err) {
|
||||||
// handle errors
|
// // handle errors
|
||||||
});
|
// });
|
||||||
});
|
// });
|
||||||
this.addRibbonIcon("dice", "Toggle Sync", () => {
|
|
||||||
this.toggleSync();
|
this.addRibbonIcon("view-log", "Show log", () => {
|
||||||
// Vault.recurseChildren();
|
new LogDisplayModal(this.app, this).open();
|
||||||
});
|
});
|
||||||
this.statusBar = this.addStatusBarItem();
|
this.statusBar = this.addStatusBarItem();
|
||||||
|
|
||||||
this.statusBar2 = this.addStatusBarItem();
|
this.statusBar2 = this.addStatusBarItem();
|
||||||
|
|
||||||
this.watchVaultChange = debounce(this.watchVaultChange.bind(this), 200, false);
|
this.watchVaultChange = debounce(this.watchVaultChange.bind(this), 200, false);
|
||||||
@@ -423,17 +479,57 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.registerEvent(this.app.workspace.on("file-open", this.watchWorkspaceOpen));
|
this.registerEvent(this.app.workspace.on("file-open", this.watchWorkspaceOpen));
|
||||||
}
|
}
|
||||||
watchWorkspaceOpen(file: TFile) {
|
watchWorkspaceOpen(file: TFile) {
|
||||||
|
if (file == null) return;
|
||||||
this.showIfConflicted(file);
|
this.showIfConflicted(file);
|
||||||
}
|
}
|
||||||
watchVaultChange(file: TFile, ...args: any[]) {
|
watchVaultChange(file: TFile, ...args: any[]) {
|
||||||
this.updateDB(file);
|
this.updateDB(file);
|
||||||
}
|
}
|
||||||
watchVaultDelete(file: TFile) {
|
watchVaultDelete(file: TFile & TFolder) {
|
||||||
this.deleteDB(file);
|
// debugger;
|
||||||
|
if (file.children) {
|
||||||
|
//folder
|
||||||
|
this.deleteFolderOnDB(file);
|
||||||
|
// this.app.vault.delete(file);
|
||||||
|
} else {
|
||||||
|
this.deleteDB(file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
watchVaultRename(file: any, oldFile: any) {
|
async deleteFolderOnDB(folder: TFolder) {
|
||||||
this.updateDB(file);
|
this.addLog(`delete folder:${folder.path}`);
|
||||||
this.deleteDBbyPath(oldFile);
|
for (var v of folder.children) {
|
||||||
|
let entry = v as TFile & TFolder;
|
||||||
|
this.addLog(`->entry:${entry.path}`);
|
||||||
|
if (entry.children) {
|
||||||
|
this.addLog(`->is dir`);
|
||||||
|
await this.deleteFolderOnDB(entry);
|
||||||
|
await this.app.vault.delete(entry);
|
||||||
|
} else {
|
||||||
|
this.addLog(`->is file`);
|
||||||
|
await this.deleteDB(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await this.app.vault.delete(folder);
|
||||||
|
}
|
||||||
|
watchVaultRename(file: TFile & TFolder, oldFile: any) {
|
||||||
|
if (file.children) {
|
||||||
|
// this.renameFolder(file,oldFile);
|
||||||
|
this.addLog(`folder name changed:(this operation is not supported) ${file.path}`);
|
||||||
|
} else {
|
||||||
|
this.updateDB(file);
|
||||||
|
this.deleteDBbyPath(oldFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async renameFolder(folder: TFolder, oldFile: any) {
|
||||||
|
for (var v of folder.children) {
|
||||||
|
let entry = v as TFile & TFolder;
|
||||||
|
if (entry.children) {
|
||||||
|
await this.deleteFolderOnDB(entry);
|
||||||
|
this.app.vault.delete(entry);
|
||||||
|
} else {
|
||||||
|
await this.deleteDB(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --> conflict resolving
|
// --> conflict resolving
|
||||||
@@ -454,7 +550,18 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
async getConflictedStatus(path: string): Promise<diff_check_result> {
|
async getConflictedStatus(path: string): Promise<diff_check_result> {
|
||||||
let test = await this.localDatabase.get(path, { conflicts: true });
|
let test: Notes & PouchDB.Core.GetMeta = null;
|
||||||
|
try {
|
||||||
|
test = await this.localDatabase.get(path, { conflicts: true });
|
||||||
|
} catch (ex) {
|
||||||
|
if (ex.status && ex.status == 404) {
|
||||||
|
this.addLog(`Getting conflicted status, but there was not ${path}`);
|
||||||
|
// NO OP.
|
||||||
|
} else {
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (test == null) return false;
|
||||||
if (!test._conflicts) return false;
|
if (!test._conflicts) return false;
|
||||||
if (test._conflicts.length == 0) return false;
|
if (test._conflicts.length == 0) return false;
|
||||||
// should be two or more conflicts;
|
// should be two or more conflicts;
|
||||||
@@ -527,6 +634,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
let doc = await this.localDatabase.get(filename);
|
let doc = await this.localDatabase.get(filename);
|
||||||
await this.doc2storate_modify(doc, file, force);
|
await this.doc2storate_modify(doc, file, force);
|
||||||
} else {
|
} else {
|
||||||
|
this.addLog(`target files:${filename} is two or more files in your vault`);
|
||||||
//something went wrong..
|
//something went wrong..
|
||||||
}
|
}
|
||||||
//when to opened file;
|
//when to opened file;
|
||||||
@@ -559,8 +667,8 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
let old = await this.localDatabase.get(fullpath);
|
let old = await this.localDatabase.get(fullpath);
|
||||||
let oldData = { data: old.data };
|
let oldData = { data: old.data, deleted: old._deleted };
|
||||||
let newData = { data: d.data };
|
let newData = { data: d.data, deleted: d._deleted };
|
||||||
if (JSON.stringify(oldData) == JSON.stringify(newData)) {
|
if (JSON.stringify(oldData) == JSON.stringify(newData)) {
|
||||||
this.addLog("no changed" + fullpath);
|
this.addLog("no changed" + fullpath);
|
||||||
return;
|
return;
|
||||||
@@ -575,7 +683,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
let ret = await this.localDatabase.put(d);
|
let ret = await this.localDatabase.put(d);
|
||||||
|
|
||||||
this.addLog("put database " + fullpath);
|
this.addLog("put database:" + fullpath);
|
||||||
this.addLog(ret);
|
this.addLog(ret);
|
||||||
if (this.settings.syncOnSave) {
|
if (this.settings.syncOnSave) {
|
||||||
await this.replicate();
|
await this.replicate();
|
||||||
@@ -623,8 +731,36 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
class LogDisplayModal extends Modal {
|
class LogDisplayModal extends Modal {
|
||||||
constructor(app: App) {
|
plugin: ObsidianLiveSyncPlugin;
|
||||||
|
logEl: HTMLDivElement;
|
||||||
|
constructor(app: App, plugin: ObsidianLiveSyncPlugin) {
|
||||||
super(app);
|
super(app);
|
||||||
|
this.plugin = plugin;
|
||||||
|
}
|
||||||
|
updateLog() {
|
||||||
|
let logs = [...this.plugin.logMessage];
|
||||||
|
let msg = "";
|
||||||
|
for (var v of logs) {
|
||||||
|
msg += escapeStringToHTML(v) + "<br>";
|
||||||
|
}
|
||||||
|
this.logEl.innerHTML = msg;
|
||||||
|
}
|
||||||
|
onOpen() {
|
||||||
|
let { contentEl } = this;
|
||||||
|
|
||||||
|
contentEl.empty();
|
||||||
|
contentEl.createEl("h2", { text: "Sync Status" });
|
||||||
|
let div = contentEl.createDiv("");
|
||||||
|
div.addClass("op-scrollable");
|
||||||
|
div.addClass("op-pre");
|
||||||
|
this.logEl = div;
|
||||||
|
this.plugin.onLogChanged = this.updateLog;
|
||||||
|
this.updateLog();
|
||||||
|
}
|
||||||
|
onClose() {
|
||||||
|
let { contentEl } = this;
|
||||||
|
contentEl.empty();
|
||||||
|
this.plugin.onLogChanged = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
class ConflictResolveModal extends Modal {
|
class ConflictResolveModal extends Modal {
|
||||||
@@ -700,18 +836,17 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
}
|
}
|
||||||
async test(): Promise<void> {
|
async test(): Promise<void> {
|
||||||
let db = new PouchDB(this.plugin.settings.couchDB_URI, {
|
let db = await connectRemoteCouchDB(this.plugin.settings.couchDB_URI, {
|
||||||
auth: {
|
username: this.plugin.settings.couchDB_USER,
|
||||||
username: this.plugin.settings.couchDB_USER,
|
password: this.plugin.settings.couchDB_PASSWORD,
|
||||||
password: this.plugin.settings.couchDB_PASSWORD,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
try {
|
if (db === false) {
|
||||||
var info = await db.info();
|
this.plugin.addLog(`could not connect to ${this.plugin.settings.couchDB_URI}`);
|
||||||
new Notice(`Connected to ${info.db_name}`);
|
new Notice(`could not connect to ${this.plugin.settings.couchDB_URI}`);
|
||||||
} catch (ex) {
|
return;
|
||||||
new Notice("Could not connect to db:" + ex);
|
|
||||||
}
|
}
|
||||||
|
new Notice(`Connected to ${db.info.db_name}`);
|
||||||
|
this.plugin.addLog(`Connected to ${db.info.db_name}`);
|
||||||
}
|
}
|
||||||
display(): void {
|
display(): void {
|
||||||
let { containerEl } = this;
|
let { containerEl } = this;
|
||||||
@@ -720,18 +855,15 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
|
|
||||||
containerEl.createEl("h2", { text: "Settings for obsidian-livesync." });
|
containerEl.createEl("h2", { text: "Settings for obsidian-livesync." });
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl).setName("CouchDB Remote URI").addText((text) =>
|
||||||
.setName("CouchDB Remote URI")
|
text
|
||||||
.setDesc("It's a secret")
|
.setPlaceholder("https://........")
|
||||||
.addText((text) =>
|
.setValue(this.plugin.settings.couchDB_URI)
|
||||||
text
|
.onChange(async (value) => {
|
||||||
.setPlaceholder("https://........")
|
this.plugin.settings.couchDB_URI = value;
|
||||||
.setValue(this.plugin.settings.couchDB_URI)
|
await this.plugin.saveSettings();
|
||||||
.onChange(async (value) => {
|
})
|
||||||
this.plugin.settings.couchDB_URI = value;
|
);
|
||||||
await this.plugin.saveSettings();
|
|
||||||
})
|
|
||||||
);
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName("CouchDB Username")
|
.setName("CouchDB Username")
|
||||||
.setDesc("username")
|
.setDesc("username")
|
||||||
@@ -794,7 +926,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName("Database Operations")
|
.setName("Local Database Operations")
|
||||||
.addButton((button) =>
|
.addButton((button) =>
|
||||||
button
|
button
|
||||||
.setButtonText("Reset local database")
|
.setButtonText("Reset local database")
|
||||||
|
|||||||
@@ -1,40 +1,37 @@
|
|||||||
import typescript from '@rollup/plugin-typescript';
|
import typescript from "@rollup/plugin-typescript";
|
||||||
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
import { nodeResolve } from "@rollup/plugin-node-resolve";
|
||||||
import commonjs from '@rollup/plugin-commonjs';
|
import commonjs from "@rollup/plugin-commonjs";
|
||||||
import nodePolyfills from 'rollup-plugin-polyfill-node';
|
import nodePolyfills from "rollup-plugin-polyfill-node";
|
||||||
|
|
||||||
const isProd = (process.env.BUILD === 'production');
|
const isProd = process.env.BUILD === "production";
|
||||||
|
|
||||||
const banner =
|
const banner = `/*
|
||||||
`/*
|
|
||||||
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
|
THIS IS A GENERATED/BUNDLED FILE BY ROLLUP
|
||||||
if you want to view the source visit the plugins github repository
|
if you want to view the source visit the plugins github repository
|
||||||
*/
|
*/
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
input: 'main.ts',
|
input: "main.ts",
|
||||||
output: {
|
output: {
|
||||||
dir: '.',
|
dir: ".",
|
||||||
sourcemap: 'inline',
|
sourcemap: "inline",
|
||||||
sourcemapExcludeSources: isProd,
|
sourcemapExcludeSources: isProd,
|
||||||
format: 'cjs',
|
format: "cjs",
|
||||||
exports: 'default',
|
exports: "default",
|
||||||
banner,
|
banner,
|
||||||
},
|
},
|
||||||
// treeshake: "safest",
|
// treeshake: "safest",
|
||||||
external: ['obsidian'],
|
external: ["obsidian"],
|
||||||
plugins: [
|
plugins: [
|
||||||
typescript({exclude:["pouchdb-browser.js"]}),
|
typescript({ exclude: ["pouchdb-browser.js", "pouchdb-browser-webpack"] }),
|
||||||
nodePolyfills(
|
nodeResolve({
|
||||||
// // {crypto:true}
|
browser: true,
|
||||||
{ include: "pouchdb-browser" }
|
}),
|
||||||
),
|
commonjs(),
|
||||||
nodeResolve({
|
// nodePolyfills(
|
||||||
browser: true,
|
// // // {crypto:true}
|
||||||
// preferBuiltins: true
|
// { include: "pouchdb-browser" }
|
||||||
}),
|
// ),
|
||||||
commonjs(),
|
],
|
||||||
|
|
||||||
]
|
|
||||||
};
|
};
|
||||||
@@ -1,22 +1,20 @@
|
|||||||
/* Sets all the text color to red! */
|
|
||||||
/* body {
|
|
||||||
color: red;
|
|
||||||
} */
|
|
||||||
|
|
||||||
.added {
|
.added {
|
||||||
color:black;
|
color: black;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
.normal {
|
.normal {
|
||||||
color:lightgray;
|
color: lightgray;
|
||||||
}
|
}
|
||||||
.deleted {
|
.deleted {
|
||||||
color:white;
|
color: white;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
/* text-decoration: line-through; */
|
/* text-decoration: line-through; */
|
||||||
}
|
}
|
||||||
.op-scrollable{
|
.op-scrollable {
|
||||||
overflow-y:scroll;
|
overflow-y: scroll;
|
||||||
/* min-height: 280px; */
|
/* min-height: 280px; */
|
||||||
max-height: 280px;
|
max-height: 280px;
|
||||||
}
|
}
|
||||||
|
.op-pre {
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
@@ -12,5 +12,6 @@
|
|||||||
"lib": ["dom", "es5", "scripthost", "es2015"]
|
"lib": ["dom", "es5", "scripthost", "es2015"]
|
||||||
},
|
},
|
||||||
"include": ["**/*.ts"],
|
"include": ["**/*.ts"],
|
||||||
"exclude": ["pouchdb-browser.js"]
|
"files": ["./main.ts"],
|
||||||
|
"exclude": ["pouchdb-browser-webpack"]
|
||||||
}
|
}
|
||||||