Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Storage: Download functionality on bucket files #1424

Merged
merged 12 commits into from
Aug 11, 2021
15 changes: 7 additions & 8 deletions packages/storage-ui/src/Components/Pages/BucketPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { useLocalStorage } from "@chainsafe/browser-storage-hooks"
import { DISMISSED_SURVEY_KEY } from "../Modules/SurveyBanner"

const BucketPage: React.FC<IFileBrowserModuleProps> = () => {
const { storageBuckets, uploadFiles, uploadsInProgress, getStorageSummary } = useStorage()
const { storageBuckets, uploadFiles, uploadsInProgress, getStorageSummary, downloadFile } = useStorage()
const { storageApiClient } = useStorageApi()
const { addToastMessage } = useToaster()
const [loadingCurrentPath, setLoadingCurrentPath] = useState(false)
Expand Down Expand Up @@ -137,15 +137,14 @@ const BucketPage: React.FC<IFileBrowserModuleProps> = () => {
}, [addToastMessage, pathContents, refreshContents, storageApiClient, bucket, currentPath])

const handleDownload = useCallback(async (
//cid: string
toDownload: ISelectedFile
) => {
throw new Error("Not implemented")
// const itemToDownload = pathContents.find(item => item.cid === cid)
// if (!itemToDownload || !bucket) return
const itemToDownload = pathContents.find(item => item.cid === toDownload.cid)
if (!itemToDownload || !bucket) return

// downloadFile(bucket.id, itemToDownload, currentPath)
downloadFile(bucket.id, itemToDownload, currentPath)
}, [
//pathContents, currentPath, bucket
pathContents, currentPath, bucket, downloadFile
])

// Breadcrumbs/paths
Expand Down Expand Up @@ -198,7 +197,7 @@ const BucketPage: React.FC<IFileBrowserModuleProps> = () => {
[CONTENT_TYPES.Image]: [],
[CONTENT_TYPES.Pdf]: [],
[CONTENT_TYPES.Text]: [],
[CONTENT_TYPES.File]: ["delete", "move"],
[CONTENT_TYPES.File]: ["download", "move", "delete"],
[CONTENT_TYPES.Directory]: ["delete", "move"]
}), [])

Expand Down
98 changes: 97 additions & 1 deletion packages/storage-ui/src/Contexts/StorageContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import { useStorageApi } from "./StorageApiContext"
import { v4 as uuidv4 } from "uuid"
import { t } from "@lingui/macro"
import { readFileAsync } from "../Utils/Helpers"
import axios, { CancelToken } from "axios"
import { getPathWithFile } from "../Utils/pathUtils"

type StorageContextProps = {
children: React.ReactNode | React.ReactNode[]
Expand All @@ -42,13 +44,22 @@ export type DownloadProgress = {
complete: boolean
}

interface GetFileContentParams {
cid: string
cancelToken?: CancelToken
onDownloadProgress?: (progressEvent: ProgressEvent<EventTarget>) => void
file: FileSystemItem
path: string
}

type StorageContext = {
pins: PinStatus[]
uploadsInProgress: UploadProgress[]
downloadsInProgress: DownloadProgress[]
storageSummary: BucketSummaryResponse | undefined
getStorageSummary: () => Promise<void>
uploadFiles: (bucketId: string, files: File[], path: string) => Promise<void>
downloadFile: (bucketId: string, itemToDownload: FileSystemItem, path: string) => void
addPin: (cid: string) => Promise<PinStatus>
refreshPins: () => void
unpin: (requestId: string) => void
Expand Down Expand Up @@ -132,7 +143,7 @@ const StorageProvider = ({ children }: StorageContextProps) => {
[]
)

const [downloadsInProgress] = useReducer(
const [downloadsInProgress, dispatchDownloadsInProgress] = useReducer(
downloadsInProgressReducer,
[]
)
Expand Down Expand Up @@ -257,6 +268,90 @@ const StorageProvider = ({ children }: StorageContextProps) => {
}
}, [storageBuckets, storageApiClient, getStorageSummary])

const getFileContent = useCallback(async (
bucketId: string,
{ cid, cancelToken, onDownloadProgress, file, path }: GetFileContentParams
) => {

// when a file is accessed from the search page, a file and a path are passed in
// because the current path will not reflect the right state of the app
const fileToGet = file

if (!fileToGet) {
console.error("No file passed, and no file found for cid:", cid, "in pathContents:", path)
throw new Error("No file found.")
}
Tbaut marked this conversation as resolved.
Show resolved Hide resolved

try {
const result = await storageApiClient.getBucketObjectContent(
bucketId,
{ path: path },
cancelToken,
onDownloadProgress
)

return result.data

} catch (error) {
if (axios.isCancel(error)) {
return Promise.reject()
} else {
console.error(error)
return Promise.reject(error)
}
}
}, [storageApiClient])

const downloadFile = useCallback(async (bucketId: string, itemToDownload: FileSystemItem, path: string) => {
const toastId = uuidv4()
try {
const downloadProgress: DownloadProgress = {
id: toastId,
fileName: itemToDownload.name,
complete: false,
error: false,
progress: 0
}
dispatchDownloadsInProgress({ type: "add", payload: downloadProgress })
const result = await getFileContent(bucketId, {
cid: itemToDownload.cid,
file: itemToDownload,
path: getPathWithFile(path, itemToDownload.name),
onDownloadProgress: (progressEvent) => {
dispatchDownloadsInProgress({
type: "progress",
payload: {
id: toastId,
progress: Math.ceil(
(progressEvent.loaded / itemToDownload.size) * 100
)
}
})
}
})
if (!result) return
const link = document.createElement("a")
link.href = URL.createObjectURL(result)
link.download = itemToDownload?.name || "file"
link.click()
dispatchDownloadsInProgress({
type: "complete",
payload: { id: toastId }
})
URL.revokeObjectURL(link.href)
setTimeout(() => {
dispatchDownloadsInProgress({
type: "remove",
payload: { id: toastId }
})
}, REMOVE_UPLOAD_PROGRESS_DELAY)
return Promise.resolve()
} catch (error) {
dispatchDownloadsInProgress({ type: "error", payload: { id: toastId } })
return Promise.reject()
}
}, [getFileContent])

return (
<StorageContext.Provider
value={{
Expand All @@ -269,6 +364,7 @@ const StorageProvider = ({ children }: StorageContextProps) => {
refreshPins,
unpin,
storageBuckets,
downloadFile,
createBucket,
removeBucket,
uploadFiles
Expand Down