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
+14 -2
View File
@@ -1,4 +1,4 @@
import { useMemo } from 'preact/hooks';
import { useMemo, useState } from 'preact/hooks';
import type { ImportAttachmentFile, ImportResultSummary } from '@/components/ImportPage';
import type { ExportRequest, ZipAttachmentEntry } from '@/lib/export-formats';
import {
@@ -91,6 +91,8 @@ export default function useVaultSendActions(options: UseVaultSendActionsOptions)
refetchSends,
onNotify,
} = options;
const [downloadingAttachmentKey, setDownloadingAttachmentKey] = useState('');
const [attachmentDownloadPercent, setAttachmentDownloadPercent] = useState<number | null>(null);
return useMemo(() => {
const refetchVault = async () => {
@@ -189,13 +191,19 @@ export default function useVaultSendActions(options: UseVaultSendActionsOptions)
async downloadVaultAttachment(cipher: Cipher, attachmentId: string) {
if (!session) return;
const downloadKey = `${cipher.id}:${attachmentId}`;
setDownloadingAttachmentKey(downloadKey);
setAttachmentDownloadPercent(null);
try {
const file = await downloadCipherAttachmentDecrypted(authedFetch, session, cipher, attachmentId);
const file = await downloadCipherAttachmentDecrypted(authedFetch, session, cipher, attachmentId, setAttachmentDownloadPercent);
const fileName = String(file.fileName || '').trim() || 'attachment.bin';
downloadBytesAsFile(file.bytes, fileName, 'application/octet-stream');
} catch (error) {
onNotify('error', error instanceof Error ? error.message : t('txt_download_failed'));
throw error;
} finally {
setDownloadingAttachmentKey('');
setAttachmentDownloadPercent(null);
}
},
@@ -686,10 +694,14 @@ export default function useVaultSendActions(options: UseVaultSendActionsOptions)
if (!result) throw new Error(t('txt_unsupported_export_format'));
downloadBytesAsFile(result.bytes, result.fileName, result.mimeType);
},
downloadingAttachmentKey,
attachmentDownloadPercent,
};
}, [
attachmentDownloadPercent,
authedFetch,
defaultKdfIterations,
downloadingAttachmentKey,
encryptedCiphers,
encryptedFolders,
importAuthedFetch,