@@ -2,7 +2,15 @@ import React, { useCallback, useEffect, useMemo, useState } from 'react';
2import cn from 'classnames';
2import cn from 'classnames';
3import Maybe from 'folktale/maybe';
3import Maybe from 'folktale/maybe';
4import * as azimuth from 'azimuth-js';
4import * as azimuth from 'azimuth-js';
5−import { Grid, Flex, Input, IconButton, HelpText, Text } from 'indigo-react';
13+} from 'indigo-react';
6import { uniq } from 'lodash';
14import { uniq } from 'lodash';
8import * as need from 'lib/need';
16import * as need from 'lib/need';
@@ -35,8 +43,7 @@ import { useWallet } from 'store/wallet';
35import useSetState from 'lib/useSetState';
43import useSetState from 'lib/useSetState';
36import pluralize from 'lib/pluralize';
44import pluralize from 'lib/pluralize';
37import useMailer from 'lib/useMailer';
45import useMailer from 'lib/useMailer';
38−
46+import useRenderCount from 'lib/useRenderCount';
39−const kStubMailer = process.env.REACT_APP_STUB_MAILER === 'true';
41const GAS_PRICE_GWEI = 20; // we pay the premium for faster ux
48const GAS_PRICE_GWEI = 20; // we pay the premium for faster ux
42const GAS_LIMIT = 350000;
49const GAS_LIMIT = 350000;
@@ -89,7 +96,6 @@ const buildAccessoryFor = (dones, errors) => name => (
92−// TODO: how to handle dependency loop between inputConfigs and hasReceived?
93// TODO: test with tank, successful txs
99// TODO: test with tank, successful txs
94// TODO: put accessory inside of input
100// TODO: put accessory inside of input
95export default function InviteEmail() {
101export default function InviteEmail() {
@@ -111,10 +117,11 @@ export default function InviteEmail() {
111 inputConfigs,
117 inputConfigs,
112 { append: appendInput, removeAt: removeInputAt },
118 { append: appendInput, removeAt: removeInputAt },
113 ] = useArray(
119 ] = useArray(
114− [buildInputConfig({ placeholder: 'Email Addresss' })],
120+ [buildInputConfig({ placeholder: 'Email Address' })],
115 buildInputConfig
121 buildInputConfig
124+ const { getHasRecieved, syncHasReceivedForEmail, sendMail } = useMailer();
118 const [hovered, setHovered] = useSetState();
125 const [hovered, setHovered] = useSetState();
119 const [invites, addInvite, clearInvites] = useSetState();
126 const [invites, addInvite, clearInvites] = useSetState();
120 const [receipts, addReceipt, clearReceipts] = useSetState();
127 const [receipts, addReceipt, clearReceipts] = useSetState();
@@ -136,16 +143,19 @@ export default function InviteEmail() {
137 inputConfigs.map(config => {
144 inputConfigs.map(config => {
138 config.disabled = !canInput;
145 config.disabled = !canInput;
146+ const hasReceivedError = getHasRecieved(config.name).matchWith({
147+ Nothing: () => null, // loading
148+ Just: p => p.value && 'This email has already received an invite.',
139− config.error = errors[config.name];
150+ config.error = hasReceivedError || errors[config.name];
140 return config;
151 return config;
142− [inputConfigs, errors, canInput]
153+ [inputConfigs, errors, canInput, getHasRecieved]
144 const { inputs, pass } = useForm(dynamicConfigs);
155 const { inputs, pass } = useForm(dynamicConfigs);
145 const emails = useMemo(() => inputs.map(i => i.data).filter(d => !!d), [
156 const emails = useMemo(() => inputs.map(i => i.data).filter(d => !!d), [
148− const { getHasRecieved, sendMail } = useMailer(emails);
150 const canAddInvite = canInput && inputs.length < maxInvitesToSend;
160 const canAddInvite = canInput && inputs.length < maxInvitesToSend;
151 const allPass = pass && !generalError;
161 const allPass = pass && !generalError;
@@ -301,14 +311,10 @@ export default function InviteEmail() {
301 addError({
311 addError({
302 [input.name]: `Transaction Failure for ${invite.email}`,
312 [input.name]: `Transaction Failure for ${invite.email}`,
308− console.log(`${invite.email} - ${invite.ticket}`);
312 const mailSuccess = await sendMail(
318 const mailSuccess = await sendMail(
313 invite.email,
319 invite.email,
314 invite.ticket,
320 invite.ticket,
@@ -345,6 +351,7 @@ export default function InviteEmail() {
347 addError,
353 addError,
350 const onClickGenerate = useCallback(async () => {
357 const onClickGenerate = useCallback(async () => {
@@ -439,6 +446,8 @@ export default function InviteEmail() {
439 as={Input}
446 as={Input}
440 cols={[1, 11]}
447 cols={[1, 11]}
441 {...input}
448 {...input}
449+ onValue={email => syncHasReceivedForEmail(email)}
450+ // NB(shrugs): ^ this feels like a hack?
442 accessory={accessoryFor(input.name)}
451 accessory={accessoryFor(input.name)}
444 {!isFirst &&
453 {!isFirst &&
@@ -483,7 +492,7 @@ export default function InviteEmail() {
484 {generalError && (
493 {generalError && (
485 <Grid.Item full>
494 <Grid.Item full>
486− <Highlighted>{generalError.message.toString()}</Highlighted>
495+ <ErrorText>{generalError.message.toString()}</ErrorText>
487 </Grid.Item>
496 </Grid.Item>