on master help sign in
WIP: transfer point at end of Activate flow
1268c9ftomholford
src/form/Inputs.js−6
@@ -210,12 +210,6 @@ export function EmailInput({ ...rest }) {
210 );
210 );
211}
211}
212
212
213−export function LegacyTicketSegmentInput({ ...rest }) {
214− return (
215− <Input autoCapitalize="none" autoCorrect="off" type="text" {...rest} />
216− );
217−}
218−
219export function TicketSegmentInput({ name, ...rest }) {
213export function TicketSegmentInput({ name, ...rest }) {
220 const { input } = useField(name);
214 const { input } = useField(name);
221
215
src/form/validators.js−3
@@ -20,7 +20,6 @@ import {
20 validateLength,
20 validateLength,
21 validateUnique,
21 validateUnique,
22 validatePsbt,
22 validatePsbt,
23− validateSegmentedTicket,
24} from 'lib/validators';
23} from 'lib/validators';
25import { isPromise } from 'lib/isPromise';
24import { isPromise } from 'lib/isPromise';
26
25
@@ -63,8 +62,6 @@ export const buildArrayValidator = validator => values => {
63export const hasErrors = iter =>
62export const hasErrors = iter =>
64 some(iter, v => (Array.isArray(v) ? hasErrors(v) : v !== undefined));
63 some(iter, v => (Array.isArray(v) ? hasErrors(v) : v !== undefined));
65
64
66−export const buildSegmentedTicketValidator = (validators = []) =>
67− buildValidator([validateNotEmpty, validateSegmentedTicket, ...validators]);
68export const buildPatqValidator = (validators = []) =>
65export const buildPatqValidator = (validators = []) =>
69 buildValidator([validateNotEmpty, validatePatq, ...validators]);
66 buildValidator([validateNotEmpty, validatePatq, ...validators]);
70export const buildShardValidator = (validators = []) =>
67export const buildShardValidator = (validators = []) =>
src/lib/constants.ts+1−1
@@ -122,7 +122,7 @@ const POINT_PROXIES = {
122
122
123// In ms
123// In ms
124const DEFAULT_FADE_TIMEOUT = 300;
124const DEFAULT_FADE_TIMEOUT = 300;
125−const MASTER_KEY_DURATION = 1900;
125+const MASTER_KEY_DURATION = 850;
126
126
127export {
127export {
128 CHECK_BLOCK_EVERY_MS,
128 CHECK_BLOCK_EVERY_MS,
src/lib/useRoller.ts+41
@@ -22,6 +22,8 @@ import {
22 Options,
22 Options,
23 EthAddress,
23 EthAddress,
24 UnspawnedPoints,
24 UnspawnedPoints,
25+ From,
26+ Hash,
25} from '@urbit/roller-api';
27} from '@urbit/roller-api';
26
28
27import { isDevelopment, isRopsten } from './flags';
29import { isDevelopment, isRopsten } from './flags';
@@ -464,6 +466,44 @@ export default function useRoller() {
464 ]
466 ]
465 );
467 );
466
468
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+
467 // On load, get initial config
507 // On load, get initial config
468 useEffect(() => {
508 useEffect(() => {
469 if (config) {
509 if (config) {
@@ -497,5 +537,6 @@ export default function useRoller() {
497 getInvites,
537 getInvites,
498 getPendingTransactions,
538 getPendingTransactions,
499 generateInviteCodes,
539 generateInviteCodes,
540+ transferPoint,
500 };
541 };
501}
542}
src/lib/validators.ts−6
@@ -12,7 +12,6 @@ import {
12 MIN_GALAXY,
12 MIN_GALAXY,
13 MAX_GALAXY,
13 MAX_GALAXY,
14} from './constants';
14} from './constants';
15−import { ticketFromSegments } from 'form/formatters';
16
15
17// NOTE: do not use the /g modifier for these regexes
16// NOTE: do not use the /g modifier for these regexes
18// https://stackoverflow.com/a/21373261
17// https://stackoverflow.com/a/21373261
@@ -147,8 +146,3 @@ export const validatePsbt = base64 => {
147 return 'Invalid Partially Signed Bitcoin Transaction';
146 return 'Invalid Partially Signed Bitcoin Transaction';
148 }
147 }
149};
148};
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+
1.fadeable-enter {
3.fadeable-enter {
2 opacity: 0;
4 opacity: 0;
3}
5}
src/style/master-key.scss+2−2
@@ -7,7 +7,7 @@
7 }
7 }
8}
8}
9
9
10−@mixin generate-progress-fill($numSegments, $duration: 300ms, $delayDelta: 100ms) {
10+@mixin generate-progress-fill($numSegments, $duration: 150ms, $delayDelta: 50ms) {
11 @for $i from 1 through $numSegments {
11 @for $i from 1 through $numSegments {
12 .segment-#{$i} {
12 .segment-#{$i} {
13 animation: opacity-fill $duration linear forwards;
13 animation: opacity-fill $duration linear forwards;
@@ -23,7 +23,7 @@ svg.master-key {
23 }
23 }
24
24
25 // Animations
25 // Animations
26− @include generate-progress-fill(18, 300ms, 100ms);
26+ @include generate-progress-fill(18);
27
27
28 &.paused g {
28 &.paused g {
29 animation-play-state: paused;
29 animation-play-state: paused;
src/views/Activate/ActivateButton.tsx+7−2
@@ -6,24 +6,29 @@ interface ActivateButtonProps {
6 children: React.ReactNode | string;
6 children: React.ReactNode | string;
7 onClick: MouseEventHandler;
7 onClick: MouseEventHandler;
8 disabled?: boolean;
8 disabled?: boolean;
9+ success?: boolean;
9}
10}
10
11
11const ActivateButton = ({
12const ActivateButton = ({
12 children,
13 children,
13 onClick,
14 onClick,
14− disabled,
15+ disabled = false,
16+ success = false,
15 ...rest
17 ...rest
16}: ActivateButtonProps & React.ComponentPropsWithoutRef<'button'>) => {
18}: ActivateButtonProps & React.ComponentPropsWithoutRef<'button'>) => {
19+ const backgroundColor = success ? '#009F65' : 'black';
20+
17 return (
21 return (
18 <Button
22 <Button
19 onClick={onClick}
23 onClick={onClick}
20− backgroundColor={disabled ? 'rgba(0,0,0,0.2)' : 'black'}
24+ backgroundColor={backgroundColor}
21 color={'white'}
25 color={'white'}
22 padding={'16px'}
26 padding={'16px'}
23 fontFamily="Inter"
27 fontFamily="Inter"
24 height={'50px'}
28 height={'50px'}
25 fontWeight={'400'}
29 fontWeight={'400'}
26 fontSize={'18px'}
30 fontSize={'18px'}
31+ opacity={disabled ? 0.2 : 1.0}
27 {...rest}>
32 {...rest}>
28 {children}
33 {children}
29 </Button>
34 </Button>
src/views/Activate/ActivateCode.tsx+1−1
@@ -42,7 +42,7 @@ export default function ActivateCode() {
42 return (
42 return (
43 <View inset>
43 <View inset>
44 <ActivateView
44 <ActivateView
45− header={<ActivateHeader copy={'Welcome. This is your Urbit.'} />}
45+ header={<ActivateHeader content={'Welcome. This is your Urbit.'} />}
46 footer={<ActivateCodeForm afterSubmit={goToMasterKey} />}>
46 footer={<ActivateCodeForm afterSubmit={goToMasterKey} />}>
47 <Box
47 <Box
48 alignItems={'center'}
48 alignItems={'center'}
src/views/Activate/ActivateCodeForm.tsx+7
@@ -1,5 +1,6 @@
1import * as azimuth from 'azimuth-js';
1import * as azimuth from 'azimuth-js';
2import * as need from 'lib/need';
2import * as need from 'lib/need';
3+import * as ob from 'urbit-ob';
3import { WARNING } from 'form/helpers';
4import { WARNING } from 'form/helpers';
4import { FORM_ERROR } from 'final-form';
5import { FORM_ERROR } from 'final-form';
5import { Just } from 'folktale/maybe';
6import { Just } from 'folktale/maybe';
@@ -44,7 +45,9 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
44 const didWarn = useRef<boolean>(false);
45 const didWarn = useRef<boolean>(false);
45
46
46 const {
47 const {
48+ setDerivedPatp,
47 setDerivedPoint,
49 setDerivedPoint,
50+ setDerivedPointDominion,
48 setDerivedWallet,
51 setDerivedWallet,
49 setInviteWallet,
52 setInviteWallet,
50 setIsIn,
53 setIsIn,
@@ -141,7 +144,9 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
141
144
142 const point = convertToInt(incoming[0], 10);
145 const point = convertToInt(incoming[0], 10);
143
146
147+ setDerivedPatp(Just(ob.patp(point)));
144 setDerivedPoint(Just(point));
148 setDerivedPoint(Just(point));
149+ setDerivedPointDominion(Just(rollerPoint.dominion));
145 setDerivedWallet(Just(await generateWallet(point, ticket, true)));
150 setDerivedWallet(Just(await generateWallet(point, ticket, true)));
146 setIsIn(false);
151 setIsIn(false);
147 await timeout(100);
152 await timeout(100);
@@ -159,7 +164,9 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
159 impliedTicket,
164 impliedTicket,
160 loadL1Points,
165 loadL1Points,
161 loadl2points,
166 loadl2points,
167+ setDerivedPatp,
162 setDerivedPoint,
168 setDerivedPoint,
169+ setDerivedPointDominion,
163 setDerivedWallet,
170 setDerivedWallet,
164 setInviteWallet,
171 setInviteWallet,
165 setIsIn,
172 setIsIn,
src/views/Activate/ActivateHeader.tsx+3−3
@@ -2,12 +2,12 @@ import { Box, Text } from '@tlon/indigo-react';
2import withFadeable from './withFadeable';
2import withFadeable from './withFadeable';
3
3
4type ActivateHeaderProps = {
4type ActivateHeaderProps = {
5− copy: string;
5+ content: string | React.ReactNode;
6 fadeTimeout?: number;
6 fadeTimeout?: number;
7 isInOverride?: boolean;
7 isInOverride?: boolean;
8};
8};
9
9
10−const ActivateHeader = ({ copy }: ActivateHeaderProps) => {
10+const ActivateHeader = ({ content }: ActivateHeaderProps) => {
11 return (
11 return (
12 <Box>
12 <Box>
13 <Text
13 <Text
@@ -18,7 +18,7 @@ const ActivateHeader = ({ copy }: ActivateHeaderProps) => {
18 mx={3}
18 mx={3}
19 my={3}
19 my={3}
20 textAlign="center">
20 textAlign="center">
21− {copy}
21+ {content}
22 </Text>
22 </Text>
23 </Box>
23 </Box>
24 );
24 );
src/views/Activate/ActivateMasterKey.tsx+2−18
@@ -5,7 +5,7 @@ import View from 'components/View';
5import useRouter from 'lib/useRouter';
5import useRouter from 'lib/useRouter';
6
6
7import MasterKeyDownload from './MasterKeyDownload';
7import MasterKeyDownload from './MasterKeyDownload';
8−import PassportTransfer from './PassportTransfer';
8+import MasterKeyTransfer from './MasterKeyTransfer';
9import MasterKeyReveal from './MasterKeyReveal';
9import MasterKeyReveal from './MasterKeyReveal';
10import MasterKeyConfirm from './MasterKeyConfirm';
10import MasterKeyConfirm from './MasterKeyConfirm';
11
11
@@ -20,7 +20,7 @@ const VIEWS = {
20 [NAMES.REVEAL]: MasterKeyReveal,
20 [NAMES.REVEAL]: MasterKeyReveal,
21 [NAMES.DOWNLOAD]: MasterKeyDownload,
21 [NAMES.DOWNLOAD]: MasterKeyDownload,
22 [NAMES.CONFIRM]: MasterKeyConfirm,
22 [NAMES.CONFIRM]: MasterKeyConfirm,
23− [NAMES.TRANSFER]: PassportTransfer,
23+ [NAMES.TRANSFER]: MasterKeyTransfer,
24};
24};
25
25
26export default function ActivateMasterKey() {
26export default function ActivateMasterKey() {
@@ -32,22 +32,6 @@ export default function ActivateMasterKey() {
32 initialRoutes: [{ key: NAMES.REVEAL }],
32 initialRoutes: [{ key: NAMES.REVEAL }],
33 });
33 });
34
34
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−
51 return (
35 return (
52 <View inset>
36 <View inset>
53 <LocalRouterProvider value={router}>
37 <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';
2import withFadeable from './withFadeable';
2import withFadeable from './withFadeable';
3
3
4type ActivateStepsProps = {
4type ActivateStepsProps = {
@@ -19,15 +19,15 @@ export const ActivateSteps = ({
19 const isLastStep = i === totalSteps - 1;
19 const isLastStep = i === totalSteps - 1;
20 steps.push(
20 steps.push(
21 <Box
21 <Box
22− borderTopLeftRadius={isFirstStep ? '5px' : null}
22+ borderTopLeftRadius={isFirstStep ? '3px' : null}
23− borderBottomLeftRadius={isFirstStep ? '5px' : null}
23+ borderBottomLeftRadius={isFirstStep ? '3px' : null}
24− borderTopRightRadius={isLastStep ? '5px' : null}
24+ borderTopRightRadius={isLastStep ? '3px' : null}
25− borderBottomRightRadius={isLastStep ? '5px' : null}
25+ borderBottomRightRadius={isLastStep ? '3px' : null}
26 key={i}
26 key={i}
27 backgroundColor={i <= currentStep ? 'black' : 'gray'}
27 backgroundColor={i <= currentStep ? 'black' : 'gray'}
28− height={'10px'}
28+ height={'4px'}
29 width={`${100 / totalSteps}%`}
29 width={`${100 / totalSteps}%`}
30− marginLeft={isFirstStep ? null : '1px'}
30+ marginLeft={'0px'}
31 marginRight={isLastStep ? null : '1px'}></Box>
31 marginRight={isLastStep ? null : '1px'}></Box>
32 );
32 );
33 }
33 }
@@ -41,6 +41,8 @@ export const ActivateSteps = ({
41 flexWrap={'nowrap'}
41 flexWrap={'nowrap'}
42 width={'100%'}
42 width={'100%'}
43 className={className}>
43 className={className}>
44+ {/* TODO: Ask Jimmy: */}
45+ {/* <SegmentedProgressBar current={currentStep} segments={totalSteps} /> */}
44 {renderSteps()}
46 {renderSteps()}
45 </Box>
47 </Box>
46 );
48 );
src/views/Activate/MasterKeyConfirm.tsx+7−19
@@ -18,10 +18,7 @@ import { validateExactly } from 'lib/validators';
18import BridgeForm from 'form/BridgeForm';
18import BridgeForm from 'form/BridgeForm';
19import FormError from 'form/FormError';
19import FormError from 'form/FormError';
20import useFadeIn from './useFadeIn';
20import useFadeIn from './useFadeIn';
21−import {
22− HiddenInput,
23− TicketSegmentInput,
21+import { HiddenInput, TicketSegmentInput } from 'form/Inputs';
24−} from 'form/Inputs';
25
22
26const MasterKeyConfirm = () => {
23const MasterKeyConfirm = () => {
27 const { derivedWallet, setIsIn } = useActivateFlow();
24 const { derivedWallet, setIsIn } = useActivateFlow();
@@ -31,21 +28,12 @@ const MasterKeyConfirm = () => {
31 const ticketSegments = useMemo(() => ticketToSegments(ticket), [ticket]);
28 const ticketSegments = useMemo(() => ticketToSegments(ticket), [ticket]);
32
29
33 const onValues = useCallback(({ valid, values, form }) => {
30 const onValues = useCallback(({ valid, values, form }) => {
34− console.log('valid:', valid);
35− console.log('values:', values);
36−
37 const { ticket0, ticket1, ticket2, ticket3 } = values;
31 const { ticket0, ticket1, ticket2, ticket3 } = values;
38
32
33+ form.change(
34+ 'ticket',
39− const updatedTicket = ticketFromSegments([
35+ ticketFromSegments([ticket0, ticket1, ticket2, ticket3])
40− ticket0,
36+ );
41− ticket1,
42− ticket2,
43− ticket3,
44− ]);
45−
46− console.log(updatedTicket);
47−
48− form.change('ticket', updatedTicket);
49 }, []);
37 }, []);
50
38
51 const validate = useMemo(
39 const validate = useMemo(
@@ -64,7 +52,7 @@ const MasterKeyConfirm = () => {
64 ticket0: STUB_VERIFY_TICKET ? ticketSegments[0] : undefined,
52 ticket0: STUB_VERIFY_TICKET ? ticketSegments[0] : undefined,
65 ticket1: STUB_VERIFY_TICKET ? ticketSegments[1] : undefined,
53 ticket1: STUB_VERIFY_TICKET ? ticketSegments[1] : undefined,
66 ticket2: STUB_VERIFY_TICKET ? ticketSegments[2] : undefined,
54 ticket2: STUB_VERIFY_TICKET ? ticketSegments[2] : undefined,
67− ticket3: undefined,
55+ ticket3: STUB_VERIFY_TICKET ? ticketSegments[3] : undefined,
68 }),
56 }),
69 [STUB_VERIFY_TICKET, ticket, ticketSegments]
57 [STUB_VERIFY_TICKET, ticket, ticketSegments]
70 );
58 );
@@ -78,7 +66,7 @@ const MasterKeyConfirm = () => {
78 const header = useMemo(() => {
66 const header = useMemo(() => {
79 return (
67 return (
80 <Box>
68 <Box>
81− <ActivateHeader copy={'Confirm Backup'} />
69+ <ActivateHeader content={'Confirm Backup'} />
82 <ActivateParagraph
70 <ActivateParagraph
83 copy={
71 copy={
84 'Confirm that you have stored your Master Key somewhere safe by entering it below'
72 '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';
17import { compileNetworkingKey } from 'lib/keys';
17import { compileNetworkingKey } from 'lib/keys';
18import { downloadWallet } from 'lib/invite';
18import { downloadWallet } from 'lib/invite';
19import PaperBuilder from 'components/PaperBuilder';
19import PaperBuilder from 'components/PaperBuilder';
20−import { DEFAULT_FADE_TIMEOUT } from 'lib/constants';
20+import { DEFAULT_FADE_TIMEOUT, MASTER_KEY_DURATION } from 'lib/constants';
21import { timeout } from 'lib/timeout';
21import { timeout } from 'lib/timeout';
22
22
23const MasterKeyDownload = () => {
23const MasterKeyDownload = () => {
@@ -42,11 +42,11 @@ const MasterKeyDownload = () => {
42 Just: p => p.value.toFixed(),
42 Just: p => p.value.toFixed(),
43 });
43 });
44
44
45− const download = useCallback(() => {
45+ const download = useCallback(async () => {
46 const netkey = compileNetworkingKey(wallet.network.keys, point, 1);
46 const netkey = compileNetworkingKey(wallet.network.keys, point, 1);
47 //TODO could be deduplicated with useKeyfileGenerator's logic
47 //TODO could be deduplicated with useKeyfileGenerator's logic
48 const filename = ob.patp(point).slice(1) + '-1.key';
48 const filename = ob.patp(point).slice(1) + '-1.key';
49− downloadWallet(paper.getOrElse([]), netkey, filename);
49+ await downloadWallet(paper.getOrElse([]), netkey, filename);
50 setDownloaded(true);
50 setDownloaded(true);
51 }, [paper, wallet, point, setDownloaded]);
51 }, [paper, wallet, point, setDownloaded]);
52
52
@@ -62,16 +62,17 @@ const MasterKeyDownload = () => {
62 [paper, setGenerated]
62 [paper, setGenerated]
63 );
63 );
64
64
65− const goToConfirm = useCallback(async () => {
65+ const onDownloadClick = useCallback(async () => {
66+ await download();
66 setIsIn(false);
67 setIsIn(false);
67 await timeout(DEFAULT_FADE_TIMEOUT); // Pause for UI fade animation
68 await timeout(DEFAULT_FADE_TIMEOUT); // Pause for UI fade animation
68 push(names.CONFIRM);
69 push(names.CONFIRM);
69− }, [setIsIn, push, names.CONFIRM]);
70+ }, [download, setIsIn, push, names.CONFIRM]);
70
71
71 const header = useMemo(() => {
72 const header = useMemo(() => {
72 return triggerAnimation ? (
73 return triggerAnimation ? (
73 <Box>
74 <Box>
74− <ActivateHeader copy={'Backup your Master Key.'} />
75+ <ActivateHeader content={'Backup your Master Key.'} />
75 <ActivateParagraph
76 <ActivateParagraph
76 copy={
77 copy={
77 'Download your backup and store it somewhere safe, e.g. your security deposit box or password manager.'
78 'Download your backup and store it somewhere safe, e.g. your security deposit box or password manager.'
@@ -83,20 +84,17 @@ const MasterKeyDownload = () => {
83
84
84 const footer = useMemo(() => {
85 const footer = useMemo(() => {
85 return triggerAnimation ? (
86 return triggerAnimation ? (
86− <ActivateButton
87+ <ActivateButton onClick={onDownloadClick}>
87− disabled={!generated}
88− onClick={!downloaded ? download : goToConfirm}
89− success={downloaded}>
90− {!downloaded ? 'Download Backup (Passport)' : 'Continue'}
88+ {'Download Backup (Passport)'}
91 </ActivateButton>
89 </ActivateButton>
92 ) : null;
90 ) : null;
93− }, [download, downloaded, generated, goToConfirm, triggerAnimation]);
91+ }, [onDownloadClick, triggerAnimation]);
94
92
95 const delayedReveal = useCallback(async () => {
93 const delayedReveal = useCallback(async () => {
96 setTimeout(() => {
94 setTimeout(() => {
97 setTriggerAnimation(true);
95 setTriggerAnimation(true);
98 setIsIn(true);
96 setIsIn(true);
99− }, 800);
97+ }, MASTER_KEY_DURATION);
100 }, [setIsIn]);
98 }, [setIsIn]);
101
99
102 useEffect(() => {
100 useEffect(() => {
src/views/Activate/MasterKeyReveal.tsx+1−1
@@ -30,7 +30,7 @@ const MasterKeyReveal = () => {
30 const header = useMemo(() => {
30 const header = useMemo(() => {
31 return triggerAnimation ? (
31 return triggerAnimation ? (
32 <Box>
32 <Box>
33− <ActivateHeader copy={'Here is your Master Key.'} />
33+ <ActivateHeader content={'Here is your Master Key.'} />
34 <ActivateParagraph
34 <ActivateParagraph
35 copy={
35 copy={
36 "Your Master Key is your 4-word password for your Urbit. Make sure you're in a private place before you reveal it."
36 "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';
5type PointPresenterArgs = {
5type PointPresenterArgs = {
6 patp: string;
6 patp: string;
7 className?: string;
7 className?: string;
8+ showLabel?: boolean;
9+ success?: boolean;
8};
10};
9
11
10−const PointPresenter = ({ patp, className }: PointPresenterArgs) => {
12+const PointPresenter = ({
13+ patp,
14+ className,
15+ showLabel = true,
16+ success = false,
17+}: PointPresenterArgs) => {
11 return patp ? (
18 return patp ? (
12 <Box
19 <Box
13 width={256}
20 width={256}
@@ -20,31 +27,32 @@ const PointPresenter = ({ patp, className }: PointPresenterArgs) => {
20 background="white"
27 background="white"
21 className={className}>
28 className={className}>
22 <Box
29 <Box
23− background="black"
30+ background={success ? '#009F65' : 'black'}
24 borderRadius={5}
31 borderRadius={5}
25− padding={32}
32+ padding={24}
26 mb={3}
33 mb={3}
27 width={64}
34 width={64}
28 height={64}>
35 height={64}>
29 <Sigil
36 <Sigil
30 patp={patp}
37 patp={patp}
38+ colors={[success ? '#009F65' : 'black', 'white']}
31 size={64}
39 size={64}
32 display="block"
40 display="block"
33− colors={['black', 'white']}
34− icon
35 padding={32}
41 padding={32}
36 borderRadius={5}
42 borderRadius={5}
37 />
43 />
38 </Box>
44 </Box>
45+ {showLabel && (
39− <Box>
46+ <Box>
40− <Text
47+ <Text
41− display="block"
48+ display="block"
42− fontFamily={'Source Code Pro'}
49+ fontFamily={'Source Code Pro'}
43− fontSize={'18px'}
50+ fontSize={'18px'}
44− textAlign={'center'}>
51+ textAlign={'center'}>
45− {patp}
52+ {patp}
46− </Text>
53+ </Text>
47− </Box>
54+ </Box>
55+ )}
48 </Box>
56 </Box>
49 ) : null;
57 ) : null;
50};
58};
src/views/Activate/useActivateFlowState.js+22−13
@@ -2,40 +2,49 @@ import { useCallback, useState } from 'react';
2import { Nothing } from 'folktale/maybe';
2import { Nothing } from 'folktale/maybe';
3
3
4export default function useActivateFlowState() {
4export default function useActivateFlowState() {
5− const [derivedWallet, setDerivedWallet] = useState(Nothing());
5+ const [derivedPatp, setDerivedPatp] = useState(Nothing());
6− const [inviteWallet, setInviteWallet] = useState(Nothing());
7 const [derivedPoint, setDerivedPoint] = useState(Nothing());
6 const [derivedPoint, setDerivedPoint] = useState(Nothing());
7+ const [derivedPointDominion, setDerivedPointDominion] = useState(Nothing());
8+ const [derivedWallet, setDerivedWallet] = useState(Nothing());
8 const [generated, setGenerated] = useState(false);
9 const [generated, setGenerated] = useState(false);
9 const [incomingPoints, setIncomingPoints] = useState(Nothing());
10 const [incomingPoints, setIncomingPoints] = useState(Nothing());
11+ const [inviteWallet, setInviteWallet] = useState(Nothing());
10 const [isIn, setIsIn] = useState(false);
12 const [isIn, setIsIn] = useState(false);
11
13
12 const reset = useCallback(() => {
14 const reset = useCallback(() => {
13− setDerivedWallet(Nothing());
15+ setDerivedPatp(Nothing());
14− setInviteWallet(Nothing());
15 setDerivedPoint(Nothing());
16 setDerivedPoint(Nothing());
17+ setDerivedPointDominion(Nothing());
18+ setDerivedWallet(Nothing());
16 setGenerated(false);
19 setGenerated(false);
17 setIncomingPoints(Nothing());
20 setIncomingPoints(Nothing());
21+ setInviteWallet(Nothing());
18 }, [
22 }, [
19− setDerivedWallet,
20− setInviteWallet,
21 setDerivedPoint,
23 setDerivedPoint,
24+ setDerivedPointDominion,
25+ setDerivedWallet,
22 setGenerated,
26 setGenerated,
23 setIncomingPoints,
27 setIncomingPoints,
28+ setInviteWallet,
24 ]);
29 ]);
25
30
26 return {
31 return {
32+ derivedPatp,
33+ derivedPoint,
34+ derivedPointDominion,
27 derivedWallet,
35 derivedWallet,
28− setDerivedWallet,
36+ generated,
37+ incomingPoints,
29 inviteWallet,
38 inviteWallet,
30− setInviteWallet,
39+ isIn,
31− derivedPoint,
40+ reset,
41+ setDerivedPatp,
32 setDerivedPoint,
42 setDerivedPoint,
33− generated,
43+ setDerivedPointDominion,
44+ setDerivedWallet,
34 setGenerated,
45 setGenerated,
35− incomingPoints,
36 setIncomingPoints,
46 setIncomingPoints,
37− isIn,
47+ setInviteWallet,
38 setIsIn,
48 setIsIn,
39− reset,
40 };
49 };
41}
50}