@@ -2,7 +2,15 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
22import cn from 'classnames';
33import Maybe from 'folktale/maybe';
44import * as azimuth from 'azimuth-js';
5−import { Grid, Flex, Input, IconButton, HelpText, Text } from 'indigo-react';
5+import {
6+ Grid,
7+ Flex,
8+ Input,
9+ IconButton,
10+ HelpText,
11+ Text,
12+ ErrorText,
13+} from 'indigo-react';
614import { uniq } from 'lodash';
715
816import * as need from 'lib/need';
@@ -35,8 +43,7 @@ import { useWallet } from 'store/wallet';
3543import useSetState from 'lib/useSetState';
3644import pluralize from 'lib/pluralize';
3745import useMailer from 'lib/useMailer';
38−
39−const kStubMailer = process.env.REACT_APP_STUB_MAILER === 'true';
46+import useRenderCount from 'lib/useRenderCount';
4047
4148const GAS_PRICE_GWEI = 20; // we pay the premium for faster ux
4249const GAS_LIMIT = 350000;
@@ -89,7 +96,6 @@ const buildAccessoryFor = (dones, errors) => name => (
8996 </Flex>
9097);
9198
92−// TODO: how to handle dependency loop between inputConfigs and hasReceived?
9399// TODO: test with tank, successful txs
94100// TODO: put accessory inside of input
95101export default function InviteEmail() {
@@ -111,10 +117,11 @@ export default function InviteEmail() {
111117 inputConfigs,
112118 { append: appendInput, removeAt: removeInputAt },
113119 ] = useArray(
114− [buildInputConfig({ placeholder: 'Email Addresss' })],
120+ [buildInputConfig({ placeholder: 'Email Address' })],
115121 buildInputConfig
116122 );
117123
124+ const { getHasRecieved, syncHasReceivedForEmail, sendMail } = useMailer();
118125 const [hovered, setHovered] = useSetState();
119126 const [invites, addInvite, clearInvites] = useSetState();
120127 const [receipts, addReceipt, clearReceipts] = useSetState();
@@ -136,16 +143,19 @@ export default function InviteEmail() {
136143 () =>
137144 inputConfigs.map(config => {
138145 config.disabled = !canInput;
139− config.error = errors[config.name];
146+ const hasReceivedError = getHasRecieved(config.name).matchWith({
147+ Nothing: () => null, // loading
148+ Just: p => p.value && 'This email has already received an invite.',
149+ });
150+ config.error = hasReceivedError || errors[config.name];
140151 return config;
141152 }),
142− [inputConfigs, errors, canInput]
153+ [inputConfigs, errors, canInput, getHasRecieved]
143154 );
144155 const { inputs, pass } = useForm(dynamicConfigs);
145156 const emails = useMemo(() => inputs.map(i => i.data).filter(d => !!d), [
146157 inputs,
147158 ]);
148− const { getHasRecieved, sendMail } = useMailer(emails);
149159
150160 const canAddInvite = canInput && inputs.length < maxInvitesToSend;
151161 const allPass = pass && !generalError;
@@ -301,14 +311,10 @@ export default function InviteEmail() {
301311 addError({
302312 [input.name]: `Transaction Failure for ${invite.email}`,
303313 });
314+ return;
304315 }
305316
306317 try {
307− if (kStubMailer) {
308− console.log(`${invite.email} - ${invite.ticket}`);
309− return;
310− }
311−
312318 const mailSuccess = await sendMail(
313319 invite.email,
314320 invite.ticket,
@@ -345,6 +351,7 @@ export default function InviteEmail() {
345351 point,
346352 wallet,
347353 addError,
354+ sendMail,
348355 ]);
349356
350357 const onClickGenerate = useCallback(async () => {
@@ -439,6 +446,8 @@ export default function InviteEmail() {
439446 as={Input}
440447 cols={[1, 11]}
441448 {...input}
449+ onValue={email => syncHasReceivedForEmail(email)}
450+ // NB(shrugs): ^ this feels like a hack?
442451 accessory={accessoryFor(input.name)}
443452 />
444453 {!isFirst &&
@@ -483,7 +492,7 @@ export default function InviteEmail() {
483492
484493 {generalError && (
485494 <Grid.Item full>
486− <Highlighted>{generalError.message.toString()}</Highlighted>
495+ <ErrorText>{generalError.message.toString()}</ErrorText>
487496 </Grid.Item>
488497 )}
489498 </>