on master help sign in
ux: Master Key Download
ac86588tomholford
src/views/Activate/ActivateButton.tsx+7−2
@@ -5,9 +5,14 @@ import withFadeable from './withFadeable';
5interface ActivateButtonProps {
5interface ActivateButtonProps {
6 children: React.ReactNode | string;
6 children: React.ReactNode | string;
7 onClick: MouseEventHandler;
7 onClick: MouseEventHandler;
8+ success?: boolean;
8}
9}
9
10
10−const ActivateButton = ({ children, onClick }: ActivateButtonProps) => {
11+const ActivateButton = ({
12+ children,
13+ onClick,
14+ success,
15+}: ActivateButtonProps & React.ComponentPropsWithoutRef<'button'>) => {
11 return (
16 return (
12 <Box
17 <Box
13 display="flex"
18 display="flex"
@@ -17,7 +22,7 @@ const ActivateButton = ({ children, onClick }: ActivateButtonProps) => {
17 justifyContent="flex-end">
22 justifyContent="flex-end">
18 <Button
23 <Button
19 onClick={onClick}
24 onClick={onClick}
20− backgroundColor="black"
25+ backgroundColor={success ? 'rgb(70,156,106)' : 'black'}
21 color={'white'}
26 color={'white'}
22 padding={'16px'}
27 padding={'16px'}
23 fontFamily="Inter"
28 fontFamily="Inter"
src/views/Activate/MasterKeyDownload.tsx+63−6
@@ -1,4 +1,7 @@
1import React, { useCallback, useEffect, useMemo, useState } from 'react';
1import React, { useCallback, useEffect, useMemo, useState } from 'react';
2+import { Just, Nothing } from 'folktale/maybe';
3+import * as need from 'lib/need';
4+import * as ob from 'urbit-ob';
2
5
3import { ActivateSteps } from './ActivateSteps';
6import { ActivateSteps } from './ActivateSteps';
4import { Box } from '@tlon/indigo-react';
7import { Box } from '@tlon/indigo-react';
@@ -10,10 +13,54 @@ import { FadeableMasterKeyCopy as MasterKeyCopy } from './MasterKeyCopy';
10import { FadeableMasterKeyPresenter as MasterKeyPresenter } from './MasterKeyPresenter';
13import { FadeableMasterKeyPresenter as MasterKeyPresenter } from './MasterKeyPresenter';
11import { useActivateFlow } from './ActivateFlow';
14import { useActivateFlow } from './ActivateFlow';
12import ActivateView from './ActivateView';
15import ActivateView from './ActivateView';
16+import { useLocalRouter } from 'lib/LocalRouter';
17+import { compileNetworkingKey } from 'lib/keys';
18+import { downloadWallet } from 'lib/invite';
19+import PaperBuilder from 'components/PaperBuilder';
13
20
14const MasterKeyDownload = () => {
21const MasterKeyDownload = () => {
22+ const {
23+ derivedPoint,
24+ derivedWallet,
25+ generated,
26+ setGenerated,
27+ setIsIn,
15− const { setIsIn } = useActivateFlow();
28+ } = useActivateFlow();
29+ const { push, names } = useLocalRouter();
30+ const point = need.point(derivedPoint);
31+ const wallet = need.wallet(derivedWallet);
32+ const ticket = wallet.ticket;
33+
34+ const [paper, setPaper] = useState(Nothing());
16 const [triggerAnimation, setTriggerAnimation] = useState<boolean>(false);
35 const [triggerAnimation, setTriggerAnimation] = useState<boolean>(false);
36+ const [downloaded, setDownloaded] = useState<boolean>(false);
37+
38+ const pointAsString = derivedPoint.matchWith({
39+ Nothing: () => '',
40+ Just: p => p.value.toFixed(),
41+ });
42+
43+ const download = useCallback(() => {
44+ const netkey = compileNetworkingKey(wallet.network.keys, point, 1);
45+ //TODO could be deduplicated with useKeyfileGenerator's logic
46+ const filename = ob.patp(point).slice(1) + '-1.key';
47+ downloadWallet(paper.getOrElse([]), netkey, filename);
48+ setDownloaded(true);
49+ }, [paper, wallet, point, setDownloaded]);
50+
51+ // sync paper value to activation state
52+ useEffect(
53+ () =>
54+ setGenerated(
55+ paper.matchWith({
56+ Nothing: () => false,
57+ Just: () => true,
58+ })
59+ ),
60+ [paper, setGenerated]
61+ );
62+
63+ const goToVerify = useCallback(() => push(names.VERIFY), [push, names]);
17
64
18 const header = useMemo(() => {
65 const header = useMemo(() => {
19 return triggerAnimation ? (
66 return triggerAnimation ? (
@@ -30,11 +77,14 @@ const MasterKeyDownload = () => {
30
77
31 const footer = useMemo(() => {
78 const footer = useMemo(() => {
32 return triggerAnimation ? (
79 return triggerAnimation ? (
33− <ActivateButton onClick={() => console.log('download')}>
80+ <ActivateButton
81+ disabled={!generated}
82+ onClick={!downloaded ? download : goToVerify}
83+ success={downloaded}>
34− Download Backup
84+ {!downloaded ? 'Download Backup (Passport)' : 'Continue'}
35 </ActivateButton>
85 </ActivateButton>
36 ) : null;
86 ) : null;
37− }, [triggerAnimation]);
87+ }, [download, downloaded, generated, goToVerify, triggerAnimation]);
38
88
39 const delayedReveal = useCallback(async () => {
89 const delayedReveal = useCallback(async () => {
40 setTimeout(() => {
90 setTimeout(() => {
@@ -64,11 +114,18 @@ const MasterKeyDownload = () => {
64 {triggerAnimation && (
114 {triggerAnimation && (
65 <DangerBox>Do not share this with anyone else!</DangerBox>
115 <DangerBox>Do not share this with anyone else!</DangerBox>
66 )}
116 )}
67− <MasterKeyPresenter fadeIn={true} />
117+ {ticket && <MasterKeyPresenter fadeIn={true} ticket={ticket} />}
68− {triggerAnimation && <MasterKeyCopy text={'test'} />}
118+ {triggerAnimation && <MasterKeyCopy text={ticket} />}
69 </Box>
119 </Box>
70 </ActivateView>
120 </ActivateView>
71 <ActivateSteps currentStep={1} totalSteps={4} />
121 <ActivateSteps currentStep={1} totalSteps={4} />
122+ <PaperBuilder
123+ point={pointAsString}
124+ wallets={[wallet]}
125+ callback={data => {
126+ setPaper(Just(data));
127+ }}
128+ />
72 </>
129 </>
73 );
130 );
74};
131};
src/views/Activate/MasterKeyPresenter.tsx+4−6
@@ -5,16 +5,14 @@ import { useMemo } from 'react';
5import withFadeable from './withFadeable';
5import withFadeable from './withFadeable';
6
6
7type MasterKeyPresenterArgs = {
7type MasterKeyPresenterArgs = {
8+ ticket: string;
8 className?: string;
9 className?: string;
9};
10};
10
11
11−const MasterKeyPresenter = ({ className }: MasterKeyPresenterArgs) => {
12+const MasterKeyPresenter = ({ ticket, className }: MasterKeyPresenterArgs) => {
12− // TODO: use actual MK ticket :)
13− const { impliedTicket } = useImpliedTicket();
14−
15 const ticketSegments = useMemo(() => {
13 const ticketSegments = useMemo(() => {
16− return impliedTicket ? stripSigPrefix(impliedTicket).split('-') : null;
14+ return ticket ? stripSigPrefix(ticket).split('-') : null;
17− }, [impliedTicket]);
15+ }, [ticket]);
18
16
19 return (
17 return (
20 <Box
18 <Box