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
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,10 @@ const useStyles = makeStyles(({ breakpoints, constants, palette }: CSSTheme) =>
},
dropdownItem: {
backgroundColor: constants.fileSystemItemRow.itemBackground,
color: constants.fileSystemItemRow.itemColor
color: constants.fileSystemItemRow.itemColor,
"& a": {
textDecoration: "none"
}
}
})
})
Expand Down Expand Up @@ -166,7 +169,8 @@ const FileSystemTableItem = React.forwardRef(
<TableRow
data-cy="file-item-row"
className={clsx(classes.tableRow, {
droppable: isFolder && (isOverMove || isOverUpload)
droppable: isFolder && (isOverMove || isOverUpload),
ipfs: desktop && fileSystemType && fileSystemType === "ipfs"
})}
type="grid"
ref={forwardedRef}
Expand Down
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
89 changes: 88 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,81 @@ const StorageProvider = ({ children }: StorageContextProps) => {
}
}, [storageBuckets, storageApiClient, getStorageSummary])

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

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 +355,7 @@ const StorageProvider = ({ children }: StorageContextProps) => {
refreshPins,
unpin,
storageBuckets,
downloadFile,
createBucket,
removeBucket,
uploadFiles
Expand Down