Skip to content

Commit

Permalink
feat: export DAG CAR via the context menu
Browse files Browse the repository at this point in the history
License: MIT
Signed-off-by: Henrique Dias <hacdias@gmail.com>
  • Loading branch information
hacdias committed Jun 2, 2022
1 parent e77afcb commit 11089b7
Show file tree
Hide file tree
Showing 7 changed files with 68 additions and 6 deletions.
3 changes: 2 additions & 1 deletion public/locales/en/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand Down
11 changes: 10 additions & 1 deletion src/bundles/files/actions.js
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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
Expand Down
9 changes: 7 additions & 2 deletions src/bundles/files/consts.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -124,5 +125,9 @@ export const cliCommandList = {
* @param {string} path
*/
[cliCmdKeys.FROM_IPFS]: (path) => `ipfs files cp /ipfs/<cid> "${path}/<dest-name>"`,
[cliCmdKeys.ADD_NEW_PEER]: () => 'ipfs swarm connect <peer-multiaddr>'
[cliCmdKeys.ADD_NEW_PEER]: () => 'ipfs swarm connect <peer-multiaddr>',
/**
* @param {string} cid
*/
[cliCmdKeys.EXPORT_DAG_COMMAND]: (cid) => `ipfs dag export ${cid}`
}
16 changes: 15 additions & 1 deletion src/files/FilesPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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])}
Expand Down Expand Up @@ -274,6 +287,7 @@ export default connect(
'selectToursEnabled',
'doFilesWrite',
'doFilesDownloadLink',
'doFilesExportDagLink',
'doExploreUserProvidedPath',
'doFilesSizeGet',
'selectIsCliTutorModeEnabled',
Expand Down
11 changes: 10 additions & 1 deletion src/files/context-menu/ContextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -87,6 +88,13 @@ class ContextMenu extends React.Component {
{t('app:actions.download')}
</Option>
}
{ !isUnknown && onExportDag &&
<Option onClick={this.wrap('onExportDag')} isCliTutorModeEnabled={isCliTutorModeEnabled}
onCliTutorMode={this.wrap('onCliTutorMode', cliCmdKeys.EXPORT_DAG_COMMAND)}>
<StrokeData className='w2 mr2 fill-aqua' />
{t('app:actions.exportDag')}
</Option>
}
{ !isUnknown && isMfs && onRename &&
<Option onClick={this.wrap('onRename')} isCliTutorModeEnabled={isCliTutorModeEnabled}
onCliTutorMode={this.wrap('onCliTutorMode', cliCmdKeys.RENAME_IPFS_OBJECT)}>
Expand Down Expand Up @@ -120,6 +128,7 @@ ContextMenu.propTypes = {
onRemove: PropTypes.func,
onRename: PropTypes.func,
onDownload: PropTypes.func,
onExportDag: PropTypes.func,
onInspect: PropTypes.func,
onShare: PropTypes.func,
className: PropTypes.string,
Expand Down
1 change: 1 addition & 0 deletions src/files/context-menu/ContextMenu.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ storiesOf('Files', module)
onInspect={action('Inspect')}
onRename={action('Rename')}
onDownload={action('Download')}
onExportDag={action('Export DAG')}
onRemove={action('Remove')}
handleClick={action('Handle Click')}
onNavigate={action('Navigate')}
Expand Down
23 changes: 23 additions & 0 deletions src/lib/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,29 @@ export async function getShareableLink (files, gatewayUrl, ipfs) {
return `${gatewayUrl}/ipfs/${cid}${filename || ''}`
}

/**
*
* @param {FileStat[]} files
* @param {string} apiUrl
* @param {IPFSService} ipfs
* @returns {Promise<FileDownload>}
*/
export async function getDagCarLink (files, apiUrl, ipfs) {
let cid

if (files.length === 1) {
cid = files[0].cid
} else {
cid = await makeCIDFromFiles(files, ipfs)
}

return {
url: `${apiUrl}/api/v0/dag/export?arg=${cid}`,
filename: `dag_${cid}.car`,
method: 'POST' // API is POST-only
}
}

/**
* @param {number} size in bytes
* @param {object} opts format customization
Expand Down

0 comments on commit 11089b7

Please sign in to comment.