cherry-studio/src/renderer/src/pages/settings/DataSettings/WebDavSettings.tsx
africa1207 839e1de332 feat: 优化webdav备份文件恢复管理功能 (#4699)
* feat: 优化webdav备份文件恢复管理功能

* fix: 恢复和删除操作更改为文字而非图标

* feat: 统一坚果云与webdav备份恢复功能
2025-04-13 21:52:16 +08:00

211 lines
8.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { FolderOpenOutlined, SaveOutlined, SyncOutlined, WarningOutlined } from '@ant-design/icons'
import { HStack } from '@renderer/components/Layout'
import { WebdavBackupManager } from '@renderer/components/WebdavBackupManager'
import { useWebdavBackupModal, WebdavBackupModal } from '@renderer/components/WebdavModals'
import { useTheme } from '@renderer/context/ThemeProvider'
import { useSettings } from '@renderer/hooks/useSettings'
import { startAutoSync, stopAutoSync } from '@renderer/services/BackupService'
import { useAppDispatch, useAppSelector } from '@renderer/store'
import {
setWebdavAutoSync,
setWebdavHost as _setWebdavHost,
setWebdavPass as _setWebdavPass,
setWebdavPath as _setWebdavPath,
setWebdavSyncInterval as _setWebdavSyncInterval,
setWebdavUser as _setWebdavUser
} from '@renderer/store/settings'
import { Button, Input, Select, Tooltip } from 'antd'
import dayjs from 'dayjs'
import { FC, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { SettingDivider, SettingGroup, SettingRow, SettingRowTitle, SettingTitle } from '..'
const WebDavSettings: FC = () => {
const {
webdavHost: webDAVHost,
webdavUser: webDAVUser,
webdavPass: webDAVPass,
webdavPath: webDAVPath,
webdavSyncInterval: webDAVSyncInterval
} = useSettings()
const [webdavHost, setWebdavHost] = useState<string | undefined>(webDAVHost)
const [webdavUser, setWebdavUser] = useState<string | undefined>(webDAVUser)
const [webdavPass, setWebdavPass] = useState<string | undefined>(webDAVPass)
const [webdavPath, setWebdavPath] = useState<string | undefined>(webDAVPath)
const [backupManagerVisible, setBackupManagerVisible] = useState(false)
const [syncInterval, setSyncInterval] = useState<number>(webDAVSyncInterval)
const dispatch = useAppDispatch()
const { theme } = useTheme()
const { t } = useTranslation()
const { webdavSync } = useAppSelector((state) => state.backup)
// 把之前备份的文件定时上传到 webdav首先先配置 webdav 的 host, port, user, pass, path
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 <span style={{ color: 'var(--text-secondary)' }}>{t('settings.data.webdav.noSync')}</span>
}
return (
<HStack gap="5px" alignItems="center">
{webdavSync.syncing && <SyncOutlined spin />}
{!webdavSync.syncing && webdavSync.lastSyncError && (
<Tooltip title={`${t('settings.data.webdav.syncError')}: ${webdavSync.lastSyncError}`}>
<WarningOutlined style={{ color: 'red' }} />
</Tooltip>
)}
{webdavSync.lastSyncTime && (
<span style={{ color: 'var(--text-secondary)' }}>
{t('settings.data.webdav.lastSync')}: {dayjs(webdavSync.lastSyncTime).format('HH:mm:ss')}
</span>
)}
</HStack>
)
}
const { isModalVisible, handleBackup, handleCancel, backuping, customFileName, setCustomFileName, showBackupModal } =
useWebdavBackupModal()
const showBackupManager = () => {
setBackupManagerVisible(true)
}
const closeBackupManager = () => {
setBackupManagerVisible(false)
}
return (
<SettingGroup theme={theme}>
<SettingTitle>{t('settings.data.webdav.title')}</SettingTitle>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.data.webdav.host')}</SettingRowTitle>
<Input
placeholder={t('settings.data.webdav.host.placeholder')}
value={webdavHost}
onChange={(e) => setWebdavHost(e.target.value)}
style={{ width: 250 }}
type="url"
onBlur={() => dispatch(_setWebdavHost(webdavHost || ''))}
/>
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.data.webdav.user')}</SettingRowTitle>
<Input
placeholder={t('settings.data.webdav.user')}
value={webdavUser}
onChange={(e) => setWebdavUser(e.target.value)}
style={{ width: 250 }}
onBlur={() => dispatch(_setWebdavUser(webdavUser || ''))}
/>
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.data.webdav.password')}</SettingRowTitle>
<Input.Password
placeholder={t('settings.data.webdav.password')}
value={webdavPass}
onChange={(e) => setWebdavPass(e.target.value)}
style={{ width: 250 }}
onBlur={() => dispatch(_setWebdavPass(webdavPass || ''))}
/>
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.data.webdav.path')}</SettingRowTitle>
<Input
placeholder={t('settings.data.webdav.path.placeholder')}
value={webdavPath}
onChange={(e) => setWebdavPath(e.target.value)}
style={{ width: 250 }}
onBlur={() => dispatch(_setWebdavPath(webdavPath || ''))}
/>
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.general.backup.title')}</SettingRowTitle>
<HStack gap="5px" justifyContent="space-between">
<Button onClick={showBackupModal} icon={<SaveOutlined />} loading={backuping}>
{t('settings.data.webdav.backup.button')}
</Button>
<Button
onClick={showBackupManager}
icon={<FolderOpenOutlined />}
disabled={!webdavHost || !webdavUser || !webdavPass || !webdavPath}>
{t('settings.data.webdav.restore.button')}
</Button>
</HStack>
</SettingRow>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.data.webdav.autoSync')}</SettingRowTitle>
<Select value={syncInterval} onChange={onSyncIntervalChange} disabled={!webdavHost} style={{ width: 120 }}>
<Select.Option value={0}>{t('settings.data.webdav.autoSync.off')}</Select.Option>
<Select.Option value={1}>{t('settings.data.webdav.minute_interval', { count: 1 })}</Select.Option>
<Select.Option value={5}>{t('settings.data.webdav.minute_interval', { count: 5 })}</Select.Option>
<Select.Option value={15}>{t('settings.data.webdav.minute_interval', { count: 15 })}</Select.Option>
<Select.Option value={30}>{t('settings.data.webdav.minute_interval', { count: 30 })}</Select.Option>
<Select.Option value={60}>{t('settings.data.webdav.hour_interval', { count: 1 })}</Select.Option>
<Select.Option value={120}>{t('settings.data.webdav.hour_interval', { count: 2 })}</Select.Option>
<Select.Option value={360}>{t('settings.data.webdav.hour_interval', { count: 6 })}</Select.Option>
<Select.Option value={720}>{t('settings.data.webdav.hour_interval', { count: 12 })}</Select.Option>
<Select.Option value={1440}>{t('settings.data.webdav.hour_interval', { count: 24 })}</Select.Option>
</Select>
</SettingRow>
{webdavSync && syncInterval > 0 && (
<>
<SettingDivider />
<SettingRow>
<SettingRowTitle>{t('settings.data.webdav.syncStatus')}</SettingRowTitle>
{renderSyncStatus()}
</SettingRow>
</>
)}
<>
<WebdavBackupModal
isModalVisible={isModalVisible}
handleBackup={handleBackup}
handleCancel={handleCancel}
backuping={backuping}
customFileName={customFileName}
setCustomFileName={setCustomFileName}
/>
<WebdavBackupManager
visible={backupManagerVisible}
onClose={closeBackupManager}
webdavConfig={{
webdavHost,
webdavUser,
webdavPass,
webdavPath
}}
/>
</>
</SettingGroup>
)
}
export default WebDavSettings