diff --git a/public/locales/en/app.json b/public/locales/en/app.json index 82ba213de..427d1f3b9 100644 --- a/public/locales/en/app.json +++ b/public/locales/en/app.json @@ -27,7 +27,8 @@ "setPinning": "Set pinning", "submit": "Submit", "unpin": "Unpin", - "unselectAll": "Unselect all" + "unselectAll": "Unselect all", + "exportDag": "Export DAG" }, "cliModal": { "description": "Paste the following into your terminal to do this task in IPFS via the command line. Remember that you'll need to replace placeholders with your specific parameters." diff --git a/src/bundles/files/actions.js b/src/bundles/files/actions.js index 650ee715d..be44a624e 100644 --- a/src/bundles/files/actions.js +++ b/src/bundles/files/actions.js @@ -1,7 +1,7 @@ /* eslint-disable require-yield */ import { join, dirname, basename } from 'path' -import { getDownloadLink, getShareableLink } from '../../lib/files' +import { getDownloadLink, getShareableLink, getDagCarLink } from '../../lib/files' import countDirs from '../../lib/count-dirs' import memoize from 'p-memoize' import all from 'it-all' @@ -423,6 +423,15 @@ const actions = () => ({ return await getDownloadLink(files, gatewayUrl, apiUrl, ipfs) }), + /** + * Creates a download link for the DAG CAR. + * @param {FileStat[]} files + */ + doFilesExportDagLink: (files) => perform(ACTIONS.DOWNLOAD_LINK, async (ipfs, { store }) => { + const apiUrl = store.selectApiUrl() + return await getDagCarLink(files, apiUrl, ipfs) + }), + /** * Generates sharable link for the provided files. * @param {FileStat[]} files diff --git a/src/bundles/files/consts.js b/src/bundles/files/consts.js index 0bb4818d2..3f648a953 100644 --- a/src/bundles/files/consts.js +++ b/src/bundles/files/consts.js @@ -78,7 +78,8 @@ export const cliCmdKeys = { ADD_DIRECTORY: 'addNewDirectory', CREATE_NEW_DIRECTORY: 'createNewDirectory', FROM_IPFS: 'fromIpfs', - ADD_NEW_PEER: 'addNewPeer' + ADD_NEW_PEER: 'addNewPeer', + EXPORT_DAG_COMMAND: 'exportDagCommand' } export const cliCmdPrefixes = { @@ -124,5 +125,9 @@ export const cliCommandList = { * @param {string} path */ [cliCmdKeys.FROM_IPFS]: (path) => `ipfs files cp /ipfs/ "${path}/"`, - [cliCmdKeys.ADD_NEW_PEER]: () => 'ipfs swarm connect ' + [cliCmdKeys.ADD_NEW_PEER]: () => 'ipfs swarm connect ', + /** + * @param {string} cid + */ + [cliCmdKeys.EXPORT_DAG_COMMAND]: (cid) => `ipfs dag export ${cid}` } diff --git a/src/files/FilesPage.js b/src/files/FilesPage.js index a728fea53..ab5fad3d4 100644 --- a/src/files/FilesPage.js +++ b/src/files/FilesPage.js @@ -21,7 +21,7 @@ import Header from './header/Header' import FileImportStatus from './file-import-status/FileImportStatus' const FilesPage = ({ - doFetchPinningServices, doFilesFetch, doPinsFetch, doFilesSizeGet, doFilesDownloadLink, doFilesWrite, doFilesAddPath, doUpdateHash, + doFetchPinningServices, doFilesFetch, doPinsFetch, doFilesSizeGet, doFilesDownloadLink, doFilesExportDagLink, doFilesWrite, doFilesAddPath, doUpdateHash, doFilesUpdateSorting, doFilesNavigateTo, doFilesMove, doSetCliOptions, doFetchRemotePins, remotePins, doExploreUserProvidedPath, ipfsProvider, ipfsConnected, doFilesMakeDir, doFilesShareLink, doFilesDelete, doSetPinning, onRemotePinClick, files, filesPathInfo, pinningServices, toursEnabled, handleJoyrideCallback, isCliTutorModeEnabled, cliOptions, t @@ -67,6 +67,18 @@ const FilesPage = ({ const { abort } = await downloadFile(url, filename, updater, method) setDownloadAbort(() => abort) } + + const onExportDag = async (files) => { + if (downloadProgress !== null) { + return downloadAbort() + } + + const updater = (v) => setDownloadProgress(v) + const { url, filename, method } = await doFilesExportDagLink(files) + const { abort } = await downloadFile(url, filename, updater, method) + setDownloadAbort(() => abort) + } + const onAddFiles = (raw, root = '') => { if (root === '') root = files.path @@ -202,6 +214,7 @@ const FilesPage = ({ onRename={() => showModal(RENAME, [contextMenu.file])} onInspect={() => onInspect(contextMenu.file.cid)} onDownload={() => onDownload([contextMenu.file])} + onExportDag={() => onExportDag([contextMenu.file])} onPinning={() => showModal(PINNING, [contextMenu.file])} isCliTutorModeEnabled={isCliTutorModeEnabled} onCliTutorMode={() => showModal(CLI_TUTOR_MODE, [contextMenu.file])} @@ -274,6 +287,7 @@ export default connect( 'selectToursEnabled', 'doFilesWrite', 'doFilesDownloadLink', + 'doFilesExportDagLink', 'doExploreUserProvidedPath', 'doFilesSizeGet', 'selectIsCliTutorModeEnabled', diff --git a/src/files/context-menu/ContextMenu.js b/src/files/context-menu/ContextMenu.js index abd97cb7b..492bfebfd 100644 --- a/src/files/context-menu/ContextMenu.js +++ b/src/files/context-menu/ContextMenu.js @@ -9,6 +9,7 @@ import StrokePencil from '../../icons/StrokePencil' import StrokeIpld from '../../icons/StrokeIpld' import StrokeTrash from '../../icons/StrokeTrash' import StrokeDownload from '../../icons/StrokeDownload' +import StrokeData from '../../icons/StrokeData' import StrokePin from '../../icons/StrokePin' import { cliCmdKeys } from '../../bundles/files/consts' @@ -43,7 +44,7 @@ class ContextMenu extends React.Component { render () { const { - t, onRename, onRemove, onDownload, onInspect, onShare, + t, onRename, onRemove, onDownload, onInspect, onShare, onExportDag, translateX, translateY, className, isMfs, isUnknown, isCliTutorModeEnabled } = this.props return ( @@ -87,6 +88,13 @@ class ContextMenu extends React.Component { {t('app:actions.download')} } + { !isUnknown && onExportDag && + + } { !isUnknown && isMfs && onRename &&