Skip to content

Commit 6ad7b93

Browse files
authored
Merge pull request #6 from ipfs-shipyard/feat/node-info
feat: add node info bar
2 parents f2111ef + d007e90 commit 6ad7b93

File tree

8 files changed

+525
-31
lines changed

8 files changed

+525
-31
lines changed

package-lock.json

Lines changed: 132 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"cytoscape-dagre": "^2.2.2",
99
"ipfs": "^0.35.0",
1010
"ipfs-css": "^0.12.0",
11+
"ipld-dag-pb": "^0.17.0",
1112
"parcel": "^1.12.3",
1213
"react": "^16.8.6",
1314
"react-dom": "^16.8.6",

src/App.js

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,23 @@ import Dag from './Dag'
66
import { Buffer } from 'ipfs'
77
import { ipfsAdd } from './lib/ipfs'
88
import DropTarget from './DropTarget'
9+
import NodeInfo from './NodeInfo'
910

1011
export default function App () {
1112
const [files, setFiles] = useState([])
1213
const [chunker, setChunker] = useState('size-512')
14+
const [rawLeaves, setRawLeaves] = useState(false)
1315
const [strategy, setStrategy] = useState('balanced')
1416
const [maxChildren, setMaxChildren] = useState(11)
1517
const [layerRepeat, setLayerRepeat] = useState(4)
1618
const [rootCid, setRootCid] = useState(null)
19+
const [focusedNode, setFocusedNode] = useState(null)
1720

1821
useEffect(() => {
1922
if (!files.length) return
20-
ipfsAdd({ files, chunker, strategy, maxChildren, layerRepeat }).then(setRootCid)
21-
}, [files, chunker, strategy, maxChildren, layerRepeat])
23+
ipfsAdd({ files, chunker, rawLeaves, strategy, maxChildren, layerRepeat })
24+
.then(setRootCid)
25+
}, [files, chunker, rawLeaves, strategy, maxChildren, layerRepeat])
2226

2327
const onFileChange = file => {
2428
const fileReader = new FileReader()
@@ -35,6 +39,7 @@ export default function App () {
3539
setMaxChildren(11)
3640
setLayerRepeat(4)
3741
setRootCid(null)
42+
setFocusedNode(null)
3843
}
3944

4045
return (
@@ -46,6 +51,8 @@ export default function App () {
4651
<Controls
4752
chunker={chunker}
4853
onChunkerChange={setChunker}
54+
rawLeaves={rawLeaves}
55+
onRawLeavesChange={setRawLeaves}
4956
strategy={strategy}
5057
onStrategyChange={setStrategy}
5158
maxChildren={maxChildren}
@@ -56,7 +63,16 @@ export default function App () {
5663
</div>
5764
<div className='flex-auto'>
5865
<DropTarget onFileDrop={onFileChange} className='h-100'>
59-
{files.length ? <Dag rootCid={rootCid} /> : null}
66+
{files.length ? (
67+
<div className='flex flex-column h-100'>
68+
<div className='flex-auto'>
69+
<Dag rootCid={rootCid} onNodeFocus={setFocusedNode} />
70+
</div>
71+
<div className='flex-none'>
72+
<NodeInfo info={focusedNode} />
73+
</div>
74+
</div>
75+
) : null}
6076
</DropTarget>
6177
</div>
6278
</div>

src/Controls.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import React from 'react'
33
export default function Controls ({
44
chunker,
55
onChunkerChange,
6+
rawLeaves,
7+
onRawLeavesChange,
68
strategy,
79
onStrategyChange,
810
maxChildren,
@@ -25,6 +27,15 @@ export default function Controls ({
2527
<option value='size-262144'>26,2144 byte chunks</option>
2628
</select>
2729
</div>
30+
<div className='mr3'>
31+
<select
32+
value={rawLeaves}
33+
onChange={e => onRawLeavesChange(e.target.value === 'true')}
34+
className='charcoal ba b--black-20 br1 pv1 ph2 db center focus-outline'>
35+
<option value='false'>UnixFS leaves</option>
36+
<option value='true'>Raw leaves</option>
37+
</select>
38+
</div>
2839
<div className='mr3'>
2940
<select
3041
value={strategy}

src/Dag.js

Lines changed: 55 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React, { Component, createRef } from 'react'
22
import cytoscape from 'cytoscape'
33
import dagre from 'cytoscape-dagre'
44
import UnixFs from 'ipfs-unixfs'
5+
import { DAGNode } from 'ipld-dag-pb'
6+
import { Buffer } from 'ipfs'
57
import { getIpfs } from './lib/ipfs'
68
import DagGraphOptions from './DagGraphOptions'
79

@@ -17,8 +19,10 @@ export default class Dag extends Component {
1719
this._updateGraph()
1820
}
1921

20-
componentDidUpdate () {
21-
this._updateGraph()
22+
componentDidUpdate (prevProps) {
23+
if (prevProps.rootCid !== this.props.rootCid) {
24+
this._updateGraph()
25+
}
2226
}
2327

2428
async _updateGraph () {
@@ -35,37 +39,71 @@ export default class Dag extends Component {
3539
const container = this._graphRoot.current
3640
const elements = Array.from(nodeMap.values())
3741

38-
this._graph = cytoscape({ elements, container, ...DagGraphOptions })
39-
this._graph.layout(DagGraphOptions.layout).run()
42+
const cy = this._graph = cytoscape({ elements, container, ...DagGraphOptions })
43+
44+
const focusElement = element => {
45+
cy.nodes('.focused').removeClass('focused')
46+
element.addClass('focused')
47+
this.props.onNodeFocus(element.data())
48+
}
49+
50+
cy.on('tapdragover', e => {
51+
if (!this.props.onNodeFocus || e.target.group() !== 'nodes') return
52+
focusElement(e.target)
53+
})
54+
55+
cy.layout(DagGraphOptions.layout).run()
56+
57+
if (this.props.onNodeFocus) {
58+
focusElement(cy.getElementById(rootCid))
59+
}
4060
}
4161

4262
async _getGraphNodes (cid, nodeMap = new Map()) {
4363
if (nodeMap.get(cid)) return
4464

4565
const ipfs = await getIpfs()
4666
const { value: source } = await ipfs.dag.get(cid)
47-
67+
const classes = []
4868
let nodeData = {}
4969

50-
try {
51-
// it's a unix system?
52-
nodeData = UnixFs.unmarshal(source.data)
53-
} catch (err) {
54-
// dag-pb but not a unixfs.
55-
console.log(err)
56-
}
57-
58-
for (let i = 0; i < source.links.length; i++) {
59-
await this._getGraphNodes(source.links[i].cid.toString(), nodeMap)
70+
if (DAGNode.isDAGNode(source)) {
71+
try {
72+
// it's a unix system?
73+
const unixfsData = UnixFs.unmarshal(source.data)
74+
nodeData = {
75+
type: 'unixfs',
76+
isLeaf: Boolean(source.links.length),
77+
length: (await ipfs.block.get(cid)).data.length,
78+
unixfsData
79+
}
80+
} catch (err) {
81+
// dag-pb but not a unixfs.
82+
console.log(err)
83+
}
84+
85+
for (let i = 0; i < source.links.length; i++) {
86+
await this._getGraphNodes(source.links[i].cid.toString(), nodeMap)
87+
}
88+
89+
if (!source.links.length) classes.push('leaf')
90+
if (nodeData) classes.push('unixfs', nodeData.unixfsData.type)
91+
} else if (Buffer.isBuffer(source)) {
92+
classes.push('raw')
93+
nodeData = { type: 'raw', isLeaf: true, length: source.length }
94+
} else {
95+
// TODO: What IPLD node is this? How to extract the links?
96+
classes.push('leaf')
97+
nodeData = { type: 'unknown', isLeaf: true }
6098
}
6199

62100
nodeMap.set(cid, {
63101
group: 'nodes',
64102
data: { id: cid, ...nodeData },
65-
classes: source.links.length ? [] : ['leaf']
103+
classes
66104
})
67105

68-
source.links.forEach(link => {
106+
;(source.links || []).forEach(link => {
69107
nodeMap.set(cid + '->' + link.cid, {
70108
group: 'edges',
71109
data: { source: cid, target: link.cid.toString() }

0 commit comments

Comments
 (0)