From 35f5355c8ebc90d3921e703d09abf7be4254b959 Mon Sep 17 00:00:00 2001 From: fyears <1142836+fyears@users.noreply.github.com> Date: Mon, 17 Jun 2024 00:15:33 +0800 Subject: [PATCH] optimize sync status bar --- pro/src/sync.ts | 7 ++- src/langs/en.json | 28 ++++++---- src/langs/zh_cn.json | 28 ++++++---- src/langs/zh_tw.json | 28 ++++++---- src/localdb.ts | 22 +++++++- src/main.ts | 129 +++++++++++++++++++++++++++++++------------ src/settings.ts | 8 ++- 7 files changed, 179 insertions(+), 71 deletions(-) diff --git a/pro/src/sync.ts b/pro/src/sync.ts index df1c53d..7382f70 100644 --- a/pro/src/sync.ts +++ b/pro/src/sync.ts @@ -1417,6 +1417,7 @@ export const doActualSync = async ( db: InternalDBs, profiler: Profiler | undefined, conflictAction: ConflictActionType, + triggerSource: SyncTriggerSourceType, callbackSyncProcess?: any ) => { profiler?.addIndent(); @@ -1524,13 +1525,16 @@ export const doActualSync = async ( // ); await callbackSyncProcess?.( + triggerSource, realCounter, realTotalCount, key, val.decision ); - realCounter += 1; + if (val.change === undefined || val.change) { + realCounter += 1; + } await dispatchOperationToActualV3( key, @@ -1743,6 +1747,7 @@ export async function syncer( db, profiler, settings.conflictAction ?? "keep_newer", + triggerSource, callbackSyncProcess ); profiler?.insert(`finish step${step} (actual sync)`); diff --git a/src/langs/en.json b/src/langs/en.json index 0a0ce8d..9f1de11 100644 --- a/src/langs/en.json +++ b/src/langs/en.json @@ -45,20 +45,26 @@ "command_exportlogsindb": "export logs saved in db", - "statusbar_time_years": "Synced {{time}} years ago", - "statusbar_time_months": "Synced {{time}} months ago", - "statusbar_time_weeks": "Synced {{time}} weeks ago", - "statusbar_time_days": "Synced {{time}} days ago", - "statusbar_time_hours": "Synced {{time}} hours ago", - "statusbar_time_minutes": "Synced {{time}} minutes ago", - "statusbar_time_lessminute": "Synced last minute ago", - "statusbar_lastsync": "Synced {{time}} ago", + "statusbar_sync_source_manual": "Manual: ", + "statusbar_sync_source_dry": "Dry: ", + "statusbar_sync_source_auto": "Auto: ", + "statusbar_sync_source_auto_once_init": "Auto (init): ", + "statusbar_sync_source_auto_sync_on_save": "Auto (save): ", + "statusbar_sync_status_prefix_success": "Successfully synced ", + "statusbar_sync_status_prefix_failed": "Failed to sync ", + "statusbar_time_years": "{{time}} years ago", + "statusbar_time_months": "{{time}} months ago", + "statusbar_time_weeks": "{{time}} weeks ago", + "statusbar_time_days": "{{time}} days ago", + "statusbar_time_hours": "{{time}} hours ago", + "statusbar_time_minutes": "{{time}} minutes ago", + "statusbar_time_lessminute": "last minute ago", + "statusbar_time_now": "just now", "statusbar_syncing": "Syncing...", - "statusbar_failed": "Last sync failed", - "statusbar_now": "Synced just now", - "statusbar_lastsync_label": "Last successful Sync on {{date}}", + "statusbar_lastsync_label": "on {{date}}", "statusbar_lastsync_never": "Never Synced", "statusbar_lastsync_never_label": "Never Synced before", + "modal_password_title": "Hold on and PLEASE READ ON...", "modal_password_shortdesc": "If the field is not empty, files would be encrypted locally before being uploaded.\nIf the field is empty, then files would be uploaded without encryption.", "modal_password_attn1": "Attention 1/5: The vault name is NOT encrypted. The plugin creates a folder with the vault name on some remote services.", diff --git a/src/langs/zh_cn.json b/src/langs/zh_cn.json index a449ba3..a2e1f78 100644 --- a/src/langs/zh_cn.json +++ b/src/langs/zh_cn.json @@ -45,20 +45,26 @@ "command_exportsyncplans_all": "导出同步计划(所有)(export sync plans (all))", "command_exportlogsindb": "从数据库导出终端日志(export logs saved in db)", - "statusbar_time_years": "{{time}} 年前同步", - "statusbar_time_months": "{{time}} 月前同步", - "statusbar_time_weeks": "{{time}} 周前同步", - "statusbar_time_days": "{{time}} 天前同步", - "statusbar_time_hours": "{{time}} 小时前同步", - "statusbar_time_minutes": "{{time}} 分钟前同步", - "statusbar_time_lessminute": "一分钟之内同步", - "statusbar_lastsync": "上一次同步于:{{time}}", + "statusbar_sync_source_manual": "手动:", + "statusbar_sync_source_dry": "空跑:", + "statusbar_sync_source_auto": "自动:", + "statusbar_sync_source_auto_once_init": "启动时自动:", + "statusbar_sync_source_auto_sync_on_save": "保存时自动:", + "statusbar_sync_status_prefix_success": "同步成功:", + "statusbar_sync_status_prefix_failed": "同步失败:", + "statusbar_time_years": "{{time}} 年前", + "statusbar_time_months": "{{time}} 月前", + "statusbar_time_weeks": "{{time}} 周前", + "statusbar_time_days": "{{time}} 天前", + "statusbar_time_hours": "{{time}} 小时前", + "statusbar_time_minutes": "{{time}} 分钟前", + "statusbar_time_lessminute": "一分钟之内", + "statusbar_time_now": "刚刚", "statusbar_syncing": "正在同步", - "statusbar_failed": "上次同步失败了", - "statusbar_now": "刚同步完", - "statusbar_lastsync_label": "上一次同步于:{{date}}", + "statusbar_lastsync_label": "日期:{{date}}", "statusbar_lastsync_never": "没触发过同步", "statusbar_lastsync_never_label": "没触发过同步", + "modal_password_title": "稍等一下,请阅读下文:", "modal_password_shortdesc": "如果密码不是空的,那么文件会在上传之前,在本地先用此密码加密。\n如果密码是空的,那么文件会被非加密地上传。", "modal_password_attn1": "注意 1/5:库(Vault)名字是不会加密的!本插件会在一些远程存储里创建一个和库名字有着同名的文件夹。", diff --git a/src/langs/zh_tw.json b/src/langs/zh_tw.json index b5b8e1b..4e29002 100644 --- a/src/langs/zh_tw.json +++ b/src/langs/zh_tw.json @@ -44,20 +44,26 @@ "command_exportsyncplans_all": "匯出同步計劃(所有)(export sync plans (all))", "command_exportlogsindb": "從資料庫匯出終端日誌(export logs saved in db)", - "statusbar_time_years": "{{time}} 年前同步", - "statusbar_time_months": "{{time}} 月前同步", - "statusbar_time_weeks": "{{time}} 周前同步", - "statusbar_time_days": "{{time}} 天前同步", - "statusbar_time_hours": "{{time}} 小時前同步", - "statusbar_time_minutes": "{{time}} 分鐘前同步", - "statusbar_time_lessminute": "一分鐘之內同步", - "statusbar_lastsync": "上一次同步於:{{time}}", + "statusbar_sync_source_manual": "手動:", + "statusbar_sync_source_dry": "空跑:", + "statusbar_sync_source_auto": "自動:", + "statusbar_sync_source_auto_once_init": "啟動時自動:", + "statusbar_sync_source_auto_sync_on_save": "儲存時自動:", + "statusbar_sync_status_prefix_success": "同步成功:", + "statusbar_sync_status_prefix_failed": "同步失敗:", + "statusbar_time_years": "{{time}} 年前", + "statusbar_time_months": "{{time}} 月前", + "statusbar_time_weeks": "{{time}} 周前", + "statusbar_time_days": "{{time}} 天前", + "statusbar_time_hours": "{{time}} 小時前", + "statusbar_time_minutes": "{{time}} 分鐘前", + "statusbar_time_lessminute": "一分鐘之內", + "statusbar_time_now": "剛剛", "statusbar_syncing": "正在同步", - "statusbar_failed": "上次同步失敗了", - "statusbar_now": "剛同步完", - "statusbar_lastsync_label": "上一次同步於:{{date}}", + "statusbar_lastsync_label": "日期:{{date}}", "statusbar_lastsync_never": "沒觸發過同步", "statusbar_lastsync_never_label": "沒觸發過同步", + "modal_password_title": "稍等一下,請閱讀下文:", "modal_password_shortdesc": "如果密碼不是空的,那麼檔案會在上傳之前,在本地先用此密碼加密。\n如果密碼是空的,那麼檔案會被非加密地上傳。", "modal_password_attn1": "注意 1/5:儲存庫(Vault)名字是不會加密的!本外掛會在一些遠端儲存裡建立一個和庫名字有著同名的資料夾。", diff --git a/src/localdb.ts b/src/localdb.ts index be2a990..b4cb94c 100644 --- a/src/localdb.ts +++ b/src/localdb.ts @@ -519,7 +519,27 @@ export const getLastSuccessSyncTimeByVault = async ( ) => { return (await db.simpleKVForMiscTbl.getItem( `${vaultRandomID}-lastSuccessSyncMillis` - )) as number; + )) as number | null | undefined; +}; + +export const upsertLastFailedSyncTimeByVault = async ( + db: InternalDBs, + vaultRandomID: string, + millis: number +) => { + await db.simpleKVForMiscTbl.setItem( + `${vaultRandomID}-lastFailedSyncMillis`, + millis + ); +}; + +export const getLastFailedSyncTimeByVault = async ( + db: InternalDBs, + vaultRandomID: string +) => { + return (await db.simpleKVForMiscTbl.getItem( + `${vaultRandomID}-lastFailedSyncMillis` + )) as number | null | undefined; }; export const upsertPluginVersionByVault = async ( diff --git a/src/main.ts b/src/main.ts index a798bfd..cc3ccaa 100644 --- a/src/main.ts +++ b/src/main.ts @@ -92,8 +92,10 @@ import { type InternalDBs, clearAllLoggerOutputRecords, clearExpiredSyncPlanRecords, + getLastFailedSyncTimeByVault, getLastSuccessSyncTimeByVault, prepareDBs, + upsertLastFailedSyncTimeByVault, upsertLastSuccessSyncTimeByVault, upsertPluginVersionByVault, } from "./localdb"; @@ -176,6 +178,29 @@ const getIconSvg = () => { return res; }; +const getStatusBarShortMsgFromSyncSource = ( + t: (x: TransItemType, vars?: any) => string, + s: SyncTriggerSourceType | undefined +) => { + if (s === undefined) { + return ""; + } + switch (s) { + case "manual": + return t("statusbar_sync_source_manual"); + case "dry": + return t("statusbar_sync_source_dry"); + case "auto": + return t("statusbar_sync_source_auto"); + case "auto_once_init": + return t("statusbar_sync_source_auto_once_init"); + case "auto_sync_on_save": + return t("statusbar_sync_source_auto_sync_on_save"); + default: + throw Error(`no translate for ${s}`); + } +}; + export default class RemotelySavePlugin extends Plugin { settings!: RemotelySavePluginSettings; db!: InternalDBs; @@ -393,17 +418,15 @@ export default class RemotelySavePlugin extends Plugin { ) => { if (step === 1) { // change status to "syncing..." on statusbar - this.updateLastSuccessSyncMsg(-1); + this.updateLastSyncMsg(s, -1, -1); } else if (step === 8 && everythingOk) { - const lastSuccessSyncMillis = Date.now(); - await upsertLastSuccessSyncTimeByVault( - this.db, - this.vaultRandomID, - lastSuccessSyncMillis - ); - this.updateLastSuccessSyncMsg(lastSuccessSyncMillis); + const ts = Date.now(); + await upsertLastSuccessSyncTimeByVault(this.db, this.vaultRandomID, ts); + this.updateLastSyncMsg(s, ts, null); } else if (!everythingOk) { - this.updateLastSuccessSyncMsg(-2); // magic number + const ts = Date.now(); + await upsertLastFailedSyncTimeByVault(this.db, this.vaultRandomID, ts); + this.updateLastSyncMsg(s, null, ts); // magic number } }; @@ -412,12 +435,15 @@ export default class RemotelySavePlugin extends Plugin { }; const callbackSyncProcess = async ( + s: SyncTriggerSourceType, realCounter: number, realTotalCount: number, pathName: string, decision: string ) => { this.setCurrSyncMsg( + t, + s, realCounter, realTotalCount, pathName, @@ -1060,15 +1086,21 @@ export default class RemotelySavePlugin extends Plugin { this.statusBarElement = statusBarItem.createEl("span"); this.statusBarElement.setAttribute("data-tooltip-position", "top"); - this.updateLastSuccessSyncMsg( - await getLastSuccessSyncTimeByVault(this.db, this.vaultRandomID) + this.updateLastSyncMsg( + undefined, + await getLastSuccessSyncTimeByVault(this.db, this.vaultRandomID), + await getLastFailedSyncTimeByVault(this.db, this.vaultRandomID) ); // update statusbar text every 30 seconds this.registerInterval( window.setInterval(async () => { - this.updateLastSuccessSyncMsg( - await getLastSuccessSyncTimeByVault(this.db, this.vaultRandomID) - ); + if (!this.isSyncing) { + this.updateLastSyncMsg( + undefined, + await getLastSuccessSyncTimeByVault(this.db, this.vaultRandomID), + await getLastFailedSyncTimeByVault(this.db, this.vaultRandomID) + ); + } }, 1000 * 30) ); } @@ -1618,7 +1650,8 @@ export default class RemotelySavePlugin extends Plugin { if ( caller === "SYNC" || - (caller === "FILE_CHANGES" && lastModified > lastSuccessSyncMillis) + (caller === "FILE_CHANGES" && + lastModified > (lastSuccessSyncMillis ?? 1)) ) { console.debug( `so lastModified > lastSuccessSyncMillis or it's called while syncing before` @@ -1734,17 +1767,34 @@ export default class RemotelySavePlugin extends Plugin { } setCurrSyncMsg( + t: (x: TransItemType, vars?: any) => string, + s: SyncTriggerSourceType, i: number, totalCount: number, pathName: string, decision: string, triggerSource: SyncTriggerSourceType ) { - const msg = `syncing progress=${i}/${totalCount},decision=${decision},path=${pathName},source=${triggerSource}`; - this.currSyncMsg = msg; + const L = `${totalCount}`.length; + const iStr = `${i}`.padStart(L, "0"); + const prefix = getStatusBarShortMsgFromSyncSource(t, s); + const shortMsg = prefix + `Syncing ${iStr}/${totalCount}`; + const longMsg = + prefix + + `Syncing progress=${iStr}/${totalCount},decision=${decision},path=${pathName},source=${triggerSource}`; + this.currSyncMsg = longMsg; + + if (this.statusBarElement !== undefined) { + this.statusBarElement.setText(shortMsg); + this.statusBarElement.setAttribute("aria-label", longMsg); + } } - updateLastSuccessSyncMsg(lastSuccessSyncMillis?: number) { + updateLastSyncMsg( + s: SyncTriggerSourceType | undefined, + lastSuccessSyncMillis: number | null | undefined, + lastFailedSyncMillis: number | null | undefined + ) { if (this.statusBarElement === undefined) return; const t = (x: TransItemType, vars?: any) => { @@ -1754,18 +1804,27 @@ export default class RemotelySavePlugin extends Plugin { let lastSyncMsg = t("statusbar_lastsync_never"); let lastSyncLabelMsg = t("statusbar_lastsync_never_label"); - if (lastSuccessSyncMillis !== undefined && lastSuccessSyncMillis === -1) { - lastSyncMsg = t("statusbar_syncing"); - } + const inputTs = Math.max( + lastSuccessSyncMillis ?? -999, + lastFailedSyncMillis ?? -999 + ); + const isSuccess = + (lastSuccessSyncMillis ?? -999) >= (lastFailedSyncMillis ?? -999); - if (lastSuccessSyncMillis !== undefined && lastSuccessSyncMillis === -2) { - lastSyncMsg = t("statusbar_failed"); - lastSyncLabelMsg = t("statusbar_failed"); - } - - if (lastSuccessSyncMillis !== undefined && lastSuccessSyncMillis > 0) { - const deltaTime = Date.now() - lastSuccessSyncMillis; + if (lastSuccessSyncMillis === -1) { + // magic number + // otherwise how can we know we are syncing?? + lastSyncMsg = + getStatusBarShortMsgFromSyncSource(t, s!) + t("statusbar_syncing"); + } else if (inputTs > 0) { + let prefix = ""; + if (isSuccess) { + prefix = t("statusbar_sync_status_prefix_success"); + } else { + prefix = t("statusbar_sync_status_prefix_failed"); + } + const deltaTime = Date.now() - inputTs; // create human readable time const years = Math.floor(deltaTime / 31556952000); const months = Math.floor(deltaTime / 2629746000); @@ -1774,9 +1833,7 @@ export default class RemotelySavePlugin extends Plugin { const hours = Math.floor(deltaTime / 3600000); const minutes = Math.floor(deltaTime / 60000); const seconds = Math.floor(deltaTime / 1000); - let timeText = ""; - if (years > 0) { timeText = t("statusbar_time_years", { time: years }); } else if (months > 0) { @@ -1792,10 +1849,9 @@ export default class RemotelySavePlugin extends Plugin { } else if (seconds > 30) { timeText = t("statusbar_time_lessminute"); } else { - timeText = t("statusbar_now"); + timeText = t("statusbar_time_now"); } - - const dateText = new Date(lastSuccessSyncMillis).toLocaleTimeString( + const dateText = new Date(inputTs).toLocaleTimeString( navigator.language, { weekday: "long", @@ -1805,8 +1861,11 @@ export default class RemotelySavePlugin extends Plugin { } ); - lastSyncMsg = timeText; - lastSyncLabelMsg = t("statusbar_lastsync_label", { date: dateText }); + lastSyncMsg = prefix + timeText; + lastSyncLabelMsg = + prefix + t("statusbar_lastsync_label", { date: dateText }); + } else { + // TODO: no idea what happened. } this.statusBarElement.setText(lastSyncMsg); diff --git a/src/settings.ts b/src/settings.ts index ad5dc89..456bc6c 100644 --- a/src/settings.ts +++ b/src/settings.ts @@ -55,6 +55,7 @@ import { clearAllPrevSyncRecordByVault, clearAllSyncPlanRecords, destroyDBs, + upsertLastFailedSyncTimeByVault, upsertLastSuccessSyncTimeByVault, } from "./localdb"; import type RemotelySavePlugin from "./main"; // unavoidable @@ -2134,7 +2135,12 @@ export class RemotelySaveSettingTab extends PluginSettingTab { this.plugin.vaultRandomID, -1 ); - this.plugin.updateLastSuccessSyncMsg(-1); + await upsertLastFailedSyncTimeByVault( + this.plugin.db, + this.plugin.vaultRandomID, + -1 + ); + this.plugin.updateLastSyncMsg(undefined, null, null); new Notice(t("settings_resetstatusbar_notice")); }); });