diff --git a/Changelog.md b/Changelog.md index ff4584007..085296fc8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -8,6 +8,7 @@ ✨ 新增功能 - 允许代理页面允许高级过滤搜索 +- 备份设置页面新增导入备份按钮 diff --git a/src-tauri/src/cmd/backup.rs b/src-tauri/src/cmd/backup.rs index eecb6d733..fc80bc9b4 100644 --- a/src-tauri/src/cmd/backup.rs +++ b/src-tauri/src/cmd/backup.rs @@ -27,6 +27,12 @@ pub async fn restore_local_backup(filename: String) -> CmdResult<()> { feat::restore_local_backup(filename).await.stringify_err() } +/// Import local backup into the app's backup directory +#[tauri::command] +pub async fn import_local_backup(source: String) -> CmdResult { + feat::import_local_backup(source).await.stringify_err() +} + /// Export local backup to a user selected destination #[tauri::command] pub async fn export_local_backup(filename: String, destination: String) -> CmdResult<()> { diff --git a/src-tauri/src/feat/backup.rs b/src-tauri/src/feat/backup.rs index 23d906081..9b4d187bd 100644 --- a/src-tauri/src/feat/backup.rs +++ b/src-tauri/src/feat/backup.rs @@ -158,6 +158,53 @@ where Ok(final_name) } +/// Import an existing backup file into the local backup directory +pub async fn import_local_backup(source: String) -> Result { + let source_path = PathBuf::from(source.as_str()); + if !source_path.exists() { + return Err(anyhow!("Backup file not found: {source}")); + } + if !source_path.is_file() { + return Err(anyhow!("Backup path is not a file: {source}")); + } + + let ext = source_path + .extension() + .and_then(|ext| ext.to_str()) + .map(|ext| ext.to_ascii_lowercase()) + .unwrap_or_default(); + if ext != "zip" { + return Err(anyhow!("Only .zip backup files are supported")); + } + + let file_name = source_path + .file_name() + .and_then(|name| name.to_str()) + .ok_or_else(|| anyhow!("Invalid backup file name"))?; + + let backup_dir = local_backup_dir()?; + let target_path = backup_dir.join(file_name); + + if target_path == source_path { + // Already located in the backup directory + return Ok(file_name.to_string().into()); + } + + if let Some(parent) = target_path.parent() { + fs::create_dir_all(parent).await?; + } + + if target_path.exists() { + return Err(anyhow!("Backup file already exists: {file_name}")); + } + + fs::copy(&source_path, &target_path) + .await + .map_err(|err| anyhow!("Failed to import backup file: {err:#?}"))?; + + Ok(file_name.to_string().into()) +} + async fn move_file(from: PathBuf, to: PathBuf) -> Result<()> { if let Some(parent) = to.parent() { fs::create_dir_all(parent).await?; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 42d33c9ed..a81f292cd 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -210,6 +210,7 @@ mod app_init { cmd::list_local_backup, cmd::delete_local_backup, cmd::restore_local_backup, + cmd::import_local_backup, cmd::export_local_backup, cmd::create_webdav_backup, cmd::save_webdav_config, diff --git a/src/components/setting/mods/backup-history-viewer.tsx b/src/components/setting/mods/backup-history-viewer.tsx index d1be64b35..ee3956378 100644 --- a/src/components/setting/mods/backup-history-viewer.tsx +++ b/src/components/setting/mods/backup-history-viewer.tsx @@ -57,7 +57,9 @@ interface BackupHistoryViewerProps { interface BackupRow { filename: string; platform: string; - backup_time: dayjs.Dayjs; + backup_time: dayjs.Dayjs | null; + display_time: string; + sort_value: number; } const confirmAsync = async (message: string) => { @@ -86,16 +88,44 @@ export const BackupHistoryViewer = ({ const pageSize = 8; const isBusy = loading || isRestarting; - const buildRow = useCallback((filename: string): BackupRow | null => { - const platform = filename.split("-")[0]; - const match = filename.match(FILENAME_PATTERN); - if (!match) return null; - return { - filename, - platform, - backup_time: dayjs(match[0], DATE_FORMAT), - }; - }, []); + const buildRow = useCallback( + (item: ILocalBackupFile | IWebDavFile): BackupRow | null => { + const { filename, last_modified } = item; + if (!filename.toLowerCase().endsWith(".zip")) return null; + + const platform = + (filename.includes("-") && filename.split("-")[0]) || + t("settings.modals.backup.history.unknownPlatform", { + defaultValue: "unknown", + }); + const match = filename.match(FILENAME_PATTERN); + const parsedFromName = match ? dayjs(match[0], DATE_FORMAT, true) : null; + const parsedFromModified = + last_modified && dayjs(last_modified).isValid() + ? dayjs(last_modified) + : null; + const backupTime = parsedFromName?.isValid() + ? parsedFromName + : parsedFromModified; + + return { + filename, + platform, + backup_time: backupTime ?? null, + display_time: + backupTime?.format("YYYY-MM-DD HH:mm") ?? + parsedFromModified?.format("YYYY-MM-DD HH:mm") ?? + t("settings.modals.backup.history.unknownTime", { + defaultValue: "Unknown time", + }), + sort_value: + backupTime?.valueOf() ?? + parsedFromModified?.valueOf() ?? + Number.NEGATIVE_INFINITY, + }; + }, + [t], + ); const fetchRows = useCallback(async () => { if (!open) return; @@ -108,9 +138,13 @@ export const BackupHistoryViewer = ({ const list = isLocal ? await listLocalBackup() : await listWebDavBackup(); setRows( list - .map((item) => buildRow(item.filename)) + .map((item) => buildRow(item)) .filter((item): item is BackupRow => item !== null) - .sort((a, b) => (a.backup_time.isAfter(b.backup_time) ? -1 : 1)), + .sort((a, b) => + a.sort_value === b.sort_value + ? b.filename.localeCompare(a.filename) + : b.sort_value - a.sort_value, + ), ); } catch (error) { console.error(error); @@ -138,7 +172,8 @@ export const BackupHistoryViewer = ({ return t("settings.modals.backup.manual.webdav"); } if (!total) return t("settings.modals.backup.history.empty"); - const recent = rows[0]?.backup_time.fromNow(); + const recent = + rows[0]?.backup_time?.fromNow() ?? rows[0]?.display_time ?? ""; return t("settings.modals.backup.history.summary", { count: total, recent, @@ -283,7 +318,7 @@ export const BackupHistoryViewer = ({ spacing={1.5} > - {`${row.platform} · ${row.backup_time.format("YYYY-MM-DD HH:mm")}`} + {`${row.platform} · ${row.display_time}`} }) { const { t } = useTranslation(); const [open, setOpen] = useState(false); const [busyAction, setBusyAction] = useState(null); + const [localImporting, setLocalImporting] = useState(false); const [historyOpen, setHistoryOpen] = useState(false); const [historySource, setHistorySource] = useState("local"); const [historyPage, setHistoryPage] = useState(0); @@ -67,6 +73,28 @@ export function BackupViewer({ ref }: { ref?: Ref }) { } }); + const handleImport = useLockFn(async () => { + const selected = await openDialog({ + multiple: false, + filters: [{ name: "Backup File", extensions: ["zip"] }], + }); + if (!selected || Array.isArray(selected)) return; + try { + setLocalImporting(true); + await importLocalBackup(selected); + showNotice.success("settings.modals.backup.messages.localBackupImported"); + openHistory("local"); + } catch (error) { + console.error(error); + showNotice.error( + "settings.modals.backup.messages.localBackupImportFailed", + { error }, + ); + } finally { + setLocalImporting(false); + } + }); + const setWebdavBusy = useCallback( (loading: boolean) => { setBusyAction(loading ? "webdav" : null); @@ -74,6 +102,8 @@ export function BackupViewer({ ref }: { ref?: Ref }) { [setBusyAction], ); + const isLocalBusy = busyAction === "local" || localImporting; + return ( }) { variant="contained" size="small" loading={busyAction === "local"} + disabled={localImporting} onClick={() => handleBackup("local")} > {t("settings.modals.backup.actions.backup")} @@ -133,10 +164,21 @@ export function BackupViewer({ ref }: { ref?: Ref }) { key="history" variant="outlined" size="small" + disabled={isLocalBusy} onClick={() => openHistory("local")} > {t("settings.modals.backup.actions.viewHistory")} , + handleImport()} + > + {t("settings.modals.backup.actions.importBackup")} + , ], }, { diff --git a/src/locales/ar/settings.json b/src/locales/ar/settings.json index dab0b65c2..e4cf82fb1 100644 --- a/src/locales/ar/settings.json +++ b/src/locales/ar/settings.json @@ -283,6 +283,7 @@ "backup": "نسخ احتياطي", "export": "Export", "exportBackup": "Export Backup", + "importBackup": "Import Backup", "deleteBackup": "حذف النسخة الاحتياطية", "restore": "استعادة", "restoreBackup": "استعادة النسخة الاحتياطية", @@ -307,6 +308,8 @@ "restoreSuccess": "تمت الاستعادة بنجاح، سيعاد تشغيل التطبيق خلال ثانية واحدة", "localBackupExported": "Local backup exported successfully", "localBackupExportFailed": "Failed to export local backup", + "localBackupImported": "Local backup imported successfully", + "localBackupImportFailed": "Failed to import local backup: {{error}}", "webdavRefreshSuccess": "WebDAV refresh succeeded", "webdavRefreshFailed": "WebDAV refresh failed: {{error}}", "confirmDelete": "هل تريد بالتأكيد حذف ملف النسخة الاحتياطية هذا؟", @@ -333,7 +336,9 @@ "history": { "title": "Backup history", "summary": "{{count}} backups • latest {{recent}}", - "empty": "No backups available" + "empty": "No backups available", + "unknownPlatform": "unknown", + "unknownTime": "Unknown time" }, "webdav": { "title": "WebDAV settings" diff --git a/src/locales/de/settings.json b/src/locales/de/settings.json index 0409abf00..42166e2f3 100644 --- a/src/locales/de/settings.json +++ b/src/locales/de/settings.json @@ -283,6 +283,7 @@ "backup": "Sichern", "export": "Export", "exportBackup": "Export Backup", + "importBackup": "Import Backup", "deleteBackup": "Sicherung löschen", "restore": "Wiederherstellen", "restoreBackup": "Sicherung wiederherstellen", @@ -307,6 +308,8 @@ "restoreSuccess": "Wiederherstellung erfolgreich. Die App wird in 1 Sekunde neu starten.", "localBackupExported": "Local backup exported successfully", "localBackupExportFailed": "Failed to export local backup", + "localBackupImported": "Local backup imported successfully", + "localBackupImportFailed": "Failed to import local backup: {{error}}", "webdavRefreshSuccess": "WebDAV refresh succeeded", "webdavRefreshFailed": "WebDAV refresh failed: {{error}}", "confirmDelete": "Confirm to delete this backup file?", @@ -333,7 +336,9 @@ "history": { "title": "Backup history", "summary": "{{count}} backups • latest {{recent}}", - "empty": "No backups available" + "empty": "No backups available", + "unknownPlatform": "unknown", + "unknownTime": "Unknown time" }, "webdav": { "title": "WebDAV settings" diff --git a/src/locales/en/settings.json b/src/locales/en/settings.json index 24e821b1c..997de9b25 100644 --- a/src/locales/en/settings.json +++ b/src/locales/en/settings.json @@ -283,6 +283,7 @@ "backup": "Backup", "export": "Export", "exportBackup": "Export Backup", + "importBackup": "Import Backup", "deleteBackup": "Delete Backup", "restore": "Restore", "restoreBackup": "Restore Backup", @@ -307,6 +308,8 @@ "restoreSuccess": "Restore Success, App will restart in 1s", "localBackupExported": "Local backup exported successfully", "localBackupExportFailed": "Failed to export local backup", + "localBackupImported": "Local backup imported successfully", + "localBackupImportFailed": "Failed to import local backup: {{error}}", "webdavRefreshSuccess": "WebDAV refresh succeeded", "webdavRefreshFailed": "WebDAV refresh failed: {{error}}", "confirmDelete": "Confirm to delete this backup file?", @@ -333,7 +336,9 @@ "history": { "title": "Backup history", "summary": "{{count}} backups • latest {{recent}}", - "empty": "No backups available" + "empty": "No backups available", + "unknownPlatform": "unknown", + "unknownTime": "Unknown time" }, "webdav": { "title": "WebDAV settings" diff --git a/src/locales/es/settings.json b/src/locales/es/settings.json index 39692beea..3ed6245f1 100644 --- a/src/locales/es/settings.json +++ b/src/locales/es/settings.json @@ -283,6 +283,7 @@ "backup": "Copia de seguridad", "export": "Export", "exportBackup": "Export Backup", + "importBackup": "Import Backup", "deleteBackup": "Eliminar copia de seguridad", "restore": "Restaurar", "restoreBackup": "Restaurar copia de seguridad", @@ -307,6 +308,8 @@ "restoreSuccess": "Restauración exitosa. La aplicación se reiniciará en 1 segundo", "localBackupExported": "Local backup exported successfully", "localBackupExportFailed": "Failed to export local backup", + "localBackupImported": "Local backup imported successfully", + "localBackupImportFailed": "Failed to import local backup: {{error}}", "webdavRefreshSuccess": "WebDAV refresh succeeded", "webdavRefreshFailed": "WebDAV refresh failed: {{error}}", "confirmDelete": "Confirm to delete this backup file?", @@ -333,7 +336,9 @@ "history": { "title": "Backup history", "summary": "{{count}} backups • latest {{recent}}", - "empty": "No backups available" + "empty": "No backups available", + "unknownPlatform": "unknown", + "unknownTime": "Unknown time" }, "webdav": { "title": "WebDAV settings" diff --git a/src/locales/fa/settings.json b/src/locales/fa/settings.json index 23153f62c..984312f8a 100644 --- a/src/locales/fa/settings.json +++ b/src/locales/fa/settings.json @@ -283,6 +283,7 @@ "backup": "پشتیبان‌گیری", "export": "Export", "exportBackup": "Export Backup", + "importBackup": "Import Backup", "deleteBackup": "حذف پشتیبان", "restore": "بازیابی", "restoreBackup": "بازیابی پشتیبان", @@ -307,6 +308,8 @@ "restoreSuccess": "بازیابی با موفقیت انجام شد، برنامه در 1 ثانیه راه‌اندازی مجدد می‌شود", "localBackupExported": "Local backup exported successfully", "localBackupExportFailed": "Failed to export local backup", + "localBackupImported": "Local backup imported successfully", + "localBackupImportFailed": "Failed to import local backup: {{error}}", "webdavRefreshSuccess": "WebDAV refresh succeeded", "webdavRefreshFailed": "WebDAV refresh failed: {{error}}", "confirmDelete": "آیا از حذف این فایل پشتیبان اطمینان دارید؟", @@ -333,7 +336,9 @@ "history": { "title": "تاریخچه پشتیبان گیری", "summary": "{{count}} backups • latest {{recent}}", - "empty": "هیچ نسخه پشتیبان در دسترس نیست" + "empty": "هیچ نسخه پشتیبان در دسترس نیست", + "unknownPlatform": "unknown", + "unknownTime": "Unknown time" }, "webdav": { "title": "پیکربندی WebDAV" diff --git a/src/locales/id/settings.json b/src/locales/id/settings.json index c32b3f8ba..b74cece79 100644 --- a/src/locales/id/settings.json +++ b/src/locales/id/settings.json @@ -283,6 +283,7 @@ "backup": "Cadangan", "export": "Export", "exportBackup": "Export Backup", + "importBackup": "Import Backup", "deleteBackup": "Hapus Cadangan", "restore": "Pulihkan", "restoreBackup": "Pulihkan Cadangan", @@ -307,6 +308,8 @@ "restoreSuccess": "Pemulihan Berhasil, Aplikasi akan dimulai ulang dalam 1 detik", "localBackupExported": "Local backup exported successfully", "localBackupExportFailed": "Failed to export local backup", + "localBackupImported": "Local backup imported successfully", + "localBackupImportFailed": "Failed to import local backup: {{error}}", "webdavRefreshSuccess": "WebDAV refresh succeeded", "webdavRefreshFailed": "WebDAV refresh failed: {{error}}", "confirmDelete": "Konfirmasi untuk menghapus file cadangan ini?", @@ -333,7 +336,9 @@ "history": { "title": "Backup history", "summary": "{{count}} backups • latest {{recent}}", - "empty": "No backups available" + "empty": "No backups available", + "unknownPlatform": "unknown", + "unknownTime": "Unknown time" }, "webdav": { "title": "WebDAV settings" diff --git a/src/locales/jp/settings.json b/src/locales/jp/settings.json index b0cb62d44..817947041 100644 --- a/src/locales/jp/settings.json +++ b/src/locales/jp/settings.json @@ -283,6 +283,7 @@ "backup": "バックアップ", "export": "Export", "exportBackup": "Export Backup", + "importBackup": "バックアップをインポート", "deleteBackup": "バックアップを削除", "restore": "復元", "restoreBackup": "バックアップを復元", @@ -307,6 +308,8 @@ "restoreSuccess": "復元に成功しました。アプリケーションは1秒後に再起動します。", "localBackupExported": "Local backup exported successfully", "localBackupExportFailed": "Failed to export local backup", + "localBackupImported": "ローカルバックアップのインポートに成功しました", + "localBackupImportFailed": "ローカルバックアップのインポートに失敗しました: {{error}}", "webdavRefreshSuccess": "WebDAV refresh succeeded", "webdavRefreshFailed": "WebDAV refresh failed: {{error}}", "confirmDelete": "Confirm to delete this backup file?", @@ -333,7 +336,9 @@ "history": { "title": "Backup history", "summary": "{{count}} backups • latest {{recent}}", - "empty": "No backups available" + "empty": "No backups available", + "unknownPlatform": "unknown", + "unknownTime": "Unknown time" }, "webdav": { "title": "WebDAV settings" diff --git a/src/locales/ko/settings.json b/src/locales/ko/settings.json index 7c0096030..898af32f6 100644 --- a/src/locales/ko/settings.json +++ b/src/locales/ko/settings.json @@ -283,6 +283,7 @@ "backup": "백업", "export": "내보내기", "exportBackup": "백업 내보내기", + "importBackup": "백업 가져오기", "deleteBackup": "백업 삭제", "restore": "복원", "restoreBackup": "백업 복원", @@ -307,6 +308,8 @@ "restoreSuccess": "복원 성공, 1초 후 앱이 재시작됩니다", "localBackupExported": "로컬 백업이 내보내졌습니다", "localBackupExportFailed": "로컬 백업 내보내기 실패", + "localBackupImported": "로컬 백업을 가져왔습니다", + "localBackupImportFailed": "로컬 백업 가져오기 실패: {{error}}", "webdavRefreshSuccess": "WebDAV refresh succeeded", "webdavRefreshFailed": "WebDAV refresh failed: {{error}}", "confirmDelete": "이 백업 파일을 삭제하시겠습니까?", @@ -333,7 +336,9 @@ "history": { "title": "Backup history", "summary": "{{count}} backups • latest {{recent}}", - "empty": "No backups available" + "empty": "No backups available", + "unknownPlatform": "알 수 없음", + "unknownTime": "시간 정보 없음" }, "webdav": { "title": "WebDAV settings" diff --git a/src/locales/ru/settings.json b/src/locales/ru/settings.json index f2da50b19..96506ca70 100644 --- a/src/locales/ru/settings.json +++ b/src/locales/ru/settings.json @@ -283,6 +283,7 @@ "backup": "Резервное копирование", "export": "Export", "exportBackup": "Export Backup", + "importBackup": "Import Backup", "deleteBackup": "Удалить резервную копию", "restore": "Восстановить", "restoreBackup": "Восстановить резервную копию", @@ -307,6 +308,8 @@ "restoreSuccess": "Восстановление успешно выполнено, приложение перезапустится через 1 секунду", "localBackupExported": "Local backup exported successfully", "localBackupExportFailed": "Failed to export local backup", + "localBackupImported": "Local backup imported successfully", + "localBackupImportFailed": "Не удалось импортировать локальную резервную копию: {{error}}", "webdavRefreshSuccess": "WebDAV refresh succeeded", "webdavRefreshFailed": "WebDAV refresh failed: {{error}}", "confirmDelete": "Вы уверены, что хотите удалить этот файл резервной копии?", @@ -333,7 +336,9 @@ "history": { "title": "Backup history", "summary": "{{count}} backups • latest {{recent}}", - "empty": "No backups available" + "empty": "No backups available", + "unknownPlatform": "неизвестно", + "unknownTime": "Неизвестное время" }, "webdav": { "title": "WebDAV settings" diff --git a/src/locales/tr/settings.json b/src/locales/tr/settings.json index db7798988..e79318c17 100644 --- a/src/locales/tr/settings.json +++ b/src/locales/tr/settings.json @@ -283,6 +283,7 @@ "backup": "Yedekle", "export": "Export", "exportBackup": "Export Backup", + "importBackup": "Import Backup", "deleteBackup": "Yedeği Sil", "restore": "Geri Yükle", "restoreBackup": "Yedeği Geri Yükle", @@ -307,6 +308,8 @@ "restoreSuccess": "Geri Yükleme Başarılı, Uygulama 1 saniye içinde yeniden başlatılacak", "localBackupExported": "Local backup exported successfully", "localBackupExportFailed": "Failed to export local backup", + "localBackupImported": "Local backup imported successfully", + "localBackupImportFailed": "Failed to import local backup: {{error}}", "webdavRefreshSuccess": "WebDAV refresh succeeded", "webdavRefreshFailed": "WebDAV refresh failed: {{error}}", "confirmDelete": "Bu yedek dosyasını silmeyi onaylıyor musunuz?", @@ -333,7 +336,9 @@ "history": { "title": "Backup history", "summary": "{{count}} backups • latest {{recent}}", - "empty": "No backups available" + "empty": "No backups available", + "unknownPlatform": "unknown", + "unknownTime": "Unknown time" }, "webdav": { "title": "WebDAV settings" diff --git a/src/locales/tt/settings.json b/src/locales/tt/settings.json index fee717492..1504aaf68 100644 --- a/src/locales/tt/settings.json +++ b/src/locales/tt/settings.json @@ -283,6 +283,7 @@ "backup": "Резерв копия", "export": "Export", "exportBackup": "Export Backup", + "importBackup": "Import Backup", "deleteBackup": "Резерв копияне бетерү", "restore": "Кайтару", "restoreBackup": "Резерв копияне кайтару", @@ -307,6 +308,8 @@ "restoreSuccess": "Уңышлы кайтарылды, кушымта 1 секундтан яңадан башланачак", "localBackupExported": "Local backup exported successfully", "localBackupExportFailed": "Failed to export local backup", + "localBackupImported": "Local backup imported successfully", + "localBackupImportFailed": "Failed to import local backup: {{error}}", "webdavRefreshSuccess": "WebDAV refresh succeeded", "webdavRefreshFailed": "WebDAV refresh failed: {{error}}", "confirmDelete": "Бу резерв копия файлын бетерергә телисезме?", @@ -333,7 +336,9 @@ "history": { "title": "Backup history", "summary": "{{count}} backups • latest {{recent}}", - "empty": "No backups available" + "empty": "No backups available", + "unknownPlatform": "unknown", + "unknownTime": "Unknown time" }, "webdav": { "title": "WebDAV settings" diff --git a/src/locales/zh/settings.json b/src/locales/zh/settings.json index 8ab74085c..9d0611fcc 100644 --- a/src/locales/zh/settings.json +++ b/src/locales/zh/settings.json @@ -283,6 +283,7 @@ "backup": "备份", "export": "导出", "exportBackup": "导出备份", + "importBackup": "导入备份", "deleteBackup": "删除备份", "restore": "恢复", "restoreBackup": "恢复备份", @@ -307,6 +308,8 @@ "restoreSuccess": "恢复成功,应用将在 1 秒后重启", "localBackupExported": "本地备份导出成功", "localBackupExportFailed": "本地备份导出失败", + "localBackupImported": "本地备份导入成功", + "localBackupImportFailed": "本地备份导入失败:{{error}}", "webdavRefreshSuccess": "WebDAV 刷新成功", "webdavRefreshFailed": "WebDAV 刷新失败: {{error}}", "confirmDelete": "确认删除此备份文件吗?", @@ -333,7 +336,9 @@ "history": { "title": "备份记录", "summary": "共 {{count}} 份备份 · 最近 {{recent}}", - "empty": "暂无备份记录" + "empty": "暂无备份记录", + "unknownPlatform": "未知", + "unknownTime": "未知时间" }, "webdav": { "title": "WebDAV 设置" diff --git a/src/locales/zhtw/settings.json b/src/locales/zhtw/settings.json index a2b716f40..b829656c0 100644 --- a/src/locales/zhtw/settings.json +++ b/src/locales/zhtw/settings.json @@ -283,6 +283,7 @@ "backup": "備份", "export": "匯出", "exportBackup": "匯出備份", + "importBackup": "匯入備份", "deleteBackup": "刪除備份", "restore": "還原", "restoreBackup": "還原備份", @@ -307,6 +308,8 @@ "restoreSuccess": "還原成功,應用程式將在 1 秒後重啟", "localBackupExported": "本機備份匯出成功", "localBackupExportFailed": "本機備份匯出失敗", + "localBackupImported": "本機備份匯入成功", + "localBackupImportFailed": "本機備份匯入失敗:{{error}}", "webdavRefreshSuccess": "WebDAV 更新成功", "webdavRefreshFailed": "WebDAV 更新失敗: {{error}}", "confirmDelete": "確認是否刪除此備份檔案嗎?", @@ -333,7 +336,9 @@ "history": { "title": "備份紀錄", "summary": "共 {{count}} 份備份 · 最近 {{recent}}", - "empty": "尚無備份紀錄" + "empty": "尚無備份紀錄", + "unknownPlatform": "未知", + "unknownTime": "未知時間" }, "webdav": { "title": "WebDAV 設定" diff --git a/src/services/cmds.ts b/src/services/cmds.ts index 1c227acfc..cd34ecfc6 100644 --- a/src/services/cmds.ts +++ b/src/services/cmds.ts @@ -455,6 +455,10 @@ export async function restoreLocalBackup(filename: string) { return invoke("restore_local_backup", { filename }); } +export async function importLocalBackup(source: string) { + return invoke("import_local_backup", { source }); +} + export async function exportLocalBackup(filename: string, destination: string) { return invoke("export_local_backup", { filename, destination }); } diff --git a/src/types/generated/i18n-keys.ts b/src/types/generated/i18n-keys.ts index 4d0fdad7f..166828f85 100644 --- a/src/types/generated/i18n-keys.ts +++ b/src/types/generated/i18n-keys.ts @@ -464,6 +464,7 @@ export const translationKeys = [ "settings.modals.backup.actions.backup", "settings.modals.backup.actions.export", "settings.modals.backup.actions.exportBackup", + "settings.modals.backup.actions.importBackup", "settings.modals.backup.actions.deleteBackup", "settings.modals.backup.actions.restore", "settings.modals.backup.actions.restoreBackup", @@ -484,6 +485,8 @@ export const translationKeys = [ "settings.modals.backup.messages.restoreSuccess", "settings.modals.backup.messages.localBackupExported", "settings.modals.backup.messages.localBackupExportFailed", + "settings.modals.backup.messages.localBackupImported", + "settings.modals.backup.messages.localBackupImportFailed", "settings.modals.backup.messages.webdavRefreshSuccess", "settings.modals.backup.messages.webdavRefreshFailed", "settings.modals.backup.messages.confirmDelete", @@ -503,6 +506,8 @@ export const translationKeys = [ "settings.modals.backup.history.title", "settings.modals.backup.history.summary", "settings.modals.backup.history.empty", + "settings.modals.backup.history.unknownPlatform", + "settings.modals.backup.history.unknownTime", "settings.modals.backup.webdav.title", "settings.modals.backup.table.filename", "settings.modals.backup.table.backupTime", diff --git a/src/types/generated/i18n-resources.ts b/src/types/generated/i18n-resources.ts index 2700b138c..f21552073 100644 --- a/src/types/generated/i18n-resources.ts +++ b/src/types/generated/i18n-resources.ts @@ -694,6 +694,7 @@ export interface TranslationResources { deleteBackup: string; export: string; exportBackup: string; + importBackup: string; restore: string; restoreBackup: string; selectTarget: string; @@ -720,6 +721,8 @@ export interface TranslationResources { empty: string; summary: string; title: string; + unknownPlatform: string; + unknownTime: string; }; manual: { configureWebdav: string; @@ -737,6 +740,8 @@ export interface TranslationResources { localBackupExported: string; localBackupExportFailed: string; localBackupFailed: string; + localBackupImported: string; + localBackupImportFailed: string; passwordRequired: string; restoreSuccess: string; usernameRequired: string;