import { useMutation, useQuery, useSubscription } from "@apollo/client"; import { Link, useResponse, useRouter } from "@curi/react-dom"; import { faPlayCircle, faVial } from "@fortawesome/free-solid-svg-icons"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { Backdrop, CircularProgress, Collapse, IconButton, LinearProgress, List, ListItem, ListItemIcon, ListItemSecondaryAction, ListItemText, makeStyles, useTheme, withStyles, } from "@material-ui/core"; import { Cancel, CheckCircle, CloudDownload, ShoppingCart, Stop, Timer, } from "@material-ui/icons"; import { format } from "date-fns"; import { useSnackbar } from "notistack"; import { complement, equals, find, propEq, takeWhile } from "ramda"; import { FC, Fragment, MouseEventHandler, ReactNode, useCallback, useMemo, useState, } from "react"; import { Commit, CreatePipelineTaskInput, Pipeline, PipelineTask, TaskStatuses, PipelineUnits, } from "../generated/graphql"; import { CREATE_PIPELINE_TASK, STOP_PIPELINE_TASK } from "./mutations"; import { COMMITS } from "./queries"; import { SYNC_COMMITS } from "./subscriptions"; interface Props { pipeline: Pipeline; } const useStyles = makeStyles((theme) => ({ root: { flex: "1 1 100%", }, nested: { paddingLeft: theme.spacing(4), }, })); export const CommitList: FC = ({ pipeline }) => { const { enqueueSnackbar } = useSnackbar(); const { data, loading, refetch } = useQuery<{ commits?: Commit[] }>(COMMITS, { variables: { pipelineId: pipeline.id, }, }); const { loading: syncing } = useSubscription<{ syncCommits: boolean }>( SYNC_COMMITS, { variables: { pipelineId: pipeline.id, }, onSubscriptionData({ subscriptionData: { data, error } }) { if (error) { enqueueSnackbar(error.message, { variant: "warning", }); } if (data?.syncCommits) { refetch({ appInstance: data.syncCommits, }); } }, } ); const classes = useStyles(); return (
{(() => { if (loading) { return ; } return (
{syncing && } {data?.commits?.map((commit) => ( ))}
); })()}
); }; const unitActionPairs: Array<[PipelineUnits, ReactNode, string]> = [ [PipelineUnits.Checkout, , "checkout"], [ PipelineUnits.InstallDependencies, , "install dependencies", ], [PipelineUnits.Test, , "test"], [PipelineUnits.Deploy, , "deploy"], ]; const Item: FC<{ commit: Commit; pipeline: Pipeline }> = ({ commit, pipeline, }) => { const [isOpen, setOpen] = useState(() => false); const [createTask, { loading }] = useMutation< { createPipelineTask: PipelineTask }, { task: CreatePipelineTaskInput } >(CREATE_PIPELINE_TASK); const units = useMemo( () => pipeline.workUnitMetadata.units.map((unit) => unit.type), [pipeline] ); const { navigate, url } = useRouter(); const { response } = useResponse(); const handleCreateTask = useCallback( (unit: PipelineUnits) => { const _units = [...takeWhile(complement(equals(unit)), units), unit]; createTask({ variables: { task: { units: _units, pipelineId: pipeline.id, commit: commit.hash, }, }, }).then(({ data }) => { navigate({ url: url({ name: "pipeline-task-detail", params: { ...response.params, taskId: data?.createPipelineTask.id }, }), }); }); }, [commit, createTask, navigate, pipeline, response, units, url] ); const actions = useMemo( () => units.map((unit) => { const pair = find(propEq(0, unit), unitActionPairs); return ( pair && ( handleCreateTask(unit)} > {pair[1]} ) ); }), [units, handleCreateTask, loading] ); return ( setOpen(!isOpen)}> {actions} {loading && } {commit.tasks.map((task) => ( ))} ); }; const TaskItem: FC<{ task: PipelineTask }> = ({ task }) => { const classes = useStyles(); const theme = useTheme(); const statusIcon: ReactNode = (() => { switch (task.status) { case TaskStatuses.Pending: return ; case TaskStatuses.Success: return ; case TaskStatuses.Failed: return ; case TaskStatuses.Working: return ( ); } })(); const [stopTask, { loading: stopTaskWaiting }] = useMutation( STOP_PIPELINE_TASK, { variables: { taskId: task.id }, } ); const stop: MouseEventHandler = useCallback( (event) => { event.stopPropagation(); event.preventDefault(); stopTask(); }, [stopTask] ); return ( {statusIcon} {task.status === TaskStatuses.Working && ( )} ); }; const LimitedBackdrop = withStyles({ root: { position: "absolute", zIndex: 1, }, })(Backdrop);