auto sync
This commit is contained in:
parent
a78a2a88cf
commit
6cdbc5b32e
@ -54,6 +54,7 @@ export interface RemotelySavePluginSettings {
|
|||||||
serviceType: SUPPORTED_SERVICES_TYPE;
|
serviceType: SUPPORTED_SERVICES_TYPE;
|
||||||
currLogLevel?: string;
|
currLogLevel?: string;
|
||||||
vaultRandomID?: string;
|
vaultRandomID?: string;
|
||||||
|
autoRunEveryMilliseconds?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RemoteItem {
|
export interface RemoteItem {
|
||||||
|
|||||||
296
src/main.ts
296
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 cloneDeep from "lodash/cloneDeep";
|
||||||
import { nanoid } from "nanoid";
|
import { nanoid } from "nanoid";
|
||||||
import feather from "feather-icons";
|
import feather from "feather-icons";
|
||||||
@ -50,6 +50,7 @@ const DEFAULT_SETTINGS: RemotelySavePluginSettings = {
|
|||||||
serviceType: "s3",
|
serviceType: "s3",
|
||||||
currLogLevel: "info",
|
currLogLevel: "info",
|
||||||
vaultRandomID: "",
|
vaultRandomID: "",
|
||||||
|
autoRunEveryMilliseconds: -1,
|
||||||
};
|
};
|
||||||
|
|
||||||
interface OAuth2Info {
|
interface OAuth2Info {
|
||||||
@ -60,6 +61,19 @@ interface OAuth2Info {
|
|||||||
revokeAuthSetting?: Setting;
|
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 {
|
export default class RemotelySavePlugin extends Plugin {
|
||||||
settings: RemotelySavePluginSettings;
|
settings: RemotelySavePluginSettings;
|
||||||
// cm: CodeMirror.Editor;
|
// cm: CodeMirror.Editor;
|
||||||
@ -68,20 +82,161 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
oauth2Info: OAuth2Info;
|
oauth2Info: OAuth2Info;
|
||||||
currLogLevel: string;
|
currLogLevel: string;
|
||||||
currSyncMsg?: 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() {
|
async onload() {
|
||||||
log.info(`loading plugin ${this.manifest.id}`);
|
log.info(`loading plugin ${this.manifest.id}`);
|
||||||
|
|
||||||
const iconNameSyncWait = `${this.manifest.id}-sync-wait`;
|
addIcon(iconNameSyncWait, iconSvgSyncWait);
|
||||||
const iconNameSyncRunning = `${this.manifest.id}-sync-running`;
|
addIcon(iconNameSyncRunning, iconSvgSyncRunning);
|
||||||
addIcon(
|
|
||||||
iconNameSyncWait,
|
|
||||||
feather.icons["rotate-ccw"].toSvg({ width: 100, height: 100 })
|
|
||||||
);
|
|
||||||
addIcon(
|
|
||||||
iconNameSyncRunning,
|
|
||||||
feather.icons["refresh-ccw"].toSvg({ width: 100, height: 100 })
|
|
||||||
);
|
|
||||||
|
|
||||||
this.oauth2Info = {
|
this.oauth2Info = {
|
||||||
verifier: "",
|
verifier: "",
|
||||||
@ -304,108 +459,11 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.addRibbonIcon(iconNameSyncWait, "Remotely Save", async () => {
|
this.syncRibbon = this.addRibbonIcon(
|
||||||
if (this.syncStatus !== "idle") {
|
iconNameSyncWait,
|
||||||
new Notice(
|
`${this.manifest.name}`,
|
||||||
`Remotely Save already running in stage ${this.syncStatus}!`
|
async () => this.syncRun("manual")
|
||||||
);
|
);
|
||||||
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.addSettingTab(new RemotelySaveSettingTab(this.app, this));
|
this.addSettingTab(new RemotelySaveSettingTab(this.app, this));
|
||||||
|
|
||||||
@ -413,9 +471,17 @@ export default class RemotelySavePlugin extends Plugin {
|
|||||||
// log.info("click", evt);
|
// log.info("click", evt);
|
||||||
// });
|
// });
|
||||||
|
|
||||||
// this.registerInterval(
|
if (
|
||||||
// window.setInterval(() => log.info("setInterval"), 5 * 60 * 1000)
|
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() {
|
onunload() {
|
||||||
|
|||||||
@ -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)
|
// below for general chooser (part 1/2)
|
||||||
//////////////////////////////////////////////////
|
//////////////////////////////////////////////////
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user