on master help sign in
WIP: Reveal Master Key

- animation reorganization - create new MasterKey* flow - add isFAded to flowstate, use it for animating various UI pieces

commitc543e68ad7a6e5a9fdf40be58fc135a7b4dfa4a0 authortomholford <tomholford@users.noreply.github.com> dateTue 7 Sep 2021, 01:45 (5 years ago) parent062dcc5
13 files changed+376−101
src/style/bridge.scss+12 src/style/index.scss−3 src/style/master-key.scss+81 src/style/views/activate.scss−76 src/views/Activate.js+3 src/views/Activate/ActivateCode.tsx+24−19 src/views/Activate/ActivateMasterKey.tsx+71 src/views/Activate/ActivateParagraph.tsx+29 src/views/Activate/ActivateSteps.tsx+44 src/views/Activate/ActivateView.tsx+9−1 src/views/Activate/MasterKey.tsx+7−2 src/views/Activate/MasterKeyConfirm.tsx+92 src/views/Activate/useActivateFlowState.js+4
src/style/bridge.scss+12
@@ -28,3 +28,15 @@ div.copy-button-wide {
2828 font-size: 0.75rem;
2929 }
3030}
31+
32+// For fading content in and out
33+
34+.faded-out{
35+ opacity:0;
36+ transition: opacity 0.5s;
37+
38+}
39+.faded-in{
40+ opacity:1;
41+ transition: opacity 0.5s;
42+}
src/style/index.scss−3
@@ -12,6 +12,3 @@
1212
1313/* Bridge Styles */
1414@import './bridge.scss';
15−
16−/* View Styles */
17−@import './views/activate.scss';
src/style/master-key.scssnew file+81
@@ -0,0 +1,81 @@
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/
3+@keyframes opacity-fill {
4+ 0% {
5+ fill-opacity: 0.2;
6+ }
7+ 20% {
8+ fill-opacity: 1.0;
9+ }
10+ 100% {
11+ fill-opacity: 1.0;
12+ }
13+}
14+
15+svg.master-key {
16+ .first-one {
17+ animation: opacity-fill 2000ms linear forwards infinite;
18+ animation-delay: 100ms;
19+ }
20+
21+ .first-two {
22+ animation: opacity-fill 2000ms linear forwards infinite;
23+ animation-delay: 200ms;
24+ }
25+
26+ .first-three {
27+ animation: opacity-fill 2000ms linear forwards infinite;
28+ animation-delay: 300ms;
29+
30+ }
31+
32+ .second-one {
33+ animation: opacity-fill 2000ms linear forwards infinite;
34+ animation-delay: 400ms;
35+ }
36+ .second-two {
37+ animation: opacity-fill 2000ms linear forwards infinite;
38+ animation-delay: 500ms;
39+ }
40+
41+ .second-three {
42+ animation: opacity-fill 2000ms linear forwards infinite;
43+ animation-delay: 600ms;
44+ }
45+
46+ .third-one {
47+ animation: opacity-fill 2000ms linear forwards infinite;
48+ animation-delay: 700ms;
49+ }
50+ .third-two {
51+ animation: opacity-fill 2000ms linear forwards infinite;
52+ animation-delay: 800ms;
53+ }
54+ .third-three {
55+ animation: opacity-fill 2000ms linear forwards infinite;
56+ animation-delay: 900ms;
57+ }
58+
59+ .fourth-one {
60+ animation: opacity-fill 2000ms linear forwards infinite;
61+ animation-delay: 1s;
62+ }
63+
64+ .fourth-two {
65+ animation: opacity-fill 2000ms linear forwards infinite;
66+ animation-delay: 1.1s;
67+ }
68+
69+ .fourth-three {
70+ animation: opacity-fill 2000ms linear forwards infinite;
71+ animation-delay: 1.2s;
72+ }
73+
74+ &.paused g {
75+ animation-play-state: paused;
76+ }
77+
78+ &.paused path {
79+ animation-play-state: paused;
80+ }
81+}
src/style/views/activate.scssdeleted−76
@@ -1,76 +0,0 @@
1−div.activate-view {
2− border: 1px solid rgb(204, 204, 204);
3− border-radius: 5px;
4− padding: 10px;
5− min-width: 600px;
6− min-height: 500px;
7−
8− @keyframes opacity-fill {
9− 0% {
10− fill-opacity: 0.2;
11− }
12− 100% {
13− fill-opacity: 1.0;
14− }
15− }
16−
17− svg.master-key {
18− .first-one {
19− animation: opacity-fill 200ms linear forwards infinite;
20− animation-delay: 100ms;
21− }
22−
23− .first-two {
24− animation: opacity-fill 200ms linear forwards infinite;
25− animation-delay: 200ms;
26− }
27−
28− .first-three {
29− animation: opacity-fill 200ms linear forwards infinite;
30− animation-delay: 300ms;
31−
32− }
33−
34− .second-one {
35− animation: opacity-fill 200ms linear forwards infinite;
36− animation-delay: 400ms;
37− }
38− .second-two {
39− animation: opacity-fill 200ms linear forwards infinite;
40− animation-delay: 500ms;
41− }
42−
43− .second-three {
44− animation: opacity-fill 200ms linear forwards infinite;
45− animation-delay: 600ms;
46− }
47−
48− .third-one {
49− animation: opacity-fill 200ms linear forwards infinite;
50− animation-delay: 700ms;
51− }
52− .third-two {
53− animation: opacity-fill 200ms linear forwards infinite;
54− animation-delay: 800ms;
55− }
56− .third-three {
57− animation: opacity-fill 200ms linear forwards infinite;
58− animation-delay: 900ms;
59− }
60−
61− .fourth-one {
62− animation: opacity-fill 200ms linear forwards infinite;
63− animation-delay: 1s;
64− }
65−
66− .fourth-two {
67− animation: opacity-fill 200ms linear forwards infinite;
68− animation-delay: 1.1s;
69− }
70−
71− .fourth-three {
72− animation: opacity-fill 200ms linear forwards infinite;
73− animation-delay: 1.2s;
74− }
75− }
76−}
src/views/Activate.js+3
@@ -8,18 +8,21 @@ import { ActivateFlowProvider } from './Activate/ActivateFlow';
88import useActivateFlowState from './Activate/useActivateFlowState';
99import ActivateCode from './Activate/ActivateCode';
1010import ActivatePassport from './Activate/ActivatePassport';
11+import ActivateMasterKey from './Activate/ActivateMasterKey';
1112import Disclaimer from './Disclaimer';
1213
1314const NAMES = {
1415 CODE: 'CODE',
1516 DISCLAIMER: 'DISCLAIMER',
1617 PASSPORT: 'PASSPORT',
18+ MASTER_KEY: 'MASTER_KEY',
1719};
1820
1921const VIEWS = {
2022 [NAMES.CODE]: ActivateCode,
2123 [NAMES.PASSPORT]: ActivatePassport,
2224 [NAMES.DISCLAIMER]: Disclaimer,
25+ [NAMES.MASTER_KEY]: ActivateMasterKey,
2326};
2427
2528export default function Activate() {
src/views/Activate/ActivateCode.tsx+24−19
@@ -61,9 +61,11 @@ export default function ActivateCode() {
6161 const [isGenerating, setIsGenerating] = useState<boolean>(false);
6262
6363 const {
64+ isFaded,
6465 setDerivedPoint,
6566 setDerivedWallet,
6667 setInviteWallet,
68+ setIsFaded,
6769 } = useActivateFlow();
6870 // this is a pretty naive way to detect if we're on a mobile device
6971 // (i.e. we're checking the width of the screen)
@@ -76,13 +78,13 @@ export default function ActivateCode() {
7678 history,
7779 ]);
7880
79− const goToPassport = useCallback(() => {
81+ const goToMasterKey = useCallback(() => {
8082 if (!hasDisclaimed) {
81− push(names.DISCLAIMER, { next: names.PASSPORT });
83+ push(names.DISCLAIMER, { next: names.MASTER_KEY });
8284 } else {
83− push(names.PASSPORT);
85+ push(names.MASTER_KEY);
8486 }
85− }, [hasDisclaimed, names.DISCLAIMER, names.PASSPORT, push]);
87+ }, [hasDisclaimed, names.DISCLAIMER, names.MASTER_KEY, push]);
8688
8789 const validateForm = useCallback((values, errors) => {
8890 didWarn.current = false;
@@ -131,7 +133,8 @@ export default function ActivateCode() {
131133 const onSubmit = useCallback(
132134 async values => {
133135 setIsGenerating(true);
134− await timeout(16); // allow the ui changes to flush before we lag it out
136+ setIsFaded(true);
137+ await timeout(1400); // Allow the Master Key animation to run for one cycle
135138
136139 // Derive wallet
137140 // TODO: fallback logic to first use implied values if present, otherwise form values?
@@ -220,7 +223,7 @@ export default function ActivateCode() {
220223 <BridgeForm
221224 validate={validate}
222225 onSubmit={onSubmit}
223− afterSubmit={goToPassport}
226+ afterSubmit={goToMasterKey}
224227 initialValues={initialValues}>
225228 {({ validating, values, submitting, handleSubmit }) => (
226229 <>
@@ -275,7 +278,7 @@ export default function ActivateCode() {
275278 );
276279 }, [
277280 activationAllowed,
278− goToPassport,
281+ goToMasterKey,
279282 impliedTicket,
280283 initialValues,
281284 onSubmit,
@@ -302,19 +305,21 @@ export default function ActivateCode() {
302305 <Text className="mb2">Enter your activation code to continue.</Text>
303306 )}
304307 {!isGenerating && impliedPatp && (
305− <PointPresenter patp={impliedPatp} />
306− )}
307− {isGenerating && (
308− <Box
309− display={'flex'}
310− flexDirection={'row'}
311− flexWrap={'nowrap'}
312− width={'80%'}
313− height={'min-content'}
314− justifyContent={'center'}>
315− <MasterKey />
316− </Box>
308+ <PointPresenter
309+ patp={impliedPatp}
310+ className={!isFaded ? 'faded-in' : 'faded-out'}
311+ />
317312 )}
313+ <Box
314+ display={'flex'}
315+ flexDirection={'row'}
316+ flexWrap={'nowrap'}
317+ className={isFaded ? 'faded-in' : 'faded-out'}
318+ width={'80%'}
319+ height={'min-content'}
320+ justifyContent={'center'}>
321+ <MasterKey />
322+ </Box>
318323 </Box>
319324 </ActivateView>
320325
src/views/Activate/ActivateMasterKey.tsxnew file+71
@@ -0,0 +1,71 @@
1+import React, { useMemo } from 'react';
2+import cn from 'classnames';
3+import { Grid } from 'indigo-react';
4+
5+import { LocalRouterProvider, useLocalRouter } from 'lib/LocalRouter';
6+
7+import View from 'components/View';
8+import Passport from 'components/Passport';
9+
10+import { useActivateFlow } from './ActivateFlow';
11+import useBreakpoints from 'lib/useBreakpoints';
12+import useRouter from 'lib/useRouter';
13+
14+import PassportDownload from './PassportDownload';
15+import PassportVerify from './PassportVerify';
16+import PassportTransfer from './PassportTransfer';
17+import { walletFromMnemonic } from 'lib/wallet';
18+import { DEFAULT_HD_PATH } from 'lib/constants';
19+import { publicToAddress } from 'lib/utils/address';
20+import MasterKeyConfirm from './MasterKeyConfirm';
21+
22+const NAMES = {
23+ CONFIRM: 'CONFIRM',
24+ DOWNLOAD: 'DOWNLOAD',
25+ VERIFY: 'VERIFY',
26+ TRANSFER: 'TRANSFER',
27+};
28+
29+const VIEWS = {
30+ [NAMES.CONFIRM]: MasterKeyConfirm,
31+ [NAMES.DOWNLOAD]: PassportDownload,
32+ [NAMES.VERIFY]: PassportVerify,
33+ [NAMES.TRANSFER]: PassportTransfer,
34+};
35+
36+export default function ActivatePassport() {
37+ const { derivedPoint, generated, derivedWallet } = useActivateFlow();
38+ // pull the reset function out of the 'activate' router
39+ const { reset: resetActivateRouter } = useLocalRouter();
40+ const { Route, ...router } = useRouter({
41+ names: NAMES,
42+ views: VIEWS,
43+ initialRoutes: [{ key: NAMES.CONFIRM }],
44+ });
45+
46+ const gap = useBreakpoints([4, 4, 7]);
47+ const marginTop = useBreakpoints([false, false, 8]);
48+
49+ // addresses are not derived until we set[Urbit]Wallet(), so do that inline
50+ const address = useMemo(
51+ () =>
52+ derivedWallet
53+ .chain(wallet =>
54+ walletFromMnemonic(
55+ wallet.ownership.seed,
56+ DEFAULT_HD_PATH,
57+ wallet.meta.passphrase
58+ )
59+ )
60+ .map(wallet => publicToAddress(wallet.publicKey)),
61+ [derivedWallet]
62+ );
63+ //~hobbyn-mismur-fonrux-datber
64+ return (
65+ <View inset>
66+ <LocalRouterProvider value={router}>
67+ <Route resetActivateRouter={resetActivateRouter} />
68+ </LocalRouterProvider>
69+ </View>
70+ );
71+}
src/views/Activate/ActivateParagraph.tsxnew file+29
@@ -0,0 +1,29 @@
1+import { Box, Text } from '@tlon/indigo-react';
2+
3+type ActivateParagraphProps = {
4+ copy: string;
5+};
6+
7+const ActivateParagraph = ({ copy }: ActivateParagraphProps) => {
8+ return (
9+ <Box
10+ display={'flex'}
11+ flexDirection={'column'}
12+ flexWrap={'nowrap'}
13+ justifyContent={'center'}
14+ alignItems={'center'}>
15+ <Text
16+ display="block"
17+ fontFamily="Inter"
18+ fontSize={2}
19+ mx={3}
20+ my={3}
21+ textAlign="center"
22+ width={'80%'}>
23+ {copy}
24+ </Text>
25+ </Box>
26+ );
27+};
28+
29+export default ActivateParagraph;
src/views/Activate/ActivateSteps.tsxnew file+44
@@ -0,0 +1,44 @@
1+import { Box } from '@tlon/indigo-react';
2+
3+type ActivateStepsProps = {
4+ currentStep: number;
5+ totalSteps: number;
6+ className?: string;
7+};
8+
9+export const ActivateSteps = ({
10+ currentStep,
11+ totalSteps,
12+ className,
13+}: ActivateStepsProps) => {
14+ const renderSteps = () => {
15+ let steps = [];
16+ for (let i = 0; i < totalSteps; i++) {
17+ steps.push(
18+ <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}
23+ key={i}
24+ backgroundColor={i <= currentStep ? 'black' : 'gray'}
25+ height={'10px'}
26+ width={`${100 / totalSteps}%`}
27+ marginLeft={'2px'}
28+ marginRight={'2px'}></Box>
29+ );
30+ }
31+ return steps;
32+ };
33+
34+ return (
35+ <Box
36+ display={'flex'}
37+ flexDirection={'row'}
38+ flexWrap={'nowrap'}
39+ width={'100%'}
40+ className={className}>
41+ {renderSteps()}
42+ </Box>
43+ );
44+};
src/views/Activate/ActivateView.tsx+9−1
@@ -24,7 +24,15 @@ export default function ActivateView({
2424 gridTemplateColumns="1fr"
2525 gridTemplateRows="20% 60% 20%"
2626 gridTemplateAreas={"'header' 'content' 'footer'"}
27− className={cn(className, 'activate-view')}>
27+ border={'1px solid rgb(204, 204, 204)'}
28+ border-radius={'5px'}
29+ padding={'10px'}
30+ width={'550px'}
31+ height={'550px'}
32+ max-width={'550px'}
33+ max-height={'550px'}
34+ className={cn(className)}
35+ mb={3}>
2836 <Box gridArea="header">{header}</Box>
2937 <Box gridArea="content">{children}</Box>
3038 <Box gridArea="footer">{footer ? footer : null}</Box>
src/views/Activate/MasterKey.tsx+7−2
@@ -1,6 +1,11 @@
1−export const MasterKey = () => {
1+import cn from 'classnames';
2+import 'style/master-key.scss';
3+
4+export const MasterKey = ({ paused = false }) => {
25 return (
3− <svg className="master-key" viewBox="0 0 300 32">
6+ <svg
7+ className={cn({ 'master-key': true, paused: paused })}
8+ viewBox="0 0 300 32">
49 <g>
510 <path
611 className="first-one"
src/views/Activate/MasterKeyConfirm.tsxnew file+92
@@ -0,0 +1,92 @@
1+import React, { useEffect, useMemo, useState } from 'react';
2+
3+import ActivateView from './ActivateView';
4+import ActivateHeader from './ActivateHeader';
5+import { MasterKey } from './MasterKey';
6+import { Box, Button, Icon } from '@tlon/indigo-react';
7+import ActivateParagraph from './ActivateParagraph';
8+import { ActivateSteps } from './ActivateSteps';
9+import { useActivateFlow } from './ActivateFlow';
10+
11+const MasterKeyConfirm = () => {
12+ const { isFaded, setIsFaded } = useActivateFlow();
13+
14+ const header = useMemo(() => {
15+ return (
16+ <Box className={isFaded ? 'faded-in' : 'faded-out'}>
17+ <ActivateHeader copy={'Here is your Master Key.'} />
18+ <ActivateParagraph
19+ copy={
20+ "Your Master Key is your 4-word password for your Urbit. Make sure you're in a private place before you reveal it."
21+ }
22+ />
23+ </Box>
24+ );
25+ }, [isFaded]);
26+
27+ const footer = useMemo(() => {
28+ return (
29+ <Box
30+ display="flex"
31+ flexDirection="column"
32+ flexWrap="nowrap"
33+ height={'100%'}
34+ className={isFaded ? 'faded-in' : 'faded-out'}
35+ justifyContent="flex-end">
36+ <Button
37+ onClick={() => console.log('reveal')}
38+ backgroundColor="black"
39+ color={'white'}
40+ padding={'16px'}
41+ fontFamily="Inter"
42+ height={'50px'}
43+ fontWeight={'400'}
44+ fontSize={'18px'}>
45+ <Icon
46+ display="inline-block"
47+ icon="Visible"
48+ size="18px"
49+ color={'white'}
50+ />
51+ &nbsp; Reveal
52+ </Button>
53+ </Box>
54+ );
55+ }, [isFaded]);
56+
57+ useEffect(() => {
58+ // Fade in content once
59+ setIsFaded(true);
60+ }, []);
61+
62+ return (
63+ <>
64+ <ActivateView header={header} footer={footer}>
65+ <Box
66+ alignItems={'center'}
67+ display={'flex'}
68+ flexDirection={'column'}
69+ flexWrap={'nowrap'}
70+ height={'100%'}
71+ justifyContent={'center'}>
72+ <Box
73+ display={'flex'}
74+ flexDirection={'row'}
75+ flexWrap={'nowrap'}
76+ width={'80%'}
77+ height={'min-content'}
78+ justifyContent={'center'}>
79+ <MasterKey paused={true} />
80+ </Box>
81+ </Box>
82+ </ActivateView>
83+ <ActivateSteps
84+ currentStep={0}
85+ totalSteps={4}
86+ className={isFaded ? 'faded-in' : 'faded-out'}
87+ />
88+ </>
89+ );
90+};
91+
92+export default MasterKeyConfirm;
src/views/Activate/useActivateFlowState.js+4
@@ -7,6 +7,7 @@ export default function useActivateFlowState() {
77 const [derivedPoint, setDerivedPoint] = useState(Nothing());
88 const [generated, setGenerated] = useState(false);
99 const [incomingPoints, setIncomingPoints] = useState(Nothing());
10+ const [isFaded, setIsFaded] = useState(false);
1011
1112 const reset = useCallback(() => {
1213 setDerivedWallet(Nothing());
@@ -14,6 +15,7 @@ export default function useActivateFlowState() {
1415 setDerivedPoint(Nothing());
1516 setGenerated(false);
1617 setIncomingPoints(Nothing());
18+ setIsFaded(false);
1719 }, [
1820 setDerivedWallet,
1921 setInviteWallet,
@@ -33,6 +35,8 @@ export default function useActivateFlowState() {
3335 setGenerated,
3436 incomingPoints,
3537 setIncomingPoints,
38+ isFaded,
39+ setIsFaded,
3640 reset,
3741 };
3842}