on master help sign in
WIP: transfer point at end of Activate flow
commit1268c9fefa795a45023a1d4be3a319b68950f6b1 authortomholford <tomholford@users.noreply.github.com> dateSun 12 Sep 2021, 17:00 (5 years ago) parent5a30d53
19 files changed+350−109
src/form/Inputs.js−6 src/form/validators.js−3 src/lib/constants.ts+1−1 src/lib/useRoller.ts+41 src/lib/validators.ts−6 src/style/fadeable.scss+2 src/style/master-key.scss+2−2 src/views/Activate/ActivateButton.tsx+7−2 src/views/Activate/ActivateCode.tsx+1−1 src/views/Activate/ActivateCodeForm.tsx+7 src/views/Activate/ActivateHeader.tsx+3−3 src/views/Activate/ActivateMasterKey.tsx+2−18 src/views/Activate/ActivateSteps.tsx+9−7 src/views/Activate/MasterKeyConfirm.tsx+7−19 src/views/Activate/MasterKeyDownload.tsx+11−13 src/views/Activate/MasterKeyReveal.tsx+1−1 src/views/Activate/MasterKeyTransfer.tsx+212 src/views/Activate/PointPresenter.tsx+22−14 src/views/Activate/useActivateFlowState.js+22−13
src/form/Inputs.js−6
@@ -210,12 +210,6 @@ export function EmailInput({ ...rest }) {
210210 );
211211}
212212
213−export function LegacyTicketSegmentInput({ ...rest }) {
214− return (
215− <Input autoCapitalize="none" autoCorrect="off" type="text" {...rest} />
216− );
217−}
218−
219213export function TicketSegmentInput({ name, ...rest }) {
220214 const { input } = useField(name);
221215
src/form/validators.js−3
@@ -20,7 +20,6 @@ import {
2020 validateLength,
2121 validateUnique,
2222 validatePsbt,
23− validateSegmentedTicket,
2423} from 'lib/validators';
2524import { isPromise } from 'lib/isPromise';
2625
@@ -63,8 +62,6 @@ export const buildArrayValidator = validator => values => {
6362export const hasErrors = iter =>
6463 some(iter, v => (Array.isArray(v) ? hasErrors(v) : v !== undefined));
6564
66−export const buildSegmentedTicketValidator = (validators = []) =>
67− buildValidator([validateNotEmpty, validateSegmentedTicket, ...validators]);
6865export const buildPatqValidator = (validators = []) =>
6966 buildValidator([validateNotEmpty, validatePatq, ...validators]);
7067export const buildShardValidator = (validators = []) =>
src/lib/constants.ts+1−1
@@ -122,7 +122,7 @@ const POINT_PROXIES = {
122122
123123// In ms
124124const DEFAULT_FADE_TIMEOUT = 300;
125−const MASTER_KEY_DURATION = 1900;
125+const MASTER_KEY_DURATION = 850;
126126
127127export {
128128 CHECK_BLOCK_EVERY_MS,
src/lib/useRoller.ts+41
@@ -22,6 +22,8 @@ import {
2222 Options,
2323 EthAddress,
2424 UnspawnedPoints,
25+ From,
26+ Hash,
2527} from '@urbit/roller-api';
2628
2729import { isDevelopment, isRopsten } from './flags';
@@ -464,6 +466,44 @@ export default function useRoller() {
464466 ]
465467 );
466468
469+ interface TransferPointParams {
470+ point: Ship;
471+ from: From;
472+ to: EthAddress;
473+ fromAddress: EthAddress;
474+ signingKey: string;
475+ }
476+
477+ const transferPoint = async ({
478+ point,
479+ from,
480+ to,
481+ fromAddress,
482+ signingKey,
483+ }: TransferPointParams) => {
484+ const pointInfo = await api.getPoint(point);
485+
486+ const { nonce } = getProxyAndNonce(pointInfo, fromAddress);
487+ if (nonce === undefined) {
488+ throw new Error('Nonce unavailable');
489+ }
490+ const transferData = { address: fromAddress, reset: true };
491+ const transferHash: Hash = await api.hashTransaction(
492+ nonce,
493+ from,
494+ 'transferPoint',
495+ transferData
496+ );
497+
498+ const sig = signTransactionHash(
499+ transferHash,
500+ Buffer.from(signingKey, 'hex')
501+ );
502+ const transferTxHash = await api.transferPoint(sig, from, to, transferData);
503+
504+ return transferTxHash;
505+ };
506+
467507 // On load, get initial config
468508 useEffect(() => {
469509 if (config) {
@@ -497,5 +537,6 @@ export default function useRoller() {
497537 getInvites,
498538 getPendingTransactions,
499539 generateInviteCodes,
540+ transferPoint,
500541 };
501542}
src/lib/validators.ts−6
@@ -12,7 +12,6 @@ import {
1212 MIN_GALAXY,
1313 MAX_GALAXY,
1414} from './constants';
15−import { ticketFromSegments } from 'form/formatters';
1615
1716// NOTE: do not use the /g modifier for these regexes
1817// https://stackoverflow.com/a/21373261
@@ -147,8 +146,3 @@ export const validatePsbt = base64 => {
147146 return 'Invalid Partially Signed Bitcoin Transaction';
148147 }
149148};
150−
151−export const validateSegmentedTicket = (segments: string[]) => {
152− const ticket = ticketFromSegments(segments);
153− return validatePatq(ticket);
154−};
src/style/fadeable.scss+2
@@ -1,3 +1,5 @@
1+// These are consumed by the CSSTransition component in withFadeable
2+
13.fadeable-enter {
24 opacity: 0;
35}
src/style/master-key.scss+2−2
@@ -7,7 +7,7 @@
77 }
88}
99
10−@mixin generate-progress-fill($numSegments, $duration: 300ms, $delayDelta: 100ms) {
10+@mixin generate-progress-fill($numSegments, $duration: 150ms, $delayDelta: 50ms) {
1111 @for $i from 1 through $numSegments {
1212 .segment-#{$i} {
1313 animation: opacity-fill $duration linear forwards;
@@ -23,7 +23,7 @@ svg.master-key {
2323 }
2424
2525 // Animations
26− @include generate-progress-fill(18, 300ms, 100ms);
26+ @include generate-progress-fill(18);
2727
2828 &.paused g {
2929 animation-play-state: paused;
src/views/Activate/ActivateButton.tsx+7−2
@@ -6,24 +6,29 @@ interface ActivateButtonProps {
66 children: React.ReactNode | string;
77 onClick: MouseEventHandler;
88 disabled?: boolean;
9+ success?: boolean;
910}
1011
1112const ActivateButton = ({
1213 children,
1314 onClick,
14− disabled,
15+ disabled = false,
16+ success = false,
1517 ...rest
1618}: ActivateButtonProps & React.ComponentPropsWithoutRef<'button'>) => {
19+ const backgroundColor = success ? '#009F65' : 'black';
20+
1721 return (
1822 <Button
1923 onClick={onClick}
20− backgroundColor={disabled ? 'rgba(0,0,0,0.2)' : 'black'}
24+ backgroundColor={backgroundColor}
2125 color={'white'}
2226 padding={'16px'}
2327 fontFamily="Inter"
2428 height={'50px'}
2529 fontWeight={'400'}
2630 fontSize={'18px'}
31+ opacity={disabled ? 0.2 : 1.0}
2732 {...rest}>
2833 {children}
2934 </Button>
src/views/Activate/ActivateCode.tsx+1−1
@@ -42,7 +42,7 @@ export default function ActivateCode() {
4242 return (
4343 <View inset>
4444 <ActivateView
45− header={<ActivateHeader copy={'Welcome. This is your Urbit.'} />}
45+ header={<ActivateHeader content={'Welcome. This is your Urbit.'} />}
4646 footer={<ActivateCodeForm afterSubmit={goToMasterKey} />}>
4747 <Box
4848 alignItems={'center'}
src/views/Activate/ActivateCodeForm.tsx+7
@@ -1,5 +1,6 @@
11import * as azimuth from 'azimuth-js';
22import * as need from 'lib/need';
3+import * as ob from 'urbit-ob';
34import { WARNING } from 'form/helpers';
45import { FORM_ERROR } from 'final-form';
56import { Just } from 'folktale/maybe';
@@ -44,7 +45,9 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
4445 const didWarn = useRef<boolean>(false);
4546
4647 const {
48+ setDerivedPatp,
4749 setDerivedPoint,
50+ setDerivedPointDominion,
4851 setDerivedWallet,
4952 setInviteWallet,
5053 setIsIn,
@@ -141,7 +144,9 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
141144
142145 const point = convertToInt(incoming[0], 10);
143146
147+ setDerivedPatp(Just(ob.patp(point)));
144148 setDerivedPoint(Just(point));
149+ setDerivedPointDominion(Just(rollerPoint.dominion));
145150 setDerivedWallet(Just(await generateWallet(point, ticket, true)));
146151 setIsIn(false);
147152 await timeout(100);
@@ -159,7 +164,9 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
159164 impliedTicket,
160165 loadL1Points,
161166 loadl2points,
167+ setDerivedPatp,
162168 setDerivedPoint,
169+ setDerivedPointDominion,
163170 setDerivedWallet,
164171 setInviteWallet,
165172 setIsIn,
src/views/Activate/ActivateHeader.tsx+3−3
@@ -2,12 +2,12 @@ import { Box, Text } from '@tlon/indigo-react';
22import withFadeable from './withFadeable';
33
44type ActivateHeaderProps = {
5− copy: string;
5+ content: string | React.ReactNode;
66 fadeTimeout?: number;
77 isInOverride?: boolean;
88};
99
10−const ActivateHeader = ({ copy }: ActivateHeaderProps) => {
10+const ActivateHeader = ({ content }: ActivateHeaderProps) => {
1111 return (
1212 <Box>
1313 <Text
@@ -18,7 +18,7 @@ const ActivateHeader = ({ copy }: ActivateHeaderProps) => {
1818 mx={3}
1919 my={3}
2020 textAlign="center">
21− {copy}
21+ {content}
2222 </Text>
2323 </Box>
2424 );
src/views/Activate/ActivateMasterKey.tsx+2−18
@@ -5,7 +5,7 @@ import View from 'components/View';
55import useRouter from 'lib/useRouter';
66
77import MasterKeyDownload from './MasterKeyDownload';
8−import PassportTransfer from './PassportTransfer';
8+import MasterKeyTransfer from './MasterKeyTransfer';
99import MasterKeyReveal from './MasterKeyReveal';
1010import MasterKeyConfirm from './MasterKeyConfirm';
1111
@@ -20,7 +20,7 @@ const VIEWS = {
2020 [NAMES.REVEAL]: MasterKeyReveal,
2121 [NAMES.DOWNLOAD]: MasterKeyDownload,
2222 [NAMES.CONFIRM]: MasterKeyConfirm,
23− [NAMES.TRANSFER]: PassportTransfer,
23+ [NAMES.TRANSFER]: MasterKeyTransfer,
2424};
2525
2626export default function ActivateMasterKey() {
@@ -32,22 +32,6 @@ export default function ActivateMasterKey() {
3232 initialRoutes: [{ key: NAMES.REVEAL }],
3333 });
3434
35− // TODO
36− // addresses are not derived until we set[Urbit]Wallet(), so do that inline
37− // const address = useMemo(
38− // () =>
39− // derivedWallet
40− // .chain(wallet =>
41− // walletFromMnemonic(
42− // wallet.ownership.seed,
43− // DEFAULT_HD_PATH,
44− // wallet.meta.passphrase
45− // )
46− // )
47− // .map(wallet => publicToAddress(wallet.publicKey)),
48− // [derivedWallet]
49− // );
50−
5135 return (
5236 <View inset>
5337 <LocalRouterProvider value={router}>
src/views/Activate/ActivateSteps.tsx+9−7
@@ -1,4 +1,4 @@
1−import { Box } from '@tlon/indigo-react';
1+import { Box, SegmentedProgressBar } from '@tlon/indigo-react';
22import withFadeable from './withFadeable';
33
44type ActivateStepsProps = {
@@ -19,15 +19,15 @@ export const ActivateSteps = ({
1919 const isLastStep = i === totalSteps - 1;
2020 steps.push(
2121 <Box
22− borderTopLeftRadius={isFirstStep ? '5px' : null}
23− borderBottomLeftRadius={isFirstStep ? '5px' : null}
24− borderTopRightRadius={isLastStep ? '5px' : null}
25− borderBottomRightRadius={isLastStep ? '5px' : null}
22+ borderTopLeftRadius={isFirstStep ? '3px' : null}
23+ borderBottomLeftRadius={isFirstStep ? '3px' : null}
24+ borderTopRightRadius={isLastStep ? '3px' : null}
25+ borderBottomRightRadius={isLastStep ? '3px' : null}
2626 key={i}
2727 backgroundColor={i <= currentStep ? 'black' : 'gray'}
28− height={'10px'}
28+ height={'4px'}
2929 width={`${100 / totalSteps}%`}
30− marginLeft={isFirstStep ? null : '1px'}
30+ marginLeft={'0px'}
3131 marginRight={isLastStep ? null : '1px'}></Box>
3232 );
3333 }
@@ -41,6 +41,8 @@ export const ActivateSteps = ({
4141 flexWrap={'nowrap'}
4242 width={'100%'}
4343 className={className}>
44+ {/* TODO: Ask Jimmy: */}
45+ {/* <SegmentedProgressBar current={currentStep} segments={totalSteps} /> */}
4446 {renderSteps()}
4547 </Box>
4648 );
src/views/Activate/MasterKeyConfirm.tsx+7−19
@@ -18,10 +18,7 @@ import { validateExactly } from 'lib/validators';
1818import BridgeForm from 'form/BridgeForm';
1919import FormError from 'form/FormError';
2020import useFadeIn from './useFadeIn';
21−import {
22− HiddenInput,
23− TicketSegmentInput,
24−} from 'form/Inputs';
21+import { HiddenInput, TicketSegmentInput } from 'form/Inputs';
2522
2623const MasterKeyConfirm = () => {
2724 const { derivedWallet, setIsIn } = useActivateFlow();
@@ -31,21 +28,12 @@ const MasterKeyConfirm = () => {
3128 const ticketSegments = useMemo(() => ticketToSegments(ticket), [ticket]);
3229
3330 const onValues = useCallback(({ valid, values, form }) => {
34− console.log('valid:', valid);
35− console.log('values:', values);
36−
3731 const { ticket0, ticket1, ticket2, ticket3 } = values;
3832
39− const updatedTicket = ticketFromSegments([
40− ticket0,
41− ticket1,
42− ticket2,
43− ticket3,
44− ]);
45−
46− console.log(updatedTicket);
47−
48− form.change('ticket', updatedTicket);
33+ form.change(
34+ 'ticket',
35+ ticketFromSegments([ticket0, ticket1, ticket2, ticket3])
36+ );
4937 }, []);
5038
5139 const validate = useMemo(
@@ -64,7 +52,7 @@ const MasterKeyConfirm = () => {
6452 ticket0: STUB_VERIFY_TICKET ? ticketSegments[0] : undefined,
6553 ticket1: STUB_VERIFY_TICKET ? ticketSegments[1] : undefined,
6654 ticket2: STUB_VERIFY_TICKET ? ticketSegments[2] : undefined,
67− ticket3: undefined,
55+ ticket3: STUB_VERIFY_TICKET ? ticketSegments[3] : undefined,
6856 }),
6957 [STUB_VERIFY_TICKET, ticket, ticketSegments]
7058 );
@@ -78,7 +66,7 @@ const MasterKeyConfirm = () => {
7866 const header = useMemo(() => {
7967 return (
8068 <Box>
81− <ActivateHeader copy={'Confirm Backup'} />
69+ <ActivateHeader content={'Confirm Backup'} />
8270 <ActivateParagraph
8371 copy={
8472 'Confirm that you have stored your Master Key somewhere safe by entering it below'
src/views/Activate/MasterKeyDownload.tsx+11−13
@@ -17,7 +17,7 @@ import { useLocalRouter } from 'lib/LocalRouter';
1717import { compileNetworkingKey } from 'lib/keys';
1818import { downloadWallet } from 'lib/invite';
1919import PaperBuilder from 'components/PaperBuilder';
20−import { DEFAULT_FADE_TIMEOUT } from 'lib/constants';
20+import { DEFAULT_FADE_TIMEOUT, MASTER_KEY_DURATION } from 'lib/constants';
2121import { timeout } from 'lib/timeout';
2222
2323const MasterKeyDownload = () => {
@@ -42,11 +42,11 @@ const MasterKeyDownload = () => {
4242 Just: p => p.value.toFixed(),
4343 });
4444
45− const download = useCallback(() => {
45+ const download = useCallback(async () => {
4646 const netkey = compileNetworkingKey(wallet.network.keys, point, 1);
4747 //TODO could be deduplicated with useKeyfileGenerator's logic
4848 const filename = ob.patp(point).slice(1) + '-1.key';
49− downloadWallet(paper.getOrElse([]), netkey, filename);
49+ await downloadWallet(paper.getOrElse([]), netkey, filename);
5050 setDownloaded(true);
5151 }, [paper, wallet, point, setDownloaded]);
5252
@@ -62,16 +62,17 @@ const MasterKeyDownload = () => {
6262 [paper, setGenerated]
6363 );
6464
65− const goToConfirm = useCallback(async () => {
65+ const onDownloadClick = useCallback(async () => {
66+ await download();
6667 setIsIn(false);
6768 await timeout(DEFAULT_FADE_TIMEOUT); // Pause for UI fade animation
6869 push(names.CONFIRM);
69− }, [setIsIn, push, names.CONFIRM]);
70+ }, [download, setIsIn, push, names.CONFIRM]);
7071
7172 const header = useMemo(() => {
7273 return triggerAnimation ? (
7374 <Box>
74− <ActivateHeader copy={'Backup your Master Key.'} />
75+ <ActivateHeader content={'Backup your Master Key.'} />
7576 <ActivateParagraph
7677 copy={
7778 'Download your backup and store it somewhere safe, e.g. your security deposit box or password manager.'
@@ -83,20 +84,17 @@ const MasterKeyDownload = () => {
8384
8485 const footer = useMemo(() => {
8586 return triggerAnimation ? (
86− <ActivateButton
87− disabled={!generated}
88− onClick={!downloaded ? download : goToConfirm}
89− success={downloaded}>
90− {!downloaded ? 'Download Backup (Passport)' : 'Continue'}
87+ <ActivateButton onClick={onDownloadClick}>
88+ {'Download Backup (Passport)'}
9189 </ActivateButton>
9290 ) : null;
93− }, [download, downloaded, generated, goToConfirm, triggerAnimation]);
91+ }, [onDownloadClick, triggerAnimation]);
9492
9593 const delayedReveal = useCallback(async () => {
9694 setTimeout(() => {
9795 setTriggerAnimation(true);
9896 setIsIn(true);
99− }, 800);
97+ }, MASTER_KEY_DURATION);
10098 }, [setIsIn]);
10199
102100 useEffect(() => {
src/views/Activate/MasterKeyReveal.tsx+1−1
@@ -30,7 +30,7 @@ const MasterKeyReveal = () => {
3030 const header = useMemo(() => {
3131 return triggerAnimation ? (
3232 <Box>
33− <ActivateHeader copy={'Here is your Master Key.'} />
33+ <ActivateHeader content={'Here is your Master Key.'} />
3434 <ActivateParagraph
3535 copy={
3636 "Your Master Key is your 4-word password for your Urbit. Make sure you're in a private place before you reveal it."
src/views/Activate/MasterKeyTransfer.tsxnew file+212
@@ -0,0 +1,212 @@
1+import React, { useState, useCallback, useMemo, useEffect } from 'react';
2+
3+import { useNetwork } from 'store/network';
4+import { useWallet } from 'store/wallet';
5+import { useHistory } from 'store/history';
6+import { usePointCursor } from 'store/pointCursor';
7+
8+import * as need from 'lib/need';
9+import useLifecycle from 'lib/useLifecycle';
10+import useBlockWindowClose from 'lib/useBlockWindowClose';
11+import { reticketPointBetweenWallets } from 'lib/reticket';
12+
13+import { Anchor, Box, Text } from '@tlon/indigo-react';
14+import { useActivateFlow } from './ActivateFlow';
15+import ActivateView from './ActivateView';
16+import { POINT_DOMINIONS } from 'lib/constants';
17+import { From } from '@urbit/roller-api';
18+import useRoller from 'lib/useRoller';
19+import { FadeableActivateHeader as ActivateHeader } from './ActivateHeader';
20+import { FadeableActivateButton as ActivateButton } from './ActivateButton';
21+import useFadeIn from './useFadeIn';
22+import PointPresenter from './PointPresenter';
23+
24+const MasterKeyTransfer = ({ className, resetActivateRouter }) => {
25+ const { replaceWith, names } = useHistory();
26+ const { setUrbitWallet } = useWallet();
27+ const { setPointCursor } = usePointCursor();
28+ const { web3, contracts, networkType } = useNetwork();
29+ const { api, transferPoint } = useRoller();
30+ const {
31+ derivedPatp,
32+ derivedPoint,
33+ derivedPointDominion,
34+ derivedWallet,
35+ inviteWallet,
36+ reset: resetActivateFlow,
37+ } = useActivateFlow();
38+ const [generalError, setGeneralError] = useState();
39+ const [progress, setProcesss] = useState(0);
40+ const [needFunds, setNeedFunds] = useState();
41+
42+ // const [transferTxHash, setTransferTxHash] = useState<string | null>(null);
43+
44+ const _inviteWallet = need.wallet(inviteWallet);
45+ const _wallet = need.wallet(derivedWallet);
46+ const _point = need.point(derivedPoint);
47+ const _web3 = need.web3(web3);
48+ const _contracts = need.contracts(contracts);
49+
50+ const goToLogin = useCallback(
51+ () => replaceWith([{ key: names.LOGIN }, { key: names.POINT }]),
52+ [replaceWith, names]
53+ );
54+
55+ const goToRestart = useCallback(() => {
56+ // NOTE: because we're already on the ACTIVATE view in the history,
57+ // react (intelligently) doesn't trigger a re-render and that means the
58+ // current state of the two nested routers (activate & passport) stick
59+ // around unless we manually clear them.
60+ // we only need to clear activate because that will unmount the passport
61+ // router (which will be set to initialRoutes when mounted again)
62+
63+ // 2) reset local router
64+ resetActivateRouter();
65+ // 3) clear the state
66+ resetActivateFlow();
67+ }, [resetActivateRouter, resetActivateFlow]);
68+
69+ const claimL2Point = useCallback(async () => {
70+ console.log('claiming l2 point:');
71+ const transferFrom: From = {
72+ ship: derivedPatp.value,
73+ proxy: 'transfer',
74+ };
75+
76+ const l2point = await api.getPoint(derivedPoint.value);
77+ const fromAddress = l2point.ownership?.owner?.address!;
78+ const txHash = await transferPoint({
79+ point: derivedPoint.value,
80+ from: transferFrom,
81+ to: _inviteWallet.address,
82+ fromAddress: fromAddress,
83+ signingKey: derivedWallet.value.ownership.keys.private,
84+ });
85+
86+ // setTransferTxHash(txHash);
87+ }, [
88+ _inviteWallet.address,
89+ api,
90+ derivedPatp.value,
91+ derivedPoint.value,
92+ derivedWallet.value,
93+ transferPoint,
94+ ]);
95+
96+ // const claimL1Point = useCallback(async () => {
97+ // await reticketPointBetweenWallets({
98+ // fromWallet: _inviteWallet,
99+ // toWallet: _wallet,
100+ // point: _point,
101+ // web3: _web3,
102+ // contracts: _contracts,
103+ // networkType,
104+ // onUpdate: handleUpdate,
105+ // transferEth: true,
106+ // //NOTE not passing wc function, we always have invite ticket wallet
107+ // });
108+ // }, [_contracts, _inviteWallet, _point, _wallet, _web3, networkType]);
109+
110+ const claimPoint = useCallback(async () => {
111+ setGeneralError(false);
112+
113+ try {
114+ // If L2
115+ if (derivedPointDominion.value === POINT_DOMINIONS.L2) {
116+ await claimL2Point();
117+ } else {
118+ // await claimL1Point();
119+ console.log('do nothing for now');
120+ }
121+
122+ // If L1
123+
124+ // set the global wallet
125+ // setUrbitWallet(derivedWallet);
126+ // setPointCursor(derivedPoint);
127+
128+ // and redirect to login
129+ // await timeout(3000);
130+ // goToLogin();
131+ } catch (error) {
132+ console.error(error);
133+ // some generic error
134+ setGeneralError(error);
135+ }
136+ }, [derivedPointDominion, claimL2Point]);
137+
138+ // useBlockWindowClose();
139+ useFadeIn();
140+
141+ useLifecycle(() => {
142+ claimPoint();
143+ });
144+
145+ const header = useMemo(() => {
146+ return (
147+ <ActivateHeader
148+ content={
149+ <>
150+ Congratulations{' '}
151+ <Text fontFamily="Source Code Pro" fontSize={3} fontWeight={600}>
152+ {derivedPatp.value}
153+ </Text>{' '}
154+ is now yours
155+ </>
156+ }
157+ />
158+ );
159+ }, [derivedPatp.value]);
160+
161+ const footer = useMemo(() => {
162+ return (
163+ <Box
164+ display={'flex'}
165+ flexDirection={'column'}
166+ flexWrap={'nowrap'}
167+ justifyContent={'space-between'}>
168+ <ActivateButton
169+ onClick={() =>
170+ window.open(
171+ 'https://github.com/urbit/port/releases/latest/download/Port.dmg'
172+ )
173+ }
174+ success={true}>
175+ Download the Client for Mac
176+ </ActivateButton>
177+ <Anchor
178+ href="https://urbit.org/getting-started/cli"
179+ marginTop={'20px'}
180+ marginBottom={'10px'}
181+ underline={false}
182+ fontFamily={'Inter'}
183+ fontSize={'14px'}
184+ fontWeight={'500'}
185+ lineHeight={'16px'}
186+ textAlign={'center'}>
187+ or setup via command line
188+ </Anchor>
189+ </Box>
190+ );
191+ }, []);
192+
193+ return (
194+ <ActivateView header={header} footer={footer}>
195+ <Box
196+ alignItems={'center'}
197+ display={'flex'}
198+ flexDirection={'column'}
199+ flexWrap={'nowrap'}
200+ height={'100%'}
201+ justifyContent={'center'}>
202+ <PointPresenter
203+ patp={derivedPatp.value}
204+ showLabel={false}
205+ success={true}
206+ />
207+ </Box>
208+ </ActivateView>
209+ );
210+};
211+
212+export default MasterKeyTransfer;
src/views/Activate/PointPresenter.tsx+22−14
@@ -5,9 +5,16 @@ import withFadeable from './withFadeable';
55type PointPresenterArgs = {
66 patp: string;
77 className?: string;
8+ showLabel?: boolean;
9+ success?: boolean;
810};
911
10−const PointPresenter = ({ patp, className }: PointPresenterArgs) => {
12+const PointPresenter = ({
13+ patp,
14+ className,
15+ showLabel = true,
16+ success = false,
17+}: PointPresenterArgs) => {
1118 return patp ? (
1219 <Box
1320 width={256}
@@ -20,31 +27,32 @@ const PointPresenter = ({ patp, className }: PointPresenterArgs) => {
2027 background="white"
2128 className={className}>
2229 <Box
23− background="black"
30+ background={success ? '#009F65' : 'black'}
2431 borderRadius={5}
25− padding={32}
32+ padding={24}
2633 mb={3}
2734 width={64}
2835 height={64}>
2936 <Sigil
3037 patp={patp}
38+ colors={[success ? '#009F65' : 'black', 'white']}
3139 size={64}
3240 display="block"
33− colors={['black', 'white']}
34− icon
3541 padding={32}
3642 borderRadius={5}
3743 />
3844 </Box>
39− <Box>
40− <Text
41− display="block"
42− fontFamily={'Source Code Pro'}
43− fontSize={'18px'}
44− textAlign={'center'}>
45− {patp}
46− </Text>
47− </Box>
45+ {showLabel && (
46+ <Box>
47+ <Text
48+ display="block"
49+ fontFamily={'Source Code Pro'}
50+ fontSize={'18px'}
51+ textAlign={'center'}>
52+ {patp}
53+ </Text>
54+ </Box>
55+ )}
4856 </Box>
4957 ) : null;
5058};
src/views/Activate/useActivateFlowState.js+22−13
@@ -2,40 +2,49 @@ import { useCallback, useState } from 'react';
22import { Nothing } from 'folktale/maybe';
33
44export default function useActivateFlowState() {
5− const [derivedWallet, setDerivedWallet] = useState(Nothing());
6− const [inviteWallet, setInviteWallet] = useState(Nothing());
5+ const [derivedPatp, setDerivedPatp] = useState(Nothing());
76 const [derivedPoint, setDerivedPoint] = useState(Nothing());
7+ const [derivedPointDominion, setDerivedPointDominion] = useState(Nothing());
8+ const [derivedWallet, setDerivedWallet] = useState(Nothing());
89 const [generated, setGenerated] = useState(false);
910 const [incomingPoints, setIncomingPoints] = useState(Nothing());
11+ const [inviteWallet, setInviteWallet] = useState(Nothing());
1012 const [isIn, setIsIn] = useState(false);
1113
1214 const reset = useCallback(() => {
13− setDerivedWallet(Nothing());
14− setInviteWallet(Nothing());
15+ setDerivedPatp(Nothing());
1516 setDerivedPoint(Nothing());
17+ setDerivedPointDominion(Nothing());
18+ setDerivedWallet(Nothing());
1619 setGenerated(false);
1720 setIncomingPoints(Nothing());
21+ setInviteWallet(Nothing());
1822 }, [
19− setDerivedWallet,
20− setInviteWallet,
2123 setDerivedPoint,
24+ setDerivedPointDominion,
25+ setDerivedWallet,
2226 setGenerated,
2327 setIncomingPoints,
28+ setInviteWallet,
2429 ]);
2530
2631 return {
32+ derivedPatp,
33+ derivedPoint,
34+ derivedPointDominion,
2735 derivedWallet,
28− setDerivedWallet,
36+ generated,
37+ incomingPoints,
2938 inviteWallet,
30− setInviteWallet,
31− derivedPoint,
39+ isIn,
40+ reset,
41+ setDerivedPatp,
3242 setDerivedPoint,
33− generated,
43+ setDerivedPointDominion,
44+ setDerivedWallet,
3445 setGenerated,
35− incomingPoints,
3646 setIncomingPoints,
37− isIn,
47+ setInviteWallet,
3848 setIsIn,
39− reset,
4049 };
4150}