on master help sign in
feat: de-deup pointhome, add birthday and invite info to point cache
commit0ad03e13321d38250d3ae9e29c8f440dd90c1570 authorMatt Condon (shrugs) <mattgcondon@gmail.com> dateMon 24 Jun 2019, 13:00 (7 years ago) parent14e3c13
11 files changed+116−163
src/components/Passport.js+13−1 src/lib/dateFormat.js+8 src/lib/loadingCharacter.js+3−1 src/lib/routeNames.js−1 src/lib/router.js+2−6 src/lib/useInvites.js+3−36 src/lib/usePointBirthday.js+7 src/store/pointCache.js+73−3 src/views/Login.js+5−4 src/views/Mnemonic.js+2 src/views/PointHome.js−111
src/components/Passport.js+13−1
@@ -1,12 +1,24 @@
11import React from 'react';
22import * as ob from 'urbit-ob';
33import { Grid, Flex } from 'indigo-react';
4+import { times } from 'lodash';
45
56import Sigil from './Sigil';
7+import usePointBirthday from 'lib/usePointBirthday';
8+import { formatDots } from 'lib/dateFormat';
9+import { kLoadingCharacter } from 'lib/loadingCharacter';
10+
11+const kLoadingDate = [4, 2, 2]
12+ .map(t => times(t, () => kLoadingCharacter).join(''))
13+ .join('.');
614
715export default function Passport({ point }) {
16+ const birthday = usePointBirthday(point);
17+ const birthDate = birthday.matchWith({
18+ Nothing: () => kLoadingDate,
19+ Just: p => formatDots(p.value),
20+ });
821 const name = ob.patp(point);
9− const birthDate = '2019.1.27';
1022
1123 return (
1224 <Grid gap={16} className="bg-black r8 p7">
src/lib/dateFormat.jsnew file+8
@@ -0,0 +1,8 @@
1+// NB(shrugs): if we get any more complicated with dates, add `luxon`
2+export const formatDots = date => {
3+ const year = date.getFullYear();
4+ const month = `${date.getMonth() + 1}`.padStart(2, 0);
5+ const day = `${date.getDate()}`.padStart(2, 0);
6+
7+ return [year, month, day].join('.');
8+};
src/lib/loadingCharacter.js+3−1
@@ -1,5 +1,7 @@
1+export const kLoadingCharacter = '▓';
2+
13export default val =>
24 val.matchWith({
3− Nothing: () => '▓',
5+ Nothing: () => kLoadingCharacter,
46 Just: p => p.value,
57 });
src/lib/routeNames.js−1
@@ -7,7 +7,6 @@ export const ROUTE_NAMES = {
77 INVITES_MANAGE: Symbol('INVITES_MANAGE'),
88 //
99 LOGIN: Symbol('LOGIN'),
10− POINT_HOME: Symbol('POINT_HOME'),
1110 //
1211 ACCEPT_TRANSFER: Symbol('ACCEPT_TRANSFER'),
1312 CANCEL_TRANSFER: Symbol('CANCEL_TRANSFER'),
src/lib/router.js+2−6
@@ -6,7 +6,6 @@ import Landing from '../views/Landing';
66import InviteTicket from '../views/InviteTicket';
77import InvitesManage from '../views/InvitesManage.js';
88import Login from '../views/Login.js';
9−import PointHome from '../views/PointHome.js';
109import AcceptTransfer from '../views/AcceptTransfer';
1110import CancelTransfer from '../views/CancelTransfer';
1211import CreateGalaxy from '../views/CreateGalaxy';
@@ -45,7 +44,6 @@ export const ROUTES = {
4544 [ROUTE_NAMES.INVITE]: Invite,
4645 [ROUTE_NAMES.INVITES_MANAGE]: InvitesManage,
4746 [ROUTE_NAMES.LOGIN]: Login,
48− [ROUTE_NAMES.POINT_HOME]: PointHome,
4947 [ROUTE_NAMES.ACCEPT_TRANSFER]: AcceptTransfer,
5048 [ROUTE_NAMES.CANCEL_TRANSFER]: CancelTransfer,
5149 [ROUTE_NAMES.NETWORK]: Network,
@@ -87,8 +85,8 @@ export const getRouteBreadcrumb = ({
8785 return 'Manage invites';
8886 case ROUTE_NAMES.LOGIN:
8987 return 'Login to Bridge';
90− case ROUTE_NAMES.POINT_HOME:
91− return 'Overview: ' + ob.patp(need.pointCursor({ pointCursor }));
88+ case ROUTE_NAMES.POINT:
89+ return `Overview: ${ob.patp(need.pointCursor(pointCursor))}`;
9290 case ROUTE_NAMES.NETWORK:
9391 return renderNetworkType(networkType);
9492 case ROUTE_NAMES.WALLET:
@@ -119,8 +117,6 @@ export const getRouteBreadcrumb = ({
119117 return 'Points';
120118 case ROUTE_NAMES.VIEW_POINT:
121119 return 'View';
122− case ROUTE_NAMES.POINT:
123− return ob.patp(need.pointCursor(pointCursor));
124120 case ROUTE_NAMES.SET_TRANSFER_PROXY:
125121 case ROUTE_NAMES.SET_MANAGEMENT_PROXY:
126122 case ROUTE_NAMES.SET_SPAWN_PROXY:
src/lib/useInvites.js+3−36
@@ -1,40 +1,7 @@
1−import { useCallback, useState } from 'react';
2−import Maybe from 'folktale/maybe';
3−import * as azimuth from 'azimuth-js';
1+import { usePointCache } from 'store/pointCache';
42
5−import { useNetwork } from 'store/network';
6−import useLifecycle from './useLifecycle';
7−
8−// fetch the invites available for a point
93export default function useInvites(point) {
10− const { contracts } = useNetwork();
11−
12− const [availableInvites, _setAvailableInvites] = useState(Maybe.Nothing());
13−
14− const fetchInvites = useCallback(() => {
15− const _contracts = contracts.getOrElse(null);
16−
17− if (!contracts) {
18− return;
19− }
20−
21− (async () => {
22− const count = await azimuth.delegatedSending.getTotalUsableInvites(
23− _contracts,
24− point
25− );
26− _setAvailableInvites(Maybe.Just(count));
27− })();
28− }, [_setAvailableInvites, point, contracts]);
29−
30− // fetch invites on first render
31− useLifecycle(fetchInvites);
4+ const { getInvites } = usePointCache();
325
33− return {
34− availableInvites,
35− fetchInvites,
36− // TODO: look up sent/accepted
37− sentInvites: Maybe.Just(6),
38− acceptedInvites: Maybe.Just(5),
39− };
6+ return getInvites(point);
407}
src/lib/usePointBirthday.jsnew file+7
@@ -0,0 +1,7 @@
1+import { usePointCache } from 'store/pointCache';
2+
3+export default function usePointBirthday(point) {
4+ const { getBirthday } = usePointCache();
5+
6+ return getBirthday(point);
7+}
src/store/pointCache.js+73−3
@@ -5,36 +5,106 @@ import React, {
55 useContext,
66 useState,
77} from 'react';
8+import Maybe from 'folktale/maybe';
89import * as azimuth from 'azimuth-js';
910
1011import { useNetwork } from './network';
1112
1213export const PointCacheContext = createContext(null);
1314
15+// the default value of a point's invites
16+const kEmptyInvites = {
17+ availableInvites: Maybe.Nothing(),
18+ sentInvites: Maybe.Nothing(),
19+ acceptedInvites: Maybe.Nothing(),
20+};
21+
1422function _usePointCache() {
15− const { contracts } = useNetwork();
23+ const { contracts, web3 } = useNetwork();
1624 const [pointCache, _setPointCache] = useState({});
25+ const [birthdayCache, _setBirthdayCache] = useState({});
26+ const [invitesCache, _setInvitesCache] = useState({});
1727
1828 const addToPointCache = useCallback(
1929 entry => _setPointCache(cache => ({ ...cache, ...entry })),
2030 [_setPointCache]
2131 );
2232
33+ const addToBirthdayCache = useCallback(
34+ entry => _setBirthdayCache(cache => ({ ...cache, ...entry })),
35+ [_setBirthdayCache]
36+ );
37+
38+ const addToInvitesCache = useCallback(
39+ entry => _setInvitesCache(cache => ({ ...cache, ...entry })),
40+ [_setInvitesCache]
41+ );
42+
43+ // TODO: refactor pointCache access to use accessor like bithday
44+ const getBirthday = useCallback(
45+ point => birthdayCache[point] || Maybe.Nothing(),
46+ [birthdayCache]
47+ );
48+ const getInvites = useCallback(
49+ point => invitesCache[point] || kEmptyInvites,
50+ [invitesCache]
51+ );
52+
2353 const fetchPoint = useCallback(
2454 async point => {
2555 const _contracts = contracts.getOrElse(null);
26− if (!_contracts) {
56+ const _web3 = web3.getOrElse(null);
57+ if (!_contracts || !_web3) {
2758 return;
2859 }
2960
61+ // fetch point details
3062 const details = await azimuth.azimuth.getPoint(_contracts, point);
3163 addToPointCache({ [point]: details });
64+
65+ // fetch invites
66+ const count = await azimuth.delegatedSending.getTotalUsableInvites(
67+ _contracts,
68+ point
69+ );
70+ addToInvitesCache({
71+ [point]: {
72+ availableInvites: Maybe.Just(count),
73+ // TODO: look up sent/accepted on-chain
74+ sentInvites: Maybe.Just(6),
75+ acceptedInvites: Maybe.Just(5),
76+ },
77+ });
78+
79+ // fetch birthday (if not already known — will not change after being set)
80+ if (Maybe.Nothing.hasInstance(getBirthday(point))) {
81+ const birthBlock = await azimuth.azimuth.getActivationBlock(
82+ _contracts,
83+ point
84+ );
85+
86+ if (birthBlock > 0) {
87+ const block = await _web3.eth.getBlock(birthBlock);
88+ addToBirthdayCache({
89+ [point]: Maybe.Just(new Date(block.timestamp * 1000)),
90+ });
91+ }
92+ }
3293 },
33− [contracts, addToPointCache]
94+ [
95+ contracts,
96+ web3,
97+ addToPointCache,
98+ addToBirthdayCache,
99+ getBirthday,
100+ addToInvitesCache,
101+ ]
34102 );
35103
36104 return {
37105 pointCache,
106+ getBirthday,
107+ getInvites,
38108 addToPointCache,
39109 fetchPoint,
40110 };
src/views/Login.js+5−4
@@ -71,14 +71,15 @@ class Login extends React.Component {
7171 }
7272 }
7373
74+ // if we have a deduced point or one in the global context,
75+ // navigate to that specific point, otherwise navigate to list of points
7476 if (Just.hasInstance(deduced)) {
7577 this.props.setPointCursor(deduced);
76− this.props.history.popAndPush(ROUTE_NAMES.POINT_HOME);
78+ this.props.history.popAndPush(ROUTE_NAMES.POINT);
7779 } else if (Just.hasInstance(this.props.pointCursor)) {
78− this.props.history.popAndPush(ROUTE_NAMES.POINT_HOME);
80+ this.props.history.popAndPush(ROUTE_NAMES.POINT);
7981 } else {
80− //TODO to new overview (maybe should be merged into point home?)
81− this.props.history.popAndPush(ROUTE_NAMES.SHIPS);
82+ this.props.history.popAndPush(ROUTE_NAMES.POINTS);
8283 }
8384 }
8485
src/views/Mnemonic.js+2
@@ -33,6 +33,8 @@ export default function Mnemonic() {
3333 // when the properties change, re-derive wallet
3434 useEffect(() => {
3535 let mounted = true;
36+ // allow the browser to do something else while we derive this wallet
37+ // on the main thread by making this an async function
3638 (async () => {
3739 const wallet = walletFromMnemonic(mnemonic, walletHdPath, passphrase);
3840 mounted && setWallet(wallet);
src/views/PointHome.jsdeleted−111
@@ -1,111 +0,0 @@
1−import { Just, Nothing } from 'folktale/maybe';
2−import React from 'react';
3−import * as azimuth from 'azimuth-js';
4−import * as need from '../lib/need';
5−
6−import { Row, Col, P } from '../components/old/Base';
7−
8−import { compose } from '../lib/lib';
9−
10−import { withNetwork } from '../store/network';
11−import { withPointCursor } from '../store/pointCursor';
12−import { withPointCache } from '../store/pointCache';
13−
14−class PointHome extends React.Component {
15− constructor(props) {
16− super(props);
17−
18− this.state = {
19− birthday: Nothing(),
20− spawned: [],
21− invites: Nothing(),
22− };
23− }
24−
25− componentDidMount() {
26− this.cachePoints();
27− this.updateBirthday();
28− }
29−
30− //TODO can probably re-use this to implement point switching
31− componentDidUpdate(prevProps) {
32− //
33− // NB (jtobin)
34− //
35− // When a link in the 'issued points' component is clicked, we don't
36− // actually leave the Point component, and componentDidMount will not fire
37− // again. But, we can force a call to cachePoints here.
38− //
39− const oldCursor = prevProps.pointCursor.getOrElse(true);
40− const newCursor = this.props.pointCursor.getOrElse(true);
41−
42− if (oldCursor !== newCursor) {
43− this.cachePoints();
44− }
45− }
46−
47− cachePoints() {
48− const { web3, contracts, pointCursor, addToPointCache } = this.props;
49−
50− web3.chain(_ =>
51− contracts.chain(ctrcs =>
52− pointCursor.chain(point => {
53− azimuth.azimuth
54− .getPoint(ctrcs, point)
55− .then(details => addToPointCache({ [point]: details }));
56− azimuth.delegatedSending
57− .getTotalUsableInvites(ctrcs, point)
58− .then(count => this.setState({ invites: Just(count) }));
59− this.updateSpawned(ctrcs, point);
60− })
61− )
62− );
63− }
64−
65− async updateBirthday() {
66− const birthBlock = await azimuth.azimuth.getActivationBlock(
67− need.contracts(this.props),
68− need.pointCursor(this.props)
69− );
70− if (birthBlock === 0) {
71− this.setState({ birthday: Nothing() });
72− } else {
73− const block = await need.web3(this.props).eth.getBlock(birthBlock);
74− //TODO add to point cache
75− this.setState({ birthday: Just(new Date(block.timestamp * 1000)) });
76− }
77− }
78−
79− updateSpawned = contracts => {
80− const { pointCursor } = this.props;
81−
82− pointCursor.matchWith({
83− Nothing: _ => {},
84− Just: point => {
85− azimuth.azimuth
86− .getSpawned(contracts, point.value)
87− .then(spawned => this.setState({ spawned }));
88− },
89− });
90− };
91−
92− render() {
93− return (
94− <Row>
95− <Col>
96− <P>Admin</P>
97−
98− <P>Boot Arvo</P>
99−
100− <P>Invite</P>
101− </Col>
102− </Row>
103− );
104− }
105−}
106−
107−export default compose(
108− withNetwork,
109− withPointCursor,
110− withPointCache
111−)(PointHome);