import { Just, Nothing } from 'folktale/maybe'; import React from 'react'; import * as azimuth from 'azimuth-js'; import * as need from 'lib/need'; import View from 'components/View'; import { InnerLabel, Warning, TicketInput, VerifyTicketInput, Button, Passport, } from '../components/old/Base'; import { randomPatq, compose } from '../lib/lib'; import { ROUTE_NAMES } from '../lib/routeNames'; import { withHistory } from '../store/history'; import { DEFAULT_HD_PATH, ownershipWalletFromTicket, walletFromMnemonic, } from '../lib/wallet'; import { BRIDGE_ERROR } from '../lib/error'; import { INVITE_STAGES, WALLET_STATES, TRANSACTION_STATES, } from '../lib/invite'; import { generateWallet, startTransactions } from '../lib/invite'; import { withNetwork } from '../store/network'; import { withWallet } from '../store/wallet'; import Grid from 'components/Grid'; class InviteTicket extends React.Component { constructor(props) { super(props); this.state = { inviteTicket: '', inviteWallet: Nothing(), verifyTicket: '', realTicket: '', realPoint: Nothing(), realWallet: Nothing(), walletReady: false, walletStates: [], errors: [], stage: INVITE_STAGES.INVITE_LOGIN, transactionProgress: TRANSACTION_STATES.GENERATING, }; this.pointPlaceholder = randomPatq(4); this.ticketPlaceholder = randomPatq(12); this.handleInviteTicketInput = this.handleInviteTicketInput.bind(this); this.handleVerifyTicketInput = this.handleVerifyTicketInput.bind(this); this.updateProgress = this.updateProgress.bind(this); this.pushWalletState = this.pushWalletState.bind(this); this.navigateLogin = this.navigateLogin.bind(this); } pushWalletState(walletState) { this.setState({ walletStates: this.state.walletStates.concat(walletState), }); } navigateLogin() { this.props.history.pop(); this.props.history.push(ROUTE_NAMES.NETWORK); } handleInviteTicketInput(inviteTicket) { this.setState({ inviteTicket }); } handleVerifyTicketInput(verifyTicket) { this.setState({ verifyTicket }); } updateTransactionProgress(transactionProgress) { this.setState({ transactionProgress }); } async unlockInviteWallet(inviteTicket) { this.setState({ walletStates: this.state.walletStates.concat(WALLET_STATES.UNLOCKING), stage: INVITE_STAGES.INVITE_WALLET, }); //NOTE ~zod because tmp wallet const ownership = await ownershipWalletFromTicket(inviteTicket, 0); const mnemonic = ownership.seed; //TODO isn't all this accessible in the ownership object? const inviteWallet = walletFromMnemonic( mnemonic, DEFAULT_HD_PATH ).matchWith({ Just: w => w.value, Nothing: () => { throw BRIDGE_ERROR.MISSING_WALLET; }, }); const addr = inviteWallet.address; const contracts = need.contracts(this.props.contracts); const incoming = await azimuth.azimuth.getTransferringFor(contracts, addr); this.setState({ walletStates: this.state.walletStates.concat(WALLET_STATES.GENERATING), }); let realPoint = Nothing(); let realWallet = Nothing(); let errors = []; if (incoming.length > 0) { let pointNum = parseInt(incoming[0], 10); realPoint = Just(pointNum); this.setState({ realPoint, }); let genWallet = await generateWallet(pointNum); realWallet = Just(genWallet); if (incoming.length > 1) { //TODO notify user "...and others / ticket reusable" } } else { errors = [ 'Invite code has no claimable ship. Check your invite code and try again.', ]; } this.setState({ realWallet, inviteWallet: Just(inviteWallet), // dev mode: uncomment this out to autofill verified ticket verifyTicket: !Nothing.hasInstance(realWallet) ? realWallet.value.ticket : '', walletStates: this.state.walletStates.concat(WALLET_STATES.RENDERING), errors, }); } getTicketElement() { const phTick = this.ticketPlaceholder; let isLogin = this.state.stage === INVITE_STAGES.INVITE_LOGIN; let isVerify = this.state.stage === INVITE_STAGES.INVITE_VERIFY; if (isLogin || isVerify) { let value = isLogin ? this.state.inviteTicket : this.state.verifyTicket; let handler = isLogin ? this.handleInviteTicketInput : this.handleVerifyTicketInput; let label = isLogin ? 'Activation code' : 'ticket'; // if realWallet is Just, then VerifyTicketInput appropriate contents; if Nothing, remain Nothing const Verified = this.state.realWallet.map(realWallet => VerifyTicketInput(realWallet.ticket) ); // Verified.getOrElse(TicketInput) falls back to TicketInput if Verified is Nothing const TicketElem = isVerify ? Verified.getOrElse(TicketInput) : TicketInput; return ( {label} ); } else { return null; } } getStageText() { const { stage } = this.state; let progressWidth = this.state.transactionProgress.pct; const stageText = stage === INVITE_STAGES.INVITE_LOGIN ? (

Welcome

) : stage === INVITE_STAGES.INVITE_WALLET ? (

Passport

A passport is your digital identity. You will use your passport to access your true computer, send payments, and administer your identity. So naturally, you must keep this secure.

After you’ve downloaded your passport, back up the ticket manually or store on a trusted device.

) : stage === INVITE_STAGES.INVITE_VERIFY ? (

Verify Passport

Afer you download your passport, verify your custody. Your passport should be a folder of image files. One of them is your Master Ticket. Open it and enter the 4 word phrase below (with hyphens).

) : INVITE_STAGES.INVITE_TRANSACTIONS ? ( this.state.transactionProgress.label === TRANSACTION_STATES.DONE.label ? (

Success ✓

Transferring to Bridge...

) : (

Submitting

This step can take up to five minutes. Please do not leave this page until the transactions are complete.

{this.state.transactionProgress.label}
) ) : null; return stageText; } updateProgress(notification) { let newState = notification.type === 'progress' ? { transactionProgress: notification.value } : notification.type === 'notify' ? { errors: [notification.value] } : {}; this.setState(newState); } getContinueButton() { const { realPoint, realWallet, inviteWallet, stage, walletStates, verifyTicket, inviteTicket, } = this.state; const { web3, contracts, setUrbitWallet } = this.props; const btnColor = this.state.walletStates.includes(WALLET_STATES.PAPER_READY) ? 'green' : 'black'; const clickHandler = stage === INVITE_STAGES.INVITE_LOGIN ? () => { this.unlockInviteWallet(inviteTicket); } : stage === INVITE_STAGES.INVITE_WALLET ? () => { this.setState({ stage: INVITE_STAGES.INVITE_VERIFY, }); } : stage === INVITE_STAGES.INVITE_VERIFY ? () => { this.setState({ stage: INVITE_STAGES.INVITE_TRANSACTIONS, walletStates: this.state.walletStates.concat( WALLET_STATES.TRANSACTIONS ), }); startTransactions({ inviteWalletM: inviteWallet, realWalletM: realWallet, realPointM: realPoint, realTicket: verifyTicket, web3, contracts, setUrbitWallet, updateProgress: this.updateProgress, }).then(() => { setTimeout(() => { this.props.history.pop(); }, 5000); }); } : null; const ticketMatches = this.state.realWallet.matchWith({ Just: wal => wal.value.ticket === this.state.verifyTicket, Nothing: () => false, }); const continueReady = stage === INVITE_STAGES.INVITE_WALLET ? walletStates.includes(WALLET_STATES.DOWNLOADED) : stage === INVITE_STAGES.INVITE_VERIFY ? ticketMatches : true; const buttonElem = stage === INVITE_STAGES.INVITE_TRANSACTIONS ? null : ( ); return buttonElem; } getStageDisplay() { const { stage } = this.state; if (stage === INVITE_STAGES.INVITE_LOGIN) return null; return (
1 Passport 2 Verify 3 Invite
); } getErrors() { if (this.state.errors.length === 0) return null; let errorElems = this.state.errors.map(e => {e}); return (

{'Warning'}

{errorElems}
); } render() { let stageDisplay = this.getStageDisplay(); let stageText = this.getStageText(); let ticketElem = this.getTicketElement(); let continueButton = this.getContinueButton(); let errorDisplay = this.getErrors(); return ( {stageDisplay} {errorDisplay} {stageText} {ticketElem} {continueButton} {this.state.stage === INVITE_STAGES.INVITE_LOGIN && ( )} ); } } export default compose( withNetwork, withHistory, withWallet )(InviteTicket);