-
Notifications
You must be signed in to change notification settings - Fork 6
/
Dag.js
119 lines (96 loc) · 3.2 KB
/
Dag.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import React, { Component, createRef } from 'react'
import cytoscape from 'cytoscape'
import dagre from 'cytoscape-dagre'
import UnixFs from 'ipfs-unixfs'
import { DAGNode } from 'ipld-dag-pb'
import { Buffer } from 'ipfs'
import { getIpfs } from './lib/ipfs'
import DagGraphOptions from './DagGraphOptions'
cytoscape.use(dagre)
export default class Dag extends Component {
constructor () {
super()
this._graphRoot = createRef()
}
componentDidMount () {
this._updateGraph()
}
componentDidUpdate (prevProps) {
if (prevProps.rootCid !== this.props.rootCid) {
this._updateGraph()
}
}
async _updateGraph () {
if (this._graph) this._graph.destroy()
if (this.props.onNodeFocus) this.props.onNodeFocus(null)
const { rootCid } = this.props
if (!rootCid) return
const nodeMap = await this._getGraphNodes(rootCid)
// ...could have taken a while, did we get a new root node?
if (rootCid !== this.props.rootCid) return
const container = this._graphRoot.current
const elements = Array.from(nodeMap.values())
const cy = this._graph = cytoscape({ elements, container, ...DagGraphOptions })
const focusElement = element => {
cy.nodes('.focused').removeClass('focused')
element.addClass('focused')
this.props.onNodeFocus(element.data())
}
cy.on('tapdragover', e => {
if (!this.props.onNodeFocus || e.target.group() !== 'nodes') return
focusElement(e.target)
})
cy.layout(DagGraphOptions.layout).run()
if (this.props.onNodeFocus) focusElement(cy.getElementById(rootCid))
if (this.props.onGraphRender) this.props.onGraphRender()
}
async _getGraphNodes (cid, nodeMap = new Map()) {
if (nodeMap.get(cid)) return
const ipfs = await getIpfs()
const { value: source } = await ipfs.dag.get(cid)
const classes = []
let nodeData = {}
if (DAGNode.isDAGNode(source)) {
try {
// it's a unix system?
const unixfsData = UnixFs.unmarshal(source.Data)
nodeData = {
type: 'unixfs',
isLeaf: Boolean(source.Links.length),
length: (await ipfs.block.get(cid)).data.length,
unixfsData
}
} catch (err) {
// dag-pb but not a unixfs.
console.log(err)
}
for (let i = 0; i < source.Links.length; i++) {
await this._getGraphNodes(source.Links[i].Hash.toString(), nodeMap)
}
if (!source.Links.length) classes.push('leaf')
if (nodeData) classes.push('unixfs', nodeData.unixfsData.type)
} else if (Buffer.isBuffer(source)) {
classes.push('raw')
nodeData = { type: 'raw', isLeaf: true, length: source.length }
} else {
// TODO: What IPLD node is this? How to extract the links?
classes.push('leaf')
nodeData = { type: 'unknown', isLeaf: true }
}
nodeMap.set(cid, {
group: 'nodes',
data: { id: cid, ...nodeData },
classes
})
;(source.Links || []).forEach(link => {
nodeMap.set(cid + '->' + link.Hash, {
group: 'edges',
data: { source: cid, target: link.Hash.toString() }
})
})
return nodeMap
}
render () {
return <div ref={this._graphRoot} className='bg-snow-muted h-100' />
}
}