import React, { useState, useCallback, useMemo } from 'react';
import { Grid } from 'indigo-react';
import { FORM_ERROR } from 'final-form';
import * as ob from 'urbit-ob';
import { uniq, zip, compact, isEqual } from 'lodash';
import { usePointCursor } from 'store/pointCursor';
import * as need from 'lib/need';
import useMailer from 'lib/useMailer';
import pluralize from 'lib/pluralize';
import useInviter from 'lib/useInviter';
import {
composeValidator,
hasErrors,
buildEmailValidator,
buildArrayValidator,
} from 'form/validators';
import EmailChipInput from 'form/EmailChipInput';
import FormError from 'form/FormError';
import SubmitButton from 'form/SubmitButton';
import BridgeForm from 'form/BridgeForm';
import NeedFundsNotice from 'components/NeedFundsNotice';
import ProgressButton from 'components/ProgressButton';
const HAS_RECEIVED_TEXT = 'This email has already received an invite: ';
const STATUS = {
INPUT: 'INPUT',
SENDING: 'SENDING',
SUCCESS: 'SUCCESS',
FAILURE: 'FAILURE',
};
const InviteMail = ({ setSubmitting }) => {
const { progress, txStatus, needFunds, generateInvites } = useInviter();
const { sendMail, getHasReceived } = useMailer();
const [status, setStatus] = useState(STATUS.INPUT);
const [count, setCount] = useState(0);
const canInput = status === STATUS.INPUT;
const isSending = txStatus === STATUS.SENDING || status === STATUS.SENDING;
const isDone = status === STATUS.SUCCESS;
const isFailed = status === STATUS.FAILED;
const { pointCursor } = usePointCursor();
const point = need.point(pointCursor);
const sendInvites = useCallback(
async (emails, invites) => {
const mailingErrors = compact(
await Promise.all(
zip(emails, invites).map(([email, invite]) =>
sendMail(email, invite.ticket, ob.patp(point), '', invite.rawTx)
.then(() => null)
.catch(
() => `Sending ticket ${invite.ticket} to ${email} failed.`
)
)
)
).join(', ');
if (mailingErrors.length) {
return { [FORM_ERROR]: mailingErrors };
}
},
[point, sendMail]
);
const onSubmit = useCallback(
async values => {
const emailCount = values.emails.length;
setCount(emailCount);
setStatus(STATUS.SENDING);
setSubmitting(true);
const { errors, invites } = await generateInvites(emailCount);
if (errors) {
setStatus(STATUS.FAILED);
setSubmitting(false);
return errors;
}
const mailErrors = await sendInvites(values.emails, invites);
if (mailErrors) {
console.log(mailErrors);
setStatus(STATUS.FAILED);
setSubmitting(false);
return mailErrors;
}
setSubmitting(false);
setStatus(STATUS.SUCCESS);
},
[generateInvites, setCount, setStatus, sendInvites, setSubmitting]
);
const validateHasReceived = useCallback(
async email => {
const hasReceived = await getHasReceived(email);
if (hasReceived) {
return HAS_RECEIVED_TEXT + email;
}
},
[getHasReceived]
);
const buttonText = useCallback(() => {
switch (status) {
case STATUS.INPUT:
return 'Add to Invite Group';
case STATUS.SENDING:
return `Sending ${pluralize(count, 'Invite')}...`;
case STATUS.SUCCESS:
return `✓ ${pluralize(count, 'Invite')} sent`;
case STATUS.FAILURE:
default:
return 'Error';
}
}, [status, count]);
const renderButton = useCallback(
handleSubmit => {
if (canInput) {
return (
{buttonText()}
);
}
if (isSending) {
const percentProgress = progress / count;
return (
{buttonText(count)}
);
}
if (isDone) {
return (
{buttonText(count)}
);
}
if (isFailed) {
return (
{buttonText(count)}
);
}
return null;
},
[buttonText, isDone, isFailed, isSending, canInput, count, progress]
);
const validateForm = useCallback((values, errors) => {
if (hasErrors(errors)) {
return errors;
}
// check for email uniqenesss
const emails = values.emails.filter(d => !!d);
if (uniq(emails).length !== emails.length) {
return { [FORM_ERROR]: 'Duplicate email.' };
}
}, []);
const validate = useMemo(
() =>
composeValidator(
{
emails: buildArrayValidator(buildEmailValidator(validateHasReceived)),
},
validateForm
),
[validateHasReceived, validateForm]
);
const initialValues = { emails: [] };
return (
{({ handleSubmit, values }) => (
<>
{renderButton(handleSubmit)}
{needFunds && (
)}
>
)}
);
};
export default InviteMail;