on master help sign in
feat: implement double-confirm when warnings exist
commit0791f78b28a7835a99d5c593d76138cd5e5fa68f authorMatt Condon (shrugs) <mattgcondon@gmail.com> dateTue 13 Aug 2019, 10:08 (7 years ago) parentad8630b
3 files changed+107−59
src/form/helpers.js+4 src/views/Activate/ActivateCode.js+62−27 src/views/Login/Ticket.js+41−32
src/form/helpers.jsnew file+4
@@ -0,0 +1,4 @@
1+import { some } from 'lodash';
2+
3+// we have warnings if some of the values are truthy
4+export const hasWarnings = warnings => some(warnings, v => !!v);
src/views/Activate/ActivateCode.js+62−27
@@ -1,35 +1,41 @@
1−import React, { useCallback, useMemo } from 'react';
1+import React, { useCallback, useMemo, useRef } from 'react';
22import { Just } from 'folktale/maybe';
33import * as azimuth from 'azimuth-js';
44import { Grid, H4 } from 'indigo-react';
5+import { FORM_ERROR } from 'final-form';
56
67import View from 'components/View';
78import { ForwardButton } from 'components/Buttons';
89import Passport from 'components/Passport';
10+import WarningBox from 'components/WarningBox';
11+import FooterButton from 'components/FooterButton';
912
13+import { useNetwork } from 'store/network';
1014import { useHistory } from 'store/history';
1115
1216import * as need from 'lib/need';
1317import { ROUTE_NAMES } from 'lib/routeNames';
14−import FooterButton from 'components/FooterButton';
1518import { DEFAULT_HD_PATH, walletFromMnemonic } from 'lib/wallet';
16−import { useNetwork } from 'store/network';
1719import { generateWallet } from 'lib/invite';
1820import { generateTemporaryOwnershipWallet } from 'lib/walletgen';
19−import { useActivateFlow } from './ActivateFlow';
2021import { useLocalRouter } from 'lib/LocalRouter';
2122import useImpliedTicket from 'lib/useImpliedTicket';
2223import timeout from 'lib/timeout';
2324import useHasDisclaimed from 'lib/useHasDisclaimed';
2425import useBreakpoints from 'lib/useBreakpoints';
25−import WarningBox from 'components/WarningBox';
2626
2727import BridgeForm from 'form/BridgeForm';
2828import SubmitButton from 'form/SubmitButton';
2929import { TicketInput } from 'form/Inputs';
30−import { composeValidator, buildPatqValidator } from 'form/validators';
30+import {
31+ composeValidator,
32+ buildPatqValidator,
33+ hasErrors,
34+} from 'form/validators';
3135import FormError from 'form/FormError';
32−import { FORM_ERROR } from 'final-form';
36+
37+import { useActivateFlow } from './ActivateFlow';
38+import { hasWarnings } from 'form/helpers';
3339
3440export default function ActivateCode() {
3541 const history = useHistory();
@@ -37,6 +43,7 @@ export default function ActivateCode() {
3743 const { contracts } = useNetwork();
3844 const impliedTicket = useImpliedTicket();
3945 const [hasDisclaimed] = useHasDisclaimed();
46+ const warnings = useRef({});
4047
4148 const {
4249 setDerivedWallet,
@@ -61,11 +68,19 @@ export default function ActivateCode() {
6168 if (!hasDisclaimed) {
6269 push(names.DISCLAIMER);
6370 }
64− }, [names, push, hasDisclaimed]);
71+ }, [hasDisclaimed, names.DISCLAIMER, names.PASSPORT, push]);
72+
73+ const validateForm = useCallback((values, errors) => {
74+ warnings.current.ticket = null;
75+
76+ if (hasErrors(errors)) {
77+ return errors;
78+ }
79+ }, []);
6580
6681 const validate = useMemo(
67− () => composeValidator({ ticket: buildPatqValidator() }),
68− []
82+ () => composeValidator({ ticket: buildPatqValidator() }, validateForm),
83+ [validateForm]
6984 );
7085
7186 // set our state on submission
@@ -92,10 +107,9 @@ export default function ActivateCode() {
92107
93108 if (incoming.length > 0) {
94109 if (incoming.length > 1) {
95− // TODO: putting a warning here doesn't make sense since the user
96− // will be immediately redirected away — what do?
97− // 'This invite code has multiple points available.\n' +
98− // "Once you've activated this point, activate the next with the same process.";
110+ warnings.current.ticket =
111+ 'This invite code has multiple points available.\n' +
112+ "Once you've activated this point, activate the next with the same process.";
99113 }
100114
101115 const point = parseInt(incoming[0], 10);
@@ -114,6 +128,14 @@ export default function ActivateCode() {
114128 [contracts, setDerivedPoint, setDerivedWallet, setInviteWallet]
115129 );
116130
131+ const afterSubmit = useCallback(() => {
132+ if (hasWarnings(warnings.current)) {
133+ return;
134+ }
135+
136+ goToPassport();
137+ }, [goToPassport]);
138+
117139 const initialValues = useMemo(() => ({ ticket: impliedTicket || '' }), [
118140 impliedTicket,
119141 ]);
@@ -128,9 +150,9 @@ export default function ActivateCode() {
128150 <BridgeForm
129151 validate={validate}
130152 onSubmit={onSubmit}
131− afterSubmit={goToPassport}
153+ afterSubmit={afterSubmit}
132154 initialValues={initialValues}>
133− {({ validating, submitting, handleSubmit }) => (
155+ {({ validating, submitting, submitSucceeded, handleSubmit }) => (
134156 <>
135157 <Grid.Item
136158 full
@@ -138,21 +160,33 @@ export default function ActivateCode() {
138160 name="ticket"
139161 label="Activation Code"
140162 disabled={!activationAllowed}
163+ warning={warnings.current.ticket}
141164 />
142165
143166 <Grid.Item full as={FormError} />
144167
145− <Grid.Item
146− full
147− as={SubmitButton}
148− className="mt4"
149− handleSubmit={handleSubmit}>
150− {validating
151− ? 'Deriving...'
152− : submitting
153− ? 'Generating...'
154− : 'Go'}
155− </Grid.Item>
168+ {submitSucceeded ? (
169+ <Grid.Item
170+ full
171+ as={ForwardButton}
172+ solid
173+ className="mt4"
174+ onClick={goToPassport}>
175+ Continue Activation
176+ </Grid.Item>
177+ ) : (
178+ <Grid.Item
179+ full
180+ as={SubmitButton}
181+ className="mt4"
182+ handleSubmit={handleSubmit}>
183+ {validating
184+ ? 'Deriving...'
185+ : submitting
186+ ? 'Generating...'
187+ : 'Go'}
188+ </Grid.Item>
189+ )}
156190
157191 {!activationAllowed && (
158192 <Grid.Item full as={WarningBox} className="mt4">
@@ -163,6 +197,7 @@ export default function ActivateCode() {
163197 )}
164198 </BridgeForm>
165199 </Grid>
200+
166201 <FooterButton as={ForwardButton} onClick={goToLogin}>
167202 Login
168203 </FooterButton>
src/views/Login/Ticket.js+41−32
@@ -1,4 +1,4 @@
1−import React, { useCallback, useMemo } from 'react';
1+import React, { useCallback, useMemo, useRef } from 'react';
22import { Just } from 'folktale/maybe';
33import cn from 'classnames';
44import * as azimuth from 'azimuth-js';
@@ -28,9 +28,9 @@ import {
2828 buildPointValidator,
2929} from 'form/validators';
3030import FormError from 'form/FormError';
31−
3231import SubmitButton from 'form/SubmitButton';
33−import useSetState from 'lib/useSetState';
32+import { hasWarnings } from 'form/helpers';
33+import { ForwardButton } from 'components/Buttons';
3434
3535export default function Ticket({ className, goHome }) {
3636 useLoginView(WALLET_TYPES.TICKET);
@@ -39,27 +39,25 @@ export default function Ticket({ className, goHome }) {
3939 const { setUrbitWallet } = useWallet();
4040 const { setPointCursor } = usePointCursor();
4141 const impliedPoint = useImpliedPoint();
42− const [warnings, addWarning] = useSetState();
42+ const warnings = useRef({});
43+
44+ const validateForm = useCallback((values, errors) => {
45+ warnings.current.point = null;
46+
47+ if (errors.point) {
48+ return errors;
49+ }
4350
44− const validateForm = useCallback(
45− (values, errors) => {
46− if (errors.point) {
47− addWarning({ point: null });
51+ if (values.useShards) {
52+ if (errors.shard1 || errors.shard2 || errors.shard3) {
4853 return errors;
4954 }
50−
51− if (values.useShards) {
52− if (errors.shard1 || errors.shard2 || errors.shard3) {
53− return errors;
54− }
55− } else {
56− if (errors.ticket) {
57− return errors;
58− }
55+ } else {
56+ if (errors.ticket) {
57+ return errors;
5958 }
60− },
61− [addWarning]
62− );
59+ }
60+ }, []);
6361
6462 const onSubmit = useCallback(
6563 async values => {
@@ -93,12 +91,9 @@ export default function Ticket({ className, goHome }) {
9391
9492 const noPermissions = !isOwner && !isTransferProxy;
9593 // notify the user, but allow login regardless
96− // TODO: how should the warning work now that we generate onSubmit?
9794 if (noPermissions) {
98− addWarning({
99− point:
100− 'This wallet is not the owner or transfer proxy for this point.',
101− });
95+ warnings.current.point =
96+ 'This wallet is not the owner or transfer proxy for this point.';
10297 }
10398
10499 setUrbitWallet(Just(urbitWallet));
@@ -112,9 +107,17 @@ export default function Ticket({ className, goHome }) {
112107 };
113108 }
114109 },
115− [addWarning, contracts, setPointCursor, setUrbitWallet]
110+ [contracts, setPointCursor, setUrbitWallet]
116111 );
117112
113+ const afterSubmit = useCallback(() => {
114+ if (hasWarnings(warnings.current)) {
115+ return;
116+ }
117+
118+ goHome();
119+ }, [goHome]);
120+
118121 const validate = useMemo(
119122 () =>
120123 composeValidator(
@@ -147,15 +150,15 @@ export default function Ticket({ className, goHome }) {
147150 <BridgeForm
148151 validate={validate}
149152 onSubmit={onSubmit}
150− afterSubmit={goHome}
153+ afterSubmit={afterSubmit}
151154 initialValues={initialValues}>
152− {({ handleSubmit }) => (
155+ {({ handleSubmit, submitSucceeded }) => (
153156 <>
154157 <Grid.Item
155158 full
156159 as={PointInput}
157160 name="point"
158− warning={warnings.point}
161+ warning={warnings.current.point}
159162 />
160163
161164 <Condition when="useShards" is={false}>
@@ -197,9 +200,15 @@ export default function Ticket({ className, goHome }) {
197200
198201 <Grid.Item full as={FormError} />
199202
200− <Grid.Item full as={SubmitButton} handleSubmit={handleSubmit}>
201− Continue
202− </Grid.Item>
203+ {submitSucceeded ? (
204+ <Grid.Item full as={ForwardButton} solid onClick={goHome}>
205+ Login Anyway
206+ </Grid.Item>
207+ ) : (
208+ <Grid.Item full as={SubmitButton} handleSubmit={handleSubmit}>
209+ Continue
210+ </Grid.Item>
211+ )}
203212 </>
204213 )}
205214 </BridgeForm>