in hasher-matcher-actioner/webapp/src/pages/SubmitContent.tsx [28:307]
export default function SubmitContent(): JSX.Element {
const [submitting, setSubmitting] = useState(false);
const [submittedId, setSubmittedId] = useState('');
const [submissionError, setSubmissionError] = useState(false);
const [submissionType, setSubmissionType] = useState('');
const [additionalFields, setAdditionalFields] = useState<AdditionalFields>(
{},
);
const [inputs, setInputs] = useState(FORM_DEFAULTS);
const history = useHistory();
// for most input changes we only need to take the input name and store the event value
const handleInputChange = (e: React.SyntheticEvent) => {
e.persist();
const target = e.target as typeof e.target & {
name: string;
value: string;
};
setInputs(inputs_ => ({
...inputs_,
[target.name]: target.value,
}));
};
// image upload is a special case so that we can do the following:
// - give a preview of the image to user
// - auto populate the content id if it is currently empty
const handleInputChangeUpload = (e: React.SyntheticEvent) => {
const target = e.target as typeof e.target & {
name: string;
files: File[];
};
const file = target.files[0];
const contentId = inputs.contentId ?? file.name;
setInputs(inputs_ => ({
...inputs_,
contentId,
[target.name]: {
preview: URL.createObjectURL(file),
raw: file,
},
}));
};
const packageAdditionalFields = () => {
const entries = [] as string[];
Object.values(additionalFields).forEach(entry =>
// TODO extra additional fields spec should be established in documentation
entries.push(`${entry.value}`),
);
return entries;
};
const handleSubmit = (event: React.FormEvent) => {
event.preventDefault();
setSubmitting(true);
setSubmissionError(false);
if (inputs.content === undefined) {
return;
}
if (inputs.submissionType === 'PUT_URL_UPLOAD') {
submitContentViaPutURLUpload(
inputs.contentId,
inputs.contentType,
packageAdditionalFields(),
(inputs.content as unknown as {raw: File}).raw,
inputs.force_resubmit,
)
.then(() => {
setSubmitting(false);
setSubmittedId(inputs.contentId);
})
.catch(() => {
setSubmitting(false);
setSubmissionError(true);
});
} else {
submitContentViaURL(
inputs.contentId,
inputs.contentType,
packageAdditionalFields(),
inputs.content,
inputs.force_resubmit,
)
.then(() => {
setSubmitting(false);
setSubmittedId(inputs.contentId);
})
.catch(() => {
setSubmitting(false);
setSubmissionError(true);
});
}
};
const handleSubmitAnother = () => {
// Does not change submission type, clears out additional fields. Depending
// on feedback we may want to keep additional fields at their current
// values.
setSubmittedId('');
setSubmitting(false);
setAdditionalFields({});
setInputs(FORM_DEFAULTS);
};
return (
<FixedWidthCenterAlignedLayout title="Submit Content">
<Row>
<Col>
<Form className="hma-themed-form" onSubmit={handleSubmit}>
<Form.Group>
<Form.Label>Content Type</Form.Label>
<Row>
<Col xs="6">
<ChoiceCard
label="Photo"
description="Submit photo or other image."
selected={inputs.contentType === ContentType.Photo}
onSelect={() =>
setInputs({...inputs, contentType: ContentType.Photo})
}
/>
</Col>
<Col xs="6">
<ChoiceCard
label="Video"
description="Submit video or gif."
selected={inputs.contentType === ContentType.Video}
onSelect={() =>
setInputs({...inputs, contentType: ContentType.Video})
}
/>
</Col>
</Row>
</Form.Group>
<Form.Group>
<Form.Label>Submission Method</Form.Label>
<Form.Control
as="select"
required
className="mr-sm-2"
name="submissionType"
onChange={(e: React.FormEvent) => {
const target = e.target as typeof e.target & {
value: keyof typeof SubmissionType;
};
setSubmissionType(SubmissionType[target.value]);
handleInputChange(e);
}}
defaultValue=""
custom>
<option key="empty" value="" disabled>
Select type...
</option>
{Object.keys(SubmissionType).map(submitType => (
<option key={submitType} value={submitType}>
{SubmissionType[submitType as keyof typeof SubmissionType]}
</option>
))}
</Form.Control>
</Form.Group>
{submissionType === SubmissionType.FROM_URL && (
<Form.Group>
<Form.Label>Provide a URL to the content</Form.Label>
<Form.Control
onChange={handleInputChange}
name="content"
placeholder="url to content"
required
value={inputs.content}
/>
<Form.Text className="text-muted mt-0">
Currently behavior will store a copy of the content
</Form.Text>
</Form.Group>
)}
{submissionType === SubmissionType.PUT_URL_UPLOAD && (
<Form.Group>
<PhotoUploadField
inputs={inputs}
handleInputChange={handleInputChangeUpload}
/>
</Form.Group>
)}
<Form.Group>
<Form.Group>
<Form.Label>Unique ID for Content</Form.Label>
<Form.Control
onChange={handleInputChange}
type="text"
name="contentId"
placeholder="Enter a unique identifier for content"
required
value={inputs.contentId}
/>
<Form.Text className="text-muted mt-0">
Warning currently behavior will overwrite content with the
same id
</Form.Text>
</Form.Group>
</Form.Group>
<OptionalAdditionalFields
additionalFields={additionalFields}
setAdditionalFields={setAdditionalFields}
/>
<Form.Group>
<Form.Check
disabled={submitting || submittedId !== ''}
name="force_resubmit"
inline
label="Resubmit if content id already present in system"
type="checkbox"
onChange={handleInputChange}
/>
<Form.Text className="text-muted mt-0">
Be careful submitting different content with the same id is not
supported and will likely error.
</Form.Text>
</Form.Group>
<Form.Group as={Row}>
<Collapse in={submitting}>
<Spinner
as="span"
animation="border"
role="status"
variant="primary"
/>
</Collapse>
<Collapse in={!submittedId}>
<Button
style={{maxHeight: 38}}
className="ml-3"
variant="primary"
disabled={submitting}
type="submit">
Submit
</Button>
</Collapse>
<Collapse in={submittedId !== ''}>
<Col>
<Card>
<Card.Header>Your content is submitted!</Card.Header>
<Card.Body>
<Button
variant="primary"
onClick={() =>
history.push(`/pipeline-progress/${submittedId}`)
}>
Track Submission
</Button>{' '}
<Button variant="secondary" onClick={handleSubmitAnother}>
Submit Another
</Button>
</Card.Body>
</Card>
</Col>
</Collapse>
<Collapse in={submissionError}>
<Col className="ml-4">
<Card border="danger">
<Card.Header>Error when submitting.</Card.Header>
<Card.Body>
Error submitting. This can occur if content with that id
already exists.
</Card.Body>
</Card>
</Col>
</Collapse>
</Form.Group>
</Form>
</Col>
</Row>
</FixedWidthCenterAlignedLayout>
);
}