From 1d3a01dd49bf2e66e181444e9659c377d11dcc9a Mon Sep 17 00:00:00 2001 From: kangfenmao Date: Thu, 2 Jan 2025 11:07:11 +0800 Subject: [PATCH] feat: add sync status show --- src/renderer/src/i18n/locales/en-us.json | 7 +- src/renderer/src/i18n/locales/ja-jp.json | 7 +- src/renderer/src/i18n/locales/ru-ru.json | 7 +- src/renderer/src/i18n/locales/zh-cn.json | 7 +- src/renderer/src/i18n/locales/zh-tw.json | 7 +- .../settings/DataSettings/WebDavSettings.tsx | 97 +++++++++++-------- src/renderer/src/services/BackupService.ts | 26 ++++- src/renderer/src/store/runtime.ts | 26 ++++- 8 files changed, 137 insertions(+), 47 deletions(-) diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 0257b720..c401fd74 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -362,7 +362,12 @@ "webdav.minutes": "Minutes", "webdav.restore.button": "Restore from WebDAV", "webdav.title": "WebDAV", - "webdav.user": "WebDAV User" + "webdav.user": "WebDAV User", + "webdav.syncStatus": "Sync Status", + "webdav.autoSync.off": "Off", + "webdav.noSync": "Waiting for next sync", + "webdav.syncError": "Sync Error", + "webdav.lastSync": "Last Sync" }, "display.title": "Display Settings", "font_size.title": "Message font size", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 4f7b75f2..4389060d 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -360,7 +360,12 @@ "webdav.minutes": "分", "webdav.restore.button": "WebDAVから復元", "webdav.title": "WebDAV", - "webdav.user": "WebDAVユーザー" + "webdav.user": "WebDAVユーザー", + "webdav.syncStatus": "同期状態", + "webdav.autoSync.off": "オフ", + "webdav.noSync": "次回の同期を待っています", + "webdav.syncError": "同期エラー", + "webdav.lastSync": "最終同期" }, "display.title": "表示設定", "font_size.title": "メッセージのフォントサイズ", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 83745f6e..061cd412 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -362,7 +362,12 @@ "webdav.minutes": "минут", "webdav.restore.button": "Восстановление с WebDAV", "webdav.title": "WebDAV", - "webdav.user": "Пользователь WebDAV" + "webdav.user": "Пользователь WebDAV", + "webdav.syncStatus": "Статус синхронизации", + "webdav.autoSync.off": "Выключено", + "webdav.noSync": "Ожидание следующей синхронизации", + "webdav.syncError": "Ошибка синхронизации", + "webdav.lastSync": "Последняя синхронизация" }, "display.title": "Настройки отображения", "font_size.title": "Размер шрифта сообщений", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index cfb258c4..bf3a4fc8 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -363,7 +363,12 @@ "webdav.minutes": "分钟", "webdav.restore.button": "从 WebDAV 恢复", "webdav.title": "WebDAV", - "webdav.user": "WebDAV 用户名" + "webdav.user": "WebDAV 用户名", + "webdav.syncStatus": "同步状态", + "webdav.autoSync.off": "关闭", + "webdav.noSync": "等待下次同步", + "webdav.syncError": "同步错误", + "webdav.lastSync": "上次同步时间" }, "display.title": "显示设置", "font_size.title": "消息字体大小", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 0473de5f..ac92e4c7 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -362,7 +362,12 @@ "webdav.minutes": "分鐘", "webdav.restore.button": "從 WebDAV 恢復", "webdav.title": "WebDAV", - "webdav.user": "WebDAV 使用者名稱" + "webdav.user": "WebDAV 使用者名稱", + "webdav.syncStatus": "同步狀態", + "webdav.autoSync.off": "關閉", + "webdav.noSync": "等待下次同步", + "webdav.syncError": "同步錯誤", + "webdav.lastSync": "上次同步時間" }, "display.title": "顯示設定", "font_size.title": "訊息字體大小", diff --git a/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx b/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx index e6480a65..b34ea5ad 100644 --- a/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx @@ -1,5 +1,6 @@ -import { FolderOpenOutlined, SaveOutlined } from '@ant-design/icons' +import { FolderOpenOutlined, SaveOutlined, SyncOutlined } from '@ant-design/icons' import { HStack } from '@renderer/components/Layout' +import { useRuntime } from '@renderer/hooks/useRuntime' import { useSettings } from '@renderer/hooks/useSettings' import { backupToWebdav, restoreFromWebdav, startAutoSync, stopAutoSync } from '@renderer/services/BackupService' import { useAppDispatch } from '@renderer/store' @@ -11,7 +12,8 @@ import { setWebdavSyncInterval as _setWebdavSyncInterval, setWebdavUser as _setWebdavUser } from '@renderer/store/settings' -import { Button, Input, Select, Switch } from 'antd' +import { Button, Input, Select } from 'antd' +import dayjs from 'dayjs' import { FC, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -32,7 +34,6 @@ const WebDavSettings: FC = () => { const [webdavPass, setWebdavPass] = useState(webDAVPass) const [webdavPath, setWebdavPath] = useState(webDAVPath) - const [autoSync, setAutoSync] = useState(webDAVAutoSync) const [syncInterval, setSyncInterval] = useState(webDAVSyncInterval) const [backuping, setBackuping] = useState(false) @@ -42,6 +43,8 @@ const WebDavSettings: FC = () => { const { t } = useTranslation() + const { webdavSync } = useRuntime() + // 把之前备份的文件定时上传到 webdav,首先先配置 webdav 的 host, port, user, pass, path const onBackup = async () => { @@ -64,18 +67,40 @@ const WebDavSettings: FC = () => { setRestoring(false) } - const onToggleAutoSync = (checked: boolean) => { - dispatch(setWebdavAutoSync(checked)) - if (checked) { - startAutoSync() - } else { - stopAutoSync() - } - } - const onSyncIntervalChange = (value: number) => { setSyncInterval(value) dispatch(_setWebdavSyncInterval(value)) + if (value === 0) { + dispatch(setWebdavAutoSync(false)) + stopAutoSync() + } else { + dispatch(setWebdavAutoSync(true)) + startAutoSync() + } + } + + const renderSyncStatus = () => { + if (!webdavHost) return null + + if (!webdavSync.lastSyncTime && !webdavSync.syncing && !webdavSync.lastSyncError) { + return {t('settings.data.webdav.noSync')} + } + + return ( + + {webdavSync.syncing && } + {webdavSync.lastSyncTime && ( + + {t('settings.data.webdav.lastSync')}: {dayjs(webdavSync.lastSyncTime).format('HH:mm:ss')} + + )} + {webdavSync.lastSyncError && ( + + {t('settings.data.webdav.syncError')}: {webdavSync.lastSyncError} + + )} + + ) } return ( @@ -127,32 +152,6 @@ const WebDavSettings: FC = () => { /> - - {t('settings.data.webdav.autoSync')} - - { - setAutoSync(checked) - onToggleAutoSync(checked) - }} - disabled={!webdavHost} - /> - - - - {t('settings.general.backup.title')} @@ -165,6 +164,28 @@ const WebDavSettings: FC = () => { + + + {t('settings.data.webdav.autoSync')} + + + {webdavSync && syncInterval > 0 && ( + <> + + + {t('settings.data.webdav.syncStatus')} + {renderSyncStatus()} + + + )} ) } diff --git a/src/renderer/src/services/BackupService.ts b/src/renderer/src/services/BackupService.ts index 51672c80..ca0f3297 100644 --- a/src/renderer/src/services/BackupService.ts +++ b/src/renderer/src/services/BackupService.ts @@ -1,6 +1,7 @@ import db from '@renderer/databases' import i18n from '@renderer/i18n' import store from '@renderer/store' +import { setWebDAVSyncState } from '@renderer/store/runtime' import dayjs from 'dayjs' export async function backup() { @@ -63,6 +64,9 @@ export async function backupToWebdav({ showMessage = true }: { showMessage?: boo console.log('[Backup] Manual backup already in progress') return } + + store.dispatch(setWebDAVSyncState({ syncing: true, lastSyncError: null })) + const { webdavHost, webdavUser, webdavPass, webdavPath } = store.getState().settings const backupData = await getBackupData() @@ -76,11 +80,19 @@ export async function backupToWebdav({ showMessage = true }: { showMessage?: boo webdavPath }) if (success) { + store.dispatch( + setWebDAVSyncState({ + lastSyncTime: Date.now(), + lastSyncError: null + }) + ) showMessage && window.message.success({ content: i18n.t('message.backup.success'), key: 'backup' }) } else { + store.dispatch(setWebDAVSyncState({ lastSyncError: 'Backup failed' })) showMessage && window.message.error({ content: i18n.t('message.backup.failed'), key: 'backup' }) } } catch (error: any) { + store.dispatch(setWebDAVSyncState({ lastSyncError: error.message })) console.error('[backup] backupToWebdav: Error uploading file to WebDAV:', error) showMessage && window.modal.error({ @@ -88,6 +100,7 @@ export async function backupToWebdav({ showMessage = true }: { showMessage?: boo content: error.message }) } finally { + store.dispatch(setWebDAVSyncState({ syncing: false })) isManualBackupRunning = false } } @@ -125,9 +138,9 @@ export function startAutoSync() { return } - const { webdavAutoSync, webdavHost, webdavSyncInterval } = store.getState().settings + const { webdavAutoSync, webdavHost } = store.getState().settings - if (!webdavAutoSync || !webdavHost || webdavSyncInterval <= 0) { + if (!webdavAutoSync || !webdavHost) { console.log('[AutoSync] Invalid sync settings, auto sync disabled') return } @@ -144,7 +157,16 @@ export function startAutoSync() { syncTimeout = null } + const { webdavSyncInterval } = store.getState().settings + + if (webdavSyncInterval <= 0) { + console.log('[AutoSync] Invalid sync interval, auto sync disabled') + stopAutoSync() + return + } + syncTimeout = setTimeout(performAutoBackup, webdavSyncInterval * 60 * 1000) + console.log(`[AutoSync] Next sync scheduled in ${webdavSyncInterval} minutes`) } diff --git a/src/renderer/src/store/runtime.ts b/src/renderer/src/store/runtime.ts index bcfd0f18..2bd8ecc8 100644 --- a/src/renderer/src/store/runtime.ts +++ b/src/renderer/src/store/runtime.ts @@ -10,6 +10,12 @@ export interface UpdateState { available: boolean } +export interface WebDAVSyncState { + lastSyncTime: number | null + syncing: boolean + lastSyncError: string | null +} + export interface RuntimeState { avatar: string generating: boolean @@ -17,6 +23,7 @@ export interface RuntimeState { searching: boolean filesPath: string update: UpdateState + webdavSync: WebDAVSyncState } const initialState: RuntimeState = { @@ -31,6 +38,11 @@ const initialState: RuntimeState = { downloading: false, downloadProgress: 0, available: false + }, + webdavSync: { + lastSyncTime: null, + syncing: false, + lastSyncError: null } } @@ -55,11 +67,21 @@ const runtimeSlice = createSlice({ }, setUpdateState: (state, action: PayloadAction>) => { state.update = { ...state.update, ...action.payload } + }, + setWebDAVSyncState: (state, action: PayloadAction>) => { + state.webdavSync = { ...state.webdavSync, ...action.payload } } } }) -export const { setAvatar, setGenerating, setMinappShow, setSearching, setFilesPath, setUpdateState } = - runtimeSlice.actions +export const { + setAvatar, + setGenerating, + setMinappShow, + setSearching, + setFilesPath, + setUpdateState, + setWebDAVSyncState +} = runtimeSlice.actions export default runtimeSlice.reducer