hasher-matcher-actioner/webapp/src/pages/ContentDetailsWithStepper.tsx (105 lines of code) (raw):
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved
*/
import React, {useEffect, useState} from 'react';
import {Button, Col, Container, Row, Spinner} from 'react-bootstrap';
import {Link, useParams} from 'react-router-dom';
import {fetchContentPipelineProgress} from '../Api';
import ContentPreview from '../components/ContentPreview';
import ContentProgressStepper from '../components/ContentProgressStepper';
import {getContentTypeForString} from '../utils/constants';
import {toDate} from '../utils/DateTimeUtils';
import FixedWidthCenterAlignedLayout from './layouts/FixedWidthCenterAlignedLayout';
const POLL_INTERVAL_IN_SECONDS = 5;
function getStepperLoadingPane(): JSX.Element {
return (
<div>
<h1>
<Spinner animation="border" /> Loading state for content.
</h1>
</div>
);
}
export default function ContentDetailsWithStepper(): JSX.Element {
const {id} = useParams<{id: string}>();
const [contentType, setContentType] = useState<string | null>(null);
const [contentPreviewURL, setContentPreviewURL] = useState<string | null>(
null,
);
const [pollBuster, setPollBuster] = useState(1);
// Stage Times
const [submittedAt, setSubmittedAt] = useState<Date>();
const [hashedAt, setHashedAt] = useState<Date>();
const [matchedAt, setMatchedAt] = useState<Date>();
const [actionEvaluatedAt, setActionEvaluatedAt] = useState<Date>();
const [actionPerformedAt, setActionPerformedAt] = useState<Date>();
// // Stage Details
// const [additionalFields, setAdditionalFields] = useState([]);
// const [hashResults, setHashResults] = useState({});
// const [matchResults, setMatchResults] = useState({});
// const [actionEvaluationResults, setActionEvaluationResults] = useState([]);
// const [actionPerformResults, setActionPerformResults] = useState([]);
useEffect(() => {
// This effect is rerun every time the value of poll buster changes.
fetchContentPipelineProgress(id).then(pipelineProgress => {
setContentType(pipelineProgress.content_type);
setContentPreviewURL(pipelineProgress.content_preview_url);
// Handle date times
setSubmittedAt(toDate(pipelineProgress.submitted_at));
setHashedAt(toDate(pipelineProgress.hashed_at));
setMatchedAt(toDate(pipelineProgress.matched_at));
setActionEvaluatedAt(toDate(pipelineProgress.action_evaluated_at));
setActionPerformedAt(toDate(pipelineProgress.action_performed_at));
if (pipelineProgress.action_performed_at === null) {
// The pipeline has not yet finished, so setup a poller to call this
// effect again. If we start storing null records, ie. match produced no
// results, then we need to revisit how we evaluate that the pipeline is
// "done".
setTimeout(() => {
setPollBuster(pollBuster + 1);
}, POLL_INTERVAL_IN_SECONDS * 1000);
}
});
}, [pollBuster]);
return (
<FixedWidthCenterAlignedLayout title="Progress">
<Row>
<Col md={{span: 6}}>
{contentPreviewURL && contentType ? (
<ContentPreview
contentId={id}
contentType={getContentTypeForString(contentType as string)}
url={contentPreviewURL}
/>
) : null}
</Col>
<Col md={{span: 6}}>
{submittedAt ? (
<ContentProgressStepper
submittedAt={submittedAt as Date}
hashedAt={hashedAt}
matchedAt={matchedAt}
actionEvaluatedAt={actionEvaluatedAt}
actionPerformedAt={actionPerformedAt}
/>
) : (
getStepperLoadingPane()
)}
<hr />
<Container>
<Row>
<Col className="mt-2" xs={{offset: 1, span: 8}}>
<Link to={`/matches/${id}`}>Go to Content Details Page</Link>
</Col>
<Col xs={{span: 2}}>
<Button
size="sm"
variant="secondary"
onClick={() => setPollBuster(pollBuster + 1)}>
Refresh
</Button>
</Col>
</Row>
</Container>
</Col>
</Row>
</FixedWidthCenterAlignedLayout>
);
}