on master help sign in
Merge pull request #864 from willbach/activate-design-fixes
a3cf616tomholford
src/assets/blank-planet-sigil.svgnew file+39
@@ -0,0 +1,39 @@
1+<svg width="96" height="96" viewBox="0 0 96 96" fill="none" xmlns="http://www.w3.org/2000/svg">
2+<rect opacity="0.9" width="96" height="96" rx="8" fill="black"/>
3+<circle cx="29" cy="29" r="1" fill="white"/>
4+<circle cx="29" cy="36" r="1" fill="white"/>
5+<circle cx="29" cy="43" r="1" fill="white"/>
6+<circle cx="36" cy="29" r="1" fill="white"/>
7+<circle cx="36" cy="36" r="1" fill="white"/>
8+<circle cx="36" cy="43" r="1" fill="white"/>
9+<circle cx="43" cy="29" r="1" fill="white"/>
10+<circle cx="43" cy="36" r="1" fill="white"/>
11+<circle cx="43" cy="43" r="1" fill="white"/>
12+<circle cx="53" cy="29" r="1" fill="white"/>
13+<circle cx="53" cy="36" r="1" fill="white"/>
14+<circle cx="53" cy="43" r="1" fill="white"/>
15+<circle cx="60" cy="29" r="1" fill="white"/>
16+<circle cx="60" cy="36" r="1" fill="white"/>
17+<circle cx="60" cy="43" r="1" fill="white"/>
18+<circle cx="67" cy="29" r="1" fill="white"/>
19+<circle cx="67" cy="36" r="1" fill="white"/>
20+<circle cx="67" cy="43" r="1" fill="white"/>
21+<circle cx="29" cy="53" r="1" fill="white"/>
22+<circle cx="29" cy="60" r="1" fill="white"/>
23+<circle cx="29" cy="67" r="1" fill="white"/>
24+<circle cx="36" cy="53" r="1" fill="white"/>
25+<circle cx="36" cy="60" r="1" fill="white"/>
26+<circle cx="36" cy="67" r="1" fill="white"/>
27+<circle cx="43" cy="53" r="1" fill="white"/>
28+<circle cx="43" cy="60" r="1" fill="white"/>
29+<circle cx="43" cy="67" r="1" fill="white"/>
30+<circle cx="53" cy="53" r="1" fill="white"/>
31+<circle cx="53" cy="60" r="1" fill="white"/>
32+<circle cx="53" cy="67" r="1" fill="white"/>
33+<circle cx="60" cy="53" r="1" fill="white"/>
34+<circle cx="60" cy="60" r="1" fill="white"/>
35+<circle cx="60" cy="67" r="1" fill="white"/>
36+<circle cx="67" cy="53" r="1" fill="white"/>
37+<circle cx="67" cy="60" r="1" fill="white"/>
38+<circle cx="67" cy="67" r="1" fill="white"/>
39+</svg>
src/components/L2/Window/Window.scss+1
@@ -5,4 +5,5 @@
5 border: 1px solid $light-gray;
5 border: 1px solid $light-gray;
6 width: 480px;
6 width: 480px;
7 max-width: calc(100vw - 32px);
7 max-width: calc(100vw - 32px);
8+ position: relative;
8}
9}
src/components/L2/Window/Window.tsx+1−1
@@ -7,7 +7,7 @@ export interface WindowProps {
7 className?: string;
7 className?: string;
8}
8}
9
9
10−const Window = ({ children, className }: WindowProps) => {
10+const Window = ({ children, className = '' }: WindowProps) => {
11 return <Box className={`window ${className}`}>{children}</Box>;
11 return <Box className={`window ${className}`}>{children}</Box>;
12};
12};
13
13
src/components/View.js+1−1
@@ -3,7 +3,7 @@ import cn from 'classnames';
3import { Flex } from 'indigo-react';
3import { Flex } from 'indigo-react';
4
4
5import Footer from './Footer';
5import Footer from './Footer';
6−import useBreakpoints from 'lib/useBreakpoints';
6+import useBreakpoints from 'lib/useBreakpoints.tsx';
7import { MiniBackButton } from './MiniBackButton';
7import { MiniBackButton } from './MiniBackButton';
8import { useHistory } from 'store/history';
8import { useHistory } from 'store/history';
9import NavHeader from './NavHeader';
9import NavHeader from './NavHeader';
src/components/form/TicketInputAccessory.scssnew file+7
@@ -0,0 +1,7 @@
1+.ticket-input-accessory {
2+ .icon {
3+ height: 14px;
4+ width: 14px;
5+ margin-right: -4px;
6+ }
7+}
src/components/form/TicketInputAccessory.tsxnew file+17
@@ -0,0 +1,17 @@
1+import { Icon } from '@tlon/indigo-react';
2+import { AccessoryIcon, ToggleInput } from 'indigo-react';
3+
4+import './TicketInputAccessory.scss';
5+
6+export default function TicketInputAccessory({ name }: { name: any }) {
7+ return (
8+ <AccessoryIcon className="ticket-input-accessory">
9+ <ToggleInput
10+ name={name}
11+ className="mt1"
12+ inverseLabel={<Icon className="icon" icon="Visible" />}
13+ label={<Icon className="icon" icon="Hidden" />}
14+ />
15+ </AccessoryIcon>
16+ );
17+}
src/form/Inputs.tsx+1−1
@@ -21,7 +21,7 @@ import {
21
21
22const PLACEHOLDER_GALAXY = '~pel';
22const PLACEHOLDER_GALAXY = '~pel';
23const PLACEHOLDER_STAR = '~sampel';
23const PLACEHOLDER_STAR = '~sampel';
24−const PLACEHOLDER_PLANET = '~sampel-ponnym';
24+export const PLACEHOLDER_PLANET = '~sampel-ponnym';
25const PLACEHOLDER_HD_PATH = DEFAULT_HD_PATH;
25const PLACEHOLDER_HD_PATH = DEFAULT_HD_PATH;
26const PLACEHOLDER_MNEMONIC =
26const PLACEHOLDER_MNEMONIC =
27 'example crew supreme gesture quantum web media hazard theory mercy wing kitten';
27 'example crew supreme gesture quantum web media hazard theory mercy wing kitten';
src/lib/useBreakpoints.jsdeleted−5
@@ -1,5 +0,0 @@
1−import { useMediaQuery } from './MediaQuery';
2−
3−export default function useBreakpoints(values = [null, null, null]) {
4− return values[useMediaQuery()];
5−}
src/lib/useBreakpoints.tsxnew file+6
@@ -0,0 +1,6 @@
1+import { useMediaQuery } from './MediaQuery';
2+
3+export default function useBreakpoints(values: any = [null, null, null]) {
4+ const query = useMediaQuery() || 0;
5+ return values[query];
6+}
src/lib/validators.ts+22−12
@@ -27,19 +27,19 @@ const isHexString = /[0-9A-Fa-f]/;
27const isHexPrefixed = /^0x/;
27const isHexPrefixed = /^0x/;
28
28
29// Validates a bip39 mnemonic
29// Validates a bip39 mnemonic
30−export const validateMnemonic = v =>
30+export const validateMnemonic = (v: string) =>
31 !bip39.validateMnemonic(v) && 'This is not a valid mnemonic.';
31 !bip39.validateMnemonic(v) && 'This is not a valid mnemonic.';
32
32
33const isHdPath = /^m(\/[0-9]+'?)*$/;
33const isHdPath = /^m(\/[0-9]+'?)*$/;
34−export const validateHdPath = v =>
34+export const validateHdPath = (v: string) =>
35 !isHdPath.test(v) && 'Invalid HD derivation path.';
35 !isHdPath.test(v) && 'Invalid HD derivation path.';
36
36
37// Checks an empty field
37// Checks an empty field
38−export const validateNotEmpty = v =>
38+export const validateNotEmpty = (v: string) =>
39 (v === undefined || v.length === 0) && 'This field is required.';
39 (v === undefined || v.length === 0) && 'This field is required.';
40
40
41// Checks if a patp is a valid galaxy
41// Checks if a patp is a valid galaxy
42−export const validateGalaxy = v => {
42+export const validateGalaxy = (v: string) => {
43 try {
43 try {
44 const point = patp2dec(v);
44 const point = patp2dec(v);
45 const isValidGalaxy = point >= MIN_GALAXY && point <= MAX_GALAXY;
45 const isValidGalaxy = point >= MIN_GALAXY && point <= MAX_GALAXY;
@@ -51,7 +51,7 @@ export const validateGalaxy = v => {
51 }
51 }
52};
52};
53
53
54−export const validatePoint = v => {
54+export const validatePoint = (v: string) => {
55 try {
55 try {
56 if (!ob.isValidPatp(v)) {
56 if (!ob.isValidPatp(v)) {
57 throw new Error();
57 throw new Error();
@@ -61,7 +61,7 @@ export const validatePoint = v => {
61 }
61 }
62};
62};
63
63
64−export const validatePatq = v => {
64+export const validatePatq = (v: string) => {
65 try {
65 try {
66 if (!ob.isValidPatq(v)) {
66 if (!ob.isValidPatq(v)) {
67 throw new Error();
67 throw new Error();
@@ -71,7 +71,17 @@ export const validatePatq = v => {
71 }
71 }
72};
72};
73
73
74+export const validateActivationTicket = (v: string) => {
75+ try {
76+ if (v.length < 42) {
77+ throw new Error();
78+ }
79+ } catch {
80+ return 'This is not a valid activation ticket.';
81+ }
82+};
83+
74−export const validateShard = v => {
84+export const validateShard = (v: string) => {
75 try {
85 try {
76 if (v !== undefined && v !== '' && !ob.isValidPatq(v)) {
86 if (v !== undefined && v !== '' && !ob.isValidPatq(v)) {
77 throw new Error();
87 throw new Error();
@@ -81,19 +91,19 @@ export const validateShard = v => {
81 }
91 }
82};
92};
83
93
84−export const validateOneOf = (options = []) => v =>
94+export const validateOneOf = (options = []) => (v: string) =>
85 !includes(options, v) && 'Is not a valid option.';
95 !includes(options, v) && 'Is not a valid option.';
86
96
87−export const validateHexString = v =>
97+export const validateHexString = (v: string) =>
88 !isHexString.test(v) && 'This is not a valid hex string.';
98 !isHexString.test(v) && 'This is not a valid hex string.';
89
99
90−export const validateHexPrefix = v =>
100+export const validateHexPrefix = (v: string) =>
91 !isHexPrefixed.test(v) && 'Must include 0x prefix.';
101 !isHexPrefixed.test(v) && 'Must include 0x prefix.';
92
102
93−export const validateEthereumAddress = v =>
103+export const validateEthereumAddress = (v: string) =>
94 !isValidAddress(v) && 'This is not a valid Ethereum address.';
104 !isValidAddress(v) && 'This is not a valid Ethereum address.';
95
105
96−export const validateEmail = v =>
106+export const validateEmail = (v: string) =>
97 !emailRegExp.test(v) && 'This is not a valid email address.';
107 !emailRegExp.test(v) && 'This is not a valid email address.';
98
108
99export const validateExactly = (value, error) => v => v !== value && error;
109export const validateExactly = (value, error) => v => v !== value && error;
src/style/master-key.scss+1
@@ -18,6 +18,7 @@
18
18
19svg.master-key {
19svg.master-key {
20 height: 44px;
20 height: 44px;
21+ margin: 107px 0;
21 // Initial state
22 // Initial state
22 path {
23 path {
23 fill-opacity: 0.2;
24 fill-opacity: 0.2;
src/views/AcceptTransfer.tsx+16−14
@@ -150,20 +150,22 @@ export default function AcceptTransfer() {
150 : `Accept the incoming transfer of ${name}.`}
150 : `Accept the incoming transfer of ${name}.`}
151 </Grid.Item>
151 </Grid.Item>
152
152
153+ {!completed && (
153− <BridgeForm
154+ <BridgeForm
154− validate={validate}
155+ validate={validate}
155− initialValues={initialValues}
156+ initialValues={initialValues}
156− onValues={onValues}>
157+ onValues={onValues}>
157− {() => (
158+ {() => (
158− <Grid.Item
159+ <Grid.Item
159− full
160+ full
160− as={CheckboxInput}
161+ as={CheckboxInput}
161− name="noReset"
162+ name="noReset"
162− label="Retain proxies and key configuration, in case of transferring to self"
163+ label="Retain proxies and key configuration, in case of transferring to self"
163− disabled={inputsLocked}
164+ disabled={inputsLocked}
164− />
165+ />
165− )}
166+ )}
166− </BridgeForm>
167+ </BridgeForm>
168+ )}
167
169
168 {point.isL2 ? (
170 {point.isL2 ? (
169 <Grid.Item
171 <Grid.Item
src/views/Activate/ActivateCode.tsx+22−15
@@ -1,4 +1,4 @@
1−import { useCallback } from 'react';
1+import { useCallback, useMemo } from 'react';
2
2
3import View from 'components/View';
3import View from 'components/View';
4import ActivateView from './ActivateView';
4import ActivateView from './ActivateView';
@@ -7,14 +7,15 @@ import { useLocalRouter } from 'lib/LocalRouter';
7import useImpliedTicket from 'lib/useImpliedTicket';
7import useImpliedTicket from 'lib/useImpliedTicket';
8import useHasDisclaimed from 'lib/useHasDisclaimed';
8import useHasDisclaimed from 'lib/useHasDisclaimed';
9
9
10+import { ReactComponent as PlaceholderSigil } from 'assets/blank-planet-sigil.svg';
10import { FadeablePointPresenter as PointPresenter } from './PointPresenter';
11import { FadeablePointPresenter as PointPresenter } from './PointPresenter';
11−import { Box, Text } from '@tlon/indigo-react';
12+import { Box } from '@tlon/indigo-react';
12import { FadeableActivateHeader as ActivateHeader } from './ActivateHeader';
13import { FadeableActivateHeader as ActivateHeader } from './ActivateHeader';
13import { FadeableActivateCodeForm as ActivateCodeForm } from './ActivateCodeForm';
14import { FadeableActivateCodeForm as ActivateCodeForm } from './ActivateCodeForm';
14import useFadeIn from './useFadeIn';
15import useFadeIn from './useFadeIn';
15
16
16export default function ActivateCode() {
17export default function ActivateCode() {
17− const { names, push } = useLocalRouter();
18+ const { names, push }: any = useLocalRouter();
18 const { impliedPatp, impliedTicket } = useImpliedTicket();
19 const { impliedPatp, impliedTicket } = useImpliedTicket();
19 const [hasDisclaimed] = useHasDisclaimed();
20 const [hasDisclaimed] = useHasDisclaimed();
20
21
@@ -29,22 +30,28 @@ export default function ActivateCode() {
29 // Fade in on load
30 // Fade in on load
30 useFadeIn();
31 useFadeIn();
31
32
33+ const hasInitialValues = useMemo(
34+ () => Boolean(impliedTicket && impliedPatp),
35+ [impliedTicket, impliedPatp]
36+ );
37+
32 return (
38 return (
33− <View centered={true}>
39+ <View inset>
34 <ActivateView
40 <ActivateView
41+ hideBack={hasInitialValues}
35− header={<ActivateHeader content={'Welcome. This is your Urbit.'} />}
42+ header={<ActivateHeader content={'Planet Code'} />}
36 footer={<ActivateCodeForm afterSubmit={goToMasterKey} />}>
43 footer={<ActivateCodeForm afterSubmit={goToMasterKey} />}>
37− <Box
44+ <Box className="flex-col align-center justify-center w-full h-full">
38− alignItems={'center'}
45+ {!hasInitialValues && (
39− display={'flex'}
46+ <Box className="mb2 sans gray5" fontSize={14}>
40− flexDirection={'column'}
47+ Enter your planet code below
41− flexWrap={'nowrap'}
48+ </Box>
42− height={'100%'}
49+ )}
43− justifyContent={'center'}>
50+ {impliedPatp ? (
44− {!impliedTicket && (
51+ <PointPresenter patp={impliedPatp} className="mb6 mt7" />
45− <Text className="mb2">Enter your activation code to continue.</Text>
52+ ) : (
53+ <PlaceholderSigil className="mv9" style={{ marginTop: 78 }} />
46 )}
54 )}
47− {impliedPatp && <PointPresenter patp={impliedPatp} />}
48 </Box>
55 </Box>
49 </ActivateView>
56 </ActivateView>
50 </View>
57 </View>
src/views/Activate/ActivateCodeForm.tsx+44−43
@@ -4,7 +4,7 @@ import { WARNING } from 'form/helpers';
4import { FORM_ERROR } from 'final-form';
4import { FORM_ERROR } from 'final-form';
5import { Just } from 'folktale/maybe';
5import { Just } from 'folktale/maybe';
6import BridgeForm from 'form/BridgeForm';
6import BridgeForm from 'form/BridgeForm';
7−import { TicketInput } from 'form/Inputs';
7+import { PLACEHOLDER_PLANET, PLACEHOLDER_TICKET, TicketInput } from 'form/Inputs';
8import {
8import {
9 composeValidator,
9 composeValidator,
10 buildPatqValidator,
10 buildPatqValidator,
@@ -12,7 +12,7 @@ import {
12 buildCheckboxValidator,
12 buildCheckboxValidator,
13} from 'form/validators';
13} from 'form/validators';
14import FormError from 'form/FormError';
14import FormError from 'form/FormError';
15−import { CheckboxInput, Grid } from 'indigo-react';
15+import { Grid } from 'indigo-react';
16import { useCallback, useMemo, useRef } from 'react';
16import { useCallback, useMemo, useRef } from 'react';
17import { useActivateFlow } from './ActivateFlow';
17import { useActivateFlow } from './ActivateFlow';
18import WarningBox from 'components/WarningBox';
18import WarningBox from 'components/WarningBox';
@@ -21,13 +21,15 @@ import useImpliedTicket from 'lib/useImpliedTicket';
21import { generateWallet } from 'lib/walletgen';
21import { generateWallet } from 'lib/walletgen';
22import { urbitWalletFromTicket, walletFromMnemonic } from 'lib/wallet';
22import { urbitWalletFromTicket, walletFromMnemonic } from 'lib/wallet';
23import { DEFAULT_HD_PATH, POINT_PROXIES } from 'lib/constants';
23import { DEFAULT_HD_PATH, POINT_PROXIES } from 'lib/constants';
24−import useBreakpoints from 'lib/useBreakpoints';
24+import useBreakpoints from 'lib/useBreakpoints.tsx';
25import { Ship } from '@urbit/roller-api';
25import { Ship } from '@urbit/roller-api';
26import { convertToInt } from 'lib/convertToInt';
26import { convertToInt } from 'lib/convertToInt';
27import { Box } from '@tlon/indigo-react';
27import { Box } from '@tlon/indigo-react';
28import { timeout } from 'lib/timeout';
28import { timeout } from 'lib/timeout';
29import withFadeable from './withFadeable';
29import withFadeable from './withFadeable';
30import ActivateButton from './ActivateButton';
30import ActivateButton from './ActivateButton';
31+import TicketInputAccessory from 'components/form/TicketInputAccessory';
32+import { validateActivationTicket } from 'lib/validators';
31
33
32interface ActivateCodeFormProps {
34interface ActivateCodeFormProps {
33 afterSubmit: VoidFunction;
35 afterSubmit: VoidFunction;
@@ -46,7 +48,7 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
46 setInviteWallet,
48 setInviteWallet,
47 setIsIn,
49 setIsIn,
48 setSendWallet,
50 setSendWallet,
49− } = useActivateFlow();
51+ }: any = useActivateFlow();
50
52
51 // this is a pretty naive way to detect if we're on a mobile device
53 // this is a pretty naive way to detect if we're on a mobile device
52 // (i.e. we're checking the width of the screen)
54 // (i.e. we're checking the width of the screen)
@@ -69,12 +71,20 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
69 const validate = useMemo(
71 const validate = useMemo(
70 () =>
72 () =>
71 composeValidator(
73 composeValidator(
74+ {
75+ ticket: buildPatqValidator([validateActivationTicket]),
72− { ticket: buildPatqValidator(), showTicket: buildCheckboxValidator() },
76+ showTicket: buildCheckboxValidator(),
77+ },
73 validateForm
78 validateForm
74 ),
79 ),
75 [validateForm]
80 [validateForm]
76 );
81 );
77
82
83+ const hasInitialValues = useMemo(
84+ () => Boolean(impliedTicket && impliedPatp),
85+ [impliedTicket, impliedPatp]
86+ );
87+
78 const getTicketAndPoint = useCallback(
88 const getTicketAndPoint = useCallback(
79 (invite: string) => {
89 (invite: string) => {
80 const segments = invite.split('-');
90 const segments = invite.split('-');
@@ -164,20 +174,24 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
164 setDerivedWallet,
174 setDerivedWallet,
165 setInviteWallet,
175 setInviteWallet,
166 setIsIn,
176 setIsIn,
177+ setSendWallet,
167 ]
178 ]
168 );
179 );
169
180
170 const initialValues = useMemo(
181 const initialValues = useMemo(
182+ () => ({
183+ ticket: hasInitialValues
184+ ? `${impliedTicket}-${impliedPatp?.slice(1)}`
185+ : '',
171− () => ({ ticket: impliedTicket || '', showTicket: true }),
186+ showTicket: true,
187+ }),
172− [impliedTicket]
188+ [impliedTicket, impliedPatp, hasInitialValues]
173 );
189 );
174
190
175 return (
191 return (
176 <Box
192 <Box
177− display="flex"
193+ className="w-full h-full flex-col"
178− flexDirection="column"
179 flexWrap="nowrap"
194 flexWrap="nowrap"
180− height={'100%'}
181 justifyContent="flex-end">
195 justifyContent="flex-end">
182 <BridgeForm
196 <BridgeForm
183 validate={validate}
197 validate={validate}
@@ -186,41 +200,28 @@ const ActivateCodeForm = ({ afterSubmit }: ActivateCodeFormProps) => {
186 initialValues={initialValues}>
200 initialValues={initialValues}>
187 {({ valid, validating, values, submitting, handleSubmit }) => (
201 {({ valid, validating, values, submitting, handleSubmit }) => (
188 <>
202 <>
189− {!impliedTicket && (
203+ {!hasInitialValues && (
190− <>
191− <Grid.Item
204+ <Grid.Item
192− full
205+ full
206+ className="mb2"
193− as={TicketInput}
207+ as={TicketInput}
208+ accessory={<TicketInputAccessory name="showTicket" />}
194− type={values.showTicket ? 'text' : 'password'}
209+ type={values.showTicket ? 'text' : 'password'}
195− name="ticket"
210+ name="ticket"
196− label="Activation Code"
197− disabled={!activationAllowed}
211+ disabled={!activationAllowed}
198− />
212+ placeholder={`${PLACEHOLDER_TICKET}-${PLACEHOLDER_PLANET.slice(
199−
213+ 1
200− <Grid.Item
214+ )}`}
201− full
215+ />
202− as={CheckboxInput}
203− name="showTicket"
204− label="Show"
205− />
206− </>
207 )}
216 )}
208
217
209− <Grid.Item full as={FormError} />
218+ <Grid.Item full as={FormError} className="mb2" />
210−
219+
211− <Box
220+ <ActivateButton
212− display="flex"
213− flexDirection="column"
214− flexWrap="nowrap"
215− height={'100%'}
216− width={'100%'}
217− justifyContent="flex-end">
218− <ActivateButton
219− disabled={!valid || submitting}
221+ disabled={(!valid && !hasInitialValues) || submitting}
220− onClick={handleSubmit}>
222+ onClick={handleSubmit}>
221− {submitting ? 'Generating...' : 'Claim'}
223+ {submitting ? 'Activating...' : 'Activate'}
222− </ActivateButton>
224+ </ActivateButton>
223− </Box>
224
225
225 {!activationAllowed && (
226 {!activationAllowed && (
226 <Grid.Item full as={WarningBox} className="mt4">
227 <Grid.Item full as={WarningBox} className="mt4">
src/views/Activate/ActivateMasterKey.tsx+3−7
@@ -1,7 +1,5 @@
1import { LocalRouterProvider, useLocalRouter } from 'lib/LocalRouter';
1import { LocalRouterProvider, useLocalRouter } from 'lib/LocalRouter';
2
2
3−import View from 'components/View';
4−
5import useRouter from 'lib/useRouter';
3import useRouter from 'lib/useRouter';
6
4
7import MasterKeyDownload from './MasterKeyDownload';
5import MasterKeyDownload from './MasterKeyDownload';
@@ -33,10 +31,8 @@ export default function ActivateMasterKey() {
33 });
31 });
34
32
35 return (
33 return (
36− <View inset>
37− <LocalRouterProvider value={router}>
34+ <LocalRouterProvider value={router}>
38− <Route resetActivateRouter={resetActivateRouter} />
35+ <Route resetActivateRouter={resetActivateRouter} />
39− </LocalRouterProvider>
36+ </LocalRouterProvider>
40− </View>
41 );
37 );
42}
38}
src/views/Activate/ActivateView.scssnew file+3
@@ -0,0 +1,3 @@
1+.activate-view {
2+ min-height: 440px;
3+}
src/views/Activate/ActivateView.tsx+67−30
@@ -1,5 +1,11 @@
1import { Box } from '@tlon/indigo-react';
1import { Box } from '@tlon/indigo-react';
2−import cn from 'classnames';
2+import { Grid, Text } from 'indigo-react';
3+import HeaderButton from 'components/L2/Headers/HeaderButton';
4+import { useHistory } from 'store/history';
5+import Window from 'components/L2/Window/Window';
6+import BodyPane from 'components/L2/Window/BodyPane';
7+
8+import './ActivateView.scss';
3
9
4interface ActivateViewProps {
10interface ActivateViewProps {
5 className?: string;
11 className?: string;
@@ -8,6 +14,8 @@ interface ActivateViewProps {
8 footer?: React.ReactNode;
14 footer?: React.ReactNode;
9 gridRows?: string;
15 gridRows?: string;
10 gridAreas?: string;
16 gridAreas?: string;
17+ hideBack?: boolean;
18+ onBack?: () => void;
11}
19}
12
20
13export default function ActivateView({
21export default function ActivateView({
@@ -17,37 +25,66 @@ export default function ActivateView({
17 footer,
25 footer,
18 gridRows,
26 gridRows,
19 gridAreas,
27 gridAreas,
28+ onBack,
29+ hideBack = false,
20}: ActivateViewProps) {
30}: ActivateViewProps) {
31+ const { pop }: any = useHistory();
32+
21 return (
33 return (
22− <>
34+ <Grid>
23− <Box
35+ <Grid.Item
24− display="grid"
36+ full
25− gridTemplateColumns="1fr"
37+ as={Text}
26− gridTemplateRows={gridRows || '20% 60% 20%'}
38+ className="flex justify-center mt9 mb7 w-max-mobile">
27− gridTemplateAreas={gridAreas || "'header' 'content' 'footer'"}
39+ <Grid.Item as={Text}>Bridge /&nbsp;</Grid.Item>
28− border={'1px solid rgb(204, 204, 204)'}
40+ <Grid.Item className="fw-bold" as={Text}>
29− borderRadius={'8px'}
41+ Activate
30− padding={'10px'}
42+ </Grid.Item>
31− width={'512px'}
43+ </Grid.Item>
32− height={'512px'}
44+ <Grid.Item>
33− maxWidth={'calc(100vw - 48px)'}
45+ {!hideBack && (
34− className={cn(className)}
46+ <HeaderButton
35− mb={3}>
47+ className="mb4"
48+ icon="ChevronWest"
49+ onClick={onBack || (() => pop())}
50+ />
51+ )}
52+ <Window className="activate-view">
53+ <BodyPane>
36− <Box gridArea="header">{header}</Box>
54+ <Box className="w-full" gridArea="header">
55+ {header}
56+ </Box>
37− <Box gridArea="content">{children}</Box>
57+ <Box className="w-full" gridArea="content">
58+ {children}
59+ </Box>
38− <Box gridArea="footer">
60+ <Box className="w-full" gridArea="footer">
39− {footer ? (
61+ {footer ? (
40− <Box
62+ <Box
41− display="flex"
63+ className="h-full flex-col"
42− flexDirection="column"
43− flexWrap="nowrap"
64+ flexWrap="nowrap"
44− height={'100%'}
45− justifyContent="flex-end">
65+ justifyContent="flex-end">
46− {footer}
66+ {footer}
67+ </Box>
68+ ) : null}
47 </Box>
69 </Box>
48− ) : null}
70+ </BodyPane>
49− </Box>
71+ </Window>
72+ </Grid.Item>
73+ </Grid>
50− </Box>
74+ // <Box
51− </>
75+ // display="grid"
76+ // gridTemplateColumns="1fr"
77+ // gridTemplateRows={gridRows || '20% 60% 20%'}
78+ // gridTemplateAreas={gridAreas || "'header' 'content' 'footer'"}
79+ // border={'1px solid rgb(204, 204, 204)'}
80+ // borderRadius={'8px'}
81+ // padding={'10px'}
82+ // width={'512px'}
83+ // height={'512px'}
84+ // maxWidth={'calc(100vw - 48px)'}
85+ // className={cn(className)}
86+ // mb={3}>
87+
88+ // </Box>
52 );
89 );
53}
90}
src/views/Activate/MasterKeyConfirm.scss+1−2
@@ -34,8 +34,7 @@
34 flex-direction: row;
34 flex-direction: row;
35 width: 80%;
35 width: 80%;
36 height: min-content;
36 height: min-content;
37− margin-top: 16px;
37+ margin: 120px 0;
38− margin-bottom: 16px;
39 flex-wrap: nowrap;
38 flex-wrap: nowrap;
40 justify-content: center;
39 justify-content: center;
41 }
40 }
src/views/Activate/MasterKeyConfirm.tsx+4−15
@@ -21,15 +21,13 @@ import FormError from 'form/FormError';
21import useFadeIn from './useFadeIn';
21import useFadeIn from './useFadeIn';
22import { HiddenInput, TicketSegmentInput } from 'form/Inputs';
22import { HiddenInput, TicketSegmentInput } from 'form/Inputs';
23import View from 'components/View';
23import View from 'components/View';
24−import NavHeader from 'components/NavHeader';
25−import { MiniBackButton } from 'components/MiniBackButton';
26
24
27import './MasterKeyConfirm.scss';
25import './MasterKeyConfirm.scss';
28
26
29const MasterKeyConfirm = () => {
27const MasterKeyConfirm = () => {
30− const { derivedWallet, setIsIn } = useActivateFlow();
28+ const { derivedWallet, setIsIn }: any = useActivateFlow();
31 const { ticket } = need.wallet(derivedWallet);
29 const { ticket } = need.wallet(derivedWallet);
32− const { push, names } = useLocalRouter();
30+ const { push, names }: any = useLocalRouter();
33 const STUB_VERIFY_TICKET = isDevelopment;
31 const STUB_VERIFY_TICKET = isDevelopment;
34 const ticketSegments = useMemo(() => ticketToSegments(ticket), [ticket]);
32 const ticketSegments = useMemo(() => ticketToSegments(ticket), [ticket]);
35 const [showError, setShowError] = useState<boolean>(false);
33 const [showError, setShowError] = useState<boolean>(false);
@@ -93,18 +91,9 @@ const MasterKeyConfirm = () => {
93 useFadeIn();
91 useFadeIn();
94
92
95 return (
93 return (
96− <View centered={true} id={'master-key-confirm'}>
94+ <View inset id={'master-key-confirm'}>
97− <NavHeader>
98− <Box marginBottom={'10px'}>
99− <MiniBackButton
100− hpadding={true}
101− vpadding={true}
102− onClick={goBack}
103− className=""
104− />
105− </Box>
106− </NavHeader>
107 <ActivateView
95 <ActivateView
96+ onBack={goBack}
108 header={header}
97 header={header}
109 gridRows={'20% 80%'}
98 gridRows={'20% 80%'}
110 gridAreas={"'header' 'content'"}>
99 gridAreas={"'header' 'content'"}>
src/views/Activate/MasterKeyDownload.tsx+7−6
@@ -16,9 +16,9 @@ import { useLocalRouter } from 'lib/LocalRouter';
16import PaperBuilder from 'components/PaperBuilder';
16import PaperBuilder from 'components/PaperBuilder';
17import { DEFAULT_FADE_TIMEOUT, MASTER_KEY_DURATION } from 'lib/constants';
17import { DEFAULT_FADE_TIMEOUT, MASTER_KEY_DURATION } from 'lib/constants';
18import { timeout } from 'lib/timeout';
18import { timeout } from 'lib/timeout';
19−import View from 'components/View';
20import useMultikeyFileGenerator from 'lib/useMultikeyFileGenerator';
19import useMultikeyFileGenerator from 'lib/useMultikeyFileGenerator';
21import { downloadWallet } from 'lib/invite';
20import { downloadWallet } from 'lib/invite';
21+import View from 'components/View';
22
22
23const MasterKeyDownload = () => {
23const MasterKeyDownload = () => {
24 const {
24 const {
@@ -28,8 +28,8 @@ const MasterKeyDownload = () => {
28 isIn,
28 isIn,
29 setGenerated,
29 setGenerated,
30 setIsIn,
30 setIsIn,
31− } = useActivateFlow();
31+ }: any = useActivateFlow();
32− const { data, push, names } = useLocalRouter();
32+ const { data, push, names }: any = useLocalRouter();
33 const point = need.point(derivedPoint);
33 const point = need.point(derivedPoint);
34 const wallet = need.wallet(derivedWallet);
34 const wallet = need.wallet(derivedWallet);
35 const ticket = wallet.ticket.replace('~', '');
35 const ticket = wallet.ticket.replace('~', '');
@@ -119,8 +119,9 @@ const MasterKeyDownload = () => {
119 }, [delayedFadeIn, fadeIn, setIsIn, skipAnimationDelay]);
119 }, [delayedFadeIn, fadeIn, setIsIn, skipAnimationDelay]);
120
120
121 return (
121 return (
122− <View centered={true}>
122+ <View inset>
123 <ActivateView
123 <ActivateView
124+ hideBack
124 header={triggerAnimation && header}
125 header={triggerAnimation && header}
125 footer={triggerAnimation && footer}>
126 footer={triggerAnimation && footer}>
126 <Box
127 <Box
@@ -132,7 +133,7 @@ const MasterKeyDownload = () => {
132 justifyContent={'space-evenly'}>
133 justifyContent={'space-evenly'}>
133 <DangerBox
134 <DangerBox
134 overrideFadeIn={triggerAnimation && isIn}
135 overrideFadeIn={triggerAnimation && isIn}
135− className={!isIn ? 'hidden' : ''}>
136+ className={`mv8 ${!isIn ? 'hidden' : ''}`}>
136 Do not share this with anyone else!
137 Do not share this with anyone else!
137 </DangerBox>
138 </DangerBox>
138 {ticket && (
139 {ticket && (
@@ -141,7 +142,7 @@ const MasterKeyDownload = () => {
141 <MasterKeyCopy
142 <MasterKeyCopy
142 text={ticket}
143 text={ticket}
143 overrideFadeIn={triggerAnimation && isIn}
144 overrideFadeIn={triggerAnimation && isIn}
144− className={!isIn ? 'hidden' : ''}
145+ className={`mv5 ${!isIn ? 'hidden' : ''}`}
145 />
146 />
146 </Box>
147 </Box>
147 </ActivateView>
148 </ActivateView>
src/views/Activate/MasterKeyReveal.tsx+4−2
@@ -15,7 +15,7 @@ import View from 'components/View';
15
15
16const MasterKeyReveal = () => {
16const MasterKeyReveal = () => {
17 const { setIsIn }: any = useActivateFlow();
17 const { setIsIn }: any = useActivateFlow();
18− const { pop, push, names }: any = useLocalRouter();
18+ const { push, names }: any = useLocalRouter();
19 const [triggerAnimation, setTriggerAnimation] = useState<boolean>(false);
19 const [triggerAnimation, setTriggerAnimation] = useState<boolean>(false);
20 const [fadeOutKey, setFadeOutKey] = useState<boolean>(false);
20 const [fadeOutKey, setFadeOutKey] = useState<boolean>(false);
21
21
@@ -74,8 +74,9 @@ const MasterKeyReveal = () => {
74 }, [delayedFadeIn, setIsIn]);
74 }, [delayedFadeIn, setIsIn]);
75
75
76 return (
76 return (
77− <View centered={true} pop={pop} header={null} className="">
77+ <View inset>
78 <ActivateView
78 <ActivateView
79+ hideBack
79 header={triggerAnimation && header}
80 header={triggerAnimation && header}
80 footer={triggerAnimation && footer}>
81 footer={triggerAnimation && footer}>
81 <Box
82 <Box
@@ -86,6 +87,7 @@ const MasterKeyReveal = () => {
86 height={'100%'}
87 height={'100%'}
87 justifyContent={'center'}>
88 justifyContent={'center'}>
88 <Box
89 <Box
90+ margin={triggerAnimation ? '0' : '75px 0'}
89 display={'flex'}
91 display={'flex'}
90 flexDirection={'row'}
92 flexDirection={'row'}
91 flexWrap={'nowrap'}
93 flexWrap={'nowrap'}
src/views/Activate/MasterKeyTransfer.tsx+2−2
@@ -110,8 +110,8 @@ const MasterKeyTransfer = () => {
110 }, [error]);
110 }, [error]);
111
111
112 return (
112 return (
113− <View centered={true}>
113+ <View inset>
114− <ActivateView header={header} footer={footer}>
114+ <ActivateView hideBack header={header} footer={footer}>
115 <Box
115 <Box
116 alignItems={'center'}
116 alignItems={'center'}
117 display={'flex'}
117 display={'flex'}
src/views/Login/Hardware.js+1−1
@@ -1,6 +1,6 @@
1import React, { useMemo, useState } from 'react';
1import React, { useMemo, useState } from 'react';
2
2
3−import useBreakpoints from 'lib/useBreakpoints';
3+import useBreakpoints from 'lib/useBreakpoints.tsx';
4
4
5import Accordion from 'components/Accordion';
5import Accordion from 'components/Accordion';
6
6
src/views/Login/Ledger.tsx+1−1
@@ -18,7 +18,7 @@ import {
18} from 'lib/ledger';
18} from 'lib/ledger';
19import { WALLET_TYPES } from 'lib/constants';
19import { WALLET_TYPES } from 'lib/constants';
20import useLoginView from 'lib/useLoginView';
20import useLoginView from 'lib/useLoginView';
21−import useBreakpoints from 'lib/useBreakpoints';
21+import useBreakpoints from 'lib/useBreakpoints.tsx';
22
22
23import {
23import {
24 composeValidator,
24 composeValidator,
src/views/Login/LoginSelector.scss−5
@@ -4,11 +4,6 @@
4 width: 482px;
4 width: 482px;
5 min-height: 300px;
5 min-height: 300px;
6
6
7− .icon {
8− height: 14px;
9− width: 14px;
10− }
11−
12 .login-menu-item {
7 .login-menu-item {
13 padding: 16px 16px;
8 padding: 16px 16px;
14 font-size: 14px;
9 font-size: 14px;
src/views/Login/Ticket.tsx+3−15
@@ -1,9 +1,9 @@
1import { useCallback, useMemo, useRef, useState } from 'react';
1import { useCallback, useMemo, useRef, useState } from 'react';
2import { Just } from 'folktale/maybe';
2import { Just } from 'folktale/maybe';
3import * as kg from 'urbit-key-generation';
3import * as kg from 'urbit-key-generation';
4−import { Grid, ToggleInput, AccessoryIcon } from 'indigo-react';
4+import { Grid } from 'indigo-react';
5import { FORM_ERROR } from 'final-form';
5import { FORM_ERROR } from 'final-form';
6−import { Icon, Row } from '@tlon/indigo-react';
6+import { Row } from '@tlon/indigo-react';
7
7
8import { useWallet } from 'store/wallet';
8import { useWallet } from 'store/wallet';
9import { usePointCursor } from 'store/pointCursor';
9import { usePointCursor } from 'store/pointCursor';
@@ -34,19 +34,7 @@ import Window from 'components/L2/Window/Window';
34import HeaderPane from 'components/L2/Window/HeaderPane';
34import HeaderPane from 'components/L2/Window/HeaderPane';
35import BodyPane from 'components/L2/Window/BodyPane';
35import BodyPane from 'components/L2/Window/BodyPane';
36import AdvancedOptions from 'components/L2/Headers/AdvancedOptions';
36import AdvancedOptions from 'components/L2/Headers/AdvancedOptions';
37−
37+import TicketInputAccessory from 'components/form/TicketInputAccessory';
38−function TicketInputAccessory({ name }: { name: any }) {
39− return (
40− <AccessoryIcon>
41− <ToggleInput
42− name={name}
43− className="mt1"
44− inverseLabel={<Icon className="icon" icon="Visible" />}
45− label={<Icon className="icon" icon="Hidden" />}
46− />
47− </AccessoryIcon>
48− );
49−}
50
38
51interface TicketProps {
39interface TicketProps {
52 className?: string;
40 className?: string;