From 6cdbc5b32e93568dc4142f3363c48b177bfa29ca Mon Sep 17 00:00:00 2001 From: fyears <1142836+fyears@users.noreply.github.com> Date: Sun, 16 Jan 2022 23:02:49 +0800 Subject: [PATCH] auto sync --- src/baseTypes.ts | 1 + src/main.ts | 298 +++++++++++++++++++++++++++++------------------ src/settings.ts | 40 +++++++ 3 files changed, 223 insertions(+), 116 deletions(-) diff --git a/src/baseTypes.ts b/src/baseTypes.ts index df2e598..c16bd54 100644 --- a/src/baseTypes.ts +++ b/src/baseTypes.ts @@ -54,6 +54,7 @@ export interface RemotelySavePluginSettings { serviceType: SUPPORTED_SERVICES_TYPE; currLogLevel?: string; vaultRandomID?: string; + autoRunEveryMilliseconds?: number; } export interface RemoteItem { diff --git a/src/main.ts b/src/main.ts index 5d32e03..64b8ed1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,4 +1,4 @@ -import { Modal, Notice, Plugin, Setting, addIcon } from "obsidian"; +import { Modal, Notice, Plugin, Setting, addIcon, setIcon } from "obsidian"; import cloneDeep from "lodash/cloneDeep"; import { nanoid } from "nanoid"; import feather from "feather-icons"; @@ -50,6 +50,7 @@ const DEFAULT_SETTINGS: RemotelySavePluginSettings = { serviceType: "s3", currLogLevel: "info", vaultRandomID: "", + autoRunEveryMilliseconds: -1, }; interface OAuth2Info { @@ -60,6 +61,19 @@ interface OAuth2Info { revokeAuthSetting?: Setting; } +type SyncTriggerSourceType = "manual" | "auto"; + +const iconNameSyncWait = `remotely-save-sync-wait`; +const iconNameSyncRunning = `remotely-save-sync-running`; +const iconSvgSyncWait = feather.icons["rotate-ccw"].toSvg({ + width: 100, + height: 100, +}); +const iconSvgSyncRunning = feather.icons["refresh-ccw"].toSvg({ + width: 100, + height: 100, +}); + export default class RemotelySavePlugin extends Plugin { settings: RemotelySavePluginSettings; // cm: CodeMirror.Editor; @@ -68,20 +82,161 @@ export default class RemotelySavePlugin extends Plugin { oauth2Info: OAuth2Info; currLogLevel: string; currSyncMsg?: string; + syncRibbon?: HTMLElement; + autoRunIntervalID?: number; + + async syncRun(triggerSource: SyncTriggerSourceType = "manual") { + const getNotice = (x: string) => { + // only show notices in manual mode + // no notice in auto mode + if (triggerSource === "manual") { + new Notice(x); + } + }; + if (this.syncStatus !== "idle") { + // here the notice is shown regardless of triggerSource + new Notice(`Remotely Save already running in stage ${this.syncStatus}!`); + if (this.currSyncMsg !== undefined && this.currSyncMsg !== "") { + new Notice(this.currSyncMsg); + } + return; + } + + let originLabel = `${this.manifest.name}`; + if (this.syncRibbon !== undefined) { + originLabel = this.syncRibbon.getAttribute("aria-label"); + } + + try { + log.info( + `${ + this.manifest.id + }-${Date.now()}: start sync, triggerSource=${triggerSource}` + ); + + if (this.syncRibbon !== undefined) { + setIcon(this.syncRibbon, iconNameSyncRunning); + this.syncRibbon.setAttribute( + "aria-label", + `${this.manifest.name}: ${triggerSource} syncing` + ); + } + + //log.info(`huh ${this.settings.password}`) + getNotice( + `1/7 Remotely Save Sync Preparing (${this.settings.serviceType})` + ); + this.syncStatus = "preparing"; + + getNotice("2/7 Starting to fetch remote meta data."); + this.syncStatus = "getting_remote_meta"; + const self = this; + const client = new RemoteClient( + this.settings.serviceType, + this.settings.s3, + this.settings.webdav, + this.settings.dropbox, + this.settings.onedrive, + this.app.vault.getName(), + () => self.saveSettings() + ); + const remoteRsp = await client.listFromRemote(); + // log.info(remoteRsp); + + getNotice("3/7 Starting to fetch local meta data."); + this.syncStatus = "getting_local_meta"; + const local = this.app.vault.getAllLoadedFiles(); + const localHistory = await loadDeleteRenameHistoryTableByVault( + this.db, + this.settings.vaultRandomID + ); + // log.info(local); + // log.info(localHistory); + + getNotice("4/7 Checking password correct or not."); + this.syncStatus = "checking_password"; + const passwordCheckResult = await isPasswordOk( + remoteRsp.Contents, + this.settings.password + ); + if (!passwordCheckResult.ok) { + getNotice("something goes wrong while checking password"); + throw Error(passwordCheckResult.reason); + } + + getNotice("5/7 Starting to generate sync plan."); + this.syncStatus = "generating_plan"; + const syncPlan = await getSyncPlan( + remoteRsp.Contents, + local, + localHistory, + this.db, + this.settings.vaultRandomID, + client.serviceType, + this.settings.password + ); + log.info(syncPlan.mixedStates); // for debugging + await insertSyncPlanRecordByVault( + this.db, + syncPlan, + this.settings.vaultRandomID + ); + + // The operations above are read only and kind of safe. + // The operations below begins to write or delete (!!!) something. + + getNotice("6/7 Remotely Save Sync data exchanging!"); + + this.syncStatus = "syncing"; + await doActualSync( + client, + this.db, + this.settings.vaultRandomID, + this.app.vault, + syncPlan, + this.settings.password, + (i: number, totalCount: number, pathName: string, decision: string) => + self.setCurrSyncMsg(i, totalCount, pathName, decision) + ); + + getNotice("7/7 Remotely Save finish!"); + this.currSyncMsg = ""; + this.syncStatus = "finish"; + this.syncStatus = "idle"; + + if (this.syncRibbon !== undefined) { + setIcon(this.syncRibbon, iconNameSyncWait); + this.syncRibbon.setAttribute("aria-label", originLabel); + } + + log.info( + `${ + this.manifest.id + }-${Date.now()}: finish sync, triggerSource=${triggerSource}` + ); + } catch (error) { + const msg = `${ + this.manifest.id + }-${Date.now()}: abort sync, triggerSource=${triggerSource}, error while ${ + this.syncStatus + }`; + log.info(msg); + log.info(error); + getNotice(msg); + getNotice(error.message); + this.syncStatus = "idle"; + if (this.syncRibbon !== undefined) { + setIcon(this.syncRibbon, iconNameSyncWait); + this.syncRibbon.setAttribute("aria-label", originLabel); + } + } + } async onload() { log.info(`loading plugin ${this.manifest.id}`); - const iconNameSyncWait = `${this.manifest.id}-sync-wait`; - const iconNameSyncRunning = `${this.manifest.id}-sync-running`; - addIcon( - iconNameSyncWait, - feather.icons["rotate-ccw"].toSvg({ width: 100, height: 100 }) - ); - addIcon( - iconNameSyncRunning, - feather.icons["refresh-ccw"].toSvg({ width: 100, height: 100 }) - ); + addIcon(iconNameSyncWait, iconSvgSyncWait); + addIcon(iconNameSyncRunning, iconSvgSyncRunning); this.oauth2Info = { verifier: "", @@ -304,108 +459,11 @@ export default class RemotelySavePlugin extends Plugin { } ); - this.addRibbonIcon(iconNameSyncWait, "Remotely Save", async () => { - if (this.syncStatus !== "idle") { - new Notice( - `Remotely Save already running in stage ${this.syncStatus}!` - ); - if (this.currSyncMsg !== undefined && this.currSyncMsg !== "") { - new Notice(this.currSyncMsg); - } - return; - } - - try { - //log.info(`huh ${this.settings.password}`) - new Notice( - `1/7 Remotely Save Sync Preparing (${this.settings.serviceType})` - ); - this.syncStatus = "preparing"; - - new Notice("2/7 Starting to fetch remote meta data."); - this.syncStatus = "getting_remote_meta"; - const self = this; - const client = new RemoteClient( - this.settings.serviceType, - this.settings.s3, - this.settings.webdav, - this.settings.dropbox, - this.settings.onedrive, - this.app.vault.getName(), - () => self.saveSettings() - ); - const remoteRsp = await client.listFromRemote(); - // log.info(remoteRsp); - - new Notice("3/7 Starting to fetch local meta data."); - this.syncStatus = "getting_local_meta"; - const local = this.app.vault.getAllLoadedFiles(); - const localHistory = await loadDeleteRenameHistoryTableByVault( - this.db, - this.settings.vaultRandomID - ); - // log.info(local); - // log.info(localHistory); - - new Notice("4/7 Checking password correct or not."); - this.syncStatus = "checking_password"; - const passwordCheckResult = await isPasswordOk( - remoteRsp.Contents, - this.settings.password - ); - if (!passwordCheckResult.ok) { - new Notice("something goes wrong while checking password"); - throw Error(passwordCheckResult.reason); - } - - new Notice("5/7 Starting to generate sync plan."); - this.syncStatus = "generating_plan"; - const syncPlan = await getSyncPlan( - remoteRsp.Contents, - local, - localHistory, - this.db, - this.settings.vaultRandomID, - client.serviceType, - this.settings.password - ); - log.info(syncPlan.mixedStates); // for debugging - await insertSyncPlanRecordByVault( - this.db, - syncPlan, - this.settings.vaultRandomID - ); - - // The operations above are read only and kind of safe. - // The operations below begins to write or delete (!!!) something. - - new Notice("6/7 Remotely Save Sync data exchanging!"); - - this.syncStatus = "syncing"; - await doActualSync( - client, - this.db, - this.settings.vaultRandomID, - this.app.vault, - syncPlan, - this.settings.password, - (i: number, totalCount: number, pathName: string, decision: string) => - self.setCurrSyncMsg(i, totalCount, pathName, decision) - ); - - new Notice("7/7 Remotely Save finish!"); - this.currSyncMsg = ""; - this.syncStatus = "finish"; - this.syncStatus = "idle"; - } catch (error) { - const msg = `Remotely Save error while ${this.syncStatus}`; - log.info(msg); - log.info(error); - new Notice(msg); - new Notice(error.message); - this.syncStatus = "idle"; - } - }); + this.syncRibbon = this.addRibbonIcon( + iconNameSyncWait, + `${this.manifest.name}`, + async () => this.syncRun("manual") + ); this.addSettingTab(new RemotelySaveSettingTab(this.app, this)); @@ -413,9 +471,17 @@ export default class RemotelySavePlugin extends Plugin { // log.info("click", evt); // }); - // this.registerInterval( - // window.setInterval(() => log.info("setInterval"), 5 * 60 * 1000) - // ); + if ( + this.settings.autoRunEveryMilliseconds !== undefined && + this.settings.autoRunEveryMilliseconds !== null && + this.settings.autoRunEveryMilliseconds > 0 + ) { + const intervalID = window.setInterval(() => { + this.syncRun("auto"); + }, this.settings.autoRunEveryMilliseconds); + this.autoRunIntervalID = intervalID; + this.registerInterval(intervalID); + } } onunload() { diff --git a/src/settings.ts b/src/settings.ts index ac38767..d67383a 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -382,6 +382,46 @@ export class RemotelySaveSettingTab extends PluginSettingTab { }); }); + const scheduleDiv = generalDiv.createEl("div"); + new Setting(scheduleDiv) + .setName("schedule for auto run") + .setDesc( + "The plugin trys to schedule the running after every interval. Battery may be impacted." + ) + .addDropdown((dropdown) => { + dropdown.addOption("-1", "(no auto run)"); + dropdown.addOption(`${1000 * 60 * 1}`, "every 1 minute"); + dropdown.addOption(`${1000 * 60 * 5}`, "every 5 minutes"); + dropdown.addOption(`${1000 * 60 * 10}`, "every 10 minutes"); + dropdown.addOption(`${1000 * 60 * 30}`, "every 30 minutes"); + + dropdown + .setValue(`${this.plugin.settings.autoRunEveryMilliseconds}`) + .onChange(async (val: string) => { + const realVal = parseInt(val); + this.plugin.settings.autoRunEveryMilliseconds = realVal; + await this.plugin.saveSettings(); + if ( + (realVal === undefined || realVal === null || realVal <= 0) && + this.plugin.autoRunIntervalID !== undefined + ) { + // clear + window.clearInterval(this.plugin.autoRunIntervalID); + this.plugin.autoRunIntervalID = undefined; + } else if ( + realVal !== undefined && + realVal !== null && + realVal > 0 + ) { + const intervalID = window.setInterval(() => { + this.plugin.syncRun("auto"); + }, realVal); + this.plugin.autoRunIntervalID = intervalID; + this.plugin.registerInterval(intervalID); + } + }); + }); + ////////////////////////////////////////////////// // below for general chooser (part 1/2) //////////////////////////////////////////////////