diff --git a/src/bundles/files/actions.js b/src/bundles/files/actions.js index f33362cd9..1d164e192 100644 --- a/src/bundles/files/actions.js +++ b/src/bundles/files/actions.js @@ -396,9 +396,8 @@ const actions = () => ({ * @param {FileStat[]} files */ doFilesDownloadLink: (files) => perform(ACTIONS.DOWNLOAD_LINK, async (ipfs, { store }) => { - const apiUrl = store.selectApiUrl() const gatewayUrl = store.selectGatewayUrl() - return await getDownloadLink(files, gatewayUrl, apiUrl, ipfs) + return getDownloadLink(files, gatewayUrl, ipfs) }), /** @@ -407,7 +406,7 @@ const actions = () => ({ */ doFilesDownloadCarLink: (files) => perform(ACTIONS.DOWNLOAD_LINK, async (ipfs, { store }) => { const gatewayUrl = store.selectGatewayUrl() - return await getCarLink(files, gatewayUrl, ipfs) + return getCarLink(files, gatewayUrl, ipfs) }), /** @@ -417,7 +416,7 @@ const actions = () => ({ doFilesShareLink: (files) => perform(ACTIONS.SHARE_LINK, async (ipfs, { store }) => { // ensureMFS deliberately omitted here, see https://github.com/ipfs/ipfs-webui/issues/1744 for context. const publicGateway = store.selectPublicGateway() - return await getShareableLink(files, publicGateway, ipfs) + return getShareableLink(files, publicGateway, ipfs) }), /** diff --git a/src/files/FilesPage.js b/src/files/FilesPage.js index 456ae861d..56aa2a077 100644 --- a/src/files/FilesPage.js +++ b/src/files/FilesPage.js @@ -6,7 +6,6 @@ import { withTranslation, Trans } from 'react-i18next' import ReactJoyride from 'react-joyride' // Lib import { filesTour } from '../lib/tours' -import downloadFile from './download-file' // Components import ContextMenu from './context-menu/ContextMenu' import withTour from '../components/tour/withTour' @@ -27,8 +26,6 @@ const FilesPage = ({ files, filesPathInfo, pinningServices, toursEnabled, handleJoyrideCallback, isCliTutorModeEnabled, cliOptions, t }) => { const contextMenuRef = useRef() - const [downloadAbort, setDownloadAbort] = useState(null) - const [downloadProgress, setDownloadProgress] = useState(null) const [modals, setModals] = useState({ show: null, files: null }) const [contextMenu, setContextMenu] = useState({ isOpen: false, @@ -58,32 +55,13 @@ const FilesPage = ({ */ const onDownload = async (files) => { - if (downloadProgress !== null) { - return downloadAbort() - } - - const { url, filename, method } = await doFilesDownloadLink(files) - - if (method === 'GET') { - const link = document.createElement('a') - link.href = url - link.click() - } else { - const updater = (v) => setDownloadProgress(v) - const { abort } = await downloadFile(url, filename, updater, method) - setDownloadAbort(() => abort) - } + const url = await doFilesDownloadLink(files) + window.location.href = url } const onDownloadCar = async (files) => { - if (downloadProgress !== null) { - return downloadAbort() - } - const url = await doFilesDownloadCarLink(files) - const link = document.createElement('a') - link.href = url - link.click() + window.location.href = url } const onAddFiles = (raw, root = '') => { @@ -167,7 +145,6 @@ const FilesPage = ({ pendingPins={pendingPins} failedPins={failedPins} upperDir={files.upper} - downloadProgress={downloadProgress} onShare={(files) => showModal(SHARE, files)} onRename={(files) => showModal(RENAME, files)} onRemove={(files) => showModal(DELETE, files)} diff --git a/src/files/download-file.js b/src/files/download-file.js deleted file mode 100644 index 9d830b918..000000000 --- a/src/files/download-file.js +++ /dev/null @@ -1,44 +0,0 @@ -const downloadFile = (srcUrl, filename, progressCallback, method = 'GET') => { - const xhr = new window.XMLHttpRequest() - let total = 0 - - const abort = () => { - xhr.abort() - progressCallback(null) - } - - xhr.responseType = 'blob' - xhr.open(method, srcUrl, true) - - xhr.onload = (e) => { - progressCallback(100) - - const res = xhr.response - const blob = new window.Blob([res]) - const url = window.URL.createObjectURL(blob) - const a = document.createElement('a') - - document.body.appendChild(a) - a.style = 'display:none' - a.href = url - a.download = filename - a.click() - - window.URL.revokeObjectURL(url) - progressCallback(null) - } - - xhr.onprogress = (e) => { - total = e.lengthComputable - ? e.total - : (total || - xhr.getResponseHeader('X-Content-Length') || - xhr.getResponseHeader('Content-Length')) - - progressCallback((e.loaded / total) * 100) - } - - xhr.send() - return { abort } -} -export default downloadFile diff --git a/src/files/files-list/FilesList.js b/src/files/files-list/FilesList.js index 8bdafe277..476aaf707 100644 --- a/src/files/files-list/FilesList.js +++ b/src/files/files-list/FilesList.js @@ -51,7 +51,7 @@ const mergeRemotePinsIntoFiles = (files, remotePins = [], pendingPins = [], fail } export const FilesList = ({ - className, files, pins, pinningServices, remotePins, pendingPins, failedPins, filesSorting, updateSorting, downloadProgress, filesIsFetching, filesPathInfo, showLoadingAnimation, + className, files, pins, pinningServices, remotePins, pendingPins, failedPins, filesSorting, updateSorting, filesIsFetching, filesPathInfo, showLoadingAnimation, onShare, onSetPinning, onInspect, onDownload, onRemove, onRename, onNavigate, onRemotePinClick, onAddFiles, onMove, doFetchRemotePins, doDismissFailedPin, handleContextMenuClick, t }) => { const [selected, setSelected] = useState([]) @@ -355,7 +355,6 @@ export const FilesList = ({ inspect={() => onInspect(selectedFiles[0].cid)} count={selectedFiles.length} isMfs={filesPathInfo.isMfs} - downloadProgress={downloadProgress} size={selectedFiles.reduce((a, b) => a + (b.size || 0), 0)} /> } } @@ -374,7 +373,6 @@ FilesList.propTypes = { asc: PropTypes.bool.isRequired }), updateSorting: PropTypes.func.isRequired, - downloadProgress: PropTypes.number, filesIsFetching: PropTypes.bool, filesPathInfo: PropTypes.object, // Actions diff --git a/src/files/selected-actions/SelectedActions.js b/src/files/selected-actions/SelectedActions.js index a722dd9e5..09f7c4968 100644 --- a/src/files/selected-actions/SelectedActions.js +++ b/src/files/selected-actions/SelectedActions.js @@ -55,7 +55,6 @@ class SelectedActions extends React.Component { download: PropTypes.func.isRequired, rename: PropTypes.func.isRequired, inspect: PropTypes.func.isRequired, - downloadProgress: PropTypes.number, t: PropTypes.func.isRequired, tReady: PropTypes.bool.isRequired, isMfs: PropTypes.bool.isRequired, @@ -70,37 +69,12 @@ class SelectedActions extends React.Component { force100: false } - componentDidUpdate (prev) { - if (this.props.downloadProgress === 100 && prev.downloadProgress !== 100) { - this.setState({ force100: true }) - setTimeout(() => { - this.setState({ force100: false }) - }, 2000) - } - } - componentDidMount () { this.containerRef.current && this.containerRef.current.focus() } - get downloadText () { - if (this.state.force100) { - return this.props.t('finished') - } - - if (!this.props.downloadProgress) { - return this.props.t('app:actions.download') - } - - if (this.props.downloadProgress === 100) { - return this.props.t('finished') - } - - return this.props.downloadProgress.toFixed(0) + '%' - } - render () { - const { t, tReady, animateOnStart, count, size, unselect, remove, share, setPinning, download, downloadProgress, rename, inspect, className, style, isMfs, ...props } = this.props + const { t, tReady, animateOnStart, count, size, unselect, remove, share, setPinning, download, rename, inspect, className, style, isMfs, ...props } = this.props const isSingle = count === 1 @@ -131,7 +105,7 @@ class SelectedActions extends React.Component { - {this.downloadText} + {t('app:actions.download')} diff --git a/src/lib/files.js b/src/lib/files.js index 1aa37c62e..0d9bb2969 100644 --- a/src/lib/files.js +++ b/src/lib/files.js @@ -35,31 +35,20 @@ export function normalizeFiles (files) { } /** - * @typedef {Object} FileDownload - * @property {string} url - * @property {string} filename - * @property {string} method - * - * @param {FileStat} file + * @param {string} type + * @param {string} name + * @param {CID} cid * @param {string} gatewayUrl - * @param {string} apiUrl - * @returns {Promise} + * @returns {string} */ -async function downloadSingle (file, gatewayUrl, apiUrl) { - let url, filename, method - - if (file.type === 'directory') { - const name = file.name || `download_${file.cid}` // Name is not always available. - url = `${apiUrl}/api/v0/get?arg=${file.cid}&archive=true&compress=true` - filename = `${name}.tar.gz` - method = 'POST' // API is POST-only +function getDownloadURL (type, name, cid, gatewayUrl) { + if (type === 'directory') { + const filename = `${name || `download_${cid.toString()}`}.tar` + return `${gatewayUrl}/ipfs/${cid.toString()}?download=true&format=tar&filename=${filename}` } else { - url = `${gatewayUrl}/ipfs/${file.cid}?download=true&filename=${file.name}` - filename = file.name - method = 'GET' + const filename = `${name || cid}` + return `${gatewayUrl}/ipfs/${cid.toString()}?download=true&filename=${filename}` } - - return { url, filename, method } } /** @@ -87,42 +76,20 @@ export async function makeCIDFromFiles (files, ipfs) { return stat.cid } -/** - * - * @param {FileStat[]} files - * @param {string} apiUrl - * @param {IPFSService} ipfs - * @returns {Promise} - */ -async function downloadMultiple (files, apiUrl, ipfs) { - if (!apiUrl) { - const e = new Error('api url undefined') - return Promise.reject(e) - } - - const cid = await makeCIDFromFiles(files, ipfs) - - return { - url: `${apiUrl}/api/v0/get?arg=${cid}&archive=true&compress=true`, - filename: `download_${cid}.tar.gz`, - method: 'POST' // API is POST-only - } -} - /** * * @param {FileStat[]} files * @param {string} gatewayUrl - * @param {string} apiUrl * @param {IPFSService} ipfs - * @returns {Promise} + * @returns {Promise} */ -export async function getDownloadLink (files, gatewayUrl, apiUrl, ipfs) { +export async function getDownloadLink (files, gatewayUrl, ipfs) { if (files.length === 1) { - return downloadSingle(files[0], gatewayUrl, apiUrl) + return getDownloadURL(files[0].type, files[0].name, files[0].cid, gatewayUrl) } - return downloadMultiple(files, apiUrl, ipfs) + const cid = await makeCIDFromFiles(files, ipfs) + return getDownloadURL('directory', '', cid, gatewayUrl) } /**
{this.downloadText}
{t('app:actions.download')}