on master help sign in
WIP: refactor fade animation behavior
commit754a6acd31ec694101c24fc2c3c6ba3604c93ca4 authortomholford <tomholford@users.noreply.github.com> dateWed 8 Sep 2021, 08:36 (5 years ago) parenta0de25b
15 files changed+308−265
src/lib/constants.ts+5−1 src/style/activate.scss+10 src/style/master-key.scss+12−17 src/views/Activate/ActivateButton.tsx+35 src/views/Activate/ActivateCode.tsx+8−13 src/views/Activate/ActivateCodeForm.tsx+68−64 src/views/Activate/ActivateHeader.tsx+22−16 src/views/Activate/ActivateMasterKey.tsx−7 src/views/Activate/ActivateParagraph.tsx+4 src/views/Activate/ActivateSteps.tsx+11−6 src/views/Activate/MasterKeyDownload.tsx+13−41 src/views/Activate/MasterKeyReveal.tsx+39−52 src/views/Activate/PointPresenter.tsx+41−48 src/views/Activate/useFadeIn.ts+16 src/views/Activate/withFadeable.tsx+24
src/lib/constants.ts+5−1
@@ -118,7 +118,10 @@ const POINT_PROXIES = {
118118 TRANSFER: 'transfer',
119119 VOTE: 'vote',
120120 SPAWN: 'spawn',
121−}
121+};
122+
123+// In ms
124+const DEFAULT_FADE_TIMEOUT = 300;
122125
123126export {
124127 CHECK_BLOCK_EVERY_MS,
@@ -150,4 +153,5 @@ export {
150153 ROLLER_HOSTS,
151154 POINT_DOMINIONS,
152155 POINT_PROXIES,
156+ DEFAULT_FADE_TIMEOUT,
153157};
src/style/activate.scss+10
@@ -17,3 +17,13 @@
1717 transition: opacity 300ms;
1818 transition-timing-function: ease-in;
1919}
20+
21+.fadeable-appear {
22+ opacity: 0;
23+}
24+
25+.fadeable-appear-active {
26+ opacity: 1;
27+ transition: opacity 300ms;
28+ transition-timing-function: ease-in;
29+}
src/style/master-key.scss+12−17
@@ -1,12 +1,7 @@
1−// Note the 20% - 100% range is a fake set of keyframes to simulate a delay between iterations
2−// See: https://css-tricks.com/css-keyframe-animation-delay-iterations/
31@keyframes opacity-fill {
42 0% {
53 fill-opacity: 0.2;
64 }
7− 20% {
8− fill-opacity: 1.0;
9− }
105 100% {
116 fill-opacity: 1.0;
127 }
@@ -14,60 +9,60 @@
149
1510svg.master-key {
1611 .first-one {
17− animation: opacity-fill 2000ms linear forwards infinite;
12+ animation: opacity-fill 200ms linear forwards;
1813 animation-delay: 100ms;
1914 }
2015
2116 .first-two {
22− animation: opacity-fill 2000ms linear forwards infinite;
17+ animation: opacity-fill 200ms linear forwards;
2318 animation-delay: 200ms;
2419 }
2520
2621 .first-three {
27− animation: opacity-fill 2000ms linear forwards infinite;
22+ animation: opacity-fill 200ms linear forwards;
2823 animation-delay: 300ms;
2924
3025 }
3126
3227 .second-one {
33− animation: opacity-fill 2000ms linear forwards infinite;
28+ animation: opacity-fill 200ms linear forwards;
3429 animation-delay: 400ms;
3530 }
3631 .second-two {
37− animation: opacity-fill 2000ms linear forwards infinite;
32+ animation: opacity-fill 200ms linear forwards;
3833 animation-delay: 500ms;
3934 }
4035
4136 .second-three {
42− animation: opacity-fill 2000ms linear forwards infinite;
37+ animation: opacity-fill 200ms linear forwards;
4338 animation-delay: 600ms;
4439 }
4540
4641 .third-one {
47− animation: opacity-fill 2000ms linear forwards infinite;
42+ animation: opacity-fill 200ms linear forwards;
4843 animation-delay: 700ms;
4944 }
5045 .third-two {
51− animation: opacity-fill 2000ms linear forwards infinite;
46+ animation: opacity-fill 200ms linear forwards;
5247 animation-delay: 800ms;
5348 }
5449 .third-three {
55− animation: opacity-fill 2000ms linear forwards infinite;
50+ animation: opacity-fill 200ms linear forwards;
5651 animation-delay: 900ms;
5752 }
5853
5954 .fourth-one {
60− animation: opacity-fill 2000ms linear forwards infinite;
55+ animation: opacity-fill 200ms linear forwards;
6156 animation-delay: 1s;
6257 }
6358
6459 .fourth-two {
65− animation: opacity-fill 2000ms linear forwards infinite;
60+ animation: opacity-fill 200ms linear forwards;
6661 animation-delay: 1.1s;
6762 }
6863
6964 .fourth-three {
70− animation: opacity-fill 2000ms linear forwards infinite;
65+ animation: opacity-fill 200ms linear forwards;
7166 animation-delay: 1.2s;
7267 }
7368
src/views/Activate/ActivateButton.tsxnew file+35
@@ -0,0 +1,35 @@
1+import { Box, Button } from '@tlon/indigo-react';
2+import { MouseEventHandler } from 'react';
3+import withFadeable from './withFadeable';
4+
5+interface ActivateButtonProps {
6+ children: React.ReactNode | string;
7+ onClick: MouseEventHandler;
8+}
9+
10+const ActivateButton = ({ children, onClick }: ActivateButtonProps) => {
11+ return (
12+ <Box
13+ display="flex"
14+ flexDirection="column"
15+ flexWrap="nowrap"
16+ height={'100%'}
17+ justifyContent="flex-end">
18+ <Button
19+ onClick={onClick}
20+ backgroundColor="black"
21+ color={'white'}
22+ padding={'16px'}
23+ fontFamily="Inter"
24+ height={'50px'}
25+ fontWeight={'400'}
26+ fontSize={'18px'}>
27+ {children}
28+ </Button>
29+ </Box>
30+ );
31+};
32+
33+export default ActivateButton;
34+
35+export const FadeableActivateButton = withFadeable(ActivateButton);
src/views/Activate/ActivateCode.tsx+8−13
@@ -1,4 +1,4 @@
1−import { useCallback, useEffect } from 'react';
1+import { useCallback } from 'react';
22
33import View from 'components/View';
44import { ForwardButton } from 'components/Buttons';
@@ -12,18 +12,17 @@ import { useLocalRouter } from 'lib/LocalRouter';
1212import useImpliedTicket from 'lib/useImpliedTicket';
1313import useHasDisclaimed from 'lib/useHasDisclaimed';
1414
15−import { useActivateFlow } from './ActivateFlow';
16−import PointPresenter from './PointPresenter';
15+import { FadeablePointPresenter as PointPresenter } from './PointPresenter';
1716import { Box, Text } from '@tlon/indigo-react';
18−import ActivateHeader from './ActivateHeader';
19−import ActivateCodeForm from './ActivateCodeForm';
17+import { FadeableActivateHeader as ActivateHeader } from './ActivateHeader';
18+import { FadeableActivateCodeForm as ActivateCodeForm } from './ActivateCodeForm';
19+import useFadeIn from './useFadeIn';
2020
2121export default function ActivateCode() {
2222 const history = useHistory();
2323 const { names, push } = useLocalRouter();
2424 const { impliedPatp, impliedTicket } = useImpliedTicket();
2525 const [hasDisclaimed] = useHasDisclaimed();
26− const { isIn, setIsIn } = useActivateFlow();
2726
2827 const goToLogin = useCallback(() => history.popAndPush(ROUTE_NAMES.LOGIN), [
2928 history,
@@ -38,16 +37,12 @@ export default function ActivateCode() {
3837 }, [hasDisclaimed, names.DISCLAIMER, names.MASTER_KEY, push]);
3938
4039 // Fade in on load
41− useEffect(() => {
42− setIsIn(true);
43− }, [setIsIn]);
40+ useFadeIn();
4441
4542 return (
4643 <View inset>
4744 <ActivateView
48− header={
49− <ActivateHeader copy={'Welcome. This is your Urbit.'} isIn={isIn} />
50− }
45+ header={<ActivateHeader copy={'Welcome. This is your Urbit.'} />}
5146 footer={<ActivateCodeForm afterSubmit={goToMasterKey} />}>
5247 <Box
5348 alignItems={'center'}
@@ -59,7 +54,7 @@ export default function ActivateCode() {
5954 {!impliedTicket && (
6055 <Text className="mb2">Enter your activation code to continue.</Text>
6156 )}
62− {impliedPatp && <PointPresenter patp={impliedPatp} isIn={isIn} />}
57+ {impliedPatp && <PointPresenter patp={impliedPatp} />}
6358 </Box>
6459 </ActivateView>
6560
src/views/Activate/ActivateCodeForm.tsx+68−64
@@ -32,6 +32,7 @@ import { Ship } from '@urbit/roller-api';
3232import { convertToInt } from 'lib/convertToInt';
3333import { Box } from '@tlon/indigo-react';
3434import { timeout } from 'lib/timeout';
35+import withFadeable from './withFadeable';
3536
3637interface ActivateCodeFormProps {
3738 afterSubmit: VoidFunction;
@@ -107,6 +108,9 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
107108 // derive and set our state on submission
108109 const onSubmit = useCallback(
109110 async values => {
111+ // Allow UI animation to start
112+ await timeout(42);
113+
110114 // Derive wallet
111115 const ticket = impliedTicket || values.ticket;
112116 const { seed } = await generateTemporaryOwnershipWallet(ticket);
@@ -153,7 +157,6 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
153157 },
154158 [
155159 api,
156− didWarn,
157160 impliedAzimuthPoint,
158161 impliedTicket,
159162 loadL1Points,
@@ -161,6 +164,7 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
161164 setDerivedPoint,
162165 setDerivedWallet,
163166 setInviteWallet,
167+ setIsIn,
164168 ]
165169 );
166170
@@ -170,71 +174,71 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
170174 );
171175
172176 return (
173− <CSSTransition classNames="fadeable" timeout={300} in={isIn}>
174− <Box
175− display="flex"
176− flexDirection="column"
177− flexWrap="nowrap"
178− height={'100%'}
179− justifyContent="flex-end">
180− <Grid>
181− <BridgeForm
182− validate={validate}
183− onSubmit={onSubmit}
184− afterSubmit={afterSubmit}
185− initialValues={initialValues}>
186− {({ validating, values, submitting, handleSubmit }) => (
187− <>
188− {!impliedTicket && (
189− <>
190− <Grid.Item
191− full
192− as={TicketInput}
193− type={values.showTicket ? 'text' : 'password'}
194− name="ticket"
195− label="Activation Code"
196− disabled={!activationAllowed}
197− />
198−
199− <Grid.Item
200− full
201− as={CheckboxInput}
202− name="showTicket"
203− label="Show"
204− />
205− </>
206− )}
207−
208− <Grid.Item full as={FormError} />
209−
210− <Grid.Item
211− full
212− as={SubmitButton}
213− className="mt4"
214− handleSubmit={handleSubmit}>
215− {isWarning =>
216− validating
217− ? 'Deriving...'
218− : submitting
219− ? 'Generating...'
220− : isWarning
221− ? 'Continue Activation'
222− : 'Claim'
223− }
177+ <Box
178+ display="flex"
179+ flexDirection="column"
180+ flexWrap="nowrap"
181+ height={'100%'}
182+ justifyContent="flex-end">
183+ <Grid>
184+ <BridgeForm
185+ validate={validate}
186+ onSubmit={onSubmit}
187+ afterSubmit={afterSubmit}
188+ initialValues={initialValues}>
189+ {({ validating, values, submitting, handleSubmit }) => (
190+ <>
191+ {!impliedTicket && (
192+ <>
193+ <Grid.Item
194+ full
195+ as={TicketInput}
196+ type={values.showTicket ? 'text' : 'password'}
197+ name="ticket"
198+ label="Activation Code"
199+ disabled={!activationAllowed}
200+ />
201+
202+ <Grid.Item
203+ full
204+ as={CheckboxInput}
205+ name="showTicket"
206+ label="Show"
207+ />
208+ </>
209+ )}
210+
211+ <Grid.Item full as={FormError} />
212+
213+ <Grid.Item
214+ full
215+ as={SubmitButton}
216+ className="mt4"
217+ handleSubmit={handleSubmit}>
218+ {isWarning =>
219+ validating
220+ ? 'Deriving...'
221+ : submitting
222+ ? 'Generating...'
223+ : isWarning
224+ ? 'Continue Activation'
225+ : 'Claim'
226+ }
227+ </Grid.Item>
228+
229+ {!activationAllowed && (
230+ <Grid.Item full as={WarningBox} className="mt4">
231+ For your security, please access Bridge on a desktop device.
224232 </Grid.Item>
225−
226− {!activationAllowed && (
227− <Grid.Item full as={WarningBox} className="mt4">
228− For your security, please access Bridge on a desktop device.
229− </Grid.Item>
230− )}
231− </>
232− )}
233− </BridgeForm>
234− </Grid>
235− </Box>
236− </CSSTransition>
233+ )}
234+ </>
235+ )}
236+ </BridgeForm>
237+ </Grid>
238+ </Box>
237239 );
238240};
239241
240242export default ActivateCodeForm;
243+
244+export const FadeableActivateCodeForm = withFadeable(ActivateCodeForm);
src/views/Activate/ActivateHeader.tsx+22−16
@@ -1,28 +1,34 @@
11import { Box, Text } from '@tlon/indigo-react';
2+import { DEFAULT_FADE_TIMEOUT } from 'lib/constants';
23import { CSSTransition } from 'react-transition-group';
4+import { useActivateFlow } from './ActivateFlow';
5+import withFadeable from './withFadeable';
36
47type ActivateHeaderProps = {
58 copy: string;
6− isIn: boolean;
9+ fadeTimeout?: number;
10+ isInOverride?: boolean;
711};
812
9−const ActivateHeader = ({ copy, isIn = false }: ActivateHeaderProps) => {
13+const ActivateHeader = ({ copy }: ActivateHeaderProps) => {
14+ const { isIn } = useActivateFlow();
15+
1016 return (
11− <CSSTransition in={isIn} classNames="fadeable" timeout={300}>
12− <Box>
13− <Text
14− display="block"
15− fontFamily="Inter"
16− fontSize={3}
17− fontWeight="medium"
18− mx={3}
19− my={3}
20− textAlign="center">
21− {copy}
22− </Text>
23− </Box>
24− </CSSTransition>
17+ <Box>
18+ <Text
19+ display="block"
20+ fontFamily="Inter"
21+ fontSize={3}
22+ fontWeight="medium"
23+ mx={3}
24+ my={3}
25+ textAlign="center">
26+ {copy}
27+ </Text>
28+ </Box>
2529 );
2630};
2731
2832export default ActivateHeader;
33+
34+export const FadeableActivateHeader = withFadeable(ActivateHeader);
src/views/Activate/ActivateMasterKey.tsx−7
@@ -1,18 +1,12 @@
1−import React, { useMemo } from 'react';
2−
31import { LocalRouterProvider, useLocalRouter } from 'lib/LocalRouter';
42
53import View from 'components/View';
64
7−import { useActivateFlow } from './ActivateFlow';
85import useRouter from 'lib/useRouter';
96
107import MasterKeyDownload from './MasterKeyDownload';
118import PassportVerify from './PassportVerify';
129import PassportTransfer from './PassportTransfer';
13−import { walletFromMnemonic } from 'lib/wallet';
14−import { DEFAULT_HD_PATH } from 'lib/constants';
15−import { publicToAddress } from 'lib/utils/address';
1610import MasterKeyReveal from './MasterKeyReveal';
1711
1812const NAMES = {
@@ -30,7 +24,6 @@ const VIEWS = {
3024};
3125
3226export default function ActivateMasterKey() {
33− const { derivedPoint, generated, derivedWallet } = useActivateFlow();
3427 // pull the reset function out of the 'activate' router
3528 const { reset: resetActivateRouter } = useLocalRouter();
3629 const { Route, ...router } = useRouter({
src/views/Activate/ActivateParagraph.tsx+4
@@ -1,7 +1,9 @@
11import { Box, Text } from '@tlon/indigo-react';
2+import withFadeable from './withFadeable';
23
34type ActivateParagraphProps = {
45 copy: string;
6+ fadeTimeout?: number;
57};
68
79const ActivateParagraph = ({ copy }: ActivateParagraphProps) => {
@@ -27,3 +29,5 @@ const ActivateParagraph = ({ copy }: ActivateParagraphProps) => {
2729};
2830
2931export default ActivateParagraph;
32+
33+export const FadeableActivateParagraph = withFadeable(ActivateParagraph);
src/views/Activate/ActivateSteps.tsx+11−6
@@ -1,4 +1,5 @@
11import { Box } from '@tlon/indigo-react';
2+import withFadeable from './withFadeable';
23
34type ActivateStepsProps = {
45 currentStep: number;
@@ -14,18 +15,20 @@ export const ActivateSteps = ({
1415 const renderSteps = () => {
1516 let steps = [];
1617 for (let i = 0; i < totalSteps; i++) {
18+ const isFirstStep = i === 0;
19+ const isLastStep = i === totalSteps - 1;
1720 steps.push(
1821 <Box
19− borderTopLeftRadius={i === 0 ? '5px' : null}
20− borderBottomLeftRadius={i === 0 ? '5px' : null}
21− borderTopRightRadius={i === totalSteps - 1 ? '5px' : null}
22− borderBottomRightRadius={i === totalSteps - 1 ? '5px' : null}
22+ borderTopLeftRadius={isFirstStep ? '5px' : null}
23+ borderBottomLeftRadius={isFirstStep ? '5px' : null}
24+ borderTopRightRadius={isLastStep ? '5px' : null}
25+ borderBottomRightRadius={isLastStep ? '5px' : null}
2326 key={i}
2427 backgroundColor={i <= currentStep ? 'black' : 'gray'}
2528 height={'10px'}
2629 width={`${100 / totalSteps}%`}
27− marginLeft={'2px'}
28− marginRight={'2px'}></Box>
30+ marginLeft={isFirstStep ? null : '2px'}
31+ marginRight={isLastStep ? null : '2px'}></Box>
2932 );
3033 }
3134 return steps;
@@ -42,3 +45,5 @@ export const ActivateSteps = ({
4245 </Box>
4346 );
4447};
48+
49+export const FadeableActivateSteps = withFadeable(ActivateSteps);
src/views/Activate/MasterKeyDownload.tsx+13−41
@@ -1,22 +1,18 @@
1−import React, { useCallback, useEffect, useMemo, useState } from 'react';
1+import React, { useMemo } from 'react';
22
33import ActivateView from './ActivateView';
4−import ActivateHeader from './ActivateHeader';
5−import { MasterKey } from './MasterKey';
6−import { Box, Button, Icon } from '@tlon/indigo-react';
4+import { FadeableActivateHeader as ActivateHeader } from './ActivateHeader';
5+import { Box } from '@tlon/indigo-react';
76import ActivateParagraph from './ActivateParagraph';
8−import { ActivateSteps } from './ActivateSteps';
9−import { useActivateFlow } from './ActivateFlow';
10−import { timeout } from 'lib/timeout';
7+import { FadeableActivateSteps as ActivateSteps } from './ActivateSteps';
118import MasterKeyPresenter from './MasterKeyPresenter';
12−import { CopyButton } from 'components/Buttons';
9+import { FadeableActivateButton as ActivateButton } from './ActivateButton';
10+import useFadeIn from './useFadeIn';
1311
1412const MasterKeyDownload = () => {
15− const { isFaded, setIsFaded } = useActivateFlow();
16−
1713 const header = useMemo(() => {
1814 return (
19− <Box className={isFaded ? 'faded-in' : 'faded-out'}>
15+ <Box>
2016 <ActivateHeader copy={'Backup your Master Key.'} />
2117 <ActivateParagraph
2218 copy={
@@ -25,41 +21,17 @@ const MasterKeyDownload = () => {
2521 />
2622 </Box>
2723 );
28− }, [isFaded]);
24+ }, []);
2925
3026 const footer = useMemo(() => {
3127 return (
32− <Box
33− display="flex"
34− flexDirection="column"
35− flexWrap="nowrap"
36− height={'100%'}
37− className={isFaded ? 'faded-in' : 'faded-out'}
38− justifyContent="flex-end">
39− <Button
40− onClick={() => console.log('reveal')}
41− backgroundColor="black"
42− color={'white'}
43− padding={'16px'}
44− fontFamily="Inter"
45− height={'50px'}
46− fontWeight={'400'}
47− fontSize={'18px'}>
48− Download Backup
49− </Button>
50− </Box>
28+ <ActivateButton onClick={() => console.log('download')}>
29+ Download Backup
30+ </ActivateButton>
5131 );
52− }, [isFaded]);
53−
54− const onViewTransition = useCallback(async () => {
55− setIsFaded(true);
56− await timeout(500); // Pause for UI fade animation
57− }, [setIsFaded]);
32+ }, []);
5833
59− useEffect(() => {
60− // Fade in content
61− onViewTransition();
62− }, [onViewTransition]);
34+ useFadeIn();
6335
6436 return (
6537 <>
src/views/Activate/MasterKeyReveal.tsx+39−52
@@ -1,32 +1,35 @@
11import React, { useCallback, useEffect, useMemo, useState } from 'react';
22
33import ActivateView from './ActivateView';
4−import ActivateHeader from './ActivateHeader';
4+import { FadeableActivateHeader as ActivateHeader } from './ActivateHeader';
55import { MasterKey } from './MasterKey';
6−import { Box, Button, Icon } from '@tlon/indigo-react';
7−import ActivateParagraph from './ActivateParagraph';
8−import { ActivateSteps } from './ActivateSteps';
6+import { Box, Icon } from '@tlon/indigo-react';
7+import { FadeableActivateParagraph as ActivateParagraph } from './ActivateParagraph';
8+import { FadeableActivateSteps as ActivateSteps } from './ActivateSteps';
99import { useActivateFlow } from './ActivateFlow';
1010import { timeout } from 'lib/timeout';
1111import { useLocalRouter } from 'lib/LocalRouter';
12+import { FadeableActivateButton as ActivateButton } from './ActivateButton';
1213
1314const MasterKeyReveal = () => {
14− const { isFaded, setIsFaded } = useActivateFlow();
15+ const { isIn, setIsIn } = useActivateFlow();
1516 const { push, names } = useLocalRouter();
17+ const [showMasterKey, setShowMasterKey] = useState<boolean>(false);
18+ const [triggerAnimation, setTriggerAnimation] = useState<boolean>(false);
1619
1720 const goToDownload = useCallback(() => {
1821 push(names.DOWNLOAD);
1922 }, [names.DOWNLOAD, push]);
2023
2124 const onRevealClick = useCallback(async () => {
22− setIsFaded(false);
25+ setShowMasterKey(true);
2326 await timeout(500); // Pause for UI fade animation
2427 goToDownload();
25− }, [goToDownload, setIsFaded]);
28+ }, [goToDownload]);
2629
2730 const header = useMemo(() => {
28− return (
29− <Box className={isFaded ? 'faded-in' : 'faded-out'}>
31+ return triggerAnimation ? (
32+ <Box>
3033 <ActivateHeader copy={'Here is your Master Key.'} />
3134 <ActivateParagraph
3235 copy={
@@ -34,49 +37,37 @@ const MasterKeyReveal = () => {
3437 }
3538 />
3639 </Box>
37− );
38− }, [isFaded]);
40+ ) : null;
41+ }, [triggerAnimation]);
3942
4043 const footer = useMemo(() => {
41− return (
42− <Box
43− display="flex"
44− flexDirection="column"
45− flexWrap="nowrap"
46− height={'100%'}
47− className={isFaded ? 'faded-in' : 'faded-out'}
48− justifyContent="flex-end">
49− <Button
50− onClick={onRevealClick}
51− backgroundColor="black"
44+ return triggerAnimation ? (
45+ <ActivateButton onClick={onRevealClick}>
46+ <Icon
47+ display="inline-block"
48+ icon="Visible"
49+ size="18px"
5250 color={'white'}
53− padding={'16px'}
54− fontFamily="Inter"
55− height={'50px'}
56− fontWeight={'400'}
57− fontSize={'18px'}>
58− <Icon
59− display="inline-block"
60− icon="Visible"
61− size="18px"
62− color={'white'}
63− />
64− &nbsp; Reveal
65− </Button>
66− </Box>
67− );
68− }, [isFaded, onRevealClick]);
51+ />
52+ &nbsp; Reveal
53+ </ActivateButton>
54+ ) : null;
55+ }, [onRevealClick, triggerAnimation]);
6956
70− const onViewTransition = useCallback(async () => {
71− await timeout(500); // Pause for UI fade animation
72− setIsFaded(true);
73− await timeout(500); // Pause for UI fade animation
74− }, [setIsFaded]);
57+ const delayedFadeIn = useCallback(async () => {
58+ setTimeout(() => {
59+ setTriggerAnimation(true);
60+ setIsIn(true);
61+ }, 1200);
62+ }, [setIsIn]);
7563
7664 useEffect(() => {
77− // Fade in content
78− onViewTransition();
79− }, [onViewTransition]);
65+ delayedFadeIn();
66+
67+ return () => {
68+ setIsIn(false);
69+ };
70+ }, [delayedFadeIn, setIsIn]);
8071
8172 return (
8273 <>
@@ -95,15 +86,11 @@ const MasterKeyReveal = () => {
9586 width={'80%'}
9687 height={'min-content'}
9788 justifyContent={'center'}>
98− <MasterKey paused={true} />
89+ <MasterKey />
9990 </Box>
10091 </Box>
10192 </ActivateView>
102− <ActivateSteps
103− currentStep={0}
104− totalSteps={4}
105− className={isFaded ? 'faded-in' : 'faded-out'}
106− />
93+ {triggerAnimation && <ActivateSteps currentStep={0} totalSteps={4} />}
10794 </>
10895 );
10996};
src/views/Activate/PointPresenter.tsx+41−48
@@ -1,61 +1,54 @@
11import { Box, Text } from '@tlon/indigo-react';
2−import { CSSTransition } from 'react-transition-group';
32import Sigil from 'components/Sigil';
3+import withFadeable from './withFadeable';
44
55type PointPresenterArgs = {
66 patp: string;
7− isIn: boolean;
87 className?: string;
98};
109
11−const PointPresenter = ({
12− patp,
13− isIn = false,
14− className,
15−}: PointPresenterArgs) => {
10+const PointPresenter = ({ patp, className }: PointPresenterArgs) => {
1611 return patp ? (
17− <>
18− <CSSTransition in={isIn} classNames="fadeable" timeout={300}>
19− <Box
20− width={256}
21− height={256}
22− display="flex"
23− flexDirection="column"
24− flexWrap="nowrap"
25− justifyContent="center"
26− alignItems="center"
27− background="white"
28− className={className}>
29− <Box
30− background="black"
31− borderRadius={5}
32− padding={32}
33− mb={3}
34− width={64}
35− height={64}>
36− <Sigil
37− patp={patp}
38− size={64}
39− display="block"
40− colors={['black', 'white']}
41− icon
42− padding={32}
43− borderRadius={5}
44− />
45− </Box>
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− </Box>
56− </CSSTransition>
57− </>
12+ <Box
13+ width={256}
14+ height={256}
15+ display="flex"
16+ flexDirection="column"
17+ flexWrap="nowrap"
18+ justifyContent="center"
19+ alignItems="center"
20+ background="white"
21+ className={className}>
22+ <Box
23+ background="black"
24+ borderRadius={5}
25+ padding={32}
26+ mb={3}
27+ width={64}
28+ height={64}>
29+ <Sigil
30+ patp={patp}
31+ size={64}
32+ display="block"
33+ colors={['black', 'white']}
34+ icon
35+ padding={32}
36+ borderRadius={5}
37+ />
38+ </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>
48+ </Box>
5849 ) : null;
5950};
6051
6152export default PointPresenter;
53+
54+export const FadeablePointPresenter = withFadeable(PointPresenter);
src/views/Activate/useFadeIn.tsnew file+16
@@ -0,0 +1,16 @@
1+import { useEffect } from 'react';
2+import { useActivateFlow } from './ActivateFlow';
3+
4+const useFadeIn = () => {
5+ const { setIsIn } = useActivateFlow();
6+
7+ useEffect(() => {
8+ setIsIn(true);
9+
10+ return () => {
11+ setIsIn(false);
12+ };
13+ }, [setIsIn]);
14+};
15+
16+export default useFadeIn;
src/views/Activate/withFadeable.tsxnew file+24
@@ -0,0 +1,24 @@
1+import { useActivateFlow } from './ActivateFlow';
2+import { CSSTransition } from 'react-transition-group';
3+import { DEFAULT_FADE_TIMEOUT } from 'lib/constants';
4+
5+interface withFadeableProps {
6+ fadeTimeout?: number;
7+}
8+
9+const withFadeable = <P extends object>(
10+ Component: React.ComponentType<P>
11+): React.FC<P & withFadeableProps> => (props: withFadeableProps) => {
12+ const { isIn } = useActivateFlow();
13+
14+ return (
15+ <CSSTransition
16+ in={isIn}
17+ classNames="fadeable"
18+ timeout={props.fadeTimeout || DEFAULT_FADE_TIMEOUT}>
19+ <Component {...(props as P)} />
20+ </CSSTransition>
21+ );
22+};
23+
24+export default withFadeable;