feat: enhance backup and download functionalities

- Updated `BackupCenterPage` to support download progress tracking during remote backup downloads.
- Modified `ImportPage` to simplify export functionality by removing unnecessary payload handling.
- Improved `JwtWarningPage` to utilize a new clipboard utility for copying text with feedback.
- Enhanced `PublicSendPage` to show download progress for files being downloaded.
- Updated `RecoverTwoFactorPage` to include autocomplete attributes for better user experience.
- Refactored `SendsPage` to use the new clipboard utility for copying access URLs.
- Enhanced `SettingsPage` to utilize the clipboard utility for copying sensitive information.
- Improved `TotpCodesPage` to use the clipboard utility for copying TOTP codes.
- Updated `VaultPage` and related components to support download progress for attachments.
- Introduced a new `app-notify` module for consistent notification handling across the application.
- Created a `clipboard` utility for improved clipboard interactions with user feedback.
- Added progress tracking for file downloads in the API layer, enhancing user experience during downloads.
This commit is contained in:
shuaiplus
2026-03-15 23:12:45 +08:00
parent 9820c2ed44
commit 4b8cad6d00
33 changed files with 387 additions and 121 deletions
@@ -27,6 +27,7 @@ interface BackupDestinationDetailProps {
remoteBrowserTotalPages: number;
loadingRemoteBrowser: boolean;
downloadingRemotePath: string;
downloadingRemotePercent: number | null;
restoringRemotePath: string;
deletingRemotePath: string;
onSaveSettings: () => void;
@@ -511,6 +512,7 @@ export function BackupDestinationDetail(props: BackupDestinationDetailProps) {
currentPage={props.remoteBrowserCurrentPage}
totalPages={props.remoteBrowserTotalPages}
downloadingRemotePath={props.downloadingRemotePath}
downloadingRemotePercent={props.downloadingRemotePercent}
restoringRemotePath={props.restoringRemotePath}
deletingRemotePath={props.deletingRemotePath}
onRefresh={props.onRefreshRemoteBrowser}
@@ -13,6 +13,7 @@ interface RemoteBackupBrowserProps {
currentPage: number;
totalPages: number;
downloadingRemotePath: string;
downloadingRemotePercent: number | null;
restoringRemotePath: string;
deletingRemotePath: string;
onRefresh: () => void;
@@ -24,6 +25,13 @@ interface RemoteBackupBrowserProps {
}
export function RemoteBackupBrowser(props: RemoteBackupBrowserProps) {
const getDownloadLabel = (path: string) => {
if (props.downloadingRemotePath !== path) return t('txt_backup_remote_download');
return props.downloadingRemotePercent == null
? t('txt_downloading')
: t('txt_downloading_percent', { percent: props.downloadingRemotePercent });
};
return (
<>
<div className="backup-divider" />
@@ -98,7 +106,7 @@ export function RemoteBackupBrowser(props: RemoteBackupBrowserProps) {
<>
<button type="button" className="btn btn-secondary small" disabled={props.disableWhileBusy || props.downloadingRemotePath === item.path || !isZipCandidate(item)} onClick={() => props.onDownload(item.path)}>
<Download size={14} className="btn-icon" />
{props.downloadingRemotePath === item.path ? t('txt_backup_remote_downloading') : t('txt_backup_remote_download')}
{getDownloadLabel(item.path)}
</button>
<button type="button" className="btn btn-primary small" disabled={props.disableWhileBusy || props.restoringRemotePath === item.path || !isZipCandidate(item)} onClick={() => props.onRestore(item.path)}>
<RotateCcw size={14} className="btn-icon" />