import React, { useState, useEffect } from "react"; import { Button, Card, ProgressBar } from "react-bootstrap"; import { mutateStopJob, useJobQueue, useJobsSubscribe, } from "src/core/StashService"; import * as GQL from "src/core/generated-graphql"; import { Icon } from "src/components/Shared"; import { useIntl } from "react-intl"; import { faBan, faCheck, faCircle, faCog, faHourglassStart, faTimes, } from "@fortawesome/free-solid-svg-icons"; type JobFragment = Pick< GQL.Job, "id" | "status" | "subTasks" | "description" | "progress" >; interface IJob { job: JobFragment; } const Task: React.FC = ({ job }) => { const [stopping, setStopping] = useState(false); const [className, setClassName] = useState(""); useEffect(() => { setTimeout(() => setClassName("fade-in")); }, []); useEffect(() => { if ( job.status === GQL.JobStatus.Cancelled || job.status === GQL.JobStatus.Finished ) { // fade out around 10 seconds setTimeout(() => { setClassName("fade-out"); }, 9800); } }, [job]); async function stopJob() { setStopping(true); await mutateStopJob(job.id); } function canStop() { return ( !stopping && (job.status === GQL.JobStatus.Ready || job.status === GQL.JobStatus.Running) ); } function getStatusClass() { switch (job.status) { case GQL.JobStatus.Ready: return "ready"; case GQL.JobStatus.Running: return "running"; case GQL.JobStatus.Stopping: return "stopping"; case GQL.JobStatus.Finished: return "finished"; case GQL.JobStatus.Cancelled: return "cancelled"; } } function getStatusIcon() { let icon = faCircle; let iconClass = ""; switch (job.status) { case GQL.JobStatus.Ready: icon = faHourglassStart; break; case GQL.JobStatus.Running: icon = faCog; iconClass = "fa-spin"; break; case GQL.JobStatus.Stopping: icon = faCog; iconClass = "fa-spin"; break; case GQL.JobStatus.Finished: icon = faCheck; break; case GQL.JobStatus.Cancelled: icon = faBan; break; } return ; } function maybeRenderProgress() { if ( job.status === GQL.JobStatus.Running && job.progress !== undefined && job.progress !== null ) { const progress = job.progress * 100; return ( ); } } function maybeRenderSubTasks() { if ( job.status === GQL.JobStatus.Running || job.status === GQL.JobStatus.Stopping ) { return (
{/* eslint-disable react/no-array-index-key */} {(job.subTasks ?? []).map((t, i) => (
{t}
))} {/* eslint-enable react/no-array-index-key */}
); } } return (
  • {getStatusIcon()} {job.description}
    {maybeRenderProgress()}
    {maybeRenderSubTasks()}
  • ); }; export const JobTable: React.FC = () => { const intl = useIntl(); const jobStatus = useJobQueue(); const jobsSubscribe = useJobsSubscribe(); const [queue, setQueue] = useState([]); useEffect(() => { setQueue(jobStatus.data?.jobQueue ?? []); }, [jobStatus]); useEffect(() => { if (!jobsSubscribe.data) { return; } const event = jobsSubscribe.data.jobsSubscribe; function updateJob() { setQueue((q) => q.map((j) => { if (j.id === event.job.id) { return event.job; } return j; }) ); } switch (event.type) { case GQL.JobStatusUpdateType.Add: // add to the end of the queue setQueue((q) => q.concat([event.job])); break; case GQL.JobStatusUpdateType.Remove: // update the job then remove after a timeout updateJob(); setTimeout(() => { setQueue((q) => q.filter((j) => j.id !== event.job.id)); }, 10000); break; case GQL.JobStatusUpdateType.Update: updateJob(); break; } }, [jobsSubscribe.data]); return (
      {!queue?.length ? ( {intl.formatMessage({ id: "config.tasks.empty_queue" })} ) : undefined} {(queue ?? []).map((j) => ( ))}
    ); };