From 09e6a7133fc4cb3db2972aed5320253f3e62a091 Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Thu, 4 Jun 2020 22:52:23 +0200 Subject: [PATCH 1/5] Build context added --- src/App.jsx | 7 +- src/components/BuildList.tsx | 29 +++---- src/contexts/build.context.tsx | 134 +++++++++++++++++++++++++++++++++ src/pages/ProjectPage.tsx | 38 ++++++---- 4 files changed, 172 insertions(+), 36 deletions(-) create mode 100644 src/contexts/build.context.tsx diff --git a/src/App.jsx b/src/App.jsx index 8a15e89b..86429ec4 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -4,14 +4,17 @@ import Header from "./components/Header"; import { AuthProvider } from "./contexts/auth.context"; import { ProjectProvider } from "./contexts/project.context"; import Router from "./Router"; +import { BuildProvider } from "./contexts/build.context"; function App() { return (
-
- + +
+ +
diff --git a/src/components/BuildList.tsx b/src/components/BuildList.tsx index 6328a41b..22deef8f 100644 --- a/src/components/BuildList.tsx +++ b/src/components/BuildList.tsx @@ -11,15 +11,12 @@ import { Chip, } from "@material-ui/core"; import { Delete } from "@material-ui/icons"; -import { Build } from "../types"; -import { buildsService } from "../services"; import { useHistory } from "react-router-dom"; - -interface IBuildList { - builds: Build[]; - setBuilds: React.Dispatch>; - selectedBuildId: string | undefined; -} +import { + useBuildState, + useBuildDispatch, + deleteBuild, +} from "../contexts/build.context"; const useStyles = makeStyles((theme: Theme) => createStyles({ @@ -34,17 +31,15 @@ const useStyles = makeStyles((theme: Theme) => }) ); -const BuildList: FunctionComponent = ({ - builds, - setBuilds, - selectedBuildId, -}) => { +const BuildList: FunctionComponent = () => { const classes = useStyles(); const history = useHistory(); + const { buildList, selectedBuildId } = useBuildState(); + const buildDispatch = useBuildDispatch(); return ( - {builds.map((build) => ( + {buildList.map((build) => ( = ({ { - buildsService.remove(build.id).then((isRemoved) => { - if (isRemoved) { - setBuilds(builds.filter((item) => item.id !== build.id)); - } - }); + deleteBuild(buildDispatch, build.id); }} > diff --git a/src/contexts/build.context.tsx b/src/contexts/build.context.tsx new file mode 100644 index 00000000..c55bc928 --- /dev/null +++ b/src/contexts/build.context.tsx @@ -0,0 +1,134 @@ +import * as React from "react"; +import { Build } from "../types"; +import { buildsService } from "../services"; + +interface IRequestAction { + type: "request"; + payload?: undefined; +} + +interface IGetAction { + type: "get"; + payload: Build[]; +} + +interface ISelectAction { + type: "select"; + payload: string; +} + +interface IDeleteAction { + type: "delete"; + payload: string; +} + +type IAction = IRequestAction | IGetAction | IDeleteAction | ISelectAction; + +type Dispatch = (action: IAction) => void; +type State = { + selectedBuildId: string | undefined; + buildList: Build[]; +}; + +type BuildProviderProps = { children: React.ReactNode }; + +const BuildStateContext = React.createContext(undefined); +const BuildDispatchContext = React.createContext( + undefined +); + +const initialState: State = { + selectedBuildId: undefined, + buildList: [], +}; + +function buildReducer(state: State, action: IAction): State { + switch (action.type) { + case "select": + return { + ...state, + selectedBuildId: action.payload, + }; + case "get": + return { + ...state, + buildList: action.payload, + }; + case "delete": + return { + ...state, + buildList: state.buildList.filter((p) => p.id !== action.payload), + }; + default: + return state; + } +} + +function BuildProvider({ children }: BuildProviderProps) { + const [state, dispatch] = React.useReducer(buildReducer, initialState); + + return ( + + + {children} + + + ); +} + +function useBuildState() { + const context = React.useContext(BuildStateContext); + if (context === undefined) { + throw new Error("must be used within a BuildProvider"); + } + return context; +} + +function useBuildDispatch() { + const context = React.useContext(BuildDispatchContext); + if (context === undefined) { + throw new Error("must be used within a BuildProvider"); + } + return context; +} + +async function getBuildList(dispatch: Dispatch, id: string) { + dispatch({ type: "request" }); + + buildsService + .getList(id) + .then((items) => { + dispatch({ type: "get", payload: items }); + }) + .catch((error) => { + console.log(error.toString()); + }); +} + +async function deleteBuild(dispatch: Dispatch, id: string) { + dispatch({ type: "request" }); + + buildsService + .remove(id) + .then((isDeleted) => { + if (isDeleted) { + dispatch({ type: "delete", payload: id }); + } + }) + .catch((error) => { + console.log(error.toString()); + }); +} + +async function selectBuild(dispatch: Dispatch, id: string) { + dispatch({ type: "select", payload: id }); +} + +export { + BuildProvider, + useBuildState, + useBuildDispatch, + getBuildList, + deleteBuild, + selectBuild, +}; diff --git a/src/pages/ProjectPage.tsx b/src/pages/ProjectPage.tsx index 4ee1f05f..cb6255d8 100644 --- a/src/pages/ProjectPage.tsx +++ b/src/pages/ProjectPage.tsx @@ -1,8 +1,8 @@ import React, { useEffect, useState } from "react"; import { Grid, Dialog, IconButton, Box, Typography } from "@material-ui/core"; import { useParams, useLocation, useHistory } from "react-router-dom"; -import { Build, TestRun } from "../types"; -import { buildsService, testRunService } from "../services"; +import { TestRun } from "../types"; +import { testRunService } from "../services"; import BuildList from "../components/BuildList"; import ProjectSelect from "../components/ProjectSelect"; import qs from "qs"; @@ -11,6 +11,12 @@ import TestDetailsModal from "../components/TestDetailsModal"; import { NavigateBefore, NavigateNext } from "@material-ui/icons"; import Filters from "../components/Filters"; import { buildTestRunLocation } from "../_helpers/route.helpers"; +import { + useBuildState, + useBuildDispatch, + getBuildList, + selectBuild, +} from "../contexts/build.context"; const getQueryParams = (guery: string) => { const queryParams = qs.parse(guery, { ignoreQueryPrefix: true }); @@ -46,9 +52,9 @@ const ProjectPage = () => { const { projectId } = useParams(); const location = useLocation(); const history = useHistory(); - const [builds, setBuilds] = useState([]); + const { buildList, selectedBuildId } = useBuildState(); + const buildDispatch = useBuildDispatch(); const [testRuns, setTestRuns] = useState([]); - const [selectedBuildId, setSelectedBuildId] = useState(); const [selectedTestdId, setSelectedTestId] = useState(); const [selectedTestRunIndex, setSelectedTestRunIndex] = useState(); @@ -64,9 +70,9 @@ const ProjectPage = () => { useEffect(() => { const queryParams = getQueryParams(location.search); if (queryParams.buildId) { - setSelectedBuildId(queryParams.buildId); - } else if (builds.length > 0) { - setSelectedBuildId(builds[0].id); + selectBuild(buildDispatch, queryParams.buildId); + } else if (buildList.length > 0) { + selectBuild(buildDispatch, buildList[0].id); } if (queryParams.testId) { setSelectedTestId(queryParams.testId); @@ -76,13 +82,19 @@ const ProjectPage = () => { setSelectedTestId(undefined); setSelectedTestRunIndex(undefined); } - }, [location.search, builds, testRuns, selectedTestRunIndex]); + }, [ + location.search, + buildList, + testRuns, + selectedTestRunIndex, + buildDispatch, + ]); useEffect(() => { if (projectId) { - buildsService.getList(projectId).then((builds) => setBuilds(builds)); + getBuildList(buildDispatch, projectId); } - }, [projectId]); + }, [projectId, buildDispatch]); useEffect(() => { if (selectedBuildId) { @@ -126,11 +138,7 @@ const ProjectPage = () => { - + From 068fa33a43d92990e22bb97cc4965aacf334b69e Mon Sep 17 00:00:00 2001 From: Pavel Strunkin Date: Fri, 5 Jun 2020 23:21:35 +0200 Subject: [PATCH 2/5] build stats added --- src/components/TestDetailsModal.tsx | 10 ++-- src/contexts/build.context.tsx | 81 +++++++++++++++++++++++++++-- src/types/build.ts | 21 +++++--- src/types/testRun.ts | 2 +- 4 files changed, 99 insertions(+), 15 deletions(-) diff --git a/src/components/TestDetailsModal.tsx b/src/components/TestDetailsModal.tsx index 19c81d9b..fe759a82 100644 --- a/src/components/TestDetailsModal.tsx +++ b/src/components/TestDetailsModal.tsx @@ -37,6 +37,7 @@ import { TestRunDetails } from "./TestRunDetails"; import useImage from "use-image"; import { routes } from "../constants"; import { NoImagePlaceholder } from "./NoImageAvailable"; +import { useBuildDispatch, updateBuild } from "../contexts/build.context"; const useStyles = makeStyles((theme) => ({ imageContainer: { @@ -65,6 +66,7 @@ const TestDetailsModal: React.FunctionComponent<{ }> = ({ testRun, updateTestRun }) => { const classes = useStyles(); const history = useHistory(); + const buildDispatch = useBuildDispatch(); const stageWidth = (window.innerWidth / 2) * 0.9; const stageHeigth = window.innerHeight; @@ -172,6 +174,7 @@ const TestDetailsModal: React.FunctionComponent<{ onClick={() => testRunService.approve(testRun.id).then((testRun) => { updateTestRun(testRun); + updateBuild(buildDispatch, testRun); }) } > @@ -180,9 +183,10 @@ const TestDetailsModal: React.FunctionComponent<{