on master help sign in
Barest bones new login page

Designs for advanced methods still under discussion

commit8cfaf0ca8ce0f8c8d98f6a0ba7bae1911fc15f32 authorFang <git@fang.io> dateFri 14 Jun 2019, 00:39 (7 years ago) parent39cf89d
4 files changed+195
src/bridge/lib/routeNames.js+2 src/bridge/lib/router.js+2 src/bridge/views/Landing.js+10 src/bridge/views/Login.js+181
src/bridge/lib/routeNames.js+2
@@ -6,6 +6,8 @@ export const ROUTE_NAMES = {
66 INVITES_SEND: Symbol('INVITES_SEND'),
77 INVITES_MANAGE: Symbol('INVITES_MANAGE'),
88 //
9+ LOGIN: Symbol('LOGIN'),
10+ //
911 ACCEPT_TRANSFER: Symbol('ACCEPT_TRANSFER'),
1012 CANCEL_TRANSFER: Symbol('CANCEL_TRANSFER'),
1113 LANDING: Symbol('LANDING'),
src/bridge/lib/router.js+2
@@ -6,6 +6,7 @@ import Landing from '../views/Landing';
66import InviteTicket from '../views/InviteTicket';
77import InvitesSend from '../views/InvitesSend.js';
88import InvitesManage from '../views/InvitesManage.js';
9+import Login from '../views/Login.js';
910import AcceptTransfer from '../views/AcceptTransfer';
1011import CancelTransfer from '../views/CancelTransfer';
1112import CreateGalaxy from '../views/CreateGalaxy';
@@ -42,6 +43,7 @@ const ROUTES = {
4243 [ROUTE_NAMES.INVITE_TICKET]: InviteTicket,
4344 [ROUTE_NAMES.INVITES_SEND]: InvitesSend,
4445 [ROUTE_NAMES.INVITES_MANAGE]: InvitesManage,
46+ [ROUTE_NAMES.LOGIN]: Login,
4547 [ROUTE_NAMES.ACCEPT_TRANSFER]: AcceptTransfer,
4648 [ROUTE_NAMES.CANCEL_TRANSFER]: CancelTransfer,
4749 [ROUTE_NAMES.NETWORK]: Network,
src/bridge/views/Landing.js+10
@@ -29,6 +29,16 @@ function Landing(props) {
2929
3030 <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.LOGIN)}>
37+ {'Login (mino) →'}
38+ </Button>
39+
40+ <P>{'This landing page needs to be revamped too.'}</P>
41+
3242 <Button
3343 prop-type="link"
3444 prop-size="lg"
src/bridge/views/Login.jsnew file+181
@@ -0,0 +1,181 @@
1+import { Nothing, Just } from 'folktale/maybe';
2+import React from 'react';
3+import * as azimuth from 'azimuth-js';
4+import * as ob from 'urbit-ob';
5+import {
6+ Button,
7+ H1,
8+ InnerLabel,
9+ PointInput,
10+ ValidatedSigil,
11+ TicketInput,
12+ Row,
13+ Col,
14+} from '../components/Base';
15+
16+import * as need from '../lib/need';
17+import { compose } from '../lib/lib';
18+import { urbitWalletFromTicket } from '../lib/wallet';
19+import { withHistory } from '../store/history';
20+import { withNetwork } from '../store/network';
21+
22+//TODO should be part of InputWithStatus component
23+const INPUT_STATUS = {
24+ SPIN: Symbol('SPIN'),
25+ GOOD: Symbol('GOOD'),
26+ FAIL: Symbol('FAIL'),
27+};
28+
29+//TODO support "advances" login methods. design under discussion...
30+class Login extends React.Component {
31+ constructor(props) {
32+ super(props);
33+
34+ this.state = {
35+ pointName: '',
36+ ticket: '',
37+ ticketStatus: Nothing(),
38+ wallet: Nothing(),
39+ };
40+
41+ this.handlePointNameInput = this.handlePointNameInput.bind(this);
42+ this.handleTicketInput = this.handleTicketInput.bind(this);
43+ this.verifyTicket = this.verifyTicket.bind(this);
44+ this.continue = this.continue.bind(this);
45+ }
46+
47+ componentDidMount() {
48+ //TODO deduce point name from URL if we can, prefill input if we found it
49+
50+ // const { networkType, setNetworkType } = useNetwork();
51+ console.log(this.props);
52+ // setNetworkType(this.props.initialNetworkType);
53+ }
54+
55+ handlePointNameInput(pointName) {
56+ if (pointName.length < 15) {
57+ this.setState({ pointName });
58+ this.verifyTicket(pointName, this.state.ticket);
59+ }
60+ }
61+
62+ handleTicketInput(ticket) {
63+ this.setState({ ticket });
64+ this.verifyTicket(this.state.pointName, ticket);
65+ }
66+
67+ //TODO maybe want to do this only on-go, because wallet derivation is slow...
68+ async verifyTicket(pointName, ticket) {
69+ if (!ob.isValidPatq(ticket) || !ob.isValidPatp(pointName)) {
70+ this.setState({ ticketStatus: Nothing(), wallet: Nothing() });
71+ return;
72+ }
73+ this.setState({ ticketStatus: Just(INPUT_STATUS.SPIN) });
74+ const contracts = need.contracts(this.props);
75+ const pointNumber = ob.patp2dec(pointName);
76+ const uhdw = await urbitWalletFromTicket(ticket, pointName);
77+ const isOwner = azimuth.azimuth.isOwner(
78+ contracts,
79+ pointNumber,
80+ uhdw.ownership.keys.address
81+ );
82+ const isTransferProxy = azimuth.azimuth.isTransferProxy(
83+ contracts,
84+ pointNumber,
85+ uhdw.ownership.keys.address
86+ );
87+ let newState = { wallet: Just(uhdw) };
88+ newState.ticketStatus =
89+ (await isOwner) || (await isTransferProxy)
90+ ? Just(INPUT_STATUS.GOOD)
91+ : Just(INPUT_STATUS.FAIL);
92+ this.setState(newState);
93+ }
94+
95+ canContinue() {
96+ // this is our only requirement, since we still want people with
97+ // non-standard wallet setups to be able to log in
98+ return Just.hasInstance(this.state.wallet);
99+ }
100+
101+ continue() {
102+ //TODO slightly different for other login methods
103+ this.props.setUrbitWallet(this.state.wallet);
104+ //TODO history.popAndPush(ROUTE_NAMES.POINT_HOME);
105+ }
106+
107+ render() {
108+ const pointInput = (
109+ <PointInput
110+ className="mono mt-8"
111+ prop-size="lg"
112+ prop-format="innerLabel"
113+ type="text"
114+ autoFocus
115+ value={this.state.pointName}
116+ onChange={this.handlePointNameInput}>
117+ <InnerLabel>{'Point'}</InnerLabel>
118+ <ValidatedSigil
119+ className={'tr-0 mt-05 mr-0 abs'}
120+ patp={this.state.pointName}
121+ size={68}
122+ margin={8}
123+ />
124+ </PointInput>
125+ );
126+
127+ const ticketStatus = this.state.ticketStatus.matchWith({
128+ Nothing: () => <span />,
129+ Just: status => {
130+ switch (status.value) {
131+ case INPUT_STATUS.SPIN:
132+ return <span>⋯</span>;
133+ case INPUT_STATUS.GOOD:
134+ return <span>✓</span>;
135+ case INPUT_STATUS.FAIL:
136+ return <span>𐄂</span>;
137+ default:
138+ throw new Error('weird input status ' + status.value);
139+ }
140+ },
141+ });
142+
143+ const ticketInput = (
144+ //TODO NewTicketInput component, that does dashes etc
145+ <TicketInput
146+ className="mono mt-8"
147+ prop-size="lg"
148+ prop-format="innerLabel"
149+ type="password"
150+ name="ticket"
151+ value={this.state.ticket}
152+ onChange={this.handleTicketInput}>
153+ <InnerLabel>{'Code'}</InnerLabel>
154+ {ticketStatus}
155+ </TicketInput>
156+ );
157+
158+ return (
159+ <Row>
160+ <Col>
161+ <H1>{'Login'}</H1>
162+
163+ {pointInput}
164+ {ticketInput}
165+
166+ <Button
167+ className={'mt-10'}
168+ disabled={!this.canContinue()}
169+ onClick={this.continue}>
170+ {'Go →'}
171+ </Button>
172+ </Col>
173+ </Row>
174+ );
175+ }
176+}
177+
178+export default compose(
179+ withHistory,
180+ withNetwork
181+)(Login);