@@ -1,26 +1,23 @@
11import { Just, Nothing } from 'folktale/maybe';
2−import React from 'react';
2+import React, { useState, useEffect } from 'react';
33import * as azimuth from 'azimuth-js';
44import * as ob from 'urbit-ob';
55
6+import View from '../../components/View';
67import {
7− Button,
8− Row,
9− Col,
10− Input,
11− InnerLabel,
12− ValidatedSigil,
138 PointInput,
149 TicketInput,
15−} from '../../components/old/Base';
10+ PassphraseInput,
11+} from '../../components/Inputs';
12+import { ForwardButton } from '../../components/Buttons';
1613
1714import * as need from '../../lib/need';
1815import { compose } from '../../lib/lib';
1916import { WALLET_TYPES, urbitWalletFromTicket } from '../../lib/wallet';
2017
21−import { withWallet } from '../../store/wallet';
22−import { withNetwork } from '../../store/network';
23−import { withPointCursor } from '../../store/pointCursor';
18+import { useNetwork } from '../../store/network';
19+import { useWallet } from '../../store/wallet';
20+import { usePointCursor } from '../../store/pointCursor';
2421
2522//TODO should be part of InputWithStatus component
2623const INPUT_STATUS = {
@@ -29,174 +26,125 @@ const INPUT_STATUS = {
2926 FAIL: Symbol('FAIL'),
3027};
3128
32−class Ticket extends React.Component {
33− constructor(props) {
34− super(props);
35−
36− this.state = {
37− pointName: '',
38− ticket: '',
39− ticketStatus: Nothing(),
40− passphrase: '',
41− };
42−
43− this.handlePointNameInput = this.handlePointNameInput.bind(this);
44− this.handleTicketInput = this.handleTicketInput.bind(this);
45− this.handlePassphraseInput = this.handlePassphraseInput.bind(this);
46− this.verifyTicket = this.verifyTicket.bind(this);
47− this.continue = this.continue.bind(this);
48− }
49−
50− componentDidMount() {
51− //TODO deduce point name from URL if we can, prefill input if we found it
52− }
53−
54− handlePointNameInput(pointName) {
55− if (pointName.length < 15) {
56− this.setState({ pointName });
57− this.verifyTicket(pointName, this.state.ticket, this.state.passphrase);
58− }
59− }
29+export default function Ticket({ advanced, loginCompleted }) {
30+ // globals
31+ const { contracts } = useNetwork();
32+ const { wallet, setWalletType, setUrbitWallet } = useWallet();
33+ const { setPointCursor } = usePointCursor();
34+
35+ // inputs
36+ //TODO deduce point name from URL if we can, prefill input if we found it
37+ const [pointName, setPointName] = useState('');
38+ const [ticket, setTicket] = useState('');
39+ const [passphrase, setPassphrase] = useState('');
6040
61− handleTicketInput(ticket) {
62− this.setState({ ticket });
63− this.verifyTicket(this.state.pointName, ticket, this.state.passphrase);
64− }
41+ // display state
42+ const [ticketStatus, setTicketStatus] = useState(Nothing());
6543
66− handlePassphraseInput(passphrase) {
67− this.setState({ passphrase });
68− this.verifyTicket(this.state.pointName, this.state.ticket, passphrase);
69− }
44+ useEffect(() => {
45+ console.log('something changed?');
46+ verifyTicket(pointName, ticket, passphrase);
47+ return () => {};
48+ }, [pointName, ticket, passphrase]);
7049
7150 //TODO maybe want to do this only on-go, because wallet derivation is slow...
72− async verifyTicket(pointName, ticket, passphrase) {
51+ const verifyTicket = async (pointName, ticket, passphrase) => {
52+ setUrbitWallet(Nothing());
7353 if (!ob.isValidPatq(ticket) || !ob.isValidPatp(pointName)) {
74− this.props.setUrbitWallet(Nothing());
75− this.setState({ ticketStatus: Nothing() });
54+ setTicketStatus(Nothing());
7655 return;
7756 }
78− this.setState({ ticketStatus: Just(INPUT_STATUS.SPIN) });
79− const contracts = need.contracts(this.props);
57+ setTicketStatus(Just(INPUT_STATUS.SPIN));
58+ const realContracts = need.contracts(contracts);
8059 const pointNumber = ob.patp2dec(pointName);
8160 const uhdw = await urbitWalletFromTicket(ticket, pointName, passphrase);
8261 const isOwner = azimuth.azimuth.isOwner(
83− contracts,
62+ realContracts,
8463 pointNumber,
8564 uhdw.ownership.keys.address
8665 );
8766 const isTransferProxy = azimuth.azimuth.isTransferProxy(
88− contracts,
67+ realContracts,
8968 pointNumber,
9069 uhdw.ownership.keys.address
9170 );
92− this.props.setUrbitWallet(Just(uhdw));
93− this.setState({
94− ticketStatus:
95− (await isOwner) || (await isTransferProxy)
96− ? Just(INPUT_STATUS.GOOD)
97− : Just(INPUT_STATUS.FAIL),
98− });
99− }
100−
101− canContinue() {
71+ setUrbitWallet(Just(uhdw));
72+ const newStatus =
73+ (await isOwner) || (await isTransferProxy)
74+ ? INPUT_STATUS.GOOD
75+ : INPUT_STATUS.FAIL;
76+ setTicketStatus(Just(newStatus));
77+ };
78+
79+ const canContinue = () => {
10280 // this is our only requirement, since we still want people with
10381 // non-standard wallet setups to be able to log in
104− return Just.hasInstance(this.props.wallet);
105− }
106−
107− continue() {
108− //TODO maybe do on-mount?
109− this.props.setWalletType(WALLET_TYPES.TICKET);
110− this.props.setPointCursor(Just(ob.patp2dec(this.state.pointName)));
111− this.props.loginCompleted();
112− }
113−
114− render() {
115− const pointInput = (
116− <PointInput
117− className="mono mt-8"
118− prop-size="lg"
119− prop-format="innerLabel"
120− type="text"
121− autoFocus
122− value={this.state.pointName}
123− onChange={this.handlePointNameInput}>
124− <InnerLabel>{'Point name'}</InnerLabel>
125− <ValidatedSigil
126− className={'tr-0 mt-05 mr-0 abs'}
127− patp={this.state.pointName}
128− size={68}
129− margin={8}
130− />
131− </PointInput>
132− );
133−
134− const ticketStatus = this.state.ticketStatus.matchWith({
135− Nothing: () => <span />,
136− Just: status => {
137− switch (status.value) {
138− case INPUT_STATUS.SPIN:
139− return <span>⋯</span>;
140− case INPUT_STATUS.GOOD:
141− return <span>✓</span>;
142− case INPUT_STATUS.FAIL:
143− return <span>𐄂</span>;
144− default:
145− throw new Error('weird input status ' + status.value);
146− }
147− },
148− });
149−
150− const ticketInput = (
151− //TODO NewTicketInput component, that does dashes etc
152− <TicketInput
153− className="mono mt-8"
154− prop-size="lg"
155− prop-format="innerLabel"
156− type="password"
157− name="ticket"
158− value={this.state.ticket}
159− onChange={this.handleTicketInput}>
160− <InnerLabel>{'Master ticket'}</InnerLabel>
161− {ticketStatus}
162− </TicketInput>
163− );
164−
165− const passphraseInput = !this.props.advanced ? null : (
166− <Input
167− className="mt-8"
168− prop-size="md"
169− prop-format="innerLabel"
170− name="passphrase"
171− type="password"
172− value={this.state.passphrase}
173− autocomplete="off"
174− onChange={this.handlePassphraseInput}>
175− <InnerLabel>{'Passphrase'}</InnerLabel>
176− </Input>
177− );
178−
179− return (
180− <Row>
181− <Col>
182− {pointInput}
183− {ticketInput}
184− {passphraseInput}
185−
186− <Button
187− className={'mt-10'}
188− disabled={!this.canContinue()}
189− onClick={this.continue}>
190− {'Go →'}
191− </Button>
192− </Col>
193− </Row>
194− );
195− }
82+ return Just.hasInstance(wallet);
83+ };
84+
85+ const doContinue = () => {
86+ setWalletType(WALLET_TYPES.TICKET);
87+ setPointCursor(Just(ob.patp2dec(pointName)));
88+ loginCompleted();
89+ };
90+
91+ const pointInput = (
92+ <PointInput
93+ name="point"
94+ label="Point"
95+ initialValue={pointName}
96+ onValue={setPointName}
97+ autoFocus
98+ />
99+ );
100+
101+ //TODO integrate into TicketInput?
102+ const displayTicketStatus = ticketStatus.matchWith({
103+ Nothing: () => <span />,
104+ Just: status => {
105+ switch (status.value) {
106+ case INPUT_STATUS.SPIN:
107+ return <span>⋯</span>;
108+ case INPUT_STATUS.GOOD:
109+ return <span>✓</span>;
110+ case INPUT_STATUS.FAIL:
111+ return <span>𐄂</span>;
112+ default:
113+ throw new Error('weird input status ' + status.value);
114+ }
115+ },
116+ });
117+
118+ const ticketInput = (
119+ <TicketInput
120+ name="ticket"
121+ label="Master ticket"
122+ initialValue={ticket}
123+ onValue={setTicket}
124+ />
125+ );
126+
127+ const passphraseInput = !advanced ? null : (
128+ <PassphraseInput
129+ name="passphrase"
130+ label="(Optional) Wallet Passphrase"
131+ initialValue={passphrase}
132+ onValue={setPassphrase}
133+ />
134+ );
135+
136+ return (
137+ <View>
138+ {pointInput}
139+ {ticketInput}
140+ {passphraseInput}
141+
142+ <ForwardButton
143+ className={'mt3'}
144+ disabled={!canContinue()}
145+ onClick={doContinue}>
146+ Continue
147+ </ForwardButton>
148+ </View>
149+ );
196150}
197−
198−export default compose(
199− withWallet,
200− withNetwork,
201− withPointCursor
202−)(Ticket);