lotus/lotuspond/front/src/ChainExplorer.js

178 lines
4.8 KiB
JavaScript
Raw Normal View History

2019-08-16 10:01:32 +00:00
import React from 'react';
import {BlockLinks} from "./BlockLink";
2019-09-19 14:27:01 +00:00
import Window from "./Window";
2019-08-16 10:01:32 +00:00
const rows = 32
class ChainExplorer extends React.Component {
2019-10-09 05:12:35 +00:00
fetching = []
2019-08-16 10:01:32 +00:00
constructor(props) {
super(props)
this.update = this.update.bind(this)
this.scroll = this.scroll.bind(this)
this.state = {
follow: true,
at: props.ts.Height,
highest: props.ts,
cache: {[props.ts.Height]: props.ts},
messages: {},
}
}
viewport() {
const base = this.state.at - this.state.at % rows
return Array(rows).fill(0)
.map((_, k) => k + base)
.map(k => k > this.state.at ? k - rows : k)
}
async componentDidMount() {
let msgcache = {}
await this.updateMessages(this.props.ts.Cids, msgcache)
this.setState(prev => ({messages: {...prev.messages, ...msgcache}}))
setInterval(this.update, 1000)
}
async updateMessages(cids, msgcache) {
const msgs = await Promise.all(cids.map(async cid => [cid['/'], await this.props.client.call('Filecoin.ChainGetParentMessages', [cid])]))
2019-08-16 10:01:32 +00:00
msgs.forEach(([cid, msg]) => msgcache[cid] = msg)
}
async update() {
const tipset = await this.props.client.call("Filecoin.ChainHead", [])
if(tipset.Height > this.state.highest.Height) {
let msgcache = {}
await this.updateMessages(tipset.Cids, msgcache)
this.setState(prev => ({highest: tipset, messages: {...prev.messages, ...msgcache}, cache: {...prev.cache, [tipset.Height]: tipset}}))
if(this.state.follow) {
this.setState({at: tipset.Height})
}
}
await this.fetchVisible()
}
2019-10-09 05:12:35 +00:00
async fetch(h, base, cache, msgcache) {
2019-10-17 10:01:53 +00:00
//console.log(h, base, cache)
2019-10-09 05:12:35 +00:00
if (this.fetching[h]) {
2019-10-17 10:01:53 +00:00
return cache[h]
2019-10-09 05:12:35 +00:00
}
this.fetching[h] = true
2019-09-06 17:42:31 +00:00
if (h < 0) {
return
}
2019-10-09 05:12:35 +00:00
if(!base.Blocks) {
2019-10-23 18:04:07 +00:00
console.log("base for H is nil blk", h, base)
2019-10-09 05:12:35 +00:00
return
}
2019-10-23 18:04:07 +00:00
let cids = base.Blocks.map(b => (b.Parents || []))
2019-10-09 05:12:35 +00:00
.reduce((acc, val) => {
let out = {...acc}
val.forEach(c => out[c['/']] = 8)
return out
}, {})
cids = Object.keys(cids).map(k => ({'/': k}))
console.log("parents", cids)
2019-08-16 10:01:32 +00:00
const blocks = await Promise.all(cids.map(cid => this.props.client.call('Filecoin.ChainGetBlock', [cid])))
2019-10-23 18:04:07 +00:00
if (!blocks[0]) {
return
}
2019-08-16 10:01:32 +00:00
cache[h] = {
Height: blocks[0].Height,
2019-08-16 10:01:32 +00:00
Cids: cids,
Blocks: blocks,
}
await this.updateMessages(cids, msgcache)
2019-10-09 05:12:35 +00:00
return cache[h]
2019-08-16 10:01:32 +00:00
}
async fetchVisible() {
await this.fetchN(this.state.at)
}
async fetchN(top) {
if(!this.state.cache[top]) {
if(top === this.state.highest.Height) {
throw "fetchN broke (tipset not fetched)"
}
let h = top + rows > this.state.highest.Height ? this.state.highest.Height : top + rows
await this.fetchN(h)
}
let cache = {...this.state.cache}
let msgcache = {...this.state.messages}
2019-10-17 10:01:53 +00:00
console.log("top", top)
let parent = cache[top]
for(let i = 0; i < rows; i++) {
let newts = await this.fetch(top - i, parent, cache, msgcache)
parent = newts ? newts : parent
}
2019-08-16 10:01:32 +00:00
this.setState({cache: cache, messages: msgcache})
}
scroll(event) {
if(event.deltaY < 0 && this.state.at > 0) {
this.setState(prev => ({at: prev.at - 1, follow: false}))
}
if(event.deltaY > 0 && this.state.at < this.state.highest.Height) {
this.setState(prev => ({at: prev.at + 1, follow: prev.at + 1 === this.state.highest.Height}))
}
}
render() {
const view = this.viewport()
const content = <div className="ChainExplorer" onWheel={this.scroll}>{view.map(row => {
const base = this.state.at - this.state.at % rows
const className = row === this.state.at ? 'ChainExplorer-at' : (row < base ? 'ChainExplorer-after' : 'ChainExplorer-before')
let info = <span>(fetching)</span>
2019-10-09 07:56:00 +00:00
let h = <i>{row}</i>
2019-08-16 10:01:32 +00:00
if(this.state.cache[row]) {
const ts = this.state.cache[row]
2019-10-09 07:56:00 +00:00
h = ts.Height
2019-08-16 10:01:32 +00:00
let msgc = -1
2019-08-19 14:47:09 +00:00
if(ts.Cids[0] && this.state.messages[ts.Cids[0]['/']]) { // TODO: get from all blks
msgc = this.state.messages[ts.Cids[0]['/']].length
2019-08-16 10:01:32 +00:00
}
2019-08-19 14:47:09 +00:00
if(msgc > 0) {
msgc = <b>{msgc}</b>
}
2019-10-03 18:07:16 +00:00
let time = '?'
if(this.state.cache[row - 1]){
time = <span>{ts.Blocks[0].Timestamp - this.state.cache[row - 1].Blocks[0].Timestamp}s</span>
}
2019-08-16 10:01:32 +00:00
info = <span>
2019-10-03 18:07:16 +00:00
<BlockLinks cids={ts.Cids} blocks={ts.Blocks} conn={this.props.client} mountWindow={this.props.mountWindow} /> Msgs: {msgc} ΔT:{time}
2019-08-16 10:01:32 +00:00
</span>
}
2019-10-09 07:56:00 +00:00
return <div key={row} className={className}>@{h} {info}</div>
2019-08-16 10:01:32 +00:00
})}</div>
2019-09-19 14:27:01 +00:00
return (<Window onClose={this.props.onClose} title={`Chain Explorer ${this.state.follow ? '(Following)' : ''}`}>
2019-08-16 10:01:32 +00:00
{content}
2019-09-19 14:27:01 +00:00
</Window>)
2019-08-16 10:01:32 +00:00
}
}
export default ChainExplorer