on master help sign in
Merge pull request #127 from urbit/zero-address
868ba2bFang
src/bridge/components/Base.js+5
@@ -37,6 +37,9 @@ import DropdownItem from './DropdownItem';
37import DropdownDivider from './DropdownDivider';
37import DropdownDivider from './DropdownDivider';
38import InnerLabelDropdown from './InnerLabelDropdown';
38import InnerLabelDropdown from './InnerLabelDropdown';
39
39
40+// Option selectors
41+import HorizontalSelector from './HorizontalSelector';
42+
40// Path Control
43// Path Control
41import Breadcrumb from './Breadcrumb';
44import Breadcrumb from './Breadcrumb';
42import BreadcrumbItem from './BreadcrumbItem';
45import BreadcrumbItem from './BreadcrumbItem';
@@ -183,6 +186,8 @@ export {
183 DropdownDivider,
186 DropdownDivider,
184 InnerLabelDropdown,
187 InnerLabelDropdown,
185
188
189+ HorizontalSelector,
190+
186 Breadcrumb,
191 Breadcrumb,
187 BreadcrumbItem,
192 BreadcrumbItem,
188
193
src/bridge/components/HorizontalSelector.jsnew file+57
@@ -0,0 +1,57 @@
1+import React from 'react'
2+import PropTypes from 'prop-types';
3+
4+class HorizontalSelector extends React.Component {
5+ constructor(props) {
6+ super(props);
7+
8+ this.state = {
9+ option: this.props.options[0].value
10+ };
11+
12+ this.selectOption = this.selectOption.bind(this);
13+ }
14+
15+ selectOption(opt) {
16+ this.props.onChange(opt);
17+ this.setState({option: opt});
18+ }
19+
20+ getOptionElems() {
21+ return this.props.options.map(opt => {
22+ let active = (opt.value === this.state.option)
23+ ? 'btn-primary active'
24+ : '';
25+ return (
26+ <button
27+ className={`btn ${active} h-10 ph-4 flex sans ${this.props.className}`}
28+ value={opt.value}
29+ onClick={() => this.selectOption(opt.value)}>
30+ { opt.title }
31+ </button>
32+ )
33+ })
34+ }
35+
36+ render() {
37+ const optionElems = this.getOptionElems();
38+
39+ return (
40+ <div className='btn-group'>
41+ { optionElems }
42+ </div>
43+ )
44+ }
45+}
46+
47+HorizontalSelector.propTypes = {
48+ className: PropTypes.string,
49+ options: PropTypes.array
50+};
51+
52+HorizontalSelector.defaultProps = {
53+ className: '',
54+ options: []
55+};
56+
57+export default HorizontalSelector
src/bridge/lib/wallet.js+1−1
@@ -70,7 +70,7 @@ const stripHexPrefix = hex =>
70const keccak256 = str =>
70const keccak256 = str =>
71 keccak('keccak256').update(str).digest()
71 keccak('keccak256').update(str).digest()
72
72
73−const isValidAddress = isAddress
73+const isValidAddress = a => (('0x0' === a) || isAddress(a))
74
74
75const toChecksumAddress = (address) => {
75const toChecksumAddress = (address) => {
76 const addr = stripHexPrefix(address).toLowerCase()
76 const addr = stripHexPrefix(address).toLowerCase()
src/bridge/views/Point/Actions.js+2−2
@@ -177,7 +177,7 @@ const Actions = (props) => {
177 pushRoute(ROUTE_NAMES.SET_SPAWN_PROXY)
177 pushRoute(ROUTE_NAMES.SET_SPAWN_PROXY)
178 }}
178 }}
179 >
179 >
180− { 'Set spawn proxy' }
180+ { 'Change spawn proxy' }
181 </Button>
181 </Button>
182
182
183 <Button
183 <Button
@@ -188,7 +188,7 @@ const Actions = (props) => {
188 pushRoute(ROUTE_NAMES.SET_MANAGEMENT_PROXY)
188 pushRoute(ROUTE_NAMES.SET_MANAGEMENT_PROXY)
189 }}
189 }}
190 >
190 >
191− { 'Set management proxy' }
191+ { 'Change management proxy' }
192 </Button>
192 </Button>
193
193
194 <Button
194 <Button
src/bridge/views/SetProxy.js+58−29
@@ -1,7 +1,7 @@
1import Maybe from 'folktale/maybe'
1import Maybe from 'folktale/maybe'
2import React from 'react'
2import React from 'react'
3import * as azimuth from 'azimuth-js'
3import * as azimuth from 'azimuth-js'
4−import { Row, Col, H1, P, InnerLabel, ShowBlockie, Anchor } from '../components/Base'
4+import { Row, Col, H1, P, InnerLabel, ShowBlockie, Anchor, HorizontalSelector } from '../components/Base'
5import { AddressInput } from '../components/Base'
5import { AddressInput } from '../components/Base'
6import * as ob from 'urbit-ob'
6import * as ob from 'urbit-ob'
7
7
@@ -46,13 +46,23 @@ class SetProxy extends React.Component {
46 this.state = {
46 this.state = {
47 proxyAddress: '',
47 proxyAddress: '',
48 issuingPoint: issuingPoint,
48 issuingPoint: issuingPoint,
49+ setProxy: true
49 }
50 }
50
51
51 this.statelessRef = React.createRef();
52 this.statelessRef = React.createRef();
53+ this.handleSetUnset = this.handleSetUnset.bind(this);
52 this.handleAddressInput = this.handleAddressInput.bind(this)
54 this.handleAddressInput = this.handleAddressInput.bind(this)
53 this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
55 this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
54 }
56 }
55
57
58+ handleSetUnset(selected) {
59+ const set = (selected === 'set');
60+ this.setState({
61+ setProxy: set,
62+ proxyAddress: set ? '' : '0x0000000000000000000000000000000000000000'
63+ });
64+ }
65+
56 handleAddressInput(proxyAddress) {
66 handleAddressInput(proxyAddress) {
57 this.setState({ proxyAddress })
67 this.setState({ proxyAddress })
58
68
@@ -116,42 +126,61 @@ class SetProxy extends React.Component {
116
126
117 const ucFirst = w => w.charAt(0).toUpperCase() + w.slice(1);
127 const ucFirst = w => w.charAt(0).toUpperCase() + w.slice(1);
118
128
129+ const setUnset = [
130+ { title: 'Set', value: 'set' },
131+ { title: 'Unset', value: 'unset' }
132+ ];
133+
134+ const titleVerb = this.state.setProxy ? 'Set' : 'Unset';
135+
136+ let addressInput = null;
137+ if (this.state.setProxy) {
138+ addressInput = (<>
139+ <P className='mt-8'>
140+ {
141+ 'Please provide an Ethereum address to act as the ' +
142+ `${renderedProxyType} proxy. You can also use Wallet ` +
143+ 'Generator to generate a keypair.'
144+ }
145+ </P>
146+
147+ <AddressInput
148+ className='mono mt-8'
149+ prop-size='lg'
150+ prop-format='innerLabel'
151+ placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
152+ value={ state.proxyAddress }
153+ onChange={ v => this.handleAddressInput(v) }>
154+ <InnerLabel>{ 'Proxy Address' }</InnerLabel>
155+ <ShowBlockie className={'mt-1'} address={state.proxyAddress} />
156+ </AddressInput>
157+
158+ <Anchor
159+ className={'mt-1'}
160+ prop-size={'sm'}
161+ prop-disabled={!isValidAddress(state.proxyAddress) || !esvisible}
162+ target={'_blank'}
163+ href={`https://${esdomain}/address/${state.proxyAddress}`}>
164+ {'View on Etherscan ↗'}
165+ </Anchor>
166+ </>)
167+ }
119
168
120 return (
169 return (
121 <Row>
170 <Row>
122 <Col>
171 <Col>
123 <H1>
172 <H1>
124− { `Set ${ucFirst(renderedProxyType)} Proxy For ` }
173+ { `${titleVerb} ${ucFirst(renderedProxyType)} Proxy For ` }
125 <code>{ `${ob.patp(state.issuingPoint)}` }</code>
174 <code>{ `${ob.patp(state.issuingPoint)}` }</code>
126 </H1>
175 </H1>
127
176
128− <P>
177+ <HorizontalSelector
129− {
178+ options={setUnset}
130− 'Please provide an Ethereum address to act as the ' +
131− `${renderedProxyType} proxy. You can also use Wallet ` +
132− 'Generator to generate a keypair.'
133− }
134− </P>
135−
136− <AddressInput
137− className='mono mt-8'
138− prop-size='lg'
139− prop-format='innerLabel'
140− placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
141− value={ state.proxyAddress }
142− onChange={ v => this.handleAddressInput(v) }>
179+ onChange={this.handleSetUnset}
143− <InnerLabel>{ 'Proxy Address' }</InnerLabel>
180+ className='mt-8'
144− <ShowBlockie className={'mt-1'} address={state.proxyAddress} />
181+ />
145− </AddressInput>
182+
146−
183+ { addressInput }
147− <Anchor
148− className={'mt-1'}
149− prop-size={'sm'}
150− prop-disabled={!isValidAddress(state.proxyAddress) || !esvisible}
151− target={'_blank'}
152− href={`https://${esdomain}/address/${state.proxyAddress}`}>
153− {'View on Etherscan ↗'}
154− </Anchor>
155
184
156 <StatelessTransaction
185 <StatelessTransaction
157 // Upper scope
186 // Upper scope