@@ -0,0 +1,148 @@
1+import { Just, Nothing } from 'folktale/maybe';
2+import React from 'react';
3+import { pour } from 'sigil-js';
4+import * as ob from 'urbit-ob';
5+import * as azimuth from 'azimuth-js';
6+import * as need from '../lib/need';
7+
8+import PointList from '../components/PointList';
9+import ReactSVGComponents from '../components/ReactSVGComponents';
10+import KeysAndMetadata from './Point/KeysAndMetadata';
11+import Actions from './Point/Actions';
12+import { Row, Col, H1, H3 } from '../components/Base';
13+
14+import { compose } from '../lib/lib';
15+import { ROUTE_NAMES } from '../lib/routeNames';
16+
17+import { withHistory } from '../store/history';
18+import { withNetwork } from '../store/network';
19+import { withPointCursor } from '../store/pointCursor';
20+import { withPointCache } from '../store/pointCache';
21+
22+class PointHome extends React.Component {
23+ constructor(props) {
24+ super(props);
25+
26+ this.state = {
27+ birthday: Nothing(),
28+ spawned: [],
29+ invites: Nothing(),
30+ };
31+ }
32+
33+ componentDidMount() {
34+ this.cachePoints();
35+ this.updateBirthday();
36+ }
37+
38+ //TODO can probably re-use this to implement point switching
39+ componentDidUpdate(prevProps) {
40+ //
41+ // NB (jtobin)
42+ //
43+ // When a link in the 'issued points' component is clicked, we don't
44+ // actually leave the Point component, and componentDidMount will not fire
45+ // again. But, we can force a call to cachePoints here.
46+ //
47+ const oldCursor = prevProps.pointCursor.getOrElse(true);
48+ const newCursor = this.props.pointCursor.getOrElse(true);
49+
50+ if (oldCursor !== newCursor) {
51+ this.cachePoints();
52+ }
53+ }
54+
55+ cachePoints() {
56+ const { web3, contracts, pointCursor, addToPointCache } = this.props;
57+
58+ web3.chain(_ =>
59+ contracts.chain(ctrcs =>
60+ pointCursor.chain(point => {
61+ azimuth.azimuth
62+ .getPoint(ctrcs, point)
63+ .then(details => addToPointCache({ [point]: details }));
64+ azimuth.delegatedSending
65+ .getTotalUsableInvites(ctrcs, point)
66+ .then(count => this.setState({ invites: Just(count) }));
67+ this.updateSpawned(ctrcs, point);
68+ })
69+ )
70+ );
71+ }
72+
73+ async updateBirthday(blockNumber) {
74+ const birthBlock = await azimuth.azimuth.getActivationBlock(
75+ need.contracts(this.props),
76+ need.pointCursor(this.props)
77+ );
78+ if (birthBlock === 0) {
79+ this.setState({ birthday: Nothing() });
80+ } else {
81+ const block = await need.web3(this.props).eth.getBlock(birthBlock);
82+ this.setState({ birthday: Just(new Date(block.timestamp * 1000)) });
83+ }
84+ }
85+
86+ updateSpawned = contracts => {
87+ const { pointCursor } = this.props;
88+
89+ pointCursor.matchWith({
90+ Nothing: _ => {},
91+ Just: point => {
92+ azimuth.azimuth
93+ .getSpawned(contracts, point.value)
94+ .then(spawned => this.setState({ spawned }));
95+ },
96+ });
97+ };
98+
99+ render() {
100+ const { web3, history, wallet, setPointCursor, pointCache } = this.props;
101+
102+ const point = need.pointCursor(this.props);
103+
104+ const pointDetails =
105+ point in pointCache ? Just(pointCache[point]) : Nothing();
106+
107+ const name = ob.patp(point);
108+
109+ const sigil = pour({
110+ patp: name,
111+ renderer: ReactSVGComponents,
112+ size: 256,
113+ });
114+
115+ const online = Just.hasInstance(web3);
116+
117+ const authenticated = Just.hasInstance(wallet);
118+
119+ return (
120+ <Row>
121+ <Col>
122+ <div className={'mt-12 pt-6'}>{sigil}</div>
123+ <H1>
124+ <code>{name}</code>
125+ </H1>
126+ {authenticated ? (
127+ <Actions
128+ online={online}
129+ wallet={wallet}
130+ point={point}
131+ pointDetails={pointDetails}
132+ invites={this.state.invites}
133+ />
134+ ) : null}
135+
136+ {online ? <KeysAndMetadata pointDetails={pointDetails} /> : <div />}
137+ </Col>
138+ </Row>
139+ );
140+ }
141+}
142+
143+export default compose(
144+ withNetwork,
145+ withHistory,
146+ withPointCursor,
147+ withPointCache
148+)(PointHome);