@@ -2,6 +2,7 @@ import { Nothing, Just } from 'folktale/maybe';
22import React from 'react';
33import * as azimuth from 'azimuth-js';
44import * as ob from 'urbit-ob';
5+
56import {
67 Button,
78 H1,
@@ -12,19 +13,27 @@ import {
1213 Row,
1314 Col,
1415} from '../components/Base';
16+import HorizontalSelector from '../components/HorizontalSelector';
17+
18+import Ticket from './Login/Ticket';
19+import Mnemonic from './Login/Mnemonic';
20+import Advanced from './Login/Advanced';
1521
1622import * as need from '../lib/need';
1723import { compose } from '../lib/lib';
1824import { urbitWalletFromTicket } from '../lib/wallet';
1925import { ROUTE_NAMES } from '../lib/routeNames';
26+import { NETWORK_TYPES } from '../lib/network';
27+
2028import { withHistory } from '../store/history';
2129import { withNetwork } from '../store/network';
30+import { withWallet } from '../store/wallet';
31+import { withPointCursor } from '../store/pointCursor';
2232
23−//TODO should be part of InputWithStatus component
24−const INPUT_STATUS = {
25− SPIN: Symbol('SPIN'),
26− GOOD: Symbol('GOOD'),
27− FAIL: Symbol('FAIL'),
33+const TABS = {
34+ TICKET: Symbol('TICKET'),
35+ MNEMONIC: Symbol('MNEMONIC'),
36+ ADVANCED: Symbol('ADVANCED'),
2837};
2938
3039//TODO support "advances" login methods. design under discussion...
@@ -33,71 +42,45 @@ class Login extends React.Component {
3342 super(props);
3443
3544 this.state = {
36− pointName: '',
37− ticket: '',
38− ticketStatus: Nothing(),
45+ currentTab: TABS.TICKET,
3946 wallet: Nothing(),
4047 };
4148
42− this.handlePointNameInput = this.handlePointNameInput.bind(this);
43− this.handleTicketInput = this.handleTicketInput.bind(this);
44− this.verifyTicket = this.verifyTicket.bind(this);
49+ this.handleTabChange = this.handleTabChange.bind(this);
4550 this.continue = this.continue.bind(this);
4651 }
4752
4853 componentDidMount() {
49− //TODO deduce point name from URL if we can, prefill input if we found it
50−
51− // const { networkType, setNetworkType } = useNetwork();
52− console.log(this.props);
53− // setNetworkType(this.props.initialNetworkType);
54+ //TODO send help, want to use initialNetworkType
55+ this.props.setNetworkType(NETWORK_TYPES.LOCAL);
5456 }
5557
56− handlePointNameInput(pointName) {
57− if (pointName.length < 15) {
58− this.setState({ pointName });
59− this.verifyTicket(pointName, this.state.ticket);
60− }
58+ handleTabChange(currentTab) {
59+ this.setState({ currentTab });
6160 }
6261
63− handleTicketInput(ticket) {
64− this.setState({ ticket });
65− this.verifyTicket(this.state.pointName, ticket);
66− }
67−
68− //TODO maybe want to do this only on-go, because wallet derivation is slow...
69− async verifyTicket(pointName, ticket) {
70− if (!ob.isValidPatq(ticket) || !ob.isValidPatp(pointName)) {
71− this.setState({ ticketStatus: Nothing(), wallet: Nothing() });
72− return;
73− }
74− this.setState({ ticketStatus: Just(INPUT_STATUS.SPIN) });
62+ async continue() {
63+ const wallet = need.wallet(this.props);
7564 const contracts = need.contracts(this.props);
76− const pointNumber = ob.patp2dec(pointName);
77− const uhdw = await urbitWalletFromTicket(ticket, pointName);
78− const isOwner = azimuth.azimuth.isOwner(
79− contracts,
80− pointNumber,
81− uhdw.ownership.keys.address
82− );
83− const isTransferProxy = azimuth.azimuth.isTransferProxy(
84− contracts,
85− pointNumber,
86− uhdw.ownership.keys.address
87− );
88− let newState = { wallet: Just(uhdw) };
89− newState.ticketStatus =
90− (await isOwner) || (await isTransferProxy)
91− ? Just(INPUT_STATUS.GOOD)
92− : Just(INPUT_STATUS.FAIL);
93− this.setState(newState);
94− }
9565
96− canContinue() {
97− // this is our only requirement, since we still want people with
98− // non-standard wallet setups to be able to log in
99− return Just.hasInstance(this.state.wallet);
100− }
66+ // if no point cursor set by login logic, try to deduce it
67+ if (Nothing.hasInstance(this.props.pointCursor)) {
68+ const owned = await azimuth.azimuth.getOwnedPoints(
69+ contracts,
70+ wallet.address
71+ );
72+ if (owned.length === 1) {
73+ this.props.setPointCursor(Just(owned[0]));
74+ } else if (owned.length === 0) {
75+ const canOwn = await azimuth.azimuth.getTransferringFor(
76+ contracts,
77+ wallet.address
78+ );
79+ if (canOwn.length === 1) {
80+ this.props.setPointCursor(Just(canOwn[0]));
81+ }
82+ }
83+ }
10184
10285 if (Just.hasInstance(this.props.pointCursor)) {
10386 this.props.history.popAndPush(ROUTE_NAMES.POINT_HOME);
@@ -107,70 +90,40 @@ class Login extends React.Component {
10790 }
10891
10992 render() {
110− const pointInput = (
111− <PointInput
112− className="mono mt-8"
113− prop-size="lg"
114− prop-format="innerLabel"
115− type="text"
116− autoFocus
117− value={this.state.pointName}
118− onChange={this.handlePointNameInput}>
119− <InnerLabel>{'Point'}</InnerLabel>
120− <ValidatedSigil
121− className={'tr-0 mt-05 mr-0 abs'}
122− patp={this.state.pointName}
123− size={68}
124− margin={8}
125− />
126− </PointInput>
93+ const tabOptions = [
94+ { title: 'Ticket', value: TABS.TICKET },
95+ { title: 'Mnemonic', value: TABS.MNEMONIC },
96+ { title: 'Advanced', value: TABS.ADVANCED },
97+ ];
98+
99+ //TODO probably needs a dedicated component because styling
100+ const tabs = (
101+ <HorizontalSelector
102+ options={tabOptions}
103+ onChange={this.handleTabChange}
104+ />
127105 );
128106
129− const ticketStatus = this.state.ticketStatus.matchWith({
130− Nothing: () => <span />,
131− Just: status => {
132− switch (status.value) {
133− case INPUT_STATUS.SPIN:
134− return <span>⋯</span>;
135− case INPUT_STATUS.GOOD:
136− return <span>✓</span>;
137− case INPUT_STATUS.FAIL:
138− return <span>𐄂</span>;
139− default:
140− throw new Error('weird input status ' + status.value);
141− }
142− },
143− });
144−
145− const ticketInput = (
146− //TODO NewTicketInput component, that does dashes etc
147− <TicketInput
148− className="mono mt-8"
149− prop-size="lg"
150− prop-format="innerLabel"
151− type="password"
152− name="ticket"
153− value={this.state.ticket}
154− onChange={this.handleTicketInput}>
155− <InnerLabel>{'Code'}</InnerLabel>
156− {ticketStatus}
157− </TicketInput>
158− );
107+ const login = (() => {
108+ switch (this.state.currentTab) {
109+ case TABS.TICKET:
110+ return <Ticket loginCompleted={this.continue} />;
111+ case TABS.MNEMONIC:
112+ return <Mnemonic loginCompleted={this.continue} />;
113+ case TABS.ADVANCED:
114+ return <Advanced loginCompleted={this.continue} />;
115+ default:
116+ throw new Error('weird tab ' + this.status.currentTab);
117+ }
118+ })();
159119
160120 return (
161121 <Row>
162122 <Col>
163123 <H1>{'Login'}</H1>
164124
165− {pointInput}
166− {ticketInput}
167−
168− <Button
169− className={'mt-10'}
170− disabled={!this.canContinue()}
171− onClick={this.continue}>
172− {'Go →'}
173− </Button>
125+ {tabs}
126+ {login}
174127 </Col>
175128 </Row>
176129 );
@@ -179,5 +132,7 @@ class Login extends React.Component {
179132
180133export default compose(
181134 withHistory,
182− withNetwork
135+ withNetwork,
136+ withWallet,
137+ withPointCursor
183138)(Login);