@@ -2,6 +2,7 @@ import { Nothing, Just } from 'folktale/maybe';
2import React from 'react';
2import React from 'react';
3import * as azimuth from 'azimuth-js';
3import * as azimuth from 'azimuth-js';
4import * as ob from 'urbit-ob';
4import * as ob from 'urbit-ob';
@@ -12,19 +13,27 @@ import {
14} from '../components/Base';
15} from '../components/Base';
16+import HorizontalSelector from '../components/HorizontalSelector';
18+import Ticket from './Login/Ticket';
19+import Mnemonic from './Login/Mnemonic';
20+import Advanced from './Login/Advanced';
16import * as need from '../lib/need';
22import * as need from '../lib/need';
17import { compose } from '../lib/lib';
23import { compose } from '../lib/lib';
18import { urbitWalletFromTicket } from '../lib/wallet';
24import { urbitWalletFromTicket } from '../lib/wallet';
19import { ROUTE_NAMES } from '../lib/routeNames';
25import { ROUTE_NAMES } from '../lib/routeNames';
26+import { NETWORK_TYPES } from '../lib/network';
20import { withHistory } from '../store/history';
28import { withHistory } from '../store/history';
21import { withNetwork } from '../store/network';
29import { withNetwork } from '../store/network';
30+import { withWallet } from '../store/wallet';
31+import { withPointCursor } from '../store/pointCursor';
23−//TODO should be part of InputWithStatus component
33+const TABS = {
24−const INPUT_STATUS = {
34+ TICKET: Symbol('TICKET'),
25− SPIN: Symbol('SPIN'),
35+ MNEMONIC: Symbol('MNEMONIC'),
26− GOOD: Symbol('GOOD'),
36+ ADVANCED: Symbol('ADVANCED'),
27− FAIL: Symbol('FAIL'),
30//TODO support "advances" login methods. design under discussion...
39//TODO support "advances" login methods. design under discussion...
@@ -33,71 +42,45 @@ class Login extends React.Component {
33 super(props);
42 super(props);
35 this.state = {
44 this.state = {
36− pointName: '',
45+ currentTab: TABS.TICKET,
38− ticketStatus: Nothing(),
39 wallet: Nothing(),
46 wallet: Nothing(),
42− this.handlePointNameInput = this.handlePointNameInput.bind(this);
49+ this.handleTabChange = this.handleTabChange.bind(this);
43− this.handleTicketInput = this.handleTicketInput.bind(this);
44− this.verifyTicket = this.verifyTicket.bind(this);
45 this.continue = this.continue.bind(this);
50 this.continue = this.continue.bind(this);
48 componentDidMount() {
53 componentDidMount() {
49− //TODO deduce point name from URL if we can, prefill input if we found it
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);
56− handlePointNameInput(pointName) {
58+ handleTabChange(currentTab) {
57− if (pointName.length < 15) {
58− this.setState({ pointName });
59+ this.setState({ currentTab });
59− this.verifyTicket(pointName, this.state.ticket);
63− handleTicketInput(ticket) {
62+ async continue() {
64− this.setState({ ticket });
63+ const wallet = need.wallet(this.props);
65− this.verifyTicket(this.state.pointName, ticket);
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() });
74− this.setState({ ticketStatus: Just(INPUT_STATUS.SPIN) });
75 const contracts = need.contracts(this.props);
64 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(
81− uhdw.ownership.keys.address
83− const isTransferProxy = azimuth.azimuth.isTransferProxy(
86− uhdw.ownership.keys.address
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);
96− canContinue() {
66+ // if no point cursor set by login logic, try to deduce it
97− // this is our only requirement, since we still want people with
67+ if (Nothing.hasInstance(this.props.pointCursor)) {
98− // non-standard wallet setups to be able to log in
68+ const owned = await azimuth.azimuth.getOwnedPoints(
99− return Just.hasInstance(this.state.wallet);
69+ contracts,
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(
79+ if (canOwn.length === 1) {
80+ this.props.setPointCursor(Just(canOwn[0]));
102 if (Just.hasInstance(this.props.pointCursor)) {
85 if (Just.hasInstance(this.props.pointCursor)) {
103 this.props.history.popAndPush(ROUTE_NAMES.POINT_HOME);
86 this.props.history.popAndPush(ROUTE_NAMES.POINT_HOME);
@@ -107,70 +90,40 @@ class Login extends React.Component {
109 render() {
92 render() {
110− const pointInput = (
93+ const tabOptions = [
111− <PointInput
94+ { title: 'Ticket', value: TABS.TICKET },
112− className="mono mt-8"
95+ { title: 'Mnemonic', value: TABS.MNEMONIC },
113− prop-size="lg"
96+ { title: 'Advanced', value: TABS.ADVANCED },
114− prop-format="innerLabel"
97+ ];
116− autoFocus
99+ //TODO probably needs a dedicated component because styling
117− value={this.state.pointName}
100+ const tabs = (
102+ options={tabOptions}
118− onChange={this.handlePointNameInput}>
103+ onChange={this.handleTabChange}
119− <InnerLabel>{'Point'}</InnerLabel>
121− className={'tr-0 mt-05 mr-0 abs'}
122− patp={this.state.pointName}
129− const ticketStatus = this.state.ticketStatus.matchWith({
107+ const login = (() => {
130− Nothing: () => <span />,
108+ switch (this.state.currentTab) {
131− Just: status => {
109+ case TABS.TICKET:
132− switch (status.value) {
110+ return <Ticket loginCompleted={this.continue} />;
133− case INPUT_STATUS.SPIN:
111+ case TABS.MNEMONIC:
134− return <span>⋯</span>;
112+ return <Mnemonic loginCompleted={this.continue} />;
135− case INPUT_STATUS.GOOD:
113+ case TABS.ADVANCED:
136− return <span>✓</span>;
114+ return <Advanced loginCompleted={this.continue} />;
137− case INPUT_STATUS.FAIL:
138− return <span>𐄂</span>;
139− default:
115+ default:
140− throw new Error('weird input status ' + status.value);
116+ throw new Error('weird tab ' + this.status.currentTab);
145− const ticketInput = (
146− //TODO NewTicketInput component, that does dashes etc
148− className="mono mt-8"
150− prop-format="innerLabel"
153− value={this.state.ticket}
154− onChange={this.handleTicketInput}>
155− <InnerLabel>{'Code'}</InnerLabel>
163 <H1>{'Login'}</H1>
123 <H1>{'Login'}</H1>
165− {pointInput}
125+ {tabs}
166− {ticketInput}
126+ {login}
170− disabled={!this.canContinue()}
171− onClick={this.continue}>
@@ -179,5 +132,7 @@ class Login extends React.Component {
180export default compose(
133export default compose(
181 withHistory,
134 withHistory,
182− withNetwork
135+ withNetwork,