fix: use upload and download abort signals (#212)
* fix: use upload and download abort signals * fix: progress trackers chore: update fm-lib and bee-js * chore: bump-up fm-lib version
This commit is contained in:
+2
-2
@@ -53,11 +53,11 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.14.0",
|
"@emotion/react": "^11.14.0",
|
||||||
"@emotion/styled": "^11.14.1",
|
"@emotion/styled": "^11.14.1",
|
||||||
"@ethersphere/bee-js": "^10.4.0",
|
"@ethersphere/bee-js": "^11.1.1",
|
||||||
"@formbricks/js": "^4.3.0",
|
"@formbricks/js": "^4.3.0",
|
||||||
"@mui/icons-material": "^7.3.7",
|
"@mui/icons-material": "^7.3.7",
|
||||||
"@mui/material": "^7.3.7",
|
"@mui/material": "^7.3.7",
|
||||||
"@solarpunkltd/file-manager-lib": "^1.0.5",
|
"@solarpunkltd/file-manager-lib": "^1.0.7",
|
||||||
"axios": "^0.30.2",
|
"axios": "^0.30.2",
|
||||||
"bignumber.js": "^9.3.1",
|
"bignumber.js": "^9.3.1",
|
||||||
"dotted-map": "^2.2.3",
|
"dotted-map": "^2.2.3",
|
||||||
|
|||||||
Generated
+17
-16
@@ -15,8 +15,8 @@ importers:
|
|||||||
specifier: ^11.14.1
|
specifier: ^11.14.1
|
||||||
version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)
|
version: 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4)
|
||||||
'@ethersphere/bee-js':
|
'@ethersphere/bee-js':
|
||||||
specifier: ^10.4.0
|
specifier: ^11.1.1
|
||||||
version: 10.4.0
|
version: 11.1.1
|
||||||
'@formbricks/js':
|
'@formbricks/js':
|
||||||
specifier: ^4.3.0
|
specifier: ^4.3.0
|
||||||
version: 4.3.0
|
version: 4.3.0
|
||||||
@@ -27,8 +27,8 @@ importers:
|
|||||||
specifier: ^7.3.7
|
specifier: ^7.3.7
|
||||||
version: 7.3.8(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
version: 7.3.8(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react@19.2.4))(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||||
'@solarpunkltd/file-manager-lib':
|
'@solarpunkltd/file-manager-lib':
|
||||||
specifier: ^1.0.5
|
specifier: ^1.0.7
|
||||||
version: 1.0.5
|
version: 1.0.7
|
||||||
axios:
|
axios:
|
||||||
specifier: ^0.30.2
|
specifier: ^0.30.2
|
||||||
version: 0.30.2(debug@4.4.3)
|
version: 0.30.2(debug@4.4.3)
|
||||||
@@ -1256,8 +1256,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
|
resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
|
||||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||||
|
|
||||||
'@ethersphere/bee-js@10.4.0':
|
'@ethersphere/bee-js@11.1.1':
|
||||||
resolution: {integrity: sha512-dtnyvKA3prBj22k2mfZWPq+uiK1hLMKr/jrGX4vaS41JsHX9uJ1GMGr4vCc41ANstGUGbp1dwCUWsXkqqjWuvg==}
|
resolution: {integrity: sha512-s2IpuVpy74cJyhj5yugO3+c9eR/r9TZsVJABk2iHWtTVkoA1qZdvgUjzV0w962o1gXrmYdxX4NOmlyIEpY11iQ==}
|
||||||
engines: {bee: 2.4.0-390a402e, beeApiVersion: 7.2.0}
|
engines: {bee: 2.4.0-390a402e, beeApiVersion: 7.2.0}
|
||||||
|
|
||||||
'@formbricks/js@4.3.0':
|
'@formbricks/js@4.3.0':
|
||||||
@@ -1833,9 +1833,9 @@ packages:
|
|||||||
'@sinonjs/fake-timers@13.0.5':
|
'@sinonjs/fake-timers@13.0.5':
|
||||||
resolution: {integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==}
|
resolution: {integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==}
|
||||||
|
|
||||||
'@solarpunkltd/file-manager-lib@1.0.5':
|
'@solarpunkltd/file-manager-lib@1.0.7':
|
||||||
resolution: {integrity: sha512-s/2HP/VCrdoWOAZfZ7gl+maxO4D8wR3INdz9XSIjjvwlWU/KBUV3Vke5ANRuy28ikyDT4qOggu4G69EBHrl5YA==}
|
resolution: {integrity: sha512-L/mE2F/4oL24PHUNcSET2TlvGpuVlm8AFedpBl/2O59ISrBvLzs6H2jep5LytgRdwaD+Hpot2ssGkWKgHnycrQ==}
|
||||||
engines: {bee: 2.6.0, node: '>=20.0.0', npm: '>=11.0.0', pnpm: '>=10.0.0'}
|
engines: {bee: 2.7.0, node: '>=24.0.0', npm: '>=11.0.0', pnpm: '>=10.0.0'}
|
||||||
|
|
||||||
'@testing-library/dom@10.4.1':
|
'@testing-library/dom@10.4.1':
|
||||||
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
|
resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==}
|
||||||
@@ -2624,8 +2624,8 @@ packages:
|
|||||||
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
|
resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
|
|
||||||
cafe-utility@33.6.0:
|
cafe-utility@33.8.0:
|
||||||
resolution: {integrity: sha512-MyXjck4cf6+WahnRWpS083qA0Ib9XAgSVOfdd+mSwAWiyK0fMs0+khXHTLHTRA+nIZIJFVDEcMX8Cnc8G8NEDw==}
|
resolution: {integrity: sha512-qfbG8nHTY5jBfCSedG9+b57avhKePdiHfj2w0FMGokJuQU+3dxeT9+qasMReoXHBDp38CFHMSEvz5X0SDCXbWA==}
|
||||||
|
|
||||||
call-bind-apply-helpers@1.0.2:
|
call-bind-apply-helpers@1.0.2:
|
||||||
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||||
@@ -6909,10 +6909,10 @@ snapshots:
|
|||||||
'@eslint/core': 0.17.0
|
'@eslint/core': 0.17.0
|
||||||
levn: 0.4.1
|
levn: 0.4.1
|
||||||
|
|
||||||
'@ethersphere/bee-js@10.4.0':
|
'@ethersphere/bee-js@11.1.1':
|
||||||
dependencies:
|
dependencies:
|
||||||
axios: 0.30.2(debug@4.4.3)
|
axios: 0.30.2(debug@4.4.3)
|
||||||
cafe-utility: 33.6.0
|
cafe-utility: 33.8.0
|
||||||
debug: 4.4.3
|
debug: 4.4.3
|
||||||
isomorphic-ws: 4.0.1(ws@8.19.0)
|
isomorphic-ws: 4.0.1(ws@8.19.0)
|
||||||
semver: 7.7.4
|
semver: 7.7.4
|
||||||
@@ -7539,9 +7539,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@sinonjs/commons': 3.0.1
|
'@sinonjs/commons': 3.0.1
|
||||||
|
|
||||||
'@solarpunkltd/file-manager-lib@1.0.5':
|
'@solarpunkltd/file-manager-lib@1.0.7':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ethersphere/bee-js': 10.4.0
|
'@ethersphere/bee-js': 11.1.1
|
||||||
|
cafe-utility: 33.8.0
|
||||||
std-env: 3.10.0
|
std-env: 3.10.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- bufferutil
|
- bufferutil
|
||||||
@@ -8461,7 +8462,7 @@ snapshots:
|
|||||||
|
|
||||||
bytes@3.1.2: {}
|
bytes@3.1.2: {}
|
||||||
|
|
||||||
cafe-utility@33.6.0: {}
|
cafe-utility@33.8.0: {}
|
||||||
|
|
||||||
call-bind-apply-helpers@1.0.2:
|
call-bind-apply-helpers@1.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
@@ -62,6 +62,8 @@ export function CreateDriveModal({
|
|||||||
const validationError = duplicate && nameExists ? 'Drive already exists. Please choose another name.' : ''
|
const validationError = duplicate && nameExists ? 'Drive already exists. Please choose another name.' : ''
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
isMountedRef.current = true
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
isMountedRef.current = false
|
isMountedRef.current = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -210,6 +210,7 @@ export function FileBrowser({ errorMessage, setErrorMessage }: FileBrowserProps)
|
|||||||
const [confirmBulkRestore, setConfirmBulkRestore] = useState(false)
|
const [confirmBulkRestore, setConfirmBulkRestore] = useState(false)
|
||||||
const [isRefreshing, setIsRefreshing] = useState(false)
|
const [isRefreshing, setIsRefreshing] = useState(false)
|
||||||
const [pendingCancelUpload, setPendingCancelUpload] = useState<string | null>(null)
|
const [pendingCancelUpload, setPendingCancelUpload] = useState<string | null>(null)
|
||||||
|
const [pendingCancelDownload, setPendingCancelDownload] = useState<string | null>(null)
|
||||||
|
|
||||||
const q = query.trim().toLowerCase()
|
const q = query.trim().toLowerCase()
|
||||||
const isSearchMode = q.length > 0
|
const isSearchMode = q.length > 0
|
||||||
@@ -371,6 +372,16 @@ export function FileBrowser({ errorMessage, setErrorMessage }: FileBrowserProps)
|
|||||||
setShowError,
|
setShowError,
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const handleDownloadClose = (uuid: string) => {
|
||||||
|
const row = downloadItems.find(i => i.uuid === uuid)
|
||||||
|
|
||||||
|
if (row?.status === TransferStatus.Downloading) {
|
||||||
|
setPendingCancelDownload(uuid)
|
||||||
|
} else {
|
||||||
|
cancelOrDismissDownload(uuid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const handleUploadClose = (uuid: string) => {
|
const handleUploadClose = (uuid: string) => {
|
||||||
const row = uploadItems.find(i => i.uuid === uuid)
|
const row = uploadItems.find(i => i.uuid === uuid)
|
||||||
|
|
||||||
@@ -425,6 +436,18 @@ export function FileBrowser({ errorMessage, setErrorMessage }: FileBrowserProps)
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [showContext, pos, contextRef])
|
}, [showContext, pos, contextRef])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
isMountedRef.current = true
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isMountedRef.current = false
|
||||||
|
|
||||||
|
if (rafIdRef.current) {
|
||||||
|
cancelAnimationFrame(rafIdRef.current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let title = currentDrive?.name || ''
|
let title = currentDrive?.name || ''
|
||||||
|
|
||||||
@@ -446,16 +469,6 @@ export function FileBrowser({ errorMessage, setErrorMessage }: FileBrowserProps)
|
|||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [isSearchMode])
|
}, [isSearchMode])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
isMountedRef.current = false
|
|
||||||
|
|
||||||
if (rafIdRef.current) {
|
|
||||||
cancelAnimationFrame(rafIdRef.current)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const doRefresh = async () => {
|
const doRefresh = async () => {
|
||||||
handleCloseContext()
|
handleCloseContext()
|
||||||
|
|
||||||
@@ -610,6 +623,14 @@ export function FileBrowser({ errorMessage, setErrorMessage }: FileBrowserProps)
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onCancelUploadCancel={() => setPendingCancelUpload(null)}
|
onCancelUploadCancel={() => setPendingCancelUpload(null)}
|
||||||
|
pendingCancelDownload={pendingCancelDownload}
|
||||||
|
onCancelDownloadConfirm={() => {
|
||||||
|
if (pendingCancelDownload) {
|
||||||
|
cancelOrDismissDownload(pendingCancelDownload)
|
||||||
|
setPendingCancelDownload(null)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onCancelDownloadCancel={() => setPendingCancelDownload(null)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{isRefreshing && (
|
{isRefreshing && (
|
||||||
@@ -644,7 +665,7 @@ export function FileBrowser({ errorMessage, setErrorMessage }: FileBrowserProps)
|
|||||||
type={FileTransferType.Download}
|
type={FileTransferType.Download}
|
||||||
open={isDownloading}
|
open={isDownloading}
|
||||||
items={downloadItems}
|
items={downloadItems}
|
||||||
onRowClose={(name: string) => cancelOrDismissDownload(name)}
|
onRowClose={handleDownloadClose}
|
||||||
onCloseAll={() => dismissAllDownloads()}
|
onCloseAll={() => dismissAllDownloads()}
|
||||||
/>
|
/>
|
||||||
<NotificationBar setErrorMessage={setErrorMessage} />
|
<NotificationBar setErrorMessage={setErrorMessage} />
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ interface FileBrowserModalsProps {
|
|||||||
confirmBulkRestore: boolean
|
confirmBulkRestore: boolean
|
||||||
showDestroyDriveModal: boolean
|
showDestroyDriveModal: boolean
|
||||||
pendingCancelUpload: string | null
|
pendingCancelUpload: string | null
|
||||||
|
pendingCancelDownload: string | null
|
||||||
onDeleteCancel: () => void
|
onDeleteCancel: () => void
|
||||||
onDeleteProceed: (action: FileAction) => void
|
onDeleteProceed: (action: FileAction) => void
|
||||||
onForgetConfirm: () => Promise<void>
|
onForgetConfirm: () => Promise<void>
|
||||||
@@ -27,6 +28,8 @@ interface FileBrowserModalsProps {
|
|||||||
onDestroyConfirm: () => Promise<void>
|
onDestroyConfirm: () => Promise<void>
|
||||||
onCancelUploadConfirm: () => void
|
onCancelUploadConfirm: () => void
|
||||||
onCancelUploadCancel: () => void
|
onCancelUploadCancel: () => void
|
||||||
|
onCancelDownloadConfirm: () => void
|
||||||
|
onCancelDownloadCancel: () => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FileBrowserModals({
|
export function FileBrowserModals({
|
||||||
@@ -38,6 +41,7 @@ export function FileBrowserModals({
|
|||||||
confirmBulkRestore,
|
confirmBulkRestore,
|
||||||
showDestroyDriveModal,
|
showDestroyDriveModal,
|
||||||
pendingCancelUpload,
|
pendingCancelUpload,
|
||||||
|
pendingCancelDownload,
|
||||||
onDeleteCancel,
|
onDeleteCancel,
|
||||||
onDeleteProceed,
|
onDeleteProceed,
|
||||||
onForgetConfirm,
|
onForgetConfirm,
|
||||||
@@ -48,6 +52,8 @@ export function FileBrowserModals({
|
|||||||
onDestroyConfirm,
|
onDestroyConfirm,
|
||||||
onCancelUploadConfirm,
|
onCancelUploadConfirm,
|
||||||
onCancelUploadCancel,
|
onCancelUploadCancel,
|
||||||
|
onCancelDownloadCancel,
|
||||||
|
onCancelDownloadConfirm,
|
||||||
}: FileBrowserModalsProps): ReactElement {
|
}: FileBrowserModalsProps): ReactElement {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -124,6 +130,17 @@ export function FileBrowserModals({
|
|||||||
onCancel={onCancelUploadCancel}
|
onCancel={onCancelUploadCancel}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{pendingCancelDownload && (
|
||||||
|
<ConfirmModal
|
||||||
|
title="Cancel download?"
|
||||||
|
message="Are you sure you want to cancel this download?"
|
||||||
|
confirmLabel="Cancel download"
|
||||||
|
cancelLabel="Keep downloading"
|
||||||
|
onConfirm={onCancelDownloadConfirm}
|
||||||
|
onCancel={onCancelDownloadCancel}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import { DownloadProgress, FileAction, TrackDownloadProps, ViewType } from '../.
|
|||||||
import { useContextMenu } from '../../../hooks/useContextMenu'
|
import { useContextMenu } from '../../../hooks/useContextMenu'
|
||||||
import { getUsableStamps, handleDestroyAndForgetDrive, verifyDriveSpace } from '../../../utils/bee'
|
import { getUsableStamps, handleDestroyAndForgetDrive, verifyDriveSpace } from '../../../utils/bee'
|
||||||
import { Dir, formatBytes, isTrashed, safeSetState, truncateNameMiddle } from '../../../utils/common'
|
import { Dir, formatBytes, isTrashed, safeSetState, truncateNameMiddle } from '../../../utils/common'
|
||||||
import { createDownloadAbort, startDownloadingQueue } from '../../../utils/download'
|
import { startDownloadingQueue } from '../../../utils/download'
|
||||||
import { FileOperation, performFileOperation } from '../../../utils/fileOperations'
|
import { FileOperation, performFileOperation } from '../../../utils/fileOperations'
|
||||||
import { GetIconElement } from '../../../utils/GetIconElement'
|
import { GetIconElement } from '../../../utils/GetIconElement'
|
||||||
import type { FilePropertyGroup } from '../../../utils/infoGroups'
|
import type { FilePropertyGroup } from '../../../utils/infoGroups'
|
||||||
@@ -168,15 +168,14 @@ export function FileItem({
|
|||||||
|
|
||||||
const rawSize = latestFileInfo.customMetadata?.size
|
const rawSize = latestFileInfo.customMetadata?.size
|
||||||
const expectedSize = rawSize ? Number(rawSize) : undefined
|
const expectedSize = rawSize ? Number(rawSize) : undefined
|
||||||
|
const uuid = uuidV4()
|
||||||
createDownloadAbort(latestFileInfo.name)
|
|
||||||
|
|
||||||
await startDownloadingQueue(
|
await startDownloadingQueue(
|
||||||
fm,
|
fm,
|
||||||
[latestFileInfo],
|
[{ uuid, info: latestFileInfo }],
|
||||||
[
|
[
|
||||||
onDownload({
|
onDownload({
|
||||||
uuid: uuidV4(),
|
uuid,
|
||||||
name: latestFileInfo.name,
|
name: latestFileInfo.name,
|
||||||
size: formatBytes(rawSize),
|
size: formatBytes(rawSize),
|
||||||
expectedSize,
|
expectedSize,
|
||||||
|
|||||||
@@ -89,6 +89,8 @@ export function InitialModal({
|
|||||||
const isMountedRef = useRef(true)
|
const isMountedRef = useRef(true)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
isMountedRef.current = true
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
isMountedRef.current = false
|
isMountedRef.current = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,6 +159,14 @@ export function UpgradeDriveModal({
|
|||||||
[beeApi, walletBalance, isMountedRef, setErrorMessage, setShowError],
|
[beeApi, walletBalance, isMountedRef, setErrorMessage, setShowError],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
isMountedRef.current = true
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isMountedRef.current = false
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchSizes = () => {
|
const fetchSizes = () => {
|
||||||
const sizes = Array.from(Utils.getStampEffectiveBytesBreakpoints(false, defaultErasureCodeLevel).values())
|
const sizes = Array.from(Utils.getStampEffectiveBytesBreakpoints(false, defaultErasureCodeLevel).values())
|
||||||
@@ -207,12 +215,6 @@ export function UpgradeDriveModal({
|
|||||||
setValidityEndDate(getExpiryDateByLifetime(lifetimeIndex, stamp.duration.toEndDate()))
|
setValidityEndDate(getExpiryDateByLifetime(lifetimeIndex, stamp.duration.toEndDate()))
|
||||||
}, [lifetimeIndex, stamp.duration])
|
}, [lifetimeIndex, stamp.duration])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
return () => {
|
|
||||||
isMountedRef.current = false
|
|
||||||
}
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const batchIdStr = stamp.batchID.toString()
|
const batchIdStr = stamp.batchID.toString()
|
||||||
const shortBatchId = batchIdStr.length > 12 ? `${batchIdStr.slice(0, 4)}...${batchIdStr.slice(-4)}` : batchIdStr
|
const shortBatchId = batchIdStr.length > 12 ? `${batchIdStr.slice(0, 4)}...${batchIdStr.slice(-4)}` : batchIdStr
|
||||||
|
|
||||||
|
|||||||
@@ -426,13 +426,16 @@ export function VersionsList({ versions, headFi, restoreVersion, onDownload }: V
|
|||||||
handleCloseContext()
|
handleCloseContext()
|
||||||
|
|
||||||
if (!fm || !beeApi) return
|
if (!fm || !beeApi) return
|
||||||
|
|
||||||
const rawSize = fileInfo.customMetadata?.size
|
const rawSize = fileInfo.customMetadata?.size
|
||||||
const expectedSize = rawSize ? Number(rawSize) : undefined
|
const expectedSize = rawSize ? Number(rawSize) : undefined
|
||||||
const driveName = drives.find(d => d.id.toString() === fileInfo.driveId.toString())?.name ?? currentDrive?.name
|
const driveName = drives.find(d => d.id.toString() === fileInfo.driveId.toString())?.name ?? currentDrive?.name
|
||||||
|
const uuid = uuidV4()
|
||||||
|
|
||||||
await startDownloadingQueue(
|
await startDownloadingQueue(
|
||||||
fm,
|
fm,
|
||||||
[fileInfo],
|
[{ uuid, info: fileInfo }],
|
||||||
[onDownload({ uuid: uuidV4(), name: fileInfo.name, size: formatBytes(rawSize), expectedSize, driveName })],
|
[onDownload({ uuid, name: fileInfo.name, size: formatBytes(rawSize), expectedSize, driveName })],
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[handleCloseContext, fm, beeApi, onDownload, drives, currentDrive],
|
[handleCloseContext, fm, beeApi, onDownload, drives, currentDrive],
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { uuidV4 } from '../../../utils'
|
|||||||
import { DownloadProgress, TrackDownloadProps } from '../constants/transfers'
|
import { DownloadProgress, TrackDownloadProps } from '../constants/transfers'
|
||||||
import { getUsableStamps } from '../utils/bee'
|
import { getUsableStamps } from '../utils/bee'
|
||||||
import { formatBytes, getFileId, safeSetState } from '../utils/common'
|
import { formatBytes, getFileId, safeSetState } from '../utils/common'
|
||||||
import { startDownloadingQueue } from '../utils/download'
|
import { FileInfoWithUUID, startDownloadingQueue } from '../utils/download'
|
||||||
import { FileOperation, performBulkFileOperation } from '../utils/fileOperations'
|
import { FileOperation, performBulkFileOperation } from '../utils/fileOperations'
|
||||||
|
|
||||||
interface BulkOptions {
|
interface BulkOptions {
|
||||||
@@ -77,23 +77,28 @@ export function useBulkActions({ listToRender, setErrorMessage, trackDownload }:
|
|||||||
async (list: FileInfo[]) => {
|
async (list: FileInfo[]) => {
|
||||||
if (!fm || !list?.length) return
|
if (!fm || !list?.length) return
|
||||||
|
|
||||||
const trackers: Array<(progress: DownloadProgress) => void> = []
|
const trackers = new Array<(progress: DownloadProgress) => void>(list.length)
|
||||||
for (const fi of list) {
|
const infoListWitIDs: FileInfoWithUUID[] = new Array<FileInfoWithUUID>(list.length)
|
||||||
|
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
const fi = list[i]
|
||||||
const rawSize = fi.customMetadata?.size as string | number | undefined
|
const rawSize = fi.customMetadata?.size as string | number | undefined
|
||||||
const prettySize = formatBytes(rawSize)
|
const prettySize = formatBytes(rawSize)
|
||||||
const expected = rawSize ? Number(rawSize) : undefined
|
const expected = rawSize ? Number(rawSize) : undefined
|
||||||
const driveName = drives.find(d => d.id.toString() === fi.driveId.toString())?.name
|
const driveName = drives.find(d => d.id.toString() === fi.driveId.toString())?.name
|
||||||
const tracker = trackDownload({
|
const uuid = uuidV4()
|
||||||
uuid: uuidV4(),
|
|
||||||
|
infoListWitIDs[i] = { uuid, info: fi }
|
||||||
|
trackers[i] = trackDownload({
|
||||||
|
uuid,
|
||||||
name: fi.name,
|
name: fi.name,
|
||||||
size: prettySize,
|
size: prettySize,
|
||||||
expectedSize: expected,
|
expectedSize: expected,
|
||||||
driveName,
|
driveName,
|
||||||
})
|
})
|
||||||
trackers.push(tracker)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await startDownloadingQueue(fm, list, trackers)
|
await startDownloadingQueue(fm, infoListWitIDs, trackers)
|
||||||
},
|
},
|
||||||
[fm, trackDownload, drives],
|
[fm, trackDownload, drives],
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ export function useTransfers({ setErrorMessage }: TransferProps) {
|
|||||||
const { fm, adminDrive, currentDrive, currentStamp, files, setShowError, refreshStamp } = useContext(FMContext)
|
const { fm, adminDrive, currentDrive, currentStamp, files, setShowError, refreshStamp } = useContext(FMContext)
|
||||||
const { beeApi } = useContext(SettingsContext)
|
const { beeApi } = useContext(SettingsContext)
|
||||||
const [openConflict, conflictPortal] = useUploadConflictDialog()
|
const [openConflict, conflictPortal] = useUploadConflictDialog()
|
||||||
|
|
||||||
const isMountedRef = useRef(true)
|
const isMountedRef = useRef(true)
|
||||||
const uploadAbortsRef = useRef<AbortManager>(new AbortManager())
|
const uploadAbortsRef = useRef<AbortManager>(new AbortManager())
|
||||||
const queueRef = useRef<UploadTask[]>([])
|
const queueRef = useRef<UploadTask[]>([])
|
||||||
@@ -406,6 +407,7 @@ export function useTransfers({ setErrorMessage }: TransferProps) {
|
|||||||
taskDrive,
|
taskDrive,
|
||||||
{ ...info, onUploadProgress: progressCb },
|
{ ...info, onUploadProgress: progressCb },
|
||||||
{ actHistoryAddress: task.isReplace ? task.replaceHistory : undefined },
|
{ actHistoryAddress: task.isReplace ? task.replaceHistory : undefined },
|
||||||
|
{ signal },
|
||||||
)
|
)
|
||||||
|
|
||||||
await Promise.race([uploadPromise, checkCancellation])
|
await Promise.race([uploadPromise, checkCancellation])
|
||||||
@@ -827,6 +829,14 @@ export function useTransfers({ setErrorMessage }: TransferProps) {
|
|||||||
cancelledDownloadingRef.current.clear()
|
cancelledDownloadingRef.current.clear()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
isMountedRef.current = true
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
isMountedRef.current = false
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleFileUploaded = (e: Event) => {
|
const handleFileUploaded = (e: Event) => {
|
||||||
const { fileInfo } = (e as CustomEvent).detail || {}
|
const { fileInfo } = (e as CustomEvent).detail || {}
|
||||||
@@ -853,7 +863,6 @@ export function useTransfers({ setErrorMessage }: TransferProps) {
|
|||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener(FILE_MANAGER_EVENTS.FILE_UPLOADED, handleFileUploaded as EventListener)
|
window.removeEventListener(FILE_MANAGER_EVENTS.FILE_UPLOADED, handleFileUploaded as EventListener)
|
||||||
isMountedRef.current = false
|
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ enum Errors {
|
|||||||
SecurityError = 'SecurityError',
|
SecurityError = 'SecurityError',
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createDownloadAbort(name: string): void {
|
export function createDownloadAbort(id: string): void {
|
||||||
downloadAborts.create(name)
|
downloadAborts.create(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function abortDownload(name: string): void {
|
export function abortDownload(id: string): void {
|
||||||
downloadAborts.abort(name)
|
downloadAborts.abort(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
const processStream = async (
|
const processStream = async (
|
||||||
@@ -130,8 +130,13 @@ const streamToBlob = async (
|
|||||||
return new Blob([combined], { type: mimeType })
|
return new Blob([combined], { type: mimeType })
|
||||||
}
|
}
|
||||||
|
|
||||||
interface FileInfoWithHandle {
|
export interface FileInfoWithUUID {
|
||||||
|
uuid: string
|
||||||
info: FileInfo
|
info: FileInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FileInfoWithHandle {
|
||||||
|
infoWithId: FileInfoWithUUID
|
||||||
handle?: FileSystemFileHandle
|
handle?: FileSystemFileHandle
|
||||||
cancelled?: boolean
|
cancelled?: boolean
|
||||||
}
|
}
|
||||||
@@ -149,17 +154,17 @@ const isUserCancellation = (error: unknown): boolean => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getSingleFileHandle = async (
|
const getSingleFileHandle = async (
|
||||||
info: FileInfo,
|
infoWithId: FileInfoWithUUID,
|
||||||
defaultDownloadFolder: string,
|
defaultDownloadFolder: string,
|
||||||
): Promise<FileInfoWithHandle[] | undefined> => {
|
): Promise<FileInfoWithHandle[] | undefined> => {
|
||||||
const { mime, ext } = guessMime(info.name, info.customMetadata)
|
const { mime, ext } = guessMime(infoWithId.info.name, infoWithId.info.customMetadata)
|
||||||
|
|
||||||
const pickerOptions: {
|
const pickerOptions: {
|
||||||
suggestedName: string
|
suggestedName: string
|
||||||
startIn: string
|
startIn: string
|
||||||
types?: Array<{ accept: Record<string, string[]> }>
|
types?: Array<{ accept: Record<string, string[]> }>
|
||||||
} = {
|
} = {
|
||||||
suggestedName: info.name,
|
suggestedName: infoWithId.info.name,
|
||||||
startIn: defaultDownloadFolder,
|
startIn: defaultDownloadFolder,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,20 +176,20 @@ const getSingleFileHandle = async (
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const handle = (await (window as any).showSaveFilePicker(pickerOptions)) as FileSystemFileHandle
|
const handle = (await (window as any).showSaveFilePicker(pickerOptions)) as FileSystemFileHandle
|
||||||
|
|
||||||
return [{ info, handle }]
|
return [{ infoWithId, handle }]
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
return isUserCancellation(error) ? [{ info, cancelled: true }] : undefined
|
return isUserCancellation(error) ? [{ infoWithId, cancelled: true }] : undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getMultipleFileHandles = async (
|
const getMultipleFileHandles = async (
|
||||||
infoList: FileInfo[],
|
infoWithIdList: FileInfoWithUUID[],
|
||||||
defaultDownloadFolder: string,
|
defaultDownloadFolder: string,
|
||||||
): Promise<FileInfoWithHandle[] | undefined> => {
|
): Promise<FileInfoWithHandle[] | undefined> => {
|
||||||
if (!isDirectoryPickerSupported()) {
|
if (!isDirectoryPickerSupported()) {
|
||||||
const handles: FileInfoWithHandle[] = []
|
const handles: FileInfoWithHandle[] = []
|
||||||
|
|
||||||
for (const info of infoList) {
|
for (const info of infoWithIdList) {
|
||||||
const result = await getSingleFileHandle(info, defaultDownloadFolder)
|
const result = await getSingleFileHandle(info, defaultDownloadFolder)
|
||||||
|
|
||||||
if (!result) return undefined
|
if (!result) return undefined
|
||||||
@@ -203,37 +208,37 @@ const getMultipleFileHandles = async (
|
|||||||
|
|
||||||
const handles: FileInfoWithHandle[] = []
|
const handles: FileInfoWithHandle[] = []
|
||||||
|
|
||||||
for (const info of infoList) {
|
for (const infoWithId of infoWithIdList) {
|
||||||
try {
|
try {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const fileHandle = (await (dirHandle as any).getFileHandle(info.name, {
|
const fileHandle = (await (dirHandle as any).getFileHandle(infoWithId.info.name, {
|
||||||
create: true,
|
create: true,
|
||||||
})) as FileSystemFileHandle
|
})) as FileSystemFileHandle
|
||||||
|
|
||||||
handles.push({ info, handle: fileHandle })
|
handles.push({ infoWithId, handle: fileHandle })
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error(`Failed to create file handle for ${info.name}:`, error)
|
console.error(`Failed to create file handle for ${infoWithId.info.name}:`, error)
|
||||||
handles.push({ info, cancelled: true })
|
handles.push({ infoWithId, cancelled: true })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return handles
|
return handles
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
return isUserCancellation(error) ? infoList.map(info => ({ info, cancelled: true })) : undefined
|
return isUserCancellation(error) ? infoWithIdList.map(infoWithId => ({ infoWithId, cancelled: true })) : undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getFileHandles = (infoList: FileInfo[]): Promise<FileInfoWithHandle[] | undefined> => {
|
const getFileHandles = (infoWithIdList: FileInfoWithUUID[]): Promise<FileInfoWithHandle[] | undefined> => {
|
||||||
const defaultDownloadFolder = 'downloads'
|
const defaultDownloadFolder = 'downloads'
|
||||||
|
|
||||||
if (!isPickerSupported()) return Promise.resolve(infoList.map(info => ({ info })))
|
if (!isPickerSupported()) return Promise.resolve(infoWithIdList.map(infoWithId => ({ infoWithId })))
|
||||||
|
|
||||||
if (infoList.length === 1) {
|
if (infoWithIdList.length === 1) {
|
||||||
return getSingleFileHandle(infoList[0], defaultDownloadFolder)
|
return getSingleFileHandle(infoWithIdList[0], defaultDownloadFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
return getMultipleFileHandles(infoList, defaultDownloadFolder)
|
return getMultipleFileHandles(infoWithIdList, defaultDownloadFolder)
|
||||||
}
|
}
|
||||||
|
|
||||||
const downloadToDisk = async (
|
const downloadToDisk = async (
|
||||||
@@ -324,26 +329,26 @@ const downloadFromUrl = (url: string, fileName: string): void => {
|
|||||||
|
|
||||||
export const startDownloadingQueue = async (
|
export const startDownloadingQueue = async (
|
||||||
fm: FileManager,
|
fm: FileManager,
|
||||||
infoList: FileInfo[],
|
infoListWithIds: FileInfoWithUUID[],
|
||||||
trackers?: Array<(progress: DownloadProgress) => void>,
|
trackers?: Array<(progress: DownloadProgress) => void>,
|
||||||
isOpenWindow?: boolean,
|
isOpenWindow?: boolean,
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
if (!infoList.length || (trackers && trackers.length !== infoList.length)) return
|
if (!infoListWithIds.length || (trackers && trackers.length !== infoListWithIds.length)) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fileHandles: FileInfoWithHandle[] | undefined = isOpenWindow
|
const fileHandles: FileInfoWithHandle[] | undefined = isOpenWindow
|
||||||
? infoList.map(info => ({ info }))
|
? infoListWithIds.map(infoWithId => ({ infoWithId }))
|
||||||
: await getFileHandles(infoList)
|
: await getFileHandles(infoListWithIds)
|
||||||
|
|
||||||
if (!fileHandles) return
|
if (!fileHandles) return
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
fileHandles.map(async (fh, i) => {
|
fileHandles.map(async (fh, i) => {
|
||||||
const name = fh.info.name
|
|
||||||
const tracker = trackers ? trackers[i] : undefined
|
const tracker = trackers ? trackers[i] : undefined
|
||||||
|
|
||||||
createDownloadAbort(name)
|
const uuid = fh.infoWithId.uuid
|
||||||
const signal = downloadAborts.getSignal(name)
|
createDownloadAbort(uuid)
|
||||||
|
const signal = downloadAborts.getSignal(uuid)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (fh.cancelled) {
|
if (fh.cancelled) {
|
||||||
@@ -352,11 +357,13 @@ export const startDownloadingQueue = async (
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const dataStreams = (await fm.download(fh.info)) as ReadableStream<Uint8Array>[]
|
const dataStreams = (await fm.download(fh.infoWithId.info, undefined, undefined, {
|
||||||
|
signal,
|
||||||
|
})) as ReadableStream<Uint8Array>[]
|
||||||
|
|
||||||
if (!dataStreams || dataStreams.length === 0) {
|
if (!dataStreams || dataStreams.length === 0) {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error(`No data streams returned for ${name}`)
|
console.error(`No data streams returned for ${fh.infoWithId.info.name}`)
|
||||||
tracker?.({ progress: 0, isDownloading: false, state: DownloadState.Error })
|
tracker?.({ progress: 0, isDownloading: false, state: DownloadState.Error })
|
||||||
|
|
||||||
return
|
return
|
||||||
@@ -365,7 +372,7 @@ export const startDownloadingQueue = async (
|
|||||||
let success = false
|
let success = false
|
||||||
|
|
||||||
if (isOpenWindow || !fh.handle) {
|
if (isOpenWindow || !fh.handle) {
|
||||||
success = await downloadToBlob(dataStreams, fh.info, tracker, isOpenWindow, signal)
|
success = await downloadToBlob(dataStreams, fh.infoWithId.info, tracker, isOpenWindow, signal)
|
||||||
} else {
|
} else {
|
||||||
success = await downloadToDisk(dataStreams, fh.handle, tracker, signal)
|
success = await downloadToDisk(dataStreams, fh.handle, tracker, signal)
|
||||||
}
|
}
|
||||||
@@ -375,7 +382,7 @@ export const startDownloadingQueue = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
const size = fh.info.customMetadata?.size
|
const size = fh.infoWithId.info.customMetadata?.size
|
||||||
const finalProgress = size ? Number(size) : 0
|
const finalProgress = size ? Number(size) : 0
|
||||||
tracker({ progress: finalProgress, isDownloading: false })
|
tracker({ progress: finalProgress, isDownloading: false })
|
||||||
|
|
||||||
@@ -397,7 +404,7 @@ export const startDownloadingQueue = async (
|
|||||||
tracker?.({ progress: 0, isDownloading: false, state: DownloadState.Cancelled })
|
tracker?.({ progress: 0, isDownloading: false, state: DownloadState.Cancelled })
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
downloadAborts.abort(name)
|
downloadAborts.abort(uuid)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -74,6 +74,7 @@ export default defineConfig(({ mode }) => {
|
|||||||
extensions: ['.ts', '.tsx', '.js', '.jsx', '.css', '.scss'],
|
extensions: ['.ts', '.tsx', '.js', '.jsx', '.css', '.scss'],
|
||||||
},
|
},
|
||||||
optimizeDeps: {
|
optimizeDeps: {
|
||||||
|
// include: [],
|
||||||
// exclude: [], // add libs for local development, if needed, e.g.: @solarpunkltd/file-manager-lib
|
// exclude: [], // add libs for local development, if needed, e.g.: @solarpunkltd/file-manager-lib
|
||||||
},
|
},
|
||||||
build: {
|
build: {
|
||||||
|
|||||||
Reference in New Issue
Block a user