on master help sign in
feat: update views to use new grid insted of row/col and update need to not depend on props
commit1171039bb64bb083d5df6555c9038a7008faf864 authorMatt Condon (shrugs) <mattgcondon@gmail.com> dateTue 18 Jun 2019, 16:19 (7 years ago) parentfd7a195
35 files changed+1164−1257
src/Bridge.js+1−7 src/components/Grid.js+30 src/components/View.js+2−5 src/components/old/StatelessTransaction.js+2−2 src/lib/keys.js+3−3 src/lib/need.js+27−70 src/lib/router.js+1−1 src/style/indigo-proposals.scss+2−2 src/views/AcceptTransfer.js+43−47 src/views/CancelTransfer.js+18−19 src/views/CreateGalaxy.js+63−64 src/views/GenKeyfile.js+36−38 src/views/InviteTicket.js+37−33 src/views/InvitesManage.js+38−41 src/views/InvitesSend.js+14−15 src/views/IssueChild.js+81−86 src/views/Keystore.js+47−48 src/views/Landing.js+42−43 src/views/Ledger.js+59−64 src/views/Mnemonic.js+58−61 src/views/Network.js+22−23 src/views/Point.js+20−21 src/views/Point/Actions.js+10−11 src/views/Point/KeysAndMetadata.js+44−50 src/views/Points.js+28−28 src/views/PrivateKey.js+27−28 src/views/SentTransaction.js+22−21 src/views/SetKeys.js+50−59 src/views/SetProxy.js+24−28 src/views/Shards.js+97−104 src/views/Ticket.js+74−77 src/views/Transfer.js+46−58 src/views/Trezor.js+26−27 src/views/ViewPoint.js+33−34 src/views/Wallet.js+37−39
src/Bridge.js+1−7
@@ -2,13 +2,10 @@ import React from 'react';
22import { Just, Nothing } from 'folktale/maybe';
33import { IndigoApp } from 'indigo-react';
44
5−import View from 'components/View';
65import Router from 'components/Router';
76
87import Provider from 'store/Provider';
98
10−import Header from './components/old/Header';
11−
129import { ROUTE_NAMES } from 'lib/routeNames';
1310import { NETWORK_TYPES } from 'lib/network';
1411import { walletFromMnemonic } from 'lib/wallet';
@@ -52,10 +49,7 @@ export default function Bridge() {
5249 initialMnemonic={kInitialMnemonic}
5350 initialPointCursor={kInitialPointCursor}>
5451 <IndigoApp>
55− <View>
56− <Header />
57− <Router />
58− </View>
52+ <Router />
5953 </IndigoApp>
6054 </Provider>
6155 );
src/components/Grid.jsnew file+30
@@ -0,0 +1,30 @@
1+import React from 'react';
2+import cn from 'classnames';
3+
4+function Grid({ className, ...rest }) {
5+ return <div className={cn('grid12', className)} {...rest} />;
6+}
7+
8+Grid.Item = function GridItem({
9+ full,
10+ half,
11+ third,
12+ fourth,
13+ className,
14+ ...rest
15+}) {
16+ return (
17+ <div
18+ className={cn(
19+ full && `full`,
20+ half && `half-${half}`,
21+ third && `third-${third}`,
22+ fourth && `fourth-${fourth}`,
23+ className
24+ )}
25+ {...rest}
26+ />
27+ );
28+};
29+
30+export default Grid;
src/components/View.js+2−5
@@ -3,15 +3,12 @@ import cn from 'classnames';
33
44// View is a top-level component that all Views must render to inherit styling
55function View({ className, ...rest }) {
6− return <div className={cn('mw-1', className)} {...rest} />;
6+ return <div className={cn('mw1', className)} {...rest} />;
77}
88
99View.Full = function FullView({ className, ...rest }) {
1010 return (
11− <div
12− className={cn('mw-2 ph-1 ph-3-ns ph-9-md ph-10-lg', className)}
13− {...rest}
14− />
11+ <div className={cn('mw2 ph1 ph3-ns ph9-md ph10-lg', className)} {...rest} />
1512 );
1613};
1714
src/components/old/StatelessTransaction.js+2−2
@@ -69,7 +69,7 @@ class StatelessTransaction extends React.Component {
6969 componentDidMount() {
7070 const { props } = this;
7171
72− const addr = need.address(props);
72+ const addr = need.addressFromWallet(props.wallet);
7373
7474 props.web3.matchWith({
7575 Nothing: () => {},
@@ -208,7 +208,7 @@ class StatelessTransaction extends React.Component {
208208 const rawTx = hexify(stx.serialize());
209209 const cost = state.gasLimit * toWei(state.gasPrice, 'gwei');
210210
211− const address = need.address(props);
211+ const address = need.addressFromWallet(props.wallet);
212212 let balance = await web3.eth.getBalance(address);
213213 let hasBalance = balance >= cost;
214214
src/lib/keys.js+3−3
@@ -73,8 +73,8 @@ const attemptNetworkSeedDerivation = async (next, args) => {
7373 //
7474 // following code is intentionally verbose for sake of clarity
7575
76− const point = need.pointCursor(args);
77− const pointDetails = need.fromPointCache(args, point);
76+ const point = need.pointCursor(args.pointCursor);
77+ const pointDetails = need.fromPointCache(args.pointCache, point);
7878
7979 const revision =
8080 next === true
@@ -95,7 +95,7 @@ const attemptNetworkSeedDerivation = async (next, args) => {
9595
9696 managementSeed = uwal.management.seed;
9797 } else if (walletType === WALLET_TYPES.MNEMONIC) {
98− const walProxy = need.address(args);
98+ const walProxy = need.addressFromWallet(args.wallet);
9999
100100 const mnemonic = authMnemonic.matchWith({
101101 Just: mnem => mnem.value,
src/lib/need.js+27−70
@@ -1,75 +1,32 @@
11// need.js: +need but worse
22//
3−// generally, pass the props object into these functions
43
54import { BRIDGE_ERROR } from './error';
65
7−function web3(obj) {
8− return 'web3' in obj === false
9− ? (() => {
10− throw BRIDGE_ERROR.MISSING_WEB3;
11− })()
12− : obj.web3.matchWith({
13− Nothing: () => {
14− throw BRIDGE_ERROR.MISSING_WEB3;
15− },
16− Just: w => w.value,
17− });
18−}
19−
20−function contracts(obj) {
21− return 'contracts' in obj === false
22− ? (() => {
23− throw BRIDGE_ERROR.MISSING_CONTRACTS;
24− })()
25− : obj.contracts.matchWith({
26− Nothing: () => {
27− throw BRIDGE_ERROR.MISSING_CONTRACTS;
28− },
29− Just: c => c.value,
30− });
31−}
32−
33−function wallet(obj) {
34− return 'wallet' in obj === false
35− ? (() => {
36− throw BRIDGE_ERROR.MISSING_WALLET;
37− })()
38− : obj.wallet.matchWith({
39− Nothing: () => {
40− throw BRIDGE_ERROR.MISSING_WALLET;
41− },
42− Just: w => w.value,
43− });
44−}
45−
46−function address(obj) {
47− return wallet(obj).address;
48−}
49−
50−function pointCursor(obj) {
51− return 'pointCursor' in obj === false
52− ? (() => {
53− throw BRIDGE_ERROR.MISSING_POINT;
54− })()
55− : obj.pointCursor.matchWith({
56− Nothing: () => {
57− throw BRIDGE_ERROR.MISSING_POINT;
58− },
59− Just: p => p.value,
60− });
61−}
62−
63−function fromPointCache(obj, point) {
64− return 'pointCache' in obj === false
65− ? (() => {
66− throw BRIDGE_ERROR.MISSING_POINT;
67− })()
68− : point in obj.pointCache
69− ? obj.pointCache[point]
70− : (() => {
71− throw BRIDGE_ERROR.MISSING_POINT;
72− })();
73−}
74−
75−export { web3, contracts, wallet, address, pointCursor, fromPointCache };
6+const needBuilder = error => obj => {
7+ if (!obj) {
8+ throw error;
9+ }
10+
11+ return obj.matchWith({
12+ Nothing: () => {
13+ throw error;
14+ },
15+ Just: p => p.value,
16+ });
17+};
18+
19+export const web3 = needBuilder(BRIDGE_ERROR.MISSING_WEB3);
20+export const contracts = needBuilder(BRIDGE_ERROR.MISSING_CONTRACTS);
21+export const wallet = needBuilder(BRIDGE_ERROR.MISSING_WALLET);
22+export const addressFromWallet = obj => wallet(obj).address;
23+export const pointCursor = needBuilder(BRIDGE_ERROR.MISSING_POINT);
24+export const pointCache = needBuilder(BRIDGE_ERROR.MISSING_POINT);
25+export const fromPointCache = (obj, point) => {
26+ const cache = pointCache(obj);
27+ if (!(point in cache)) {
28+ throw BRIDGE_ERROR.MISSING_POINT;
29+ }
30+
31+ return cache[point];
32+};
src/lib/router.js+1−1
@@ -112,7 +112,7 @@ export const getRouteBreadcrumb = ({
112112 case ROUTE_NAMES.VIEW_SHIP:
113113 return 'View';
114114 case ROUTE_NAMES.SHIP:
115− return ob.patp(need.pointCursor({ pointCursor }));
115+ return ob.patp(need.pointCursor(pointCursor));
116116 case ROUTE_NAMES.SET_TRANSFER_PROXY:
117117 case ROUTE_NAMES.SET_MANAGEMENT_PROXY:
118118 case ROUTE_NAMES.SET_SPAWN_PROXY:
src/style/indigo-proposals.scss+2−2
@@ -1,9 +1,9 @@
1−.mw-1 {
1+.mw1 {
22 margin: 0 auto;
33 max-width: 30rem;
44}
55
6−.mw-2 {
6+.mw2 {
77 margin: 0 auto;
88 max-width: 64rem;
99}
src/views/AcceptTransfer.js+43−47
@@ -1,6 +1,6 @@
11import { Just } from 'folktale/maybe';
22import React from 'react';
3−import { Row, Col, H1, P } from '../components/old/Base';
3+import { H1, P } from '../components/old/Base';
44import {
55 InnerLabel,
66 AddressInput,
@@ -18,14 +18,14 @@ import { withNetwork } from '../store/network';
1818import { compose } from '../lib/lib';
1919import { withWallet } from '../store/wallet';
2020import { withPointCursor } from '../store/pointCursor';
21+import View from 'components/View';
2122
2223class AcceptTransfer extends React.Component {
2324 constructor(props) {
2425 super(props);
2526
26− const receivingAddress = need.address(props);
27−
28− const incomingPoint = need.pointCursor(props);
27+ const receivingAddress = need.addressFromWallet(props.wallet);
28+ const incomingPoint = need.pointCursor(props.pointCursor);
2929
3030 this.state = {
3131 receivingAddress: receivingAddress,
@@ -45,9 +45,9 @@ class AcceptTransfer extends React.Component {
4545 createUnsignedTxn() {
4646 const { state, props } = this;
4747
48− const validContracts = need.contracts(props);
49−
50− const validPoint = need.pointCursor(props); //TODO state.incomingPoint ?
48+ const validContracts = need.contracts(props.contracts);
49+ const validPoint = need.pointCursor(props.pointCursor);
50+ //TODO state.incomingPoint ?
5151
5252 return Just(
5353 azimuth.ecliptic.transferPoint(
@@ -74,46 +74,42 @@ class AcceptTransfer extends React.Component {
7474 : 'etherscan.io';
7575
7676 return (
77− <Row>
78− <Col>
79− <H1>
80− {'Accept Transfer of '}{' '}
81− <code>{` ${ob.patp(state.incomingPoint)} `}</code>
82− </H1>
83−
84− <P>
85− {"By default, the recipient is the address you're logged in " +
86− 'as. But you can transfer to any address you like.'}
87− </P>
88−
89− <AddressInput
90− className="mono mt-8"
91− prop-size="lg"
92− prop-format="innerLabel"
93− value={state.receivingAddress}
94− onChange={v => this.handleAddressInput(v)}>
95− <InnerLabel>{'Receiving Address'}</InnerLabel>
96− <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
97− </AddressInput>
98−
99− <Anchor
100− className={'mt-1 sm'}
101− prop-size="sm"
102− prop-disabled={
103− !isValidAddress(state.receivingAddress) || !esvisible
104− }
105− target={'_blank'}
106− href={`https://${esdomain}/address/${state.receivingAddress}`}>
107− {'View on Etherscan ↗'}
108− </Anchor>
109−
110− <StatelessTransaction
111− canGenerate={canGenerate}
112− createUnsignedTxn={this.createUnsignedTxn}
113− ref={this.statelessRef}
114− />
115− </Col>
116− </Row>
77+ <View>
78+ <H1>
79+ {'Accept Transfer of '}{' '}
80+ <code>{` ${ob.patp(state.incomingPoint)} `}</code>
81+ </H1>
82+
83+ <P>
84+ {"By default, the recipient is the address you're logged in " +
85+ 'as. But you can transfer to any address you like.'}
86+ </P>
87+
88+ <AddressInput
89+ className="mono mt-8"
90+ prop-size="lg"
91+ prop-format="innerLabel"
92+ value={state.receivingAddress}
93+ onChange={v => this.handleAddressInput(v)}>
94+ <InnerLabel>{'Receiving Address'}</InnerLabel>
95+ <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
96+ </AddressInput>
97+
98+ <Anchor
99+ className={'mt-1 sm'}
100+ prop-size="sm"
101+ prop-disabled={!isValidAddress(state.receivingAddress) || !esvisible}
102+ target={'_blank'}
103+ href={`https://${esdomain}/address/${state.receivingAddress}`}>
104+ {'View on Etherscan ↗'}
105+ </Anchor>
106+
107+ <StatelessTransaction
108+ canGenerate={canGenerate}
109+ createUnsignedTxn={this.createUnsignedTxn}
110+ ref={this.statelessRef}
111+ />
112+ </View>
117113 );
118114 }
119115}
src/views/CancelTransfer.js+18−19
@@ -1,6 +1,6 @@
11import Maybe from 'folktale/maybe';
22import React from 'react';
3−import { Row, Col, H1, P } from '../components/old/Base';
3+import { H1, P } from '../components/old/Base';
44import * as azimuth from 'azimuth-js';
55import * as ob from 'urbit-ob';
66import * as need from '../lib/need';
@@ -11,12 +11,13 @@ import { withNetwork } from '../store/network';
1111import { compose } from '../lib/lib';
1212import { withPointCursor } from '../store/pointCursor';
1313import { withPointCache } from '../store/pointCache';
14+import View from 'components/View';
1415
1516class CancelTransfer extends React.Component {
1617 constructor(props) {
1718 super(props);
1819
19− const pointInTransfer = need.pointCursor(props);
20+ const pointInTransfer = need.pointCursor(props.pointCursor);
2021
2122 this.state = {
2223 proxyAddress: '',
@@ -29,9 +30,9 @@ class CancelTransfer extends React.Component {
2930 createUnsignedTxn() {
3031 const { props } = this;
3132
32− const validContracts = need.contracts(props);
33−
34− const validPoint = need.pointCursor(props); //TODO this.state.pointInTransfer ?
33+ const validContracts = need.contracts(props.contracts);
34+ const validPoint = need.pointCursor(props.pointCursor);
35+ //TODO this.state.pointInTransfer ?
3536
3637 const txn = azimuth.ecliptic.setTransferProxy(
3738 validContracts,
@@ -56,20 +57,18 @@ class CancelTransfer extends React.Component {
5657 const canGenerate = true;
5758
5859 return (
59− <Row>
60− <Col>
61− <H1>
62− {'Cancel Transfer of '}{' '}
63− <code>{` ${ob.patp(state.pointInTransfer)} `}</code>
64− </H1>
65−
66− <P>{`This action will cancel the transfer to ${proxy}.`}</P>
67− <StatelessTransaction
68− canGenerate={canGenerate}
69− createUnsignedTxn={this.createUnsignedTxn}
70− />
71− </Col>
72− </Row>
60+ <View>
61+ <H1>
62+ {'Cancel Transfer of '}{' '}
63+ <code>{` ${ob.patp(state.pointInTransfer)} `}</code>
64+ </H1>
65+
66+ <P>{`This action will cancel the transfer to ${proxy}.`}</P>
67+ <StatelessTransaction
68+ canGenerate={canGenerate}
69+ createUnsignedTxn={this.createUnsignedTxn}
70+ />
71+ </View>
7372 );
7473 }
7574}
src/views/CreateGalaxy.js+63−64
@@ -5,7 +5,7 @@ import * as ob from 'urbit-ob';
55import * as need from '../lib/need';
66
77import { Button } from '../components/old/Base';
8−import { Row, Col, H1, P, Anchor } from '../components/old/Base';
8+import { H1, P, Anchor } from '../components/old/Base';
99import {
1010 InnerLabel,
1111 GalaxyInput,
@@ -23,6 +23,7 @@ import { ETH_ZERO_ADDR, isValidAddress, eqAddr } from '../lib/wallet';
2323import { isValidGalaxy, compose } from '../lib/lib';
2424import { withNetwork } from '../store/network';
2525import { withWallet } from '../store/wallet';
26+import View from 'components/View';
2627
2728const buttonTriState = status => {
2829 if (status === null) return 'blue';
@@ -40,7 +41,7 @@ class CreateGalaxy extends React.Component {
4041 constructor(props) {
4142 super(props);
4243
43− const galaxyOwner = need.address(props);
44+ const galaxyOwner = need.addressFromWallet(props.wallet);
4445
4546 this.state = {
4647 galaxyOwner: galaxyOwner,
@@ -72,7 +73,7 @@ class CreateGalaxy extends React.Component {
7273 if (canDecodePatp(state.galaxyName) === false) return Maybe.Nothing();
7374 if (isValidGalaxy(state.galaxyName) === false) return Maybe.Nothing();
7475
75− const validContracts = need.contracts(props);
76+ const validContracts = need.contracts(props.contracts);
7677
7778 const galaxyDec = parseInt(ob.patp2dec(state.galaxyName), 10);
7879
@@ -93,7 +94,7 @@ class CreateGalaxy extends React.Component {
9394 return;
9495 }
9596
96− const validContracts = need.contracts(props);
97+ const validContracts = need.contracts(props.contracts);
9798
9899 const galaxyDec = ob.patp2dec(state.galaxyName);
99100
@@ -128,67 +129,65 @@ class CreateGalaxy extends React.Component {
128129 : 'etherscan.io';
129130
130131 return (
131− <Row>
132− <Col>
133− <H1> {'Create a Galaxy'} </H1>
134−
135− <P>
136− {'Enter the galaxy to create and the address that will own ' +
137− 'it (defaulting to this account, if not provided).'}
138− </P>
139−
140− <GalaxyInput
141− className="mono"
142− prop-size="lg"
143− prop-format="innerLabel"
144− autoFocus
145− placeholder="e.g. ~zod"
146− value={state.galaxyName}
147− onChange={v => this.handleGalaxyNameInput(v)}>
148− <InnerLabel>{'Galaxy Name'}</InnerLabel>
149− <ValidatedSigil
150− className="tr-0 mt-05 mr-0 abs"
151− patp={state.galaxyName}
152− size={68}
153− margin={8}
154− />
155− </GalaxyInput>
156−
157− <AddressInput
158− className="mono mt-8"
159− prop-size="lg"
160− prop-format="innerLabel"
161− value={state.galaxyOwner}
162− onChange={v => this.handleAddressInput(v)}>
163− <InnerLabel>{'Address that will own this galaxy'}</InnerLabel>
164− <ShowBlockie className={'mt-1'} address={state.galaxyOwner} />
165− </AddressInput>
166−
167− <Anchor
168− className={'mt-1'}
169− prop-size={'sm'}
170− prop-disabled={!validAddress || !esvisible}
171− target={'_blank'}
172− href={`https://${esdomain}/address/${state.galaxyOwner}`}>
173− {'View on Etherscan ↗'}
174− </Anchor>
175−
176− <Button
177− prop-size="lg wide"
178− className="mt-8"
179− prop-color={buttonTriState(state.isAvailable)}
180− disabled={!validGalaxy}
181− onClick={() => this.confirmAvailability()}>
182− {buttonTriStateText(state.isAvailable)}
183− </Button>
184−
185− <StatelessTransaction
186− ref={this.statelessRef}
187− canGenerate={canGenerate}
188− createUnsignedTxn={this.createUnsignedTxn}
132+ <View>
133+ <H1> {'Create a Galaxy'} </H1>
134+
135+ <P>
136+ {'Enter the galaxy to create and the address that will own ' +
137+ 'it (defaulting to this account, if not provided).'}
138+ </P>
139+
140+ <GalaxyInput
141+ className="mono"
142+ prop-size="lg"
143+ prop-format="innerLabel"
144+ autoFocus
145+ placeholder="e.g. ~zod"
146+ value={state.galaxyName}
147+ onChange={v => this.handleGalaxyNameInput(v)}>
148+ <InnerLabel>{'Galaxy Name'}</InnerLabel>
149+ <ValidatedSigil
150+ className="tr-0 mt-05 mr-0 abs"
151+ patp={state.galaxyName}
152+ size={68}
153+ margin={8}
189154 />
190− </Col>
191− </Row>
155+ </GalaxyInput>
156+
157+ <AddressInput
158+ className="mono mt-8"
159+ prop-size="lg"
160+ prop-format="innerLabel"
161+ value={state.galaxyOwner}
162+ onChange={v => this.handleAddressInput(v)}>
163+ <InnerLabel>{'Address that will own this galaxy'}</InnerLabel>
164+ <ShowBlockie className={'mt-1'} address={state.galaxyOwner} />
165+ </AddressInput>
166+
167+ <Anchor
168+ className={'mt-1'}
169+ prop-size={'sm'}
170+ prop-disabled={!validAddress || !esvisible}
171+ target={'_blank'}
172+ href={`https://${esdomain}/address/${state.galaxyOwner}`}>
173+ {'View on Etherscan ↗'}
174+ </Anchor>
175+
176+ <Button
177+ prop-size="lg wide"
178+ className="mt-8"
179+ prop-color={buttonTriState(state.isAvailable)}
180+ disabled={!validGalaxy}
181+ onClick={() => this.confirmAvailability()}>
182+ {buttonTriStateText(state.isAvailable)}
183+ </Button>
184+
185+ <StatelessTransaction
186+ ref={this.statelessRef}
187+ canGenerate={canGenerate}
188+ createUnsignedTxn={this.createUnsignedTxn}
189+ />
190+ </View>
192191 );
193192 }
194193}
src/views/GenKeyfile.js+36−38
@@ -2,7 +2,7 @@ import React from 'react';
22import * as need from '../lib/need';
33
44import { Button } from '../components/old/Base';
5−import { Row, Col, H1, P } from '../components/old/Base';
5+import { H1, P } from '../components/old/Base';
66
77import * as ob from 'urbit-ob';
88import * as kg from 'urbit-key-generation/dist';
@@ -14,6 +14,7 @@ import { compose } from '../lib/lib';
1414import { withWallet } from '../store/wallet';
1515import { withPointCursor } from '../store/pointCursor';
1616import { withPointCache } from '../store/pointCache';
17+import View from 'components/View';
1718
1819class GenKeyfile extends React.Component {
1920 constructor(props) {
@@ -26,9 +27,8 @@ class GenKeyfile extends React.Component {
2627 }
2728
2829 getPointDetails() {
29− const point = need.pointCursor(this.props);
30−
31− const pointDetails = need.fromPointCache(this.props, point);
30+ const point = need.pointCursor(this.props.pointCursor);
31+ const pointDetails = need.fromPointCache(this.props.pointCache, point);
3232
3333 // in case we did SetKeys earlier this session, make sure to generate the
3434 // newer keyfile, rather than the one that will expire soon
@@ -94,45 +94,43 @@ class GenKeyfile extends React.Component {
9494 const { keyfile, loaded } = this.state;
9595
9696 return (
97− <Row>
98− <Col className={'col-md-8'}>
99− <H1>{'Generate keyfile'}</H1>
97+ <View>
98+ <H1>{'Generate keyfile'}</H1>
10099
101− <P>{'Download a private key file for booting this point in Arvo.'}</P>
100+ <P>{'Download a private key file for booting this point in Arvo.'}</P>
102101
103− {keyfile === '' && !loaded && <P>{'Generating keyfile...'}</P>}
102+ {keyfile === '' && !loaded && <P>{'Generating keyfile...'}</P>}
104103
105− {keyfile === '' && loaded && (
106− <React.Fragment>
107− <P>
108− <b>Warning: </b>
109− {`No valid network seed detected. To generate a keyfile, you
104+ {keyfile === '' && loaded && (
105+ <React.Fragment>
106+ <P>
107+ <b>Warning: </b>
108+ {`No valid network seed detected. To generate a keyfile, you
110109 must reset your networking keys, or try logging in with your
111110 master ticket or management mnemonic.`}
112− </P>
113−
114− <P>
115− {`If you've just reset your networking keys, you may need to wait for the transaction to go through. Check back shortly.`}
116− </P>
117− </React.Fragment>
118− )}
119−
120− {keyfile !== '' && (
121− <React.Fragment>
122− <div className="pb-5 text-code keyfile">{keyfile}</div>
123− <Button
124− onClick={() => {
125− let blob = new Blob([keyfile], {
126− type: 'text/plain;charset=utf-8',
127− });
128− saveAs(blob, `${ob.patp(point).slice(1)}-${revision}.key`);
129− }}>
130− Download →
131− </Button>
132− </React.Fragment>
133− )}
134− </Col>
135− </Row>
111+ </P>
112+
113+ <P>
114+ {`If you've just reset your networking keys, you may need to wait for the transaction to go through. Check back shortly.`}
115+ </P>
116+ </React.Fragment>
117+ )}
118+
119+ {keyfile !== '' && (
120+ <React.Fragment>
121+ <div className="pb-5 text-code keyfile">{keyfile}</div>
122+ <Button
123+ onClick={() => {
124+ let blob = new Blob([keyfile], {
125+ type: 'text/plain;charset=utf-8',
126+ });
127+ saveAs(blob, `${ob.patp(point).slice(1)}-${revision}.key`);
128+ }}>
129+ Download →
130+ </Button>
131+ </React.Fragment>
132+ )}
133+ </View>
136134 );
137135 }
138136}
src/views/InviteTicket.js+37−33
@@ -1,17 +1,18 @@
11import { Just, Nothing } from 'folktale/maybe';
22import React from 'react';
3+import * as azimuth from 'azimuth-js';
4+import * as need from 'lib/need';
5+
6+import View from 'components/View';
7+
38import {
49 InnerLabel,
510 Warning,
611 TicketInput,
712 VerifyTicketInput,
813 Button,
9− Row,
10− Col,
1114 Passport,
1215} from '../components/old/Base';
13−import * as azimuth from 'azimuth-js';
14−import * as need from '../lib/need';
1516
1617import { randomPatq, compose } from '../lib/lib';
1718import { ROUTE_NAMES } from '../lib/routeNames';
@@ -30,6 +31,7 @@ import {
3031import { generateWallet, startTransactions } from '../lib/invite';
3132import { withNetwork } from '../store/network';
3233import { withWallet } from '../store/wallet';
34+import Grid from 'components/Grid';
3335
3436class InviteTicket extends React.Component {
3537 constructor(props) {
@@ -104,9 +106,9 @@ class InviteTicket extends React.Component {
104106
105107 const addr = inviteWallet.address;
106108
107− const ctrcs = need.contracts(this.props);
109+ const contracts = need.contracts(this.props.contracts);
108110
109− const incoming = await azimuth.azimuth.getTransferringFor(ctrcs, addr);
111+ const incoming = await azimuth.azimuth.getTransferringFor(contracts, addr);
110112
111113 this.setState({
112114 walletStates: this.state.walletStates.concat(WALLET_STATES.GENERATING),
@@ -393,33 +395,35 @@ class InviteTicket extends React.Component {
393395 let errorDisplay = this.getErrors();
394396
395397 return (
396− <Row>
397− <Col className="col-md-4 mt-">
398− {stageDisplay}
399− {errorDisplay}
400− {stageText}
401− {ticketElem}
402− {continueButton}
403−
404− {this.state.stage === INVITE_STAGES.INVITE_LOGIN && (
405− <Button
406− prop-type={`link`}
407− prop-size={`sm`}
408− className={`block mt-4`}
409− onClick={this.navigateLogin}>
410− {`Log in`}
411− </Button>
412− )}
413− </Col>
414− <Col className="col-md-offset-3 col-md-5 mt-18">
415− <Passport
416− point={this.state.realPoint}
417− wallet={this.state.realWallet}
418− walletStates={this.state.walletStates}
419− pushWalletState={this.pushWalletState}
420− />
421− </Col>
422− </Row>
398+ <View.Full>
399+ <Grid>
400+ <Grid.Item half={1}>
401+ {stageDisplay}
402+ {errorDisplay}
403+ {stageText}
404+ {ticketElem}
405+ {continueButton}
406+
407+ {this.state.stage === INVITE_STAGES.INVITE_LOGIN && (
408+ <Button
409+ prop-type={`link`}
410+ prop-size={`sm`}
411+ className={`block mt-4`}
412+ onClick={this.navigateLogin}>
413+ {`Log in`}
414+ </Button>
415+ )}
416+ </Grid.Item>
417+ <Grid.Item half={2}>
418+ <Passport
419+ point={this.state.realPoint}
420+ wallet={this.state.realWallet}
421+ walletStates={this.state.walletStates}
422+ pushWalletState={this.pushWalletState}
423+ />
424+ </Grid.Item>
425+ </Grid>
426+ </View.Full>
423427 );
424428 }
425429}
src/views/InvitesManage.js+38−41
@@ -5,8 +5,6 @@ import * as azimuth from 'azimuth-js';
55import * as need from '../lib/need';
66
77import {
8− Row,
9− Col,
108 Warning,
119 Input,
1210 PointInput,
@@ -17,6 +15,7 @@ import StatelessTransaction from '../components/old/StatelessTransaction';
1715import { withNetwork } from '../store/network';
1816import { compose } from '../lib/lib';
1917import { withPointCursor } from '../store/pointCursor';
18+import View from 'components/View';
2019
2120class InvitesManage extends React.Component {
2221 constructor(props) {
@@ -40,8 +39,8 @@ class InvitesManage extends React.Component {
4039 }
4140
4241 componentDidMount() {
43− this.point = need.pointCursor(this.props);
44− this.contracts = need.contracts(this.props);
42+ this.point = need.pointCursor(this.props.pointCursor);
43+ this.contracts = need.contracts(this.props.contracts);
4544
4645 azimuth.azimuth
4746 .isSpawnProxy(
@@ -140,44 +139,42 @@ class InvitesManage extends React.Component {
140139 });
141140
142141 return (
143− <Row>
144− <Col>
145− <p>
146− {
147− 'manage invites here, for stars. can only give invites to child planets'
148− }
149− </p>
150−
151− {spawnProxyWarning}
152−
153− <PointInput
154− prop-format="innerlabel"
155− prop-size="lg"
156− placeholder={'~sampel-sipnem'}
157− value={this.state.planetInput}
158− onChange={this.handlePointInput}>
159− <InnerLabel>{'Planet to set invites for'}</InnerLabel>
160− <ValidatedSigil
161− patp={this.state.targetPlanet}
162− validator={() => this.validatePoint}
163− />
164− </PointInput>
165−
166− <Input
167− prop-format="innerlabel"
168− prop-size="lg"
169− value={this.state.targetPoolSize}
170− onChange={this.handlePoolSizeInput}>
171− <InnerLabel>{`Available invites ${poolSizeText}`}</InnerLabel>
172− </Input>
173−
174− <StatelessTransaction
175− canGenerate={this.state.canGenerate}
176− createUnsignedTxn={this.createUnsignedTxn}
177− ref={this.statelessRef}
142+ <View>
143+ <p>
144+ {
145+ 'manage invites here, for stars. can only give invites to child planets'
146+ }
147+ </p>
148+
149+ {spawnProxyWarning}
150+
151+ <PointInput
152+ prop-format="innerlabel"
153+ prop-size="lg"
154+ placeholder={'~sampel-sipnem'}
155+ value={this.state.planetInput}
156+ onChange={this.handlePointInput}>
157+ <InnerLabel>{'Planet to set invites for'}</InnerLabel>
158+ <ValidatedSigil
159+ patp={this.state.targetPlanet}
160+ validator={() => this.validatePoint}
178161 />
179− </Col>
180− </Row>
162+ </PointInput>
163+
164+ <Input
165+ prop-format="innerlabel"
166+ prop-size="lg"
167+ value={this.state.targetPoolSize}
168+ onChange={this.handlePoolSizeInput}>
169+ <InnerLabel>{`Available invites ${poolSizeText}`}</InnerLabel>
170+ </Input>
171+
172+ <StatelessTransaction
173+ canGenerate={this.state.canGenerate}
174+ createUnsignedTxn={this.createUnsignedTxn}
175+ ref={this.statelessRef}
176+ />
177+ </View>
181178 );
182179 }
183180}
src/views/InvitesSend.js+14−15
@@ -4,7 +4,7 @@ import * as azimuth from 'azimuth-js';
44import * as need from '../lib/need';
55
66import { hasReceived, sendMail } from '../lib/inviteMail';
7−import { Row, Col, Button, Input, H3, Warning } from '../components/old/Base';
7+import { Button, Input, H3, Warning } from '../components/old/Base';
88
99// for wallet generation
1010import * as wg from '../_walletgen/lib/lib';
@@ -23,6 +23,7 @@ import { withNetwork } from '../store/network';
2323import { compose } from '../lib/lib';
2424import { withWallet } from '../store/wallet';
2525import { withPointCursor } from '../store/pointCursor';
26+import View from 'components/View';
2627
2728const GAS_PRICE_GWEI = 20; // we pay the premium for faster ux
2829const GAS_LIMIT = 350000;
@@ -72,10 +73,10 @@ class InvitesSend extends React.Component {
7273 }
7374
7475 componentDidMount() {
75− this.point = parseInt(need.pointCursor(this.props), 10);
76− this.address = need.address(this.props);
77− this.contracts = need.contracts(this.props);
78− this.web3 = need.web3(this.props);
76+ this.point = need.pointCursor(this.props.pointCursor);
77+ this.address = need.addressFromWallet(this.props.wallet);
78+ this.contracts = need.contracts(this.props.contracts);
79+ this.web3 = need.web3(this.props.web3);
7980
8081 console.log(azimuth.delegatedSending);
8182 azimuth.delegatedSending
@@ -476,21 +477,19 @@ class InvitesSend extends React.Component {
476477 }
477478
478479 return (
479− <Row>
480− <Col>
481− <p>{'send invites here, for planets'}</p>
480+ <View>
481+ <p>{'send invites here, for planets'}</p>
482482
483− <p>{invitesAvailable}</p>
483+ <p>{invitesAvailable}</p>
484484
485− <ul>{invitesSent}</ul>
485+ <ul>{invitesSent}</ul>
486486
487− {error}
487+ {error}
488488
489− {fundingMessage}
489+ {fundingMessage}
490490
491− {inputs}
492− </Col>
493− </Row>
491+ {inputs}
492+ </View>
494493 );
495494 }
496495}
src/views/IssueChild.js+81−86
@@ -5,8 +5,6 @@ import * as ob from 'urbit-ob';
55import * as need from '../lib/need';
66
77import {
8− Row,
9− Col,
108 H1,
119 P,
1210 Anchor,
@@ -27,6 +25,7 @@ import { isValidAddress } from '../lib/wallet';
2725import { withNetwork } from '../store/network';
2826import { compose } from '../lib/lib';
2927import { withPointCursor } from '../store/pointCursor';
28+import View from 'components/View';
3029
3130const setFind = (set, pred) => {
3231 for (const e of set) {
@@ -41,7 +40,7 @@ class IssueChild extends React.Component {
4140 constructor(props) {
4241 super(props);
4342
44− const issuingPoint = parseInt(need.pointCursor(props), 10);
43+ const issuingPoint = need.pointCursor(props.pointCursor);
4544
4645 const getCandidate = () => ob.patp(getSpawnCandidate(issuingPoint));
4746
@@ -76,7 +75,7 @@ class IssueChild extends React.Component {
7675 componentWillMount() {
7776 const { issuingPoint } = this.state;
7877
79− const validContracts = need.contracts(this.props);
78+ const validContracts = need.contracts(this.props.contracts);
8079
8180 azimuth
8281 .getUnspawnedChildren(validContracts, issuingPoint)
@@ -110,7 +109,7 @@ class IssueChild extends React.Component {
110109 if (state.isAvailable === false) return Nothing();
111110 if (canDecodePatp(state.desiredPoint) === false) return Nothing();
112111
113− const validContracts = need.contracts(props);
112+ const validContracts = need.contracts(props.contracts);
114113
115114 const pointDec = ob.patp2dec(state.desiredPoint);
116115
@@ -163,92 +162,88 @@ class IssueChild extends React.Component {
163162 : 'etherscan.io';
164163
165164 return (
166− <Row>
167− <Col>
168− <H1>
169− {'Issue a Child From '}{' '}
170− <code>{`${ob.patp(state.issuingPoint)}`}</code>
171− </H1>
172−
173− <P>
174− {`Please enter the point you would like to issue, and specify the
165+ <View>
166+ <H1>
167+ {'Issue a Child From '}{' '}
168+ <code>{`${ob.patp(state.issuingPoint)}`}</code>
169+ </H1>
170+
171+ <P>
172+ {`Please enter the point you would like to issue, and specify the
175173 receiving Ethereum address. If you need to create an address, you
176174 can also use Wallet Generator.`}
177− </P>
175+ </P>
178176
179− <P>
180− {`Your point can only issue children with particular names. Some
177+ <P>
178+ {`Your point can only issue children with particular names. Some
181179 valid suggestions for `}
182− {<code>{ob.patp(state.issuingPoint)}</code>}
183− {' are '}
184− <code>{state.suggestions[0]}</code>
185− {', '}
186− <code>{state.suggestions[1]}</code>
187− {', and '}
188− <code>{state.suggestions[2]}</code>
189− {'.'}
190− </P>
191−
192− <PointInput
193− prop-size="lg"
194− prop-format="innerLabel"
195− className={'mono mt-8'}
196− placeholder={`e.g. ${state.suggestions[3]}`}
197− value={state.desiredPoint}
198− onChange={this.handlePointInput}>
199− <InnerLabel>{'Point to Issue'}</InnerLabel>
200− <div
201− style={{
202− marginTop: '8.8rem',
203− marginLeft: '4.6rem',
204− fontSize: '5rem',
205− color: '#757575',
206− whiteSpace: 'pre-wrap',
207− }}
208− className="abs tl-0 mono">
209− {state.autoComplete}
210− </div>
211− <ValidatedSigil
212− className={'tr-0 mt-05 mr-0 abs'}
213− patp={state.desiredPoint}
214− size={68}
215− margin={8}
216− validator={() => this.validatePoint(state.desiredPoint)}
217− />
218− </PointInput>
219−
220− <AddressInput
221− className="text-mono mt-8"
222− prop-size="lg"
223− prop-format="innerLabel"
224− placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
225− value={state.receivingAddress}
226− disabled={
227− Nothing.hasInstance(state.isAvailable) || !state.isAvailable.value
228− }
229− onChange={v => this.handleAddressInput(v)}>
230− <InnerLabel>{'Receiving Address'}</InnerLabel>
231− <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
232− </AddressInput>
233−
234− <Anchor
235− className={'mt-1'}
236− prop-size={'sm'}
237− prop-disabled={
238− !isValidAddress(state.receivingAddress) || !esvisible
239− }
240− target={'_blank'}
241− href={`https://${esdomain}/address/${state.receivingAddress}`}>
242− {'View on Etherscan ↗'}
243− </Anchor>
244−
245− <StatelessTransaction
246− canGenerate={canGenerate}
247− createUnsignedTxn={this.createUnsignedTxn}
248− ref={this.statelessRef}
180+ {<code>{ob.patp(state.issuingPoint)}</code>}
181+ {' are '}
182+ <code>{state.suggestions[0]}</code>
183+ {', '}
184+ <code>{state.suggestions[1]}</code>
185+ {', and '}
186+ <code>{state.suggestions[2]}</code>
187+ {'.'}
188+ </P>
189+
190+ <PointInput
191+ prop-size="lg"
192+ prop-format="innerLabel"
193+ className={'mono mt-8'}
194+ placeholder={`e.g. ${state.suggestions[3]}`}
195+ value={state.desiredPoint}
196+ onChange={this.handlePointInput}>
197+ <InnerLabel>{'Point to Issue'}</InnerLabel>
198+ <div
199+ style={{
200+ marginTop: '8.8rem',
201+ marginLeft: '4.6rem',
202+ fontSize: '5rem',
203+ color: '#757575',
204+ whiteSpace: 'pre-wrap',
205+ }}
206+ className="abs tl-0 mono">
207+ {state.autoComplete}
208+ </div>
209+ <ValidatedSigil
210+ className={'tr-0 mt-05 mr-0 abs'}
211+ patp={state.desiredPoint}
212+ size={68}
213+ margin={8}
214+ validator={() => this.validatePoint(state.desiredPoint)}
249215 />
250− </Col>
251− </Row>
216+ </PointInput>
217+
218+ <AddressInput
219+ className="text-mono mt-8"
220+ prop-size="lg"
221+ prop-format="innerLabel"
222+ placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
223+ value={state.receivingAddress}
224+ disabled={
225+ Nothing.hasInstance(state.isAvailable) || !state.isAvailable.value
226+ }
227+ onChange={v => this.handleAddressInput(v)}>
228+ <InnerLabel>{'Receiving Address'}</InnerLabel>
229+ <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
230+ </AddressInput>
231+
232+ <Anchor
233+ className={'mt-1'}
234+ prop-size={'sm'}
235+ prop-disabled={!isValidAddress(state.receivingAddress) || !esvisible}
236+ target={'_blank'}
237+ href={`https://${esdomain}/address/${state.receivingAddress}`}>
238+ {'View on Etherscan ↗'}
239+ </Anchor>
240+
241+ <StatelessTransaction
242+ canGenerate={canGenerate}
243+ createUnsignedTxn={this.createUnsignedTxn}
244+ ref={this.statelessRef}
245+ />
246+ </View>
252247 );
253248 }
254249}
src/views/Keystore.js+47−48
@@ -4,7 +4,7 @@ import React from 'react';
44import * as keythereum from 'keythereum';
55import { Button, UploadButton } from '../components/old/Base';
66import { Input, InnerLabel, InputCaption } from '../components/old/Base';
7−import { Row, Col, H1, H3, Warning } from '../components/old/Base';
7+import { H1, H3, Warning } from '../components/old/Base';
88
99import { BRIDGE_ERROR } from '../lib/error';
1010import { ROUTE_NAMES } from '../lib/routeNames';
@@ -12,6 +12,7 @@ import { withHistory } from '../store/history';
1212import { EthereumWallet } from '../lib/wallet';
1313import { compose } from '../lib/lib';
1414import { withWallet } from '../store/wallet';
15+import View from 'components/View';
1516
1617class Keystore extends React.Component {
1718 constructor(props) {
@@ -108,55 +109,53 @@ class Keystore extends React.Component {
108109 );
109110
110111 return (
111− <Row>
112− <Col className={'measure-md'}>
113− <H1 className={'mb-4'}>{'Upload Your Keystore File'}</H1>
114− <InputCaption>
115− {`Please upload your Ethereum keystore file. If your keystore
112+ <View>
113+ <H1 className={'mb-4'}>{'Upload Your Keystore File'}</H1>
114+ <InputCaption>
115+ {`Please upload your Ethereum keystore file. If your keystore
116116 file is encrypted with a password, you'll also need to enter
117117 that below.`}
118− </InputCaption>
119−
120− <UploadButton
121− className={`btn ${uploadButtonClass} mt-10`}
122− onChange={this.handleKeystoreUpload}>
123− <div className={'flex-center-all fs-4 h-11 pointer'}>
124− {'Upload Keystore file'}
125− </div>
126− </UploadButton>
127−
128− <Input
129− className="pt-8 mt-8"
130− prop-size="md"
131− prop-format="innerLabel"
132− type="password"
133− name="password"
134− onChange={this.handlePasswordInput}
135− value={password}
136− autocomplete="off"
137− autoFocus>
138− <InnerLabel>{'Password'}</InnerLabel>
139− </Input>
140−
141− <Button
142− className={` mt-10`}
143− prop-size={'wide lg'}
144− disabled={Nothing.hasInstance(keystore)}
145− onClick={this.constructWallet}>
146− {'Decrypt'}
147− </Button>
148−
149− {decryptMessage}
150−
151− <Button
152− className={'mt-10'}
153− prop-size={'wide lg'}
154− disabled={Nothing.hasInstance(wallet)}
155− onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
156− {'Continue →'}
157− </Button>
158− </Col>
159− </Row>
118+ </InputCaption>
119+
120+ <UploadButton
121+ className={`btn ${uploadButtonClass} mt-10`}
122+ onChange={this.handleKeystoreUpload}>
123+ <div className={'flex-center-all fs-4 h-11 pointer'}>
124+ {'Upload Keystore file'}
125+ </div>
126+ </UploadButton>
127+
128+ <Input
129+ className="pt-8 mt-8"
130+ prop-size="md"
131+ prop-format="innerLabel"
132+ type="password"
133+ name="password"
134+ onChange={this.handlePasswordInput}
135+ value={password}
136+ autocomplete="off"
137+ autoFocus>
138+ <InnerLabel>{'Password'}</InnerLabel>
139+ </Input>
140+
141+ <Button
142+ className={` mt-10`}
143+ prop-size={'wide lg'}
144+ disabled={Nothing.hasInstance(keystore)}
145+ onClick={this.constructWallet}>
146+ {'Decrypt'}
147+ </Button>
148+
149+ {decryptMessage}
150+
151+ <Button
152+ className={'mt-10'}
153+ prop-size={'wide lg'}
154+ disabled={Nothing.hasInstance(wallet)}
155+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
156+ {'Continue →'}
157+ </Button>
158+ </View>
160159 );
161160 }
162161}
src/views/Landing.js+42−43
@@ -5,60 +5,59 @@ import { H1, P } from '../components/old/Base';
55
66import { ROUTE_NAMES } from '../lib/routeNames';
77import { useHistory } from '../store/history';
8+import View from 'components/View';
89
910function Landing(props) {
1011 const history = useHistory();
1112
1213 return (
13− <Row>
14− <Col>
15− <H1>{'Welcome'}</H1>
14+ <View>
15+ <H1>{'Welcome'}</H1>
1616
17− <P>
18− {'Bridge is a tool for managing and viewing assets on ' +
19− 'Azimuth, the Urbit address space.'}
20− </P>
17+ <P>
18+ {'Bridge is a tool for managing and viewing assets on ' +
19+ 'Azimuth, the Urbit address space.'}
20+ </P>
2121
22− <Button
23− prop-type="link"
24− prop-size="lg"
25− className={'mb-4 mt-8'}
26− onClick={() => history.push(ROUTE_NAMES.INVITE_TICKET)}>
27− {'Claim an invite →'}
28− </Button>
22+ <Button
23+ prop-type="link"
24+ prop-size="lg"
25+ className={'mb-4 mt-8'}
26+ onClick={() => history.push(ROUTE_NAMES.INVITE_TICKET)}>
27+ {'Claim an invite →'}
28+ </Button>
2929
30− <P>{'If you were sent an Azimuth invite code, start here.'}</P>
30+ <P>{'If you were sent an Azimuth invite code, start here.'}</P>
3131
32− <Button
33− prop-type="link"
34− prop-size="lg"
35− className={'mb-4 mt-8'}
36− onClick={() => history.push(ROUTE_NAMES.NETWORK)}>
37− {'Unlock a Wallet →'}
38− </Button>
32+ <Button
33+ prop-type="link"
34+ prop-size="lg"
35+ className={'mb-4 mt-8'}
36+ onClick={() => history.push(ROUTE_NAMES.NETWORK)}>
37+ {'Unlock a Wallet →'}
38+ </Button>
3939
40− <P>
41− {'If you own Azimuth assets and want to manage them in some ' +
42− "way, start here. You'll need either your Urbit ticket or a " +
43− 'keypair.'}
44− </P>
40+ <P>
41+ {'If you own Azimuth assets and want to manage them in some ' +
42+ "way, start here. You'll need either your Urbit ticket or a " +
43+ 'keypair.'}
44+ </P>
4545
46− {
47− <Row>
48− <Col>
49− <Button
50− prop-type={'link'}
51− prop-size={'lg'}
52− className={'mb-4 mt-8'}
53− onClick={() => history.push(ROUTE_NAMES.VIEW_SHIP)}>
54− {'View a point →'}
55− </Button>
56− <P>{'View an Azimuth point without signing into a wallet.'}</P>
57− </Col>
58− </Row>
59− }
60− </Col>
61− </Row>
46+ {
47+ <Row>
48+ <Col>
49+ <Button
50+ prop-type={'link'}
51+ prop-size={'lg'}
52+ className={'mb-4 mt-8'}
53+ onClick={() => history.push(ROUTE_NAMES.VIEW_SHIP)}>
54+ {'View a point →'}
55+ </Button>
56+ <P>{'View an Azimuth point without signing into a wallet.'}</P>
57+ </Col>
58+ </Row>
59+ }
60+ </View>
6261 );
6362}
6463
src/views/Ledger.js+59−64
@@ -2,7 +2,7 @@ import * as bip32 from 'bip32';
22import React from 'react';
33import Maybe from 'folktale/maybe';
44import { Button } from '../components/old/Base';
5−import { Row, Col, H1, P, H2 } from '../components/old/Base';
5+import { H1, P, H2 } from '../components/old/Base';
66import { Input, InnerLabel, InnerLabelDropdown } from '../components/old/Base';
77import Transport from '@ledgerhq/hw-transport-u2f';
88import Eth from '@ledgerhq/hw-app-eth';
@@ -13,6 +13,7 @@ import { ROUTE_NAMES } from '../lib/routeNames';
1313import { withHistory } from '../store/history';
1414import { compose } from '../lib/lib';
1515import { withWallet } from '../store/wallet';
16+import View from 'components/View';
1617
1718const chopHdPrefix = str => (str.slice(0, 2) === 'm/' ? str.slice(2) : str);
1819
@@ -139,82 +140,76 @@ class Ledger extends React.Component {
139140 );
140141
141142 return (
142− <Row>
143− <Col className={'measure-md'}>
144− <H1>{'Authenticate With Your Ledger'}</H1>
143+ <View>
144+ <H1>{'Authenticate With Your Ledger'}</H1>
145145
146− <H2>{'Running on HTTPS?'}</H2>
146+ <H2>{'Running on HTTPS?'}</H2>
147147
148− <P>
149− {`Connect and authenticate to your Ledger, and then open the
148+ <P>
149+ {`Connect and authenticate to your Ledger, and then open the
150150 "Ethereum" application. If you're running on older firmware,
151151 make sure the "browser support" option is turned on. To sign
152152 transactions, you'll also need to enable the "contract data"
153153 option.`}
154− </P>
154+ </P>
155155
156− <P>
157− {`If you'd like to use a custom derivation path, you may enter
156+ <P>
157+ {`If you'd like to use a custom derivation path, you may enter
158158 it below.`}
159− </P>
159+ </P>
160160
161− <H2>{'Running on HTTP?'}</H2>
161+ <H2>{'Running on HTTP?'}</H2>
162162
163− <P>{`To authenticate and sign transactions with a Ledger, Bridge
163+ <P>{`To authenticate and sign transactions with a Ledger, Bridge
164164 must be serving over HTTPS on localhost. You can do this via the
165165 following:`}</P>
166166
167− <ol className={'measure-md'}>
168− <li className={'mt-4'}>
169− {'Install'}{' '}
170− <a
171− target={'_blank'}
172− href={'https://github.com/FiloSottile/mkcert'}>
173− {'mkcert'}
174− </a>
175− </li>
176− <li className={'mt-4'}>
177− {'Install a local certificate authority via '}
178− <code>{'mkcert -install'}</code>
179− </li>
180− <li className={'mt-4'}>
181− {'In your '}
182− <code>{'bridge'}</code>
183− {
184− ' directory, generate a certificate valid for localhost via '
185− }{' '}
186− <code>{'mkcert localhost'}</code>
187− {'.'}
188− {'This will produce two files: '}
189− <code>{'localhost.pem'}</code> {', the local certificate, and '}
190− <code>{'localhost-key.pem'}</code>
191− {', its corresponding private key.'}
192− </li>
193− <li className={'mt-4'}>
194− {'Run '}
195− <code>{'python bridge-https.py'}</code>
196− </li>
197− </ol>
198−
199− {basePathSelection}
200− {truePathSelection}
201−
202− <Button
203− prop-size={'wide lg'}
204− className={'mt-8'}
205− onClick={this.pollDevice}>
206− {'Authenticate →'}
207− </Button>
208−
209− <Button
210− className={'mt-8'}
211− prop-size={'wide lg'}
212− disabled={Maybe.Nothing.hasInstance(wallet)}
213− onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
214− {'Continue →'}
215− </Button>
216− </Col>
217− </Row>
167+ <ol className={'measure-md'}>
168+ <li className={'mt-4'}>
169+ {'Install'}{' '}
170+ <a target={'_blank'} href={'https://github.com/FiloSottile/mkcert'}>
171+ {'mkcert'}
172+ </a>
173+ </li>
174+ <li className={'mt-4'}>
175+ {'Install a local certificate authority via '}
176+ <code>{'mkcert -install'}</code>
177+ </li>
178+ <li className={'mt-4'}>
179+ {'In your '}
180+ <code>{'bridge'}</code>
181+ {' directory, generate a certificate valid for localhost via '}{' '}
182+ <code>{'mkcert localhost'}</code>
183+ {'.'}
184+ {'This will produce two files: '}
185+ <code>{'localhost.pem'}</code> {', the local certificate, and '}
186+ <code>{'localhost-key.pem'}</code>
187+ {', its corresponding private key.'}
188+ </li>
189+ <li className={'mt-4'}>
190+ {'Run '}
191+ <code>{'python bridge-https.py'}</code>
192+ </li>
193+ </ol>
194+
195+ {basePathSelection}
196+ {truePathSelection}
197+
198+ <Button
199+ prop-size={'wide lg'}
200+ className={'mt-8'}
201+ onClick={this.pollDevice}>
202+ {'Authenticate →'}
203+ </Button>
204+
205+ <Button
206+ className={'mt-8'}
207+ prop-size={'wide lg'}
208+ disabled={Maybe.Nothing.hasInstance(wallet)}
209+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
210+ {'Continue →'}
211+ </Button>
212+ </View>
218213 );
219214 }
220215}
src/views/Mnemonic.js+58−61
@@ -8,13 +8,14 @@ import {
88 InnerLabel,
99 InputCaption,
1010} from '../components/old/Base';
11−import { Row, Col, H1 } from '../components/old/Base';
11+import { H1 } from '../components/old/Base';
1212
1313import { ROUTE_NAMES } from '../lib/routeNames';
1414import { withHistory } from '../store/history';
1515import { DEFAULT_HD_PATH, walletFromMnemonic } from '../lib/wallet';
1616import { compose } from '../lib/lib';
1717import { withWallet } from '../store/wallet';
18+import View from 'components/View';
1819
1920class Mnemonic extends React.Component {
2021 constructor(props) {
@@ -98,66 +99,62 @@ class Mnemonic extends React.Component {
9899 const { mnemonic, hdpath, exampleMnemonic, passphrase } = this.state;
99100
100101 return (
101− <Row>
102− <Col>
103− <H1 className={'mb-4'}>{'Enter Your Mnemonic'}</H1>
104− <InputCaption>
105− {'Please enter your BIP39 mnemonic here.'}
106− </InputCaption>
107−
108− <MnemonicInput
109− className="pt-8"
110− prop-size="md"
111− prop-format="innerLabel"
112− type="text"
113− name="mnemonic"
114− placeholder={`e.g. ${exampleMnemonic}`}
115− onChange={this.handleMnemonicInput}
116− value={mnemonic}
117− autocomplete="off"
118− autoFocus>
119− <InnerLabel>{'Mnemonic'}</InnerLabel>
120− </MnemonicInput>
121−
122− <InputCaption>
123− {`If your wallet requires a passphrase, you may enter it below.`}
124− </InputCaption>
125−
126− <Input
127− className="pt-8"
128− prop-size="md"
129− prop-format="innerLabel"
130− name="passphrase"
131− type="password"
132− value={passphrase}
133− autocomplete="off"
134− onChange={this.handlePassphraseInput}>
135− <InnerLabel>{'Passphrase'}</InnerLabel>
136− </Input>
137−
138− <InputCaption>
139− {`If you'd like to use a custom derivation path, you may enter it below.`}
140− </InputCaption>
141−
142− <Input
143− className="pt-8 text-mono"
144− prop-size="md"
145− prop-format="innerLabel"
146− name="hdpath"
147− value={hdpath}
148− autocomplete="off"
149− onChange={this.handleHdPathInput}>
150− <InnerLabel>{'HD Path'}</InnerLabel>
151− </Input>
152−
153− <Button
154− className={'mt-10'}
155− disabled={Nothing.hasInstance(wallet)}
156− onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
157− {'Continue →'}
158− </Button>
159− </Col>
160− </Row>
102+ <View>
103+ <H1 className={'mb-4'}>{'Enter Your Mnemonic'}</H1>
104+ <InputCaption>{'Please enter your BIP39 mnemonic here.'}</InputCaption>
105+
106+ <MnemonicInput
107+ className="pt-8"
108+ prop-size="md"
109+ prop-format="innerLabel"
110+ type="text"
111+ name="mnemonic"
112+ placeholder={`e.g. ${exampleMnemonic}`}
113+ onChange={this.handleMnemonicInput}
114+ value={mnemonic}
115+ autocomplete="off"
116+ autoFocus>
117+ <InnerLabel>{'Mnemonic'}</InnerLabel>
118+ </MnemonicInput>
119+
120+ <InputCaption>
121+ {`If your wallet requires a passphrase, you may enter it below.`}
122+ </InputCaption>
123+
124+ <Input
125+ className="pt-8"
126+ prop-size="md"
127+ prop-format="innerLabel"
128+ name="passphrase"
129+ type="password"
130+ value={passphrase}
131+ autocomplete="off"
132+ onChange={this.handlePassphraseInput}>
133+ <InnerLabel>{'Passphrase'}</InnerLabel>
134+ </Input>
135+
136+ <InputCaption>
137+ {`If you'd like to use a custom derivation path, you may enter it below.`}
138+ </InputCaption>
139+
140+ <Input
141+ className="pt-8 text-mono"
142+ prop-size="md"
143+ prop-format="innerLabel"
144+ name="hdpath"
145+ value={hdpath}
146+ autocomplete="off"
147+ onChange={this.handleHdPathInput}>
148+ <InnerLabel>{'HD Path'}</InnerLabel>
149+ </Input>
150+
151+ <Button
152+ className={'mt-10'}
153+ disabled={Nothing.hasInstance(wallet)}
154+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
155+ {'Continue →'}
156+ </Button>
157+ </View>
161158 );
162159 }
163160}
src/views/Network.js+22−23
@@ -2,11 +2,12 @@ import React from 'react';
22
33import { Button } from '../components/old/Base';
44import { InnerLabelDropdown } from '../components/old/Base';
5−import { Row, Col, H1, P } from '../components/old/Base';
5+import { H1, P } from '../components/old/Base';
66import { NETWORK_TYPES, renderNetworkType } from '../lib/network';
77import { ROUTE_NAMES } from '../lib/routeNames';
88import { useHistory } from '../store/history';
99import { useNetwork } from '../store/network';
10+import View from 'components/View';
1011
1112const kNetworkOptions = [
1213 {
@@ -35,31 +36,29 @@ function Network() {
3536 const history = useHistory();
3637
3738 return (
38− <Row>
39− <Col>
40− <H1>{'Select Network'}</H1>
39+ <View>
40+ <H1>{'Select Network'}</H1>
4141
42− <P>
43− {"Please select the Ethereum node you'd like to send " +
44− 'transactions to. For highly valuable keys, please select ' +
45− 'offline mode.'}
46− </P>
42+ <P>
43+ {"Please select the Ethereum node you'd like to send " +
44+ 'transactions to. For highly valuable keys, please select ' +
45+ 'offline mode.'}
46+ </P>
4747
48− <InnerLabelDropdown
49− className={'mt-6'}
50− title={'Node:'}
51− handleUpdate={setNetworkType}
52− options={kNetworkOptions}
53− currentSelectionTitle={renderNetworkType(networkType)}
54− />
48+ <InnerLabelDropdown
49+ className={'mt-6'}
50+ title={'Node:'}
51+ handleUpdate={setNetworkType}
52+ options={kNetworkOptions}
53+ currentSelectionTitle={renderNetworkType(networkType)}
54+ />
5555
56− <Button
57− className={'mt-10'}
58− onClick={() => history.push(ROUTE_NAMES.WALLET)}>
59− {'Continue →'}
60− </Button>
61− </Col>
62− </Row>
56+ <Button
57+ className={'mt-10'}
58+ onClick={() => history.push(ROUTE_NAMES.WALLET)}>
59+ {'Continue →'}
60+ </Button>
61+ </View>
6362 );
6463}
6564
src/views/Point.js+20−21
@@ -9,13 +9,14 @@ import PointList from '../components/old/PointList';
99import ReactSVGComponents from '../components/old/ReactSVGComponents';
1010import KeysAndMetadata from './Point/KeysAndMetadata';
1111import Actions from './Point/Actions';
12−import { Row, Col, H1, H3 } from '../components/old/Base';
12+import { H1, H3 } from '../components/old/Base';
1313import { withHistory } from '../store/history';
1414import { withNetwork } from '../store/network';
1515import { compose } from '../lib/lib';
1616import { withWallet } from '../store/wallet';
1717import { withPointCursor } from '../store/pointCursor';
1818import { withPointCache } from '../store/pointCache';
19+import View from 'components/View';
1920
2021class Point extends React.Component {
2122 constructor(props) {
@@ -83,7 +84,7 @@ class Point extends React.Component {
8384
8485 const { spawned } = this.state;
8586
86− const point = need.pointCursor(this.props);
87+ const point = need.pointCursor(this.props.pointCursor);
8788
8889 const pointDetails =
8990 point in pointCache ? Just(pointCache[point]) : Nothing();
@@ -112,25 +113,23 @@ class Point extends React.Component {
112113 );
113114
114115 return (
115− <Row>
116− <Col>
117− <div className={'mt-12 pt-6'}>{sigil}</div>
118− <H1>
119− <code>{name}</code>
120− </H1>
121− {authenticated ? (
122− <Actions
123− online={online}
124− point={point}
125− pointDetails={pointDetails}
126− invites={this.state.invites}
127− />
128− ) : null}
129−
130− {online ? <KeysAndMetadata pointDetails={pointDetails} /> : <div />}
131− {issuedPointList}
132− </Col>
133− </Row>
116+ <View>
117+ <div className={'mt-12 pt-6'}>{sigil}</div>
118+ <H1>
119+ <code>{name}</code>
120+ </H1>
121+ {authenticated ? (
122+ <Actions
123+ online={online}
124+ point={point}
125+ pointDetails={pointDetails}
126+ invites={this.state.invites}
127+ />
128+ ) : null}
129+
130+ {online ? <KeysAndMetadata pointDetails={pointDetails} /> : <div />}
131+ {issuedPointList}
132+ </View>
134133 );
135134 }
136135}
src/views/Point/Actions.js+10−11
@@ -3,11 +3,12 @@ import { azimuth } from 'azimuth-js';
33import * as need from '../../lib/need';
44
55import { ETH_ZERO_ADDR, CURVE_ZERO_ADDR, eqAddr } from '../../lib/wallet';
6−import { Row, Col, H2, P } from '../../components/old/Base';
6+import { H2, P } from '../../components/old/Base';
77import { Button } from '../../components/old/Base';
88import { ROUTE_NAMES } from '../../lib/routeNames';
99import { useHistory } from '../../store/history';
1010import { useWallet } from '../../store/wallet';
11+import Grid from 'components/Grid';
1112
1213const isPlanet = point =>
1314 azimuth.getPointSize(point) === azimuth.PointSize.Planet;
@@ -18,7 +19,7 @@ function Actions(props) {
1819 const { wallet } = useWallet();
1920 const { online, point, pointDetails, invites } = props;
2021
21− const addr = need.address({ wallet });
22+ const addr = need.addressFromWallet(wallet);
2223
2324 const planet = isPlanet(point);
2425 const star = isStar(point);
@@ -143,8 +144,8 @@ function Actions(props) {
143144 ) : (
144145 ''
145146 )}
146− <Row>
147− <Col className={'flex flex-column items-start col-md-4'}>
147+ <Grid>
148+ <Grid.Item third={1}>
148149 <Button
149150 prop-size={'sm'}
150151 prop-type={'link'}
@@ -184,8 +185,8 @@ function Actions(props) {
184185 }}>
185186 {'Generate Arvo keyfile'}
186187 </Button>
187− </Col>
188− <Col className={'flex flex-column items-start col-md-4'}>
188+ </Grid.Item>
189+ <Grid.Item third={2}>
189190 <Button
190191 disabled={online && !canSetSpawnProxy}
191192 prop-size={'sm'}
@@ -225,11 +226,9 @@ function Actions(props) {
225226 }}>
226227 {'Transfer'}
227228 </Button>
228− </Col>
229− <Col className={'flex flex-column items-start col-md-4'}>
230− {inviteAction}
231− </Col>
232− </Row>
229+ </Grid.Item>
230+ <Grid.Item third={3}>{inviteAction}</Grid.Item>
231+ </Grid>
233232 </div>
234233 );
235234}
src/views/Point/KeysAndMetadata.js+44−50
@@ -1,7 +1,8 @@
11import React from 'react';
2−import { Row, Col, H2, H4 } from '../../components/old/Base';
2+import { H2, H4 } from '../../components/old/Base';
33
44import { ETH_ZERO_ADDR, eqAddr } from '../../lib/wallet';
5+import Grid from 'components/Grid';
56
67const NULL_KEY =
78 '0x0000000000000000000000000000000000000000000000000000000000000000';
@@ -53,47 +54,42 @@ const KeysAndMetadata = props => {
5354 const { pointDetails } = props;
5455
5556 return (
56− <Row>
57− <Col>
57+ <Grid>
58+ <Grid.Item full>
5859 <H2>{'Ownership and Proxy Addresses'}</H2>
59−
60− <Row>
61− <Col className={'flex flex-column items-start col-md-3'}>
62− <H4>{'Owner'}</H4>
63− {pointDetails.matchWith({
64− Nothing: () => <div />,
65− Just: deets => renderAddress(deets.value.owner),
66− })}
67− </Col>
68−
69− <Col className={'flex flex-column items-start col-md-3'}>
70− <H4>{'Transfer Proxy'}</H4>
71− {pointDetails.matchWith({
72− Nothing: () => <div />,
73− Just: deets => renderAddress(deets.value.transferProxy),
74− })}
75− </Col>
76−
77− <Col className={'flex flex-column items-start col-md-3'}>
78− <H4>{'Spawn Proxy'}</H4>
79− {pointDetails.matchWith({
80− Nothing: () => <div />,
81− Just: deets => renderAddress(deets.value.spawnProxy),
82− })}
83− </Col>
84−
85− <Col className={'flex flex-column items-start col-md-3'}>
86− <H4>{'Mgmt Proxy'}</H4>
87− {pointDetails.matchWith({
88− Nothing: () => <div />,
89− Just: deets => renderAddress(deets.value.managementProxy),
90− })}
91− </Col>
92− </Row>
93−
60+ </Grid.Item>
61+ <Grid.Item fourth={1}>
62+ <H4>{'Owner'}</H4>
63+ {pointDetails.matchWith({
64+ Nothing: () => <div />,
65+ Just: deets => renderAddress(deets.value.owner),
66+ })}
67+ </Grid.Item>
68+ <Grid.Item fourth={2}>
69+ <H4>{'Transfer Proxy'}</H4>
70+ {pointDetails.matchWith({
71+ Nothing: () => <div />,
72+ Just: deets => renderAddress(deets.value.transferProxy),
73+ })}
74+ </Grid.Item>
75+ <Grid.Item fourth={3}>
76+ <H4>{'Spawn Proxy'}</H4>
77+ {pointDetails.matchWith({
78+ Nothing: () => <div />,
79+ Just: deets => renderAddress(deets.value.spawnProxy),
80+ })}
81+ </Grid.Item>
82+ <Grid.Item fourth={4}>
83+ <H4>{'Mgmt Proxy'}</H4>
84+ {pointDetails.matchWith({
85+ Nothing: () => <div />,
86+ Just: deets => renderAddress(deets.value.managementProxy),
87+ })}
88+ </Grid.Item>
89+ <Grid.Item full>
9490 <UrbitNetworking pointDetails={pointDetails} />
95− </Col>
96− </Row>
91+ </Grid.Item>
92+ </Grid>
9793 );
9894};
9995
@@ -108,24 +104,22 @@ const UrbitNetworking = props => {
108104 const body = !booted ? (
109105 <div>{'(not set)'}</div>
110106 ) : (
111− <Row>
112− <Col className={'flex flex-column items-start col-md-4'}>
107+ <Grid>
108+ <Grid.Item third={1}>
113109 <H4>{'Authentication'}</H4>
114110 {pointDetails.matchWith({
115111 Nothing: () => <div />,
116112 Just: deets => renderNetworkKey(deets.value.authenticationKey),
117113 })}
118− </Col>
119−
120− <Col className={'flex flex-column items-start col-md-4'}>
114+ </Grid.Item>
115+ <Grid.Item third={2}>
121116 <H4>{'Encryption'}</H4>
122117 {pointDetails.matchWith({
123118 Nothing: () => <div />,
124119 Just: deets => renderNetworkKey(deets.value.encryptionKey),
125120 })}
126− </Col>
127−
128− <Col className={'flex flex-column items-start col-md-4'}>
121+ </Grid.Item>
122+ <Grid.Item third={3}>
129123 <H4>{'Revision'}</H4>
130124 {pointDetails.matchWith({
131125 Nothing: () => '-',
@@ -143,8 +137,8 @@ const UrbitNetworking = props => {
143137 Nothing: () => '-',
144138 Just: deets => deets.value.cryptoSuiteVersion,
145139 })}
146− </Col>
147− </Row>
140+ </Grid.Item>
141+ </Grid>
148142 );
149143
150144 return (
src/views/Points.js+28−28
@@ -1,5 +1,5 @@
11import React from 'react';
2−import { Row, Col, H1, H2, P } from '../components/old/Base';
2+import { H1, H2, P } from '../components/old/Base';
33import { Button } from '../components/old/Base';
44import * as azimuth from 'azimuth-js';
55
@@ -12,6 +12,8 @@ import { withNetwork } from '../store/network';
1212import { compose } from '../lib/lib';
1313import { withWallet } from '../store/wallet';
1414import { withPointCache } from '../store/pointCache';
15+import View from 'components/View';
16+import Grid from 'components/Grid';
1517
1618const hasTransferProxy = (cache, point) =>
1719 point in cache ? !eqAddr(cache[point].transferProxy, ETH_ZERO_ADDR) : false;
@@ -97,8 +99,8 @@ class Points extends React.Component {
9799 } = this.state;
98100
99101 const lookupPointButton = (
100− <Row>
101− <Col>
102+ <Grid>
103+ <Grid.Item full>
102104 <Button
103105 prop-type={'link'}
104106 prop-size={'md'}
@@ -106,16 +108,16 @@ class Points extends React.Component {
106108 {'View a point →'}
107109 </Button>
108110 <P>{'View a point on Azimuth.'}</P>
109− </Col>
110− </Row>
111+ </Grid.Item>
112+ </Grid>
111113 );
112114
113115 const createGalaxyButton =
114116 eclipticOwner === false ? (
115117 <div />
116118 ) : (
117− <Row>
118− <Col>
119+ <Grid>
120+ <Grid.Item full>
119121 <Button
120122 prop-type={'link'}
121123 prop-size={'md'}
@@ -127,8 +129,8 @@ class Points extends React.Component {
127129 'You have the authority to create a new Galaxy and you can do so here.'
128130 }
129131 </P>
130− </Col>
131− </Row>
132+ </Grid.Item>
133+ </Grid>
132134 );
133135
134136 const outgoing = points.filter(point =>
@@ -203,36 +205,34 @@ class Points extends React.Component {
203205 );
204206
205207 return (
206− <Row>
207− <Col>
208− <H1>{'Points'}</H1>
208+ <View>
209+ <H1>{'Points'}</H1>
209210
210− <P>{`A point is an identity on the Ethereum blockchain.
211+ <P>{`A point is an identity on the Ethereum blockchain.
211212 Points declare keys for ships on the Arvo network.`}</P>
212213
213− {lookupPointButton}
214+ {lookupPointButton}
214215
215− {createGalaxyButton}
216+ {createGalaxyButton}
216217
217− {incomingPoints}
218+ {incomingPoints}
218219
219− {outgoingPoints}
220+ {outgoingPoints}
220221
221− <H2>{'Your Points'}</H2>
222+ <H2>{'Your Points'}</H2>
222223
223− <PointList
224− routeHandler={history.push}
225− points={points}
226− loading={loading}
227− />
224+ <PointList
225+ routeHandler={history.push}
226+ points={points}
227+ loading={loading}
228+ />
228229
229− {managingPoints}
230+ {managingPoints}
230231
231− {votingPoints}
232+ {votingPoints}
232233
233− {spawningPoints}
234− </Col>
235− </Row>
234+ {spawningPoints}
235+ </View>
236236 );
237237 }
238238}
src/views/PrivateKey.js+27−28
@@ -6,13 +6,14 @@ import {
66 InnerLabel,
77 InputCaption,
88} from '../components/old/Base';
9−import { Row, Col, H1 } from '../components/old/Base';
9+import { H1 } from '../components/old/Base';
1010
1111import { ROUTE_NAMES } from '../lib/routeNames';
1212import { withHistory } from '../store/history';
1313import { EthereumWallet } from '../lib/wallet';
1414import { compose } from '../lib/lib';
1515import { withWallet } from '../store/wallet';
16+import View from 'components/View';
1617
1718class PrivateKey extends React.Component {
1819 constructor(props) {
@@ -46,35 +47,33 @@ class PrivateKey extends React.Component {
4647 const { privateKey } = this.state;
4748
4849 return (
49− <Row>
50− <Col className={'measure-md'}>
51− <H1 className={'mb-4'}>{'Enter Your Private Key'}</H1>
52− <InputCaption>
53− {`Please enter your raw Ethereum private key here.`}
54− </InputCaption>
50+ <View>
51+ <H1 className={'mb-4'}>{'Enter Your Private Key'}</H1>
52+ <InputCaption>
53+ {`Please enter your raw Ethereum private key here.`}
54+ </InputCaption>
5555
56− <RequiredInput
57− className="pt-8 mt-8"
58− prop-size="md"
59− prop-format="innerLabel"
60− type="password"
61− name="privateKey"
62− onChange={this.handlePrivateKeyInput}
63− value={privateKey}
64− autocomplete="off"
65− autoFocus>
66− <InnerLabel>{'Private Key'}</InnerLabel>
67− </RequiredInput>
56+ <RequiredInput
57+ className="pt-8 mt-8"
58+ prop-size="md"
59+ prop-format="innerLabel"
60+ type="password"
61+ name="privateKey"
62+ onChange={this.handlePrivateKeyInput}
63+ value={privateKey}
64+ autocomplete="off"
65+ autoFocus>
66+ <InnerLabel>{'Private Key'}</InnerLabel>
67+ </RequiredInput>
6868
69− <Button
70− className={'mt-10'}
71− prop-size={'wide lg'}
72− disabled={Maybe.Nothing.hasInstance(wallet)}
73− onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
74− {'Continue →'}
75− </Button>
76− </Col>
77− </Row>
69+ <Button
70+ className={'mt-10'}
71+ prop-size={'wide lg'}
72+ disabled={Maybe.Nothing.hasInstance(wallet)}
73+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
74+ {'Continue →'}
75+ </Button>
76+ </View>
7877 );
7978 }
8079}
src/views/SentTransaction.js+22−21
@@ -1,7 +1,7 @@
11import Maybe from 'folktale/maybe';
22import React from 'react';
33import * as need from '../lib/need';
4−import { Row, Col, H1, H3, P, Warning, Anchor } from '../components/old/Base';
4+import { H1, H3, P, Warning, Anchor } from '../components/old/Base';
55import { Button } from '../components/old/Base';
66
77import { ROUTE_NAMES } from '../lib/routeNames';
@@ -11,6 +11,8 @@ import { NETWORK_TYPES } from '../lib/network';
1111import { useTxnConfirmations } from '../store/txnConfirmations';
1212import { useNetwork } from '../store/network';
1313import { useTxnCursor } from '../store/txnCursor';
14+import View from 'components/View';
15+import Grid from 'components/Grid';
1416
1517class Success extends React.Component {
1618 constructor(props) {
@@ -77,8 +79,8 @@ class Success extends React.Component {
7779 : `Confirmed! (x${confirmations} confirmations)!`;
7880
7981 return (
80− <Row>
81− <Col>
82+ <Grid>
83+ <Grid.Item full>
8284 <H1>{'Your Transaction was Sent'}</H1>
8385
8486 <P>
@@ -92,23 +94,23 @@ class Success extends React.Component {
9294 <H3>{'Transaction Status'}</H3>
9395 <P>{status}</P>
9496 <P>{esanchor}</P>
95− </Col>
96− </Row>
97+ </Grid.Item>
98+ </Grid>
9799 );
98100 }
99101}
100102
101103const Failure = props => (
102− <Row>
103− <Col>
104+ <Grid>
105+ <Grid.Item full>
104106 <H1>{'Error!'}</H1>
105107
106108 <Warning>
107109 <H3>{'There was an error sending your transaction.'}</H3>
108110 {renderTxnError(props.web3, props.message)}
109111 </Warning>
110− </Col>
111− </Row>
112+ </Grid.Item>
113+ </Grid>
112114);
113115
114116function SentTransaction(props) {
@@ -119,8 +121,7 @@ function SentTransaction(props) {
119121
120122 const promptKeyfile = history.data && history.data.promptKeyfile;
121123
122− // TODO: remove need's assumption about accepting the props object
123− const w3 = need.web3({ web3 });
124+ const w3 = need.web3(web3);
124125
125126 const result = txnCursor.matchWith({
126127 Nothing: _ => {
@@ -141,8 +142,8 @@ function SentTransaction(props) {
141142 });
142143
143144 const ok = (
144− <Row>
145− <Col>
145+ <Grid>
146+ <Grid.Item full>
146147 <Button
147148 prop-type={'link'}
148149 onClick={() => {
@@ -150,32 +151,32 @@ function SentTransaction(props) {
150151 }}>
151152 {'Ok →'}
152153 </Button>
153− </Col>
154− </Row>
154+ </Grid.Item>
155+ </Grid>
155156 );
156157
157158 let keyfile;
158159
159160 if (promptKeyfile) {
160161 keyfile = (
161− <Row>
162− <Col>
162+ <Grid>
163+ <Grid.Item full>
163164 <Button
164165 prop-type={'link'}
165166 onClick={() => history.popAndPush(ROUTE_NAMES.GEN_KEYFILE)}>
166167 {'Download Keyfile →'}
167168 </Button>
168− </Col>
169− </Row>
169+ </Grid.Item>
170+ </Grid>
170171 );
171172 }
172173
173174 return (
174− <div>
175+ <View>
175176 {body}
176177 {ok}
177178 {keyfile}
178− </div>
179+ </View>
179180 );
180181}
181182
src/views/SetKeys.js+50−59
@@ -4,14 +4,7 @@ import * as azimuth from 'azimuth-js';
44import * as ob from 'urbit-ob';
55import * as need from '../lib/need';
66import * as kg from 'urbit-key-generation/dist';
7−import {
8− Row,
9− Col,
10− H1,
11− P,
12− Warning,
13− CheckboxButton,
14−} from '../components/old/Base';
7+import { H1, P, Warning, CheckboxButton } from '../components/old/Base';
158
169import StatelessTransaction from '../components/old/StatelessTransaction';
1710import { attemptNetworkSeedDerivation } from '../lib/keys';
@@ -23,12 +16,13 @@ import { withNetwork } from '../store/network';
2316import { withWallet } from '../store/wallet';
2417import { withPointCursor } from '../store/pointCursor';
2518import { withPointCache } from '../store/pointCache';
19+import View from 'components/View';
2620
2721class SetKeys extends React.Component {
2822 constructor(props) {
2923 super(props);
3024
31− const point = need.pointCursor(props);
25+ const point = need.pointCursor(props.pointCursor);
3226
3327 this.state = {
3428 auth: '',
@@ -55,7 +49,7 @@ class SetKeys extends React.Component {
5549
5650 this.deriveSeed();
5751
58− const addr = need.address(props);
52+ const addr = need.addressFromWallet(props.wallet);
5953
6054 this.determineManagementSeed(props.contracts.value, addr);
6155 }
@@ -120,9 +114,8 @@ class SetKeys extends React.Component {
120114 createUnsignedTxn() {
121115 const { state, props } = this;
122116
123− const validContracts = need.contracts(props);
124−
125− const validPoint = need.pointCursor(props);
117+ const validContracts = need.contracts(props.contracts);
118+ const validPoint = need.pointCursor(props.pointCursor);
126119
127120 // TODO: move this to a lib for validating things
128121 const hexRegExp = /[0-9A-Fa-f]{64}/g;
@@ -155,58 +148,56 @@ class SetKeys extends React.Component {
155148 const canGenerate =
156149 state.newNetworkSeed.length === 64 && state.newNetworkSeed.length === 64;
157150
158− const pointDetails = need.fromPointCache(props, state.point);
151+ const pointDetails = need.fromPointCache(props.pointCache, state.point);
159152
160153 return (
161− <Row>
162− <Col>
163− <H1>
164− {'Set Network Keys For '} <code>{`${ob.patp(state.point)}`}</code>
165− </H1>
166−
167− <P className="mt-10">
168− {`Set new authentication and encryption keys for your Arvo ship.`}
169− </P>
170−
171− {state.nondeterministicSeed && (
172− <Warning>
173− <h3 className={'mb-2'}>{'Warning'}</h3>
174− {`Your network seed could not be derived automatically. We've
154+ <View>
155+ <H1>
156+ {'Set Network Keys For '} <code>{`${ob.patp(state.point)}`}</code>
157+ </H1>
158+
159+ <P className="mt-10">
160+ {`Set new authentication and encryption keys for your Arvo ship.`}
161+ </P>
162+
163+ {state.nondeterministicSeed && (
164+ <Warning>
165+ <h3 className={'mb-2'}>{'Warning'}</h3>
166+ {`Your network seed could not be derived automatically. We've
175167 generated a random one for you, so you must download your Arvo
176168 keyfile during this session after setting your keys.`}
177− </Warning>
178− )}
179−
180− {pointDetails.keyRevisionNumber === 0 ? (
181− <Warning>
182− <h3 className={'mb-2'}>{'Warning'}</h3>
183− {'Once these keys have been set, your point is considered ' +
184− "'linked'. This operation cannot be undone."}
185− </Warning>
186− ) : (
187− <div />
188− )}
189−
190− <CheckboxButton
191− className="mt-8"
192− onToggle={this.toggleDiscontinuity}
193− state={state.discontinuity}
194− label={`I have lost important off-chain data relating to this point
169+ </Warning>
170+ )}
171+
172+ {pointDetails.keyRevisionNumber === 0 ? (
173+ <Warning>
174+ <h3 className={'mb-2'}>{'Warning'}</h3>
175+ {'Once these keys have been set, your point is considered ' +
176+ "'linked'. This operation cannot be undone."}
177+ </Warning>
178+ ) : (
179+ <div />
180+ )}
181+
182+ <CheckboxButton
183+ className="mt-8"
184+ onToggle={this.toggleDiscontinuity}
185+ state={state.discontinuity}
186+ label={`I have lost important off-chain data relating to this point
195187 and need to do a hard reset.
196188 (For example, rebooting an Arvo ship.)`}
197− />
198−
199− <StatelessTransaction
200− // Upper scope
201− {...props}
202− // Tx
203− canGenerate={canGenerate}
204− createUnsignedTxn={this.createUnsignedTxn}
205− newNetworkSeed={state.newNetworkSeed}
206− newRevision={parseInt(pointDetails.keyRevisionNumber) + 1}
207− />
208− </Col>
209− </Row>
189+ />
190+
191+ <StatelessTransaction
192+ // Upper scope
193+ {...props}
194+ // Tx
195+ canGenerate={canGenerate}
196+ createUnsignedTxn={this.createUnsignedTxn}
197+ newNetworkSeed={state.newNetworkSeed}
198+ newRevision={parseInt(pointDetails.keyRevisionNumber) + 1}
199+ />
200+ </View>
210201 );
211202 }
212203}
src/views/SetProxy.js+24−28
@@ -3,8 +3,6 @@ import React from 'react';
33import * as need from '../lib/need';
44import * as azimuth from 'azimuth-js';
55import {
6− Row,
7− Col,
86 H1,
97 P,
108 InnerLabel,
@@ -25,12 +23,13 @@ import { isValidAddress } from '../lib/wallet';
2523import { withNetwork } from '../store/network';
2624import { compose } from '../lib/lib';
2725import { withPointCursor } from '../store/pointCursor';
26+import View from 'components/View';
2827
2928class _SetProxy extends React.Component {
3029 constructor(props) {
3130 super(props);
3231
33− const issuingPoint = need.pointCursor(props);
32+ const issuingPoint = need.pointCursor(props.pointCursor);
3433
3534 this.state = {
3635 proxyAddress: '',
@@ -61,9 +60,8 @@ class _SetProxy extends React.Component {
6160 createUnsignedTxn(proxyAddress) {
6261 const { state, props } = this;
6362
64− const validContracts = need.contracts(props);
65−
66− const validPoint = need.pointCursor(props);
63+ const validContracts = need.contracts(props.contracts);
64+ const validPoint = need.pointCursor(props.pointCursor);
6765
6866 const txArgs = [validContracts, validPoint, state.proxyAddress];
6967
@@ -141,28 +139,26 @@ class _SetProxy extends React.Component {
141139 }
142140
143141 return (
144− <Row>
145− <Col>
146− <H1>
147− {`${titleVerb} ${ucFirst(renderedProxyType)} Proxy For `}
148− <code>{`${ob.patp(state.issuingPoint)}`}</code>
149− </H1>
150−
151− <HorizontalSelector
152− options={setUnset}
153− onChange={this.handleSetUnset}
154− className="mt-8"
155− />
156−
157− {addressInput}
158−
159− <StatelessTransaction
160− ref={this.statelessRef}
161− createUnsignedTxn={this.createUnsignedTxn}
162− canGenerate={canGenerate}
163− />
164− </Col>
165− </Row>
142+ <View>
143+ <H1>
144+ {`${titleVerb} ${ucFirst(renderedProxyType)} Proxy For `}
145+ <code>{`${ob.patp(state.issuingPoint)}`}</code>
146+ </H1>
147+
148+ <HorizontalSelector
149+ options={setUnset}
150+ onChange={this.handleSetUnset}
151+ className="mt-8"
152+ />
153+
154+ {addressInput}
155+
156+ <StatelessTransaction
157+ ref={this.statelessRef}
158+ createUnsignedTxn={this.createUnsignedTxn}
159+ canGenerate={canGenerate}
160+ />
161+ </View>
166162 );
167163 }
168164}
src/views/Shards.js+97−104
@@ -9,7 +9,7 @@ import {
99 InputCaption,
1010 Input,
1111} from '../components/old/Base';
12−import { Row, Col, H1, P } from '../components/old/Base';
12+import { H1, P } from '../components/old/Base';
1313import * as kg from 'urbit-key-generation/dist';
1414import * as ob from 'urbit-ob';
1515
@@ -18,6 +18,7 @@ import { withHistory } from '../store/history';
1818import { urbitWalletFromTicket } from '../lib/wallet';
1919import { compose } from '../lib/lib';
2020import { withWallet } from '../store/wallet';
21+import View from 'components/View';
2122
2223const placeholder = len => {
2324 let bytes = window.crypto.getRandomValues(new Uint8Array(len));
@@ -103,112 +104,104 @@ class Shards extends React.Component {
103104 const ready = shards.filter(x => x !== '').length > 1;
104105
105106 return (
106− <Row>
107− <Col>
108− <H1>{'Authenticate'}</H1>
107+ <View>
108+ <H1>{'Authenticate'}</H1>
109109
110− <P>
111− {`Enter your point and at least two of your three Urbit master
110+ <P>
111+ {`Enter your point and at least two of your three Urbit master
112112 ticket shards here. The index of the input field needs to
113113 match the index of the shard.`}
114− </P>
115−
116− <PointInput
117− className="mono mt-8"
118− prop-size="lg"
119− prop-format="innerLabel"
120− type="text"
121− autoFocus
122− placeholder={`e.g. ${phPoint}`}
123− value={pointName}
124− onChange={this.handlePointNameInput}>
125− <InnerLabel>{'Point'}</InnerLabel>
126− <ValidatedSigil
127− className={'tr-0 mt-05 mr-0 abs'}
128− patp={pointName}
129− size={68}
130− margin={8}
131− />
132− </PointInput>
133−
134− <ShardInput
135− className="mono mt-8"
136− prop-size="md"
137− prop-format="innerLabel"
138− type="text"
139− name="shard1"
140− placeholder={`e.g. ${phTick}`}
141− value={shard1}
142− onChange={inp => this.handleShardInput(SHARDS.SHARD1, inp)}>
143− <InnerLabel>{'Shard 1'}</InnerLabel>
144− </ShardInput>
145−
146− <ShardInput
147− className="mono mt-8"
148− prop-size="md"
149− prop-format="innerLabel"
150− type="text"
151− name="shard2"
152− placeholder={`e.g. ${phTick}`}
153− value={shard2}
154− onChange={inp => this.handleShardInput(SHARDS.SHARD2, inp)}>
155− <InnerLabel>{'Shard 2'}</InnerLabel>
156− </ShardInput>
157−
158− <ShardInput
159− className="mono mt-8"
160− prop-size="md"
161− prop-format="innerLabel"
162− type="text"
163− name="shard3"
164− placeholder={`e.g. ${phTick}`}
165− value={shard3}
166− onChange={inp => this.handleShardInput(SHARDS.SHARD3, inp)}>
167− <InnerLabel>{'Shard 3'}</InnerLabel>
168− </ShardInput>
169−
170− <InputCaption>
171− {'If your wallet requires a passphrase, you may enter it below.'}
172− </InputCaption>
173−
174− <Input
175− className="pt-8"
176− prop-size="md"
177− prop-format="innerLabel"
178− name="passphrase"
179− type="password"
180− value={passphrase}
181− autocomplete="off"
182− onChange={this.handlePassphraseInput}>
183− <InnerLabel>{'Passphrase'}</InnerLabel>
184− </Input>
185−
186− <Button
187− disabled={!ready}
188− className={'mt-8'}
189− prop-size={'lg wide'}
190− // prop-color={this.buttonTriState(wallet)}
191− onClick={() =>
192− this.walletFromShards(
193− shard1,
194− shard2,
195− shard3,
196− pointName,
197− passphrase
198− )
199− }>
200− {'Unlock Wallet →'}
201− </Button>
202−
203− <Button
204− className={'mt-4'}
205− prop-size={'xl wide'}
206− disabled={Nothing.hasInstance(wallet)}
207− onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
208− {'Continue →'}
209− </Button>
210− </Col>
211− </Row>
114+ </P>
115+
116+ <PointInput
117+ className="mono mt-8"
118+ prop-size="lg"
119+ prop-format="innerLabel"
120+ type="text"
121+ autoFocus
122+ placeholder={`e.g. ${phPoint}`}
123+ value={pointName}
124+ onChange={this.handlePointNameInput}>
125+ <InnerLabel>{'Point'}</InnerLabel>
126+ <ValidatedSigil
127+ className={'tr-0 mt-05 mr-0 abs'}
128+ patp={pointName}
129+ size={68}
130+ margin={8}
131+ />
132+ </PointInput>
133+
134+ <ShardInput
135+ className="mono mt-8"
136+ prop-size="md"
137+ prop-format="innerLabel"
138+ type="text"
139+ name="shard1"
140+ placeholder={`e.g. ${phTick}`}
141+ value={shard1}
142+ onChange={inp => this.handleShardInput(SHARDS.SHARD1, inp)}>
143+ <InnerLabel>{'Shard 1'}</InnerLabel>
144+ </ShardInput>
145+
146+ <ShardInput
147+ className="mono mt-8"
148+ prop-size="md"
149+ prop-format="innerLabel"
150+ type="text"
151+ name="shard2"
152+ placeholder={`e.g. ${phTick}`}
153+ value={shard2}
154+ onChange={inp => this.handleShardInput(SHARDS.SHARD2, inp)}>
155+ <InnerLabel>{'Shard 2'}</InnerLabel>
156+ </ShardInput>
157+
158+ <ShardInput
159+ className="mono mt-8"
160+ prop-size="md"
161+ prop-format="innerLabel"
162+ type="text"
163+ name="shard3"
164+ placeholder={`e.g. ${phTick}`}
165+ value={shard3}
166+ onChange={inp => this.handleShardInput(SHARDS.SHARD3, inp)}>
167+ <InnerLabel>{'Shard 3'}</InnerLabel>
168+ </ShardInput>
169+
170+ <InputCaption>
171+ {'If your wallet requires a passphrase, you may enter it below.'}
172+ </InputCaption>
173+
174+ <Input
175+ className="pt-8"
176+ prop-size="md"
177+ prop-format="innerLabel"
178+ name="passphrase"
179+ type="password"
180+ value={passphrase}
181+ autocomplete="off"
182+ onChange={this.handlePassphraseInput}>
183+ <InnerLabel>{'Passphrase'}</InnerLabel>
184+ </Input>
185+
186+ <Button
187+ disabled={!ready}
188+ className={'mt-8'}
189+ prop-size={'lg wide'}
190+ // prop-color={this.buttonTriState(wallet)}
191+ onClick={() =>
192+ this.walletFromShards(shard1, shard2, shard3, pointName, passphrase)
193+ }>
194+ {'Unlock Wallet →'}
195+ </Button>
196+
197+ <Button
198+ className={'mt-4'}
199+ prop-size={'xl wide'}
200+ disabled={Nothing.hasInstance(wallet)}
201+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
202+ {'Continue →'}
203+ </Button>
204+ </View>
212205 );
213206 }
214207}
src/views/Ticket.js+74−77
@@ -9,13 +9,14 @@ import {
99 Input,
1010 InputCaption,
1111} from '../components/old/Base';
12−import { Row, Col, H1, P } from '../components/old/Base';
12+import { H1, P } from '../components/old/Base';
1313
1414import { randomPatq, compose } from '../lib/lib';
1515import { ROUTE_NAMES } from '../lib/routeNames';
1616import { withHistory } from '../store/history';
1717import { urbitWalletFromTicket } from '../lib/wallet';
1818import { withWallet } from '../store/wallet';
19+import View from 'components/View';
1920
2021class Ticket extends React.Component {
2122 constructor(props) {
@@ -71,82 +72,78 @@ class Ticket extends React.Component {
7172 const phTick = this.ticketPlaceholder;
7273
7374 return (
74− <Row>
75− <Col>
76− <H1>{'Authenticate'}</H1>
77−
78− <P>
79− {`Please enter your point and Urbit master ticket here. This information is written on your Urbit HD paper wallets.`}
80− </P>
81−
82− <PointInput
83− className="mono mt-8"
84− prop-size="lg"
85− prop-format="innerLabel"
86− type="text"
87− autoFocus
88− placeholder={`e.g. ${phPoint}`}
89− value={pointName}
90− onChange={this.handlePointNameInput}>
91− <InnerLabel>{'Point'}</InnerLabel>
92− <ValidatedSigil
93− className={'tr-0 mt-05 mr-0 abs'}
94− patp={pointName}
95− size={68}
96− margin={8}
97− />
98− </PointInput>
99−
100− <TicketInput
101− className="mono mt-8"
102− prop-size="lg"
103− prop-format="innerLabel"
104− type="password"
105− name="ticket"
106− placeholder={`e.g. ${phTick}`}
107− value={ticket}
108− onChange={this.handleTicketInput}>
109− <InnerLabel>{'Ticket'}</InnerLabel>
110− </TicketInput>
111−
112− <InputCaption>
113− {'If your wallet requires a passphrase, you may enter it below.'}
114− </InputCaption>
115−
116− <Input
117− className="pt-8"
118− prop-size="md"
119− prop-format="innerLabel"
120− name="passphrase"
121− type="password"
122− value={passphrase}
123− autocomplete="off"
124− onChange={this.handlePassphraseInput}>
125− <InnerLabel>{'Passphrase'}</InnerLabel>
126− </Input>
127−
128− <Button
129− className={'mt-8'}
130− prop-size={'lg wide'}
131− disabled={this.state.isUnlocking || Just.hasInstance(wallet)}
132− onClick={() =>
133− this.walletFromTicket(ticket, pointName, passphrase)
134− }>
135− <span className="relative">
136− {this.state.isUnlocking && <span className="btn-spinner" />}
137− {'Unlock Wallet →'}
138− </span>
139− </Button>
140−
141− <Button
142− className={'mt-4'}
143− prop-size={'xl wide'}
144− disabled={Nothing.hasInstance(wallet)}
145− onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
146− {'Continue →'}
147− </Button>
148− </Col>
149− </Row>
75+ <View>
76+ <H1>{'Authenticate'}</H1>
77+
78+ <P>
79+ {`Please enter your point and Urbit master ticket here. This information is written on your Urbit HD paper wallets.`}
80+ </P>
81+
82+ <PointInput
83+ className="mono mt-8"
84+ prop-size="lg"
85+ prop-format="innerLabel"
86+ type="text"
87+ autoFocus
88+ placeholder={`e.g. ${phPoint}`}
89+ value={pointName}
90+ onChange={this.handlePointNameInput}>
91+ <InnerLabel>{'Point'}</InnerLabel>
92+ <ValidatedSigil
93+ className={'tr-0 mt-05 mr-0 abs'}
94+ patp={pointName}
95+ size={68}
96+ margin={8}
97+ />
98+ </PointInput>
99+
100+ <TicketInput
101+ className="mono mt-8"
102+ prop-size="lg"
103+ prop-format="innerLabel"
104+ type="password"
105+ name="ticket"
106+ placeholder={`e.g. ${phTick}`}
107+ value={ticket}
108+ onChange={this.handleTicketInput}>
109+ <InnerLabel>{'Ticket'}</InnerLabel>
110+ </TicketInput>
111+
112+ <InputCaption>
113+ {'If your wallet requires a passphrase, you may enter it below.'}
114+ </InputCaption>
115+
116+ <Input
117+ className="pt-8"
118+ prop-size="md"
119+ prop-format="innerLabel"
120+ name="passphrase"
121+ type="password"
122+ value={passphrase}
123+ autocomplete="off"
124+ onChange={this.handlePassphraseInput}>
125+ <InnerLabel>{'Passphrase'}</InnerLabel>
126+ </Input>
127+
128+ <Button
129+ className={'mt-8'}
130+ prop-size={'lg wide'}
131+ disabled={this.state.isUnlocking || Just.hasInstance(wallet)}
132+ onClick={() => this.walletFromTicket(ticket, pointName, passphrase)}>
133+ <span className="relative">
134+ {this.state.isUnlocking && <span className="btn-spinner" />}
135+ {'Unlock Wallet →'}
136+ </span>
137+ </Button>
138+
139+ <Button
140+ className={'mt-4'}
141+ prop-size={'xl wide'}
142+ disabled={Nothing.hasInstance(wallet)}
143+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
144+ {'Continue →'}
145+ </Button>
146+ </View>
150147 );
151148 }
152149}
src/views/Transfer.js+46−58
@@ -4,15 +4,7 @@ import * as azimuth from 'azimuth-js';
44import * as ob from 'urbit-ob';
55import * as need from '../lib/need';
66
7−import {
8− Row,
9− Col,
10− H1,
11− InnerLabel,
12− ShowBlockie,
13− P,
14− Anchor,
15−} from '../components/old/Base';
7+import { H1, InnerLabel, ShowBlockie, P, Anchor } from '../components/old/Base';
168import { AddressInput } from '../components/old/Base';
179import StatelessTransaction from '../components/old/StatelessTransaction';
1810
@@ -22,12 +14,13 @@ import { isValidAddress } from '../lib/wallet';
2214import { withNetwork } from '../store/network';
2315import { compose } from '../lib/lib';
2416import { withPointCursor } from '../store/pointCursor';
17+import View from 'components/View';
2518
2619class Transfer extends React.Component {
2720 constructor(props) {
2821 super(props);
2922
30− const issuingPoint = need.pointCursor(props);
23+ const issuingPoint = need.pointCursor(props.pointCursor);
3124
3225 this.state = {
3326 receivingAddress: '',
@@ -53,9 +46,8 @@ class Transfer extends React.Component {
5346 createUnsignedTxn() {
5447 const { state, props } = this;
5548
56− const validContracts = need.contracts(props);
57−
58− const validPoint = need.pointCursor(props);
49+ const validContracts = need.contracts(props.contracts);
50+ const validPoint = need.pointCursor(props.pointCursor);
5951
6052 const txn = azimuth.ecliptic.setTransferProxy(
6153 validContracts,
@@ -82,53 +74,49 @@ class Transfer extends React.Component {
8274 : 'etherscan.io';
8375
8476 return (
85− <Row>
86− <Col>
87− <H1>
88− {'Transfer'} <code>{` ${ob.patp(state.issuingPoint)} `}</code>
89− {'To a New Owner'}
90− </H1>
91−
92− <P>
93− {`Please provide the Ethereum address of the new owner. You own these
77+ <View>
78+ <H1>
79+ {'Transfer'} <code>{` ${ob.patp(state.issuingPoint)} `}</code>
80+ {'To a New Owner'}
81+ </H1>
82+
83+ <P>
84+ {`Please provide the Ethereum address of the new owner. You own these
9485 points until the recipient accepts the incoming transfer.
9586 You may cancel the transfer until the transfer is accepted.`}
96− </P>
97−
98− <AddressInput
99− className={'mono mt-8'}
100− prop-size="lg"
101− prop-format="innerLabel"
102− value={state.receivingAddress}
103− placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
104− onChange={v => this.handleAddressInput(v)}>
105− <InnerLabel>{'New ownership address'}</InnerLabel>
106− <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
107− </AddressInput>
108−
109− <Anchor
110− className={'mt-1'}
111− prop-size={'s'}
112− prop-disabled={
113− !isValidAddress(state.receivingAddress) || !esvisible
114− }
115− target={'_blank'}
116− href={`https://${esdomain}/address/${state.receivingAddress}`}>
117− {'View on Etherscan ↗'}
118− </Anchor>
119−
120− <StatelessTransaction
121− // Upper scope
122− {...props}
123− // Checks
124− userApproval={state.userApproval}
125− canGenerate={canGenerate}
126− // Methods
127− createUnsignedTxn={this.createUnsignedTxn}
128− ref={this.statelessRef}
129− />
130− </Col>
131− </Row>
87+ </P>
88+
89+ <AddressInput
90+ className={'mono mt-8'}
91+ prop-size="lg"
92+ prop-format="innerLabel"
93+ value={state.receivingAddress}
94+ placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
95+ onChange={v => this.handleAddressInput(v)}>
96+ <InnerLabel>{'New ownership address'}</InnerLabel>
97+ <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
98+ </AddressInput>
99+
100+ <Anchor
101+ className={'mt-1'}
102+ prop-size={'s'}
103+ prop-disabled={!isValidAddress(state.receivingAddress) || !esvisible}
104+ target={'_blank'}
105+ href={`https://${esdomain}/address/${state.receivingAddress}`}>
106+ {'View on Etherscan ↗'}
107+ </Anchor>
108+
109+ <StatelessTransaction
110+ // Upper scope
111+ {...props}
112+ // Checks
113+ userApproval={state.userApproval}
114+ canGenerate={canGenerate}
115+ // Methods
116+ createUnsignedTxn={this.createUnsignedTxn}
117+ ref={this.statelessRef}
118+ />
119+ </View>
132120 );
133121 }
134122}
src/views/Trezor.js+26−27
@@ -2,7 +2,7 @@ import * as bip32 from 'bip32';
22import React from 'react';
33import Maybe from 'folktale/maybe';
44import { Button } from '../components/old/Base';
5−import { Row, Col, H1, P } from '../components/old/Base';
5+import { H1, P } from '../components/old/Base';
66import { InnerLabel, Input, InnerLabelDropdown } from '../components/old/Base';
77import TrezorConnect from 'trezor-connect';
88import * as secp256k1 from 'secp256k1';
@@ -12,6 +12,7 @@ import { ROUTE_NAMES } from '../lib/routeNames';
1212import { withHistory } from '../store/history';
1313import { compose } from '../lib/lib';
1414import { withWallet } from '../store/wallet';
15+import View from 'components/View';
1516
1617class Trezor extends React.Component {
1718 constructor(props) {
@@ -109,34 +110,32 @@ class Trezor extends React.Component {
109110 );
110111
111112 return (
112− <Row>
113− <Col className={'measure-md'}>
114− <H1>{'Authenticate With Your Trezor'}</H1>
113+ <View>
114+ <H1>{'Authenticate With Your Trezor'}</H1>
115115
116− <P>
117− {`Connect and authenticate to your Trezor. If you'd like
116+ <P>
117+ {`Connect and authenticate to your Trezor. If you'd like
118118 to use a custom derivation path, you may enter it below.`}
119− </P>
120−
121− {accountSelection}
122− {pathSelection}
123−
124− <Button
125− className={'mt-8'}
126− prop-size={'wide lg'}
127− onClick={this.pollDevice}>
128− {'Authenticate →'}
129− </Button>
130−
131− <Button
132− className={'mt-8'}
133− prop-size={'wide lg'}
134− disabled={Maybe.Nothing.hasInstance(wallet)}
135− onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
136− {'Continue →'}
137− </Button>
138− </Col>
139− </Row>
119+ </P>
120+
121+ {accountSelection}
122+ {pathSelection}
123+
124+ <Button
125+ className={'mt-8'}
126+ prop-size={'wide lg'}
127+ onClick={this.pollDevice}>
128+ {'Authenticate →'}
129+ </Button>
130+
131+ <Button
132+ className={'mt-8'}
133+ prop-size={'wide lg'}
134+ disabled={Maybe.Nothing.hasInstance(wallet)}
135+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}>
136+ {'Continue →'}
137+ </Button>
138+ </View>
140139 );
141140 }
142141}
src/views/ViewPoint.js+33−34
@@ -3,13 +3,14 @@ import React from 'react';
33import * as ob from 'urbit-ob';
44
55import { Button } from '../components/old/Base';
6−import { Row, Col, H1, P } from '../components/old/Base';
6+import { H1, P } from '../components/old/Base';
77import { InnerLabel, PointInput, ValidatedSigil } from '../components/old/Base';
88
99import { ROUTE_NAMES } from '../lib/routeNames';
1010import { withHistory } from '../store/history';
1111import { compose } from '../lib/lib';
1212import { withPointCursor } from '../store/pointCursor';
13+import View from 'components/View';
1314
1415class ViewPoint extends React.Component {
1516 constructor(props) {
@@ -43,42 +44,40 @@ class ViewPoint extends React.Component {
4344 }
4445
4546 return (
46− <Row>
47− <Col>
48− <H1>{'View a Point'}</H1>
47+ <View>
48+ <H1>{'View a Point'}</H1>
4949
50− <P>{'Enter a point name to view its public information.'}</P>
50+ <P>{'Enter a point name to view its public information.'}</P>
5151
52− <PointInput
53− autoFocus
54− prop-size="lg"
55− prop-format="innerLabel"
56− className="mono"
57− placeholder="e.g. ~zod"
58− onChange={this.handlePointInput}
59− value={pointName}>
60− <InnerLabel>{'Point Name'}</InnerLabel>
61− <ValidatedSigil
62− className={'tr-0 mt-05 mr-0 abs'}
63− patp={pointName}
64− show
65− size={68}
66− margin={8}
67− />
68− </PointInput>
52+ <PointInput
53+ autoFocus
54+ prop-size="lg"
55+ prop-format="innerLabel"
56+ className="mono"
57+ placeholder="e.g. ~zod"
58+ onChange={this.handlePointInput}
59+ value={pointName}>
60+ <InnerLabel>{'Point Name'}</InnerLabel>
61+ <ValidatedSigil
62+ className={'tr-0 mt-05 mr-0 abs'}
63+ patp={pointName}
64+ show
65+ size={68}
66+ margin={8}
67+ />
68+ </PointInput>
6969
70− <Button
71− className={'mt-8'}
72− disabled={valid === false}
73− onClick={() => {
74− setPointCursor(Maybe.Just(parseInt(ob.patp2dec(pointName), 10)));
75− // ^ pointCursor expects native number type, not string
76− history.popAndPush(ROUTE_NAMES.SHIP);
77− }}>
78− {'Continue →'}
79− </Button>
80− </Col>
81− </Row>
70+ <Button
71+ className={'mt-8'}
72+ disabled={valid === false}
73+ onClick={() => {
74+ setPointCursor(Maybe.Just(parseInt(ob.patp2dec(pointName), 10)));
75+ // ^ pointCursor expects native number type, not string
76+ history.popAndPush(ROUTE_NAMES.SHIP);
77+ }}>
78+ {'Continue →'}
79+ </Button>
80+ </View>
8281 );
8382 }
8483}
src/views/Wallet.js+37−39
@@ -2,13 +2,13 @@ import Maybe from 'folktale/maybe';
22import React from 'react';
33import { Button, H1, P } from '../components/old/Base';
44import { InnerLabelDropdown } from '../components/old/Base';
5−import { Row, Col } from '../components/old/Base';
65
76import { ROUTE_NAMES } from '../lib/routeNames';
87import { withHistory } from '../store/history';
98import { WALLET_TYPES, renderWalletType } from '../lib/wallet';
109import { compose } from '../lib/lib';
1110import { withWallet } from '../store/wallet';
11+import View from 'components/View';
1212
1313class Wallet extends React.Component {
1414 componentDidMount() {
@@ -60,48 +60,46 @@ class Wallet extends React.Component {
6060 const walletOptions = this.getWalletOptions();
6161
6262 return (
63− <Row>
64− <Col>
65− <H1>{'Unlock a Wallet'}</H1>
63+ <View>
64+ <H1>{'Unlock a Wallet'}</H1>
6665
67− <P>
68− {`To manage your assets, you need to unlock a wallet.
66+ <P>
67+ {`To manage your assets, you need to unlock a wallet.
6968 Please select how you would like to access your wallet.`}
70− </P>
69+ </P>
7170
72− <InnerLabelDropdown
73− className={'mb-10 mt-6'}
74− title={'Wallet Type:'}
75− options={walletOptions}
76− handleUpdate={props.setWalletType}
77− currentSelectionTitle={renderWalletType(props.walletType)}
78− />
71+ <InnerLabelDropdown
72+ className={'mb-10 mt-6'}
73+ title={'Wallet Type:'}
74+ options={walletOptions}
75+ handleUpdate={props.setWalletType}
76+ currentSelectionTitle={renderWalletType(props.walletType)}
77+ />
7978
80− <Button
81− className={'mt-10'}
82− onClick={() =>
83− props.history.push(
84− props.walletType === WALLET_TYPES.MNEMONIC
85− ? ROUTE_NAMES.MNEMONIC
86− : props.walletType === WALLET_TYPES.TICKET
87− ? ROUTE_NAMES.TICKET
88− : props.walletType === WALLET_TYPES.SHARDS
89− ? ROUTE_NAMES.SHARDS
90− : props.walletType === WALLET_TYPES.LEDGER
91− ? ROUTE_NAMES.LEDGER
92− : props.walletType === WALLET_TYPES.TREZOR
93− ? ROUTE_NAMES.TREZOR
94− : props.walletType === WALLET_TYPES.PRIVATE_KEY
95− ? ROUTE_NAMES.PRIVATE_KEY
96− : props.walletType === WALLET_TYPES.KEYSTORE
97− ? ROUTE_NAMES.KEYSTORE
98− : ROUTE_NAMES.DEFAULT
99− )
100− }>
101− {'Continue →'}
102− </Button>
103− </Col>
104− </Row>
79+ <Button
80+ className={'mt-10'}
81+ onClick={() =>
82+ props.history.push(
83+ props.walletType === WALLET_TYPES.MNEMONIC
84+ ? ROUTE_NAMES.MNEMONIC
85+ : props.walletType === WALLET_TYPES.TICKET
86+ ? ROUTE_NAMES.TICKET
87+ : props.walletType === WALLET_TYPES.SHARDS
88+ ? ROUTE_NAMES.SHARDS
89+ : props.walletType === WALLET_TYPES.LEDGER
90+ ? ROUTE_NAMES.LEDGER
91+ : props.walletType === WALLET_TYPES.TREZOR
92+ ? ROUTE_NAMES.TREZOR
93+ : props.walletType === WALLET_TYPES.PRIVATE_KEY
94+ ? ROUTE_NAMES.PRIVATE_KEY
95+ : props.walletType === WALLET_TYPES.KEYSTORE
96+ ? ROUTE_NAMES.KEYSTORE
97+ : ROUTE_NAMES.DEFAULT
98+ )
99+ }>
100+ {'Continue →'}
101+ </Button>
102+ </View>
105103 );
106104 }
107105}