import React, { useCallback, useMemo, useRef } from 'react'; import { Just } from 'folktale/maybe'; import cn from 'classnames'; import * as azimuth from 'azimuth-js'; import * as kg from 'urbit-key-generation'; import { Grid, CheckboxInput, Flex, ToggleInput, AccessoryIcon, } from 'indigo-react'; import { FORM_ERROR } from 'final-form'; import { useNetwork } from 'store/network'; import { useWallet } from 'store/wallet'; import { usePointCursor } from 'store/pointCursor'; import * as need from 'lib/need'; import { WALLET_TYPES, urbitWalletFromTicket } from 'lib/wallet'; import useImpliedPoint from 'lib/useImpliedPoint'; import useLoginView from 'lib/useLoginView'; import patp2dec from 'lib/patp2dec'; import timeout from 'lib/timeout'; import BridgeForm from 'form/BridgeForm'; import Condition from 'form/Condition'; import { TicketInput, PassphraseInput, PointInput } from 'form/Inputs'; import { composeValidator, buildCheckboxValidator, buildPatqValidator, buildShardValidator, buildPassphraseValidator, buildPointValidator, } from 'form/validators'; import FormError from 'form/FormError'; import SubmitButton from 'form/SubmitButton'; import { WARNING } from 'form/helpers'; import { ReactComponent as SecretShowIcon } from 'assets/secret-show.svg'; import { ReactComponent as SecretHideIcon } from 'assets/secret-hidden.svg'; function AdvancedOptions() { return ( ); } function TicketInputAccessory({ name }) { return ( } label={} /> ); } export default function Ticket({ className, goHome }) { useLoginView(WALLET_TYPES.TICKET); const { contracts } = useNetwork(); const { setUrbitWallet } = useWallet(); const { setPointCursor } = usePointCursor(); const impliedPoint = useImpliedPoint(); const didWarn = useRef(false); const validateForm = useCallback((values, errors) => { didWarn.current = false; if (errors.point) { return errors; } if (values.useShards) { if (errors.shard1 || errors.shard2 || errors.shard3) { return errors; } const empty = ['shard1', 'shard2', 'shard3'].filter(s => !values[s]); if (empty.length > 1) { let errors = {}; empty.map(s => { errors[s] = 'Please provide at least two out of three shards.'; }); return errors; } } else { if (errors.ticket) { return errors; } } }, []); const onSubmit = useCallback( async values => { const ticket = values.useShards ? kg.combine( [values.shard1, values.shard2, values.shard3].map(v => v === '' ? undefined : v ) ) : values.ticket; try { const _contracts = need.contracts(contracts); const point = patp2dec(values.point); await timeout(16); // allow ui events to flush const urbitWallet = await urbitWalletFromTicket( ticket, point, values.passphrase ); const [isOwner, isTransferProxy] = await Promise.all([ azimuth.azimuth.isOwner( _contracts, point, urbitWallet.ownership.keys.address ), azimuth.azimuth.isTransferProxy( _contracts, point, urbitWallet.ownership.keys.address ), ]); const noPermissions = !isOwner && !isTransferProxy; // warn the user if (noPermissions && !didWarn.current) { didWarn.current = true; return { [WARNING]: 'This wallet is not the owner or transfer proxy for this point.', }; } setUrbitWallet(Just(urbitWallet)); setPointCursor(Just(patp2dec(values.point))); } catch (error) { console.error(error); return { [FORM_ERROR]: `Unable to derive wallet from ${ values.useShards ? 'shards' : 'ticket' }.`, }; } }, [contracts, setPointCursor, setUrbitWallet] ); const validate = useMemo( () => composeValidator( { useAdvanced: buildCheckboxValidator(), usePassphrase: buildCheckboxValidator(), useShards: buildCheckboxValidator(), point: buildPointValidator(4), ticket: buildPatqValidator(), shard1: buildShardValidator(), shard2: buildShardValidator(), shard3: buildShardValidator(), ticketHidden: buildCheckboxValidator(), passphrase: buildPassphraseValidator(), }, validateForm ), [validateForm] ); const initialValues = useMemo( () => ({ point: impliedPoint || '', usePasshrase: false, useShards: false, ticketHidden: true, useAdvanced: false, }), [impliedPoint] ); return ( {({ handleSubmit, values, submitting }) => ( <> } hidden={values.ticketHidden} labelAccessory={} className="mt3" name="shard1" label="Shard 1" /> } hidden={values.ticketHidden} className="mt3" name="shard2" label="Shard 2" /> } hidden={values.ticketHidden} name="shard3" label="Shard 3" /> {isWarning => submitting ? 'Logging in...' : isWarning ? 'Login Anyway' : 'Login' } )} ); }