mirror of
https://github.com/vrtmrz/obsidian-livesync.git
synced 2024-12-12 09:04:06 +02:00
Fixing issues and tidy up the setting dialog.
Fixed timing problem of synchronization note and contents. Tidy up the setting dialog. Add Escape hatch
This commit is contained in:
parent
20bdf057fe
commit
39e2eab023
238
main.ts
238
main.ts
@ -11,7 +11,7 @@ const MAX_DOC_SIZE_BIN = 102400; // 100kb
|
|||||||
const VER = 10;
|
const VER = 10;
|
||||||
|
|
||||||
const RECENT_MOFIDIED_DOCS_QTY = 30;
|
const RECENT_MOFIDIED_DOCS_QTY = 30;
|
||||||
|
const LEAF_WAIT_TIMEOUT = 30000; // in synchronization, waiting missing leaf time out.
|
||||||
const LOG_LEVEL = {
|
const LOG_LEVEL = {
|
||||||
VERBOSE: 1,
|
VERBOSE: 1,
|
||||||
INFO: 10,
|
INFO: 10,
|
||||||
@ -34,6 +34,7 @@ interface ObsidianLiveSyncSettings {
|
|||||||
minimumChunkSize: number;
|
minimumChunkSize: number;
|
||||||
longLineThreshold: number;
|
longLineThreshold: number;
|
||||||
showVerboseLog: boolean;
|
showVerboseLog: boolean;
|
||||||
|
suspendFileWatching: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_SETTINGS: ObsidianLiveSyncSettings = {
|
const DEFAULT_SETTINGS: ObsidianLiveSyncSettings = {
|
||||||
@ -45,11 +46,12 @@ const DEFAULT_SETTINGS: ObsidianLiveSyncSettings = {
|
|||||||
syncOnStart: false,
|
syncOnStart: false,
|
||||||
savingDelay: 200,
|
savingDelay: 200,
|
||||||
lessInformationInLog: false,
|
lessInformationInLog: false,
|
||||||
gcDelay: 30,
|
gcDelay: 300,
|
||||||
versionUpFlash: "",
|
versionUpFlash: "",
|
||||||
minimumChunkSize: 20,
|
minimumChunkSize: 20,
|
||||||
longLineThreshold: 250,
|
longLineThreshold: 250,
|
||||||
showVerboseLog: false,
|
showVerboseLog: false,
|
||||||
|
suspendFileWatching: false,
|
||||||
};
|
};
|
||||||
interface Entry {
|
interface Entry {
|
||||||
_id: string;
|
_id: string;
|
||||||
@ -99,7 +101,9 @@ interface EntryLeaf {
|
|||||||
_rev?: string;
|
_rev?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type EntryDoc = Entry | NewEntry | PlainEntry | LoadedEntry | EntryLeaf;
|
type EntryBody = Entry | NewEntry | PlainEntry;
|
||||||
|
type EntryDoc = EntryBody | LoadedEntry | EntryLeaf;
|
||||||
|
|
||||||
type diff_result_leaf = {
|
type diff_result_leaf = {
|
||||||
rev: string;
|
rev: string;
|
||||||
data: string;
|
data: string;
|
||||||
@ -188,7 +192,7 @@ const connectRemoteCouchDB = async (uri: string, auth: { username: string; passw
|
|||||||
let info = await db.info();
|
let info = await db.info();
|
||||||
return { db: db, info: info };
|
return { db: db, info: info };
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -212,6 +216,8 @@ class LocalPouchDB {
|
|||||||
[key: string]: string;
|
[key: string]: string;
|
||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
|
corruptedEntries: { [key: string]: EntryDoc } = {};
|
||||||
|
|
||||||
constructor(app: App, plugin: ObsidianLiveSyncPlugin, dbname: string) {
|
constructor(app: App, plugin: ObsidianLiveSyncPlugin, dbname: string) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.app = app;
|
this.app = app;
|
||||||
@ -251,23 +257,112 @@ class LocalPouchDB {
|
|||||||
let idrev = id + rev;
|
let idrev = id + rev;
|
||||||
return this.recentModifiedDocs.indexOf(idrev) !== -1;
|
return this.recentModifiedDocs.indexOf(idrev) !== -1;
|
||||||
}
|
}
|
||||||
|
changeHandler: PouchDB.Core.Changes<{}> = null;
|
||||||
async initializeDatabase() {
|
async initializeDatabase() {
|
||||||
if (this.localDatabase != null) this.localDatabase.close();
|
if (this.localDatabase != null) this.localDatabase.close();
|
||||||
|
if (this.changeHandler != null) {
|
||||||
|
this.changeHandler.cancel();
|
||||||
|
}
|
||||||
this.localDatabase = null;
|
this.localDatabase = null;
|
||||||
this.localDatabase = new PouchDB<EntryDoc>(this.dbname + "-livesync", {
|
this.localDatabase = new PouchDB<EntryDoc>(this.dbname + "-livesync", {
|
||||||
auto_compaction: true,
|
auto_compaction: true,
|
||||||
revs_limit: 100,
|
revs_limit: 100,
|
||||||
deterministic_revs: true,
|
deterministic_revs: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Traceing the leaf id
|
||||||
|
let changes = this.localDatabase
|
||||||
|
.changes({
|
||||||
|
since: "now",
|
||||||
|
live: true,
|
||||||
|
filter: (doc) => doc.type == "leaf",
|
||||||
|
})
|
||||||
|
.on("change", (e) => {
|
||||||
|
if (e.deleted) return;
|
||||||
|
this.leafArrived(e.id);
|
||||||
|
});
|
||||||
|
this.changeHandler = changes;
|
||||||
await this.prepareHashFunctions();
|
await this.prepareHashFunctions();
|
||||||
}
|
}
|
||||||
|
|
||||||
async prepareHashFunctions() {
|
async prepareHashFunctions() {
|
||||||
if (this.h32 != null) return;
|
if (this.h32 != null) return;
|
||||||
const { h32, h64 } = await xxhash();
|
const { h32, h64 } = await xxhash();
|
||||||
this.h32 = h32;
|
this.h32 = h32;
|
||||||
this.h64 = h64;
|
this.h64 = h64;
|
||||||
}
|
}
|
||||||
async getDBEntry(id: string, opt?: PouchDB.Core.GetOptions): Promise<false | LoadedEntry> {
|
|
||||||
|
// leaf waiting
|
||||||
|
leafArrivedCallbacks: { [key: string]: (() => void)[] } = {};
|
||||||
|
|
||||||
|
leafArrived(id: string) {
|
||||||
|
if (typeof this.leafArrivedCallbacks[id] !== "undefined") {
|
||||||
|
for (let func of this.leafArrivedCallbacks[id]) {
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
delete this.leafArrivedCallbacks[id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// wait
|
||||||
|
waitForLeafReady(id: string): Promise<boolean> {
|
||||||
|
return new Promise((_, res) => {
|
||||||
|
// Set timeout.
|
||||||
|
let timer = setTimeout(() => res(false), LEAF_WAIT_TIMEOUT);
|
||||||
|
if (typeof this.leafArrivedCallbacks[id] == "undefined") {
|
||||||
|
this.leafArrivedCallbacks[id] = [];
|
||||||
|
}
|
||||||
|
this.leafArrivedCallbacks[id].push(() => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
res(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDBLeaf(id: string): Promise<string> {
|
||||||
|
// when in cache, use that.
|
||||||
|
if (this.hashCacheRev[id]) {
|
||||||
|
return this.hashCacheRev[id];
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
let w = await this.localDatabase.get(id);
|
||||||
|
if (w.type == "leaf") {
|
||||||
|
this.hashCache[w.data] = id;
|
||||||
|
this.hashCacheRev[id] = w.data;
|
||||||
|
return w.data;
|
||||||
|
}
|
||||||
|
throw new Error(`retrive leaf, but it was not leaf.`);
|
||||||
|
} catch (ex) {
|
||||||
|
if (ex.status && ex.status == 404) {
|
||||||
|
// just leaf is not ready.
|
||||||
|
// wait for on
|
||||||
|
if ((await this.waitForLeafReady(id)) === false) {
|
||||||
|
throw new Error(`time out (waiting leaf)`);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
// retrive again.
|
||||||
|
let w = await this.localDatabase.get(id);
|
||||||
|
|
||||||
|
if (w.type == "leaf") {
|
||||||
|
this.hashCache[w.data] = id;
|
||||||
|
this.hashCacheRev[id] = w.data;
|
||||||
|
return w.data;
|
||||||
|
}
|
||||||
|
throw new Error(`retrive leaf, but it was not leaf.`);
|
||||||
|
} catch (ex) {
|
||||||
|
if (ex.status && ex.status == 404) {
|
||||||
|
throw new Error("leaf is not found");
|
||||||
|
}
|
||||||
|
this.addLog(`Something went wrong on retriving leaf`);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.addLog(`Something went wrong on retriving leaf`);
|
||||||
|
throw ex;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getDBEntry(id: string, opt?: PouchDB.Core.GetOptions, retryCount = 5): Promise<false | LoadedEntry> {
|
||||||
try {
|
try {
|
||||||
let obj: EntryDocResponse = null;
|
let obj: EntryDocResponse = null;
|
||||||
if (opt) {
|
if (opt) {
|
||||||
@ -296,33 +391,24 @@ class LocalPouchDB {
|
|||||||
children: [],
|
children: [],
|
||||||
datatype: "newnote",
|
datatype: "newnote",
|
||||||
};
|
};
|
||||||
|
if (typeof this.corruptedEntries[doc._id] != "undefined") {
|
||||||
|
delete this.corruptedEntries[doc._id];
|
||||||
|
}
|
||||||
return doc;
|
return doc;
|
||||||
// simple note
|
// simple note
|
||||||
}
|
}
|
||||||
if (obj.type == "newnote" || obj.type == "plain") {
|
if (obj.type == "newnote" || obj.type == "plain") {
|
||||||
// search childrens
|
// search childrens
|
||||||
try {
|
try {
|
||||||
let childrens = [];
|
let childrens;
|
||||||
for (var v of obj.children) {
|
try {
|
||||||
if (typeof this.hashCacheRev[v] !== "undefined") {
|
childrens = await Promise.all(obj.children.map((e) => this.getDBLeaf(e)));
|
||||||
childrens.push(this.hashCacheRev[v]);
|
} catch (ex) {
|
||||||
} else {
|
this.addLog(`Something went wrong on reading elements of ${obj._id} from database.`, LOG_LEVEL.NOTICE);
|
||||||
try {
|
this.corruptedEntries[obj._id] = obj;
|
||||||
let elem = await this.localDatabase.get(v);
|
return false;
|
||||||
if (elem.type && elem.type == "leaf") {
|
|
||||||
childrens.push(elem.data);
|
|
||||||
} else {
|
|
||||||
throw new Error("linked document is not leaf");
|
|
||||||
}
|
|
||||||
} catch (ex) {
|
|
||||||
if (ex.status && ex.status == 404) {
|
|
||||||
this.addLog(`Missing document content!, could not read ${v} of ${obj._id}(${obj._rev}) from database.`, LOG_LEVEL.NOTICE);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let data = childrens.join("");
|
let data = childrens.join("");
|
||||||
let doc: LoadedEntry & PouchDB.Core.IdMeta & PouchDB.Core.GetMeta = {
|
let doc: LoadedEntry & PouchDB.Core.IdMeta & PouchDB.Core.GetMeta = {
|
||||||
data: data,
|
data: data,
|
||||||
@ -336,7 +422,9 @@ class LocalPouchDB {
|
|||||||
datatype: obj.type,
|
datatype: obj.type,
|
||||||
_conflicts: obj._conflicts,
|
_conflicts: obj._conflicts,
|
||||||
};
|
};
|
||||||
|
if (typeof this.corruptedEntries[doc._id] != "undefined") {
|
||||||
|
delete this.corruptedEntries[doc._id];
|
||||||
|
}
|
||||||
return doc;
|
return doc;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
if (ex.status && ex.status == 404) {
|
if (ex.status && ex.status == 404) {
|
||||||
@ -373,6 +461,9 @@ class LocalPouchDB {
|
|||||||
obj._deleted = true;
|
obj._deleted = true;
|
||||||
let r = await this.localDatabase.put(obj);
|
let r = await this.localDatabase.put(obj);
|
||||||
this.updateRecentModifiedDocs(r.id, r.rev, true);
|
this.updateRecentModifiedDocs(r.id, r.rev, true);
|
||||||
|
if (typeof this.corruptedEntries[obj._id] != "undefined") {
|
||||||
|
delete this.corruptedEntries[obj._id];
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
// simple note
|
// simple note
|
||||||
}
|
}
|
||||||
@ -381,6 +472,9 @@ class LocalPouchDB {
|
|||||||
let r = await this.localDatabase.put(obj);
|
let r = await this.localDatabase.put(obj);
|
||||||
this.addLog(`entry removed:${obj._id}-${r.rev}`);
|
this.addLog(`entry removed:${obj._id}-${r.rev}`);
|
||||||
this.updateRecentModifiedDocs(r.id, r.rev, true);
|
this.updateRecentModifiedDocs(r.id, r.rev, true);
|
||||||
|
if (typeof this.corruptedEntries[obj._id] != "undefined") {
|
||||||
|
delete this.corruptedEntries[obj._id];
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
@ -528,7 +622,6 @@ class LocalPouchDB {
|
|||||||
size: note.size,
|
size: note.size,
|
||||||
type: plainSplit ? "plain" : "newnote",
|
type: plainSplit ? "plain" : "newnote",
|
||||||
};
|
};
|
||||||
|
|
||||||
// Here for upsert logic,
|
// Here for upsert logic,
|
||||||
try {
|
try {
|
||||||
let old = await this.localDatabase.get(newDoc._id);
|
let old = await this.localDatabase.get(newDoc._id);
|
||||||
@ -545,6 +638,9 @@ class LocalPouchDB {
|
|||||||
}
|
}
|
||||||
let r = await this.localDatabase.put(newDoc);
|
let r = await this.localDatabase.put(newDoc);
|
||||||
this.updateRecentModifiedDocs(r.id, r.rev, newDoc._deleted);
|
this.updateRecentModifiedDocs(r.id, r.rev, newDoc._deleted);
|
||||||
|
if (typeof this.corruptedEntries[note._id] != "undefined") {
|
||||||
|
delete this.corruptedEntries[note._id];
|
||||||
|
}
|
||||||
this.addLog(`note saven:${newDoc._id}:${r.rev}`);
|
this.addLog(`note saven:${newDoc._id}:${r.rev}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -576,11 +672,13 @@ class LocalPouchDB {
|
|||||||
let syncOption: PouchDB.Replication.SyncOptions = keepAlive ? { live: true, retry: true, heartbeat: 30000, ...syncOptionBase } : { ...syncOptionBase };
|
let syncOption: PouchDB.Replication.SyncOptions = keepAlive ? { live: true, retry: true, heartbeat: 30000, ...syncOptionBase } : { ...syncOptionBase };
|
||||||
|
|
||||||
let db = dbret.db;
|
let db = dbret.db;
|
||||||
|
|
||||||
//replicate once
|
//replicate once
|
||||||
let replicate = this.localDatabase.replicate.from(db, syncOptionBase);
|
let replicate = this.localDatabase.replicate.from(db, syncOptionBase);
|
||||||
replicate
|
replicate
|
||||||
.on("change", async (e) => {
|
.on("change", async (e) => {
|
||||||
|
// when in first run, replication will send us tombstone data
|
||||||
|
// and in normal cases, all leavs should sent before the entry that contains these item.
|
||||||
|
// so skip to completed all, we should treat all changes.
|
||||||
try {
|
try {
|
||||||
callback(e.docs);
|
callback(e.docs);
|
||||||
this.addLog(`pulled ${e.docs.length} doc(s)`);
|
this.addLog(`pulled ${e.docs.length} doc(s)`);
|
||||||
@ -590,10 +688,11 @@ class LocalPouchDB {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on("complete", async (info) => {
|
.on("complete", async (info) => {
|
||||||
replicate.removeAllListeners();
|
|
||||||
replicate.cancel();
|
replicate.cancel();
|
||||||
|
replicate.removeAllListeners();
|
||||||
this.syncHandler = null;
|
this.syncHandler = null;
|
||||||
if (this.syncHandler != null) {
|
if (this.syncHandler != null) {
|
||||||
|
this.syncHandler.cancel();
|
||||||
this.syncHandler.removeAllListeners();
|
this.syncHandler.removeAllListeners();
|
||||||
}
|
}
|
||||||
this.syncHandler = this.localDatabase.sync(db, syncOption);
|
this.syncHandler = this.localDatabase.sync(db, syncOption);
|
||||||
@ -644,10 +743,14 @@ class LocalPouchDB {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async resetDatabase() {
|
async resetDatabase() {
|
||||||
|
if (this.changeHandler != null) {
|
||||||
|
this.changeHandler.cancel();
|
||||||
|
}
|
||||||
await this.closeReplication();
|
await this.closeReplication();
|
||||||
await this.localDatabase.destroy();
|
await this.localDatabase.destroy();
|
||||||
this.localDatabase = null;
|
this.localDatabase = null;
|
||||||
await this.initializeDatabase();
|
await this.initializeDatabase();
|
||||||
|
this.disposeHashCache();
|
||||||
this.addLog("Local Database Reset", LOG_LEVEL.NOTICE);
|
this.addLog("Local Database Reset", LOG_LEVEL.NOTICE);
|
||||||
}
|
}
|
||||||
async tryResetRemoteDatabase(setting: ObsidianLiveSyncSettings) {
|
async tryResetRemoteDatabase(setting: ObsidianLiveSyncSettings) {
|
||||||
@ -741,7 +844,6 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
//localDatabase: PouchDB.Database<EntryDoc>;
|
//localDatabase: PouchDB.Database<EntryDoc>;
|
||||||
localDatabase: LocalPouchDB;
|
localDatabase: LocalPouchDB;
|
||||||
logMessage: string[] = [];
|
logMessage: string[] = [];
|
||||||
// onLogChanged: () => void;
|
|
||||||
statusBar: HTMLElement;
|
statusBar: HTMLElement;
|
||||||
statusBar2: HTMLElement;
|
statusBar2: HTMLElement;
|
||||||
|
|
||||||
@ -827,6 +929,14 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.replicate();
|
this.replicate();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
// this.addCommand({
|
||||||
|
// id: "livesync-test",
|
||||||
|
// name: "test reset db and replicate",
|
||||||
|
// callback: async () => {
|
||||||
|
// await this.resetLocalDatabase();
|
||||||
|
// await this.replicate();
|
||||||
|
// },
|
||||||
|
// });
|
||||||
this.addCommand({
|
this.addCommand({
|
||||||
id: "livesync-gc",
|
id: "livesync-gc",
|
||||||
name: "garbage collect now",
|
name: "garbage collect now",
|
||||||
@ -904,6 +1014,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watchWindowVisiblity() {
|
watchWindowVisiblity() {
|
||||||
|
if (this.settings.suspendFileWatching) return;
|
||||||
let isHidden = document.hidden;
|
let isHidden = document.hidden;
|
||||||
if (isHidden) {
|
if (isHidden) {
|
||||||
this.localDatabase.closeReplication();
|
this.localDatabase.closeReplication();
|
||||||
@ -919,16 +1030,19 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
watchWorkspaceOpen(file: TFile) {
|
watchWorkspaceOpen(file: TFile) {
|
||||||
|
if (this.settings.suspendFileWatching) return;
|
||||||
if (file == null) return;
|
if (file == null) return;
|
||||||
this.localDatabase.disposeHashCache();
|
this.localDatabase.disposeHashCache();
|
||||||
this.showIfConflicted(file);
|
this.showIfConflicted(file);
|
||||||
this.gcHook();
|
this.gcHook();
|
||||||
}
|
}
|
||||||
watchVaultChange(file: TFile, ...args: any[]) {
|
watchVaultChange(file: TFile, ...args: any[]) {
|
||||||
|
if (this.settings.suspendFileWatching) return;
|
||||||
this.updateIntoDB(file);
|
this.updateIntoDB(file);
|
||||||
this.gcHook();
|
this.gcHook();
|
||||||
}
|
}
|
||||||
watchVaultDelete(file: TFile & TFolder) {
|
watchVaultDelete(file: TFile & TFolder) {
|
||||||
|
if (this.settings.suspendFileWatching) return;
|
||||||
if (file.children) {
|
if (file.children) {
|
||||||
//folder
|
//folder
|
||||||
this.deleteFolderOnDB(file);
|
this.deleteFolderOnDB(file);
|
||||||
@ -939,6 +1053,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
this.gcHook();
|
this.gcHook();
|
||||||
}
|
}
|
||||||
watchVaultRename(file: TFile & TFolder, oldFile: any) {
|
watchVaultRename(file: TFile & TFolder, oldFile: any) {
|
||||||
|
if (this.settings.suspendFileWatching) return;
|
||||||
if (file.children) {
|
if (file.children) {
|
||||||
// this.renameFolder(file,oldFile);
|
// this.renameFolder(file,oldFile);
|
||||||
this.addLog(`folder name changed:(this operation is not supported) ${file.path}`);
|
this.addLog(`folder name changed:(this operation is not supported) ${file.path}`);
|
||||||
@ -995,7 +1110,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async doc2storage_create(docEntry: Entry, force?: boolean) {
|
async doc2storage_create(docEntry: EntryBody, force?: boolean) {
|
||||||
let doc = await this.localDatabase.getDBEntry(docEntry._id, { rev: docEntry._rev });
|
let doc = await this.localDatabase.getDBEntry(docEntry._id, { rev: docEntry._rev });
|
||||||
if (doc === false) return;
|
if (doc === false) return;
|
||||||
if (doc.datatype == "newnote") {
|
if (doc.datatype == "newnote") {
|
||||||
@ -1026,7 +1141,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
await this.deleteVaultItem(dir);
|
await this.deleteVaultItem(dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async doc2storate_modify(docEntry: Entry, file: TFile, force?: boolean) {
|
async doc2storate_modify(docEntry: EntryBody, file: TFile, force?: boolean) {
|
||||||
if (docEntry._deleted) {
|
if (docEntry._deleted) {
|
||||||
//basically pass.
|
//basically pass.
|
||||||
//but if there're no docs left, delete file.
|
//but if there're no docs left, delete file.
|
||||||
@ -1072,7 +1187,7 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
//eq.case
|
//eq.case
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
async handleDBChanged(change: Entry) {
|
async handleDBChanged(change: EntryBody) {
|
||||||
let allfiles = this.app.vault.getFiles();
|
let allfiles = this.app.vault.getFiles();
|
||||||
let targetFiles = allfiles.filter((e) => e.path == change._id);
|
let targetFiles = allfiles.filter((e) => e.path == change._id);
|
||||||
if (targetFiles.length == 0) {
|
if (targetFiles.length == 0) {
|
||||||
@ -1091,13 +1206,15 @@ export default class ObsidianLiveSyncPlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//---> Sync
|
//---> Sync
|
||||||
async parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<Entry>>): Promise<void> {
|
async parseReplicationResult(docs: Array<PouchDB.Core.ExistingDocument<EntryDoc>>): Promise<void> {
|
||||||
for (var change of docs) {
|
for (var change of docs) {
|
||||||
if (this.localDatabase.isSelfModified(change._id, change._rev)) {
|
if (this.localDatabase.isSelfModified(change._id, change._rev)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.addLog("replication change arrived", LOG_LEVEL.VERBOSE);
|
this.addLog("replication change arrived", LOG_LEVEL.VERBOSE);
|
||||||
await this.handleDBChanged(change);
|
if (change.type != "leaf") {
|
||||||
|
await this.handleDBChanged(change);
|
||||||
|
}
|
||||||
this.gcHook();
|
this.gcHook();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1588,6 +1705,8 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
containerEl.createEl("h3", { text: "Database configuration" });
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName("File to Database saving delay")
|
.setName("File to Database saving delay")
|
||||||
.setDesc("ms, between 200 and 5000, restart required.")
|
.setDesc("ms, between 200 and 5000, restart required.")
|
||||||
@ -1615,15 +1734,17 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
let v = Number(value);
|
let v = Number(value);
|
||||||
if (isNaN(v) || v > 5000) {
|
if (isNaN(v) || v > 5000) {
|
||||||
return 0;
|
return 0;
|
||||||
//text.inputEl.va;
|
|
||||||
}
|
}
|
||||||
this.plugin.settings.gcDelay = v;
|
this.plugin.settings.gcDelay = v;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
});
|
});
|
||||||
text.inputEl.setAttribute("type", "number");
|
text.inputEl.setAttribute("type", "number");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
containerEl.createEl("h3", { text: "Log Setting" });
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName("Log")
|
.setName("Do not show low-priority Log")
|
||||||
.setDesc("Reduce log infomations")
|
.setDesc("Reduce log infomations")
|
||||||
.addToggle((toggle) =>
|
.addToggle((toggle) =>
|
||||||
toggle.setValue(this.plugin.settings.lessInformationInLog).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.lessInformationInLog).onChange(async (value) => {
|
||||||
@ -1640,18 +1761,21 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
containerEl.createEl("h3", { text: "Sync setting" });
|
||||||
|
|
||||||
if (this.plugin.settings.versionUpFlash != "") {
|
if (this.plugin.settings.versionUpFlash != "") {
|
||||||
let c = containerEl.createEl("div", { text: this.plugin.settings.versionUpFlash });
|
let c = containerEl.createEl("div", { text: this.plugin.settings.versionUpFlash });
|
||||||
c.createEl("button", { text: "I got it and updated." }, (e) => {
|
c.createEl("button", { text: "I got it and updated." }, (e) => {
|
||||||
e.addEventListener("click", async () => {
|
e.addEventListener("click", async () => {
|
||||||
this.plugin.settings.versionUpFlash = "";
|
this.plugin.settings.versionUpFlash = "";
|
||||||
this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
c.remove();
|
c.remove();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
c.addClass("op-warn");
|
c.addClass("op-warn");
|
||||||
}
|
}
|
||||||
// containerEl.createDiv(this.plugin.settings.versionUpFlash);
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName("LiveSync")
|
.setName("LiveSync")
|
||||||
.setDesc("Sync realtime")
|
.setDesc("Sync realtime")
|
||||||
@ -1664,7 +1788,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
);
|
);
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName("Sync on Save")
|
.setName("Sync on Save")
|
||||||
.setDesc("Sync on Save")
|
.setDesc("When you save file, sync automatically")
|
||||||
.addToggle((toggle) =>
|
.addToggle((toggle) =>
|
||||||
toggle.setValue(this.plugin.settings.syncOnSave).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.syncOnSave).onChange(async (value) => {
|
||||||
this.plugin.settings.syncOnSave = value;
|
this.plugin.settings.syncOnSave = value;
|
||||||
@ -1673,13 +1797,14 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
);
|
);
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName("Sync on Start")
|
.setName("Sync on Start")
|
||||||
.setDesc("Sync on Start")
|
.setDesc("Start synchronization on Obsidian started.")
|
||||||
.addToggle((toggle) =>
|
.addToggle((toggle) =>
|
||||||
toggle.setValue(this.plugin.settings.syncOnStart).onChange(async (value) => {
|
toggle.setValue(this.plugin.settings.syncOnStart).onChange(async (value) => {
|
||||||
this.plugin.settings.syncOnStart = value;
|
this.plugin.settings.syncOnStart = value;
|
||||||
await this.plugin.saveSettings();
|
await this.plugin.saveSettings();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName("Minimum chunk size")
|
.setName("Minimum chunk size")
|
||||||
.setDesc("(letters), minimum chunk size.")
|
.setDesc("(letters), minimum chunk size.")
|
||||||
@ -1696,6 +1821,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
});
|
});
|
||||||
text.inputEl.setAttribute("type", "number");
|
text.inputEl.setAttribute("type", "number");
|
||||||
});
|
});
|
||||||
|
|
||||||
new Setting(containerEl)
|
new Setting(containerEl)
|
||||||
.setName("LongLine Threshold")
|
.setName("LongLine Threshold")
|
||||||
.setDesc("(letters), If the line is longer than this, make the line to chunk")
|
.setDesc("(letters), If the line is longer than this, make the line to chunk")
|
||||||
@ -1712,6 +1838,7 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
});
|
});
|
||||||
text.inputEl.setAttribute("type", "number");
|
text.inputEl.setAttribute("type", "number");
|
||||||
});
|
});
|
||||||
|
|
||||||
new Setting(containerEl).setName("Local Database Operations").addButton((button) =>
|
new Setting(containerEl).setName("Local Database Operations").addButton((button) =>
|
||||||
button
|
button
|
||||||
.setButtonText("Reset local database")
|
.setButtonText("Reset local database")
|
||||||
@ -1738,6 +1865,19 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
await this.plugin.garbageCollect();
|
await this.plugin.garbageCollect();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
containerEl.createEl("h3", { text: "Hatch" });
|
||||||
|
|
||||||
|
new Setting(containerEl)
|
||||||
|
.setName("Suspend file watching")
|
||||||
|
.setDesc("if enables it, all file operations are ignored.")
|
||||||
|
.addToggle((toggle) =>
|
||||||
|
toggle.setValue(this.plugin.settings.suspendFileWatching).onChange(async (value) => {
|
||||||
|
this.plugin.settings.suspendFileWatching = value;
|
||||||
|
await this.plugin.saveSettings();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
new Setting(containerEl).setName("Remote Database Operations").addButton((button) =>
|
new Setting(containerEl).setName("Remote Database Operations").addButton((button) =>
|
||||||
button
|
button
|
||||||
.setButtonText("Reset remote database")
|
.setButtonText("Reset remote database")
|
||||||
@ -1754,5 +1894,21 @@ class ObsidianLiveSyncSettingTab extends PluginSettingTab {
|
|||||||
await this.plugin.tryResetRemoteDatabase();
|
await this.plugin.tryResetRemoteDatabase();
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
containerEl.createEl("h3", { text: "Corrupted data" });
|
||||||
|
|
||||||
|
if (Object.keys(this.plugin.localDatabase.corruptedEntries).length > 0) {
|
||||||
|
let cx = containerEl.createEl("div", { text: "If you have copy of these items on any device, simply edit once or twice. Or not, delete this. sorry.." });
|
||||||
|
for (let k in this.plugin.localDatabase.corruptedEntries) {
|
||||||
|
let xx = cx.createEl("div", { text: `${k}` });
|
||||||
|
|
||||||
|
let ba = xx.createEl("button", { text: `Delete this` }, (e) => {
|
||||||
|
e.addEventListener("click", async () => {
|
||||||
|
await this.plugin.localDatabase.deleteDBEntry(k);
|
||||||
|
xx.remove();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"id": "obsidian-livesync",
|
"id": "obsidian-livesync",
|
||||||
"name": "Obsidian Live sync",
|
"name": "Obsidian Live sync",
|
||||||
"version": "0.1.2",
|
"version": "0.1.3",
|
||||||
"minAppVersion": "0.9.12",
|
"minAppVersion": "0.9.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.",
|
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"author": "vorotamoroz",
|
"author": "vorotamoroz",
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "obsidian-livesync",
|
"name": "obsidian-livesync",
|
||||||
"version": "0.1.1",
|
"version": "0.1.3",
|
||||||
"description": "obsidian Live synchronization plugin.",
|
"description": "Reflect your vault changes to some other devices immediately. Please make sure to disable other synchronize solutions to avoid content corruption or duplication.",
|
||||||
"main": "main.js",
|
"main": "main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "rollup --config rollup.config.js -w",
|
"dev": "rollup --config rollup.config.js -w",
|
||||||
|
Loading…
Reference in New Issue
Block a user