Skip to content

Commit

Permalink
feat: add even more super powers to the breadcrumbs
Browse files Browse the repository at this point in the history
  • Loading branch information
rafaelramalho19 committed Sep 4, 2020
1 parent 51f5b47 commit 6afbe9f
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 111 deletions.
10 changes: 8 additions & 2 deletions src/bundles/ipfs-provider.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,9 +169,10 @@ const writeSetting = (id, value) => {
/**
* @typedef {Object} IPFSAPI
* @property {(callback?:Function) => Promise<void>} stop
* @property {Object} files
* @property {(callback?:Function) => Promise<Object>} files.stat
*/

/** @type {IPFSAPI|void} */
/** @type {IPFSAPI|null} */
let ipfs = null

/**
Expand Down Expand Up @@ -232,6 +233,11 @@ const bundle = {

doDismissIpfsInvalidAddress: () => (store) => {
store.dispatch({ type: 'IPFS_API_ADDRESS_INVALID_DISMISS' })
},

doGetCidForPath: (path) => async () => {
const { cid } = await ipfs.files.stat(path)
return cid
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/files/FilesPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ class FilesPage extends React.Component {
translateX={contextMenu.translateX}
translateY={contextMenu.translateY}
handleClick={this.handleContextMenu}
isUpperDir={contextMenu.file && contextMenu.file.name === '..'}
isDirectory={contextMenu.file && contextMenu.file.type === 'directory'}
isMfs={filesPathInfo ? filesPathInfo.isMfs : false}
isUnknown={!!(contextMenu.file && contextMenu.file.type === 'unknown')}
pinned={contextMenu.file && contextMenu.file.pinned}
Expand Down
4 changes: 4 additions & 0 deletions src/files/breadcrumbs/Breadcrumbs.css
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@
.BreadcrumbsButton.dragging::after {
opacity: 1;
}

.BreadcrumbsButton.no-events {
pointer-events: none;
}
81 changes: 54 additions & 27 deletions src/files/breadcrumbs/Breadcrumbs.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
import React, { useEffect, useState, useRef } from 'react'
import React, { useEffect, useState, useRef, useMemo } from 'react'
import classNames from 'classnames'
import PropTypes from 'prop-types'
import { basename, join } from 'path'
import { connect } from 'redux-bundler-react'
import { withTranslation } from 'react-i18next'
import { useDrop } from 'react-dnd'
import { NativeTypes } from 'react-dnd-html5-backend'

import './Breadcrumbs.css'
import { useDrop } from 'react-dnd'
import { basename, join } from 'path'
import { normalizeFiles } from '../../lib/files'

const DropableBreadcrumb = ({ index, link, path, onAddFiles, onMove, onClick, onContextMenuHandle }) => {
import './Breadcrumbs.css'

const DropableBreadcrumb = ({ index, link, fullPath, immutable, onAddFiles, onMove, onClick, onContextMenuHandle, getCidForPath }) => {
const [{ isOver }, drop] = useDrop({
accept: [NativeTypes.FILE, 'FILE'],
drop: ({ files, filesPromise, path: filePath }) => {
drop: async ({ files, filesPromise, path: filePath }) => {
if (files) {
(async () => {
const files = await filesPromise
onAddFiles(await normalizeFiles(files), path)
onAddFiles(await normalizeFiles(files), fullPath)
})()
} else {
const src = filePath
const dst = join(path, basename(filePath))
const dst = join(link.path, basename(filePath))

onMove(src, dst)
try { await onMove(src, dst) } catch (e) { console.error(e) }
}
},
collect: (monitor) => ({
Expand All @@ -32,21 +34,39 @@ const DropableBreadcrumb = ({ index, link, path, onAddFiles, onMove, onClick, on

const buttonRef = useRef()

const handleOnContextMenuHandle = (ev) => onContextMenuHandle(ev, buttonRef.current)
const handleOnContextMenuHandle = async (ev) => {
ev.preventDefault()

const { path } = link
const sanitizedPath = path.substring(path.indexOf('/', 1), path.length)
const cid = await getCidForPath(sanitizedPath)

onContextMenuHandle(undefined, buttonRef.current, {
...link,
...(!link.last && { type: 'directory' }),
cid
})
}

return (
<span className='dib pv1 pr1' ref={drop}>
<button ref={buttonRef} title={link.realName}
className={classNames('BreadcrumbsButton relative', index !== 0 && 'navy', index === 0 && 'f7 pointer pa1 bg-navy br2 mr2 white', link.last && 'b', isOver && 'dragging')}
className={classNames('BreadcrumbsButton relative',
index !== 0 && 'navy',
index === 0 && 'f7 pa1 br2 mr2',
index === 0 && (immutable ? 'bg-charcoal-muted white' : 'bg-navy white'),
immutable && (link.last || index === 0) && 'no-events',
link.last && 'b', isOver && 'dragging')}
onClick={() => onClick(link.path)} onContextMenu={(ev) => index !== 0 && handleOnContextMenuHandle(ev)}>
{link.name}
</button>
</span>
)
}

const Breadcrumbs = ({ t, tReady, path, onClick, className, onContextMenuHandle, onAddFiles, onMove, ...props }) => {
const Breadcrumbs = ({ t, tReady, path, onClick, className, onContextMenuHandle, onAddFiles, onMove, doGetCidForPath, ...props }) => {
const [overflows, setOverflows] = useState(false)
const [isImmutable, setImmutable] = useState(false)
const anchors = useRef()

useEffect(() => {
Expand All @@ -58,21 +78,22 @@ const Breadcrumbs = ({ t, tReady, path, onClick, className, onContextMenuHandle,
}
}, [overflows])

const bread = makeBread(path, t)
const bread = useMemo(() =>
makeBread(path, t, isImmutable, setImmutable)
, [isImmutable, path, t])

return (
<nav aria-label={t('breadcrumbs')} className={classNames('Breadcrumbs flex items-center sans-serif overflow-hidden', className)} {...props}>
<nav aria-label={t('breadcrumbs')} className={classNames('Breadcrumbs flex items-center sans-serif overflow-hidden sticky top-0', className)} {...props}>
<div className='nowrap overflow-hidden relative flex flex-wrap' ref={ anchors }>
<div className={`absolute left-0 top-0 h-100 w1 ${overflows ? '' : 'dn'}`} style={{ background: 'linear-gradient(to right, #ffffff 0%, transparent 100%)' }} />

{ bread.map((link, index) => (
<div key={`${index}link`}>
<DropableBreadcrumb index={index} link={link} path={path}
onAddFiles={onAddFiles} onMove={onMove} onClick={onClick} onContextMenuHandle={onContextMenuHandle} />
<span className='dib pr1 pv1 mid-gray v-top'>/</span>
<DropableBreadcrumb index={index} link={link} fullPath={path} immutable={isImmutable}
onAddFiles={onAddFiles} onMove={onMove} onClick={onClick} onContextMenuHandle={onContextMenuHandle} getCidForPath={doGetCidForPath} />
{ index !== bread.length - 1 && <span className='dib pr1 pv1 mid-gray v-top'>/</span>}
</div>
)
)}
))}

</div>
</nav>
Expand All @@ -86,17 +107,15 @@ Breadcrumbs.propTypes = {
onContextMenuHandle: PropTypes.func
}

function makeBread (root, t) {
function makeBread (root, t, isImmutable, setImmutable) {
if (root.endsWith('/')) {
root = root.substring(0, root.length - 1)
}

const parts = root.split('/').map(part => {
return {
name: part,
path: part
}
})
const parts = root.split('/').map(part => ({
name: part,
path: part
}))

for (let i = 1; i < parts.length; i++) {
const name = parts[i].name
Expand All @@ -113,10 +132,18 @@ function makeBread (root, t) {
}

parts.shift()

if (parts[0].name === 'ipfs' || parts[0].name === 'ipns') {
!isImmutable && setImmutable(true)
}

parts[0].name = t(parts[0].name)

parts[parts.length - 1].last = true
return parts
}

export default withTranslation('files')(Breadcrumbs)
export default connect(
'doGetCidForPath',
withTranslation('files')(Breadcrumbs)
)
14 changes: 7 additions & 7 deletions src/files/context-menu/ContextMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class ContextMenu extends React.Component {
const {
t, onRename, onDelete, onDownload, onInspect, onShare,
translateX, translateY, className,
isUpperDir, isMfs, isUnknown, pinned, isCliTutorModeEnabled
isDirectory, isMfs, isUnknown, pinned, isCliTutorModeEnabled
} = this.props
return (
<Dropdown className={className}>
Expand All @@ -58,7 +58,7 @@ class ContextMenu extends React.Component {
translateY={-translateY}
open={this.props.isOpen}
onDismiss={this.props.handleClick}>
{ !isUpperDir && onShare &&
{ !isDirectory && onShare &&
<Option onClick={this.wrap('onShare')}>
<StrokeShare className='w2 mr2 fill-aqua' />
{t('actions.share')}
Expand All @@ -81,21 +81,21 @@ class ContextMenu extends React.Component {
<StrokePin className='w2 mr2 fill-aqua' />
{ pinned ? t('actions.unpin') : t('actions.pin') }
</Option>
{ !isUpperDir && !isUnknown && onDownload &&
{ !isDirectory && !isUnknown && onDownload &&
<Option onClick={this.wrap('onDownload')} isCliTutorModeEnabled={isCliTutorModeEnabled}
onCliTutorMode={this.wrap('onCliTutorMode', cliCmdKeys.DOWNLOAD_OBJECT_COMMAND)}>
<StrokeDownload className='w2 mr2 fill-aqua' />
{t('actions.download')}
</Option>
}
{ !isUpperDir && !isUnknown && isMfs && onRename &&
{ !isDirectory && !isUnknown && isMfs && onRename &&
<Option onClick={this.wrap('onRename')} isCliTutorModeEnabled={isCliTutorModeEnabled}
onCliTutorMode={this.wrap('onCliTutorMode', cliCmdKeys.RENAME_IPFS_OBJECT)}>
<StrokePencil className='w2 mr2 fill-aqua' />
{t('actions.rename')}
</Option>
}
{ !isUpperDir && !isUnknown && isMfs && onDelete &&
{ !isDirectory && !isUnknown && isMfs && onDelete &&
<Option onClick={this.wrap('onDelete')} isCliTutorModeEnabled={isCliTutorModeEnabled}
onCliTutorMode={this.wrap('onCliTutorMode', cliCmdKeys.DELETE_FILE_FROM_IPFS)}
>
Expand All @@ -114,7 +114,7 @@ ContextMenu.propTypes = {
isOpen: PropTypes.bool.isRequired,
isUnknown: PropTypes.bool.isRequired,
hash: PropTypes.string,
isUpperDir: PropTypes.bool,
isDirectory: PropTypes.bool,
pinned: PropTypes.bool,
handleClick: PropTypes.func,
translateX: PropTypes.number.isRequired,
Expand All @@ -134,7 +134,7 @@ ContextMenu.propTypes = {
ContextMenu.defaultProps = {
isMfs: false,
isOpen: false,
isUpperDir: false,
isDirectory: false,
isUnknown: false,
top: 0,
left: 0,
Expand Down
1 change: 0 additions & 1 deletion src/files/context-menu/ContextMenu.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ storiesOf('Files', module)
<ContextMenu
isOpen={boolean('isOpen', false)}
isMfs={boolean('isMfs', false)}
isUpperDir={boolean('isUpperDir', false)}
pinned={boolean('pinned', false)}
top={10}
left={10}
Expand Down
Loading

0 comments on commit 6afbe9f

Please sign in to comment.