Просмотр исходного кода

Fixed live update of status on chart list

jnfrati 4 лет назад
Родитель
Сommit
67c0cfb54f

+ 95 - 66
dashboard/src/main/home/cluster-dashboard/chart/Chart.tsx

@@ -1,31 +1,27 @@
-import React, { Component } from "react";
+import React, { useContext, useEffect, useMemo, useState } from "react";
 import styled from "styled-components";
 
-import { ChartType } from "shared/types";
+import { ChartType, StorageType } from "shared/types";
 import { Context } from "shared/Context";
 import StatusIndicator from "components/StatusIndicator";
-import { pushFiltered, pushQueryParams } from "shared/routing";
-import { RouteComponentProps, withRouter } from "react-router";
+import { pushFiltered } from "shared/routing";
+import { useHistory, useLocation, useRouteMatch } from "react-router";
+import api from "shared/api";
 
-type PropsType = RouteComponentProps & {
+type Props = {
   chart: ChartType;
   controllers: Record<string, any>;
 };
 
-type StateType = {
-  expand: boolean;
-  update: any[];
-};
-
-class Chart extends Component<PropsType, StateType> {
-  state = {
-    expand: false,
-    update: [] as any[],
-  };
-
-  renderIcon = () => {
-    let { chart } = this.props;
+const Chart: React.FunctionComponent<Props> = ({ chart, controllers }) => {
+  const [expand, setExpand] = useState<boolean>(false);
+  const [chartControllers, setChartControllers] = useState<any>([]);
+  const context = useContext(Context);
+  const location = useLocation();
+  const history = useHistory();
+  const match = useRouteMatch();
 
+  const renderIcon = () => {
     if (chart.chart.metadata.icon && chart.chart.metadata.icon !== "") {
       return <Icon src={chart.chart.metadata.icon} />;
     } else {
@@ -33,65 +29,98 @@ class Chart extends Component<PropsType, StateType> {
     }
   };
 
-  readableDate = (s: string) => {
-    let ts = new Date(s);
-    let date = ts.toLocaleDateString();
-    let time = ts.toLocaleTimeString([], {
+  const getControllerForChart = async (chart: ChartType) => {
+    try {
+      const { currentCluster, currentProject } = context;
+      const res = await api.getChartControllers(
+        "<token>",
+        {
+          namespace: chart.namespace,
+          cluster_id: currentCluster.id,
+          storage: StorageType.Secret,
+        },
+        {
+          id: currentProject.id,
+          name: chart.name,
+          revision: chart.version,
+        }
+      );
+
+      const controllersUid = res.data.map((c: any) => {
+        return c.metadata.uid;
+      });
+      setChartControllers(controllersUid);
+    } catch (error) {
+      context.setCurrentError(JSON.stringify(error));
+    }
+  };
+
+  useEffect(() => {
+    getControllerForChart(chart);
+  }, [chart]);
+
+  const readableDate = (s: string) => {
+    const ts = new Date(s);
+    const date = ts.toLocaleDateString();
+    const time = ts.toLocaleTimeString([], {
       hour: "numeric",
       minute: "2-digit",
     });
     return `${time} on ${date}`;
   };
 
-  render() {
-    let { chart } = this.props;
-
-    return (
-      <StyledChart
-        onMouseEnter={() => this.setState({ expand: true })}
-        onMouseLeave={() => this.setState({ expand: false })}
-        expand={this.state.expand}
-        onClick={() => {
-          let { location, match } = this.props;
-          let urlParams = new URLSearchParams(location.search);
-          let cluster = urlParams.get("cluster");
-          let route = `${match.url}/${cluster}/${chart.namespace}/${chart.name}`;
-          pushFiltered(this.props, route, ["project_id"]);
-        }}
-      >
-        <Title>
-          <IconWrapper>{this.renderIcon()}</IconWrapper>
-          {chart.name}
-        </Title>
+  const filteredControllers = useMemo(() => {
+    let tmpControllers: any = {};
+    chartControllers.forEach((uid: any) => {
+      if (!controllers[uid]) {
+        return;
+      }
+      tmpControllers[uid] = controllers[uid];
+    });
+    return tmpControllers;
+  }, [chartControllers, controllers]);
 
-        <BottomWrapper>
-          <InfoWrapper>
-            <StatusIndicator
-              controllers={this.props.controllers}
-              status={chart.info.status}
-              margin_left={"17px"}
-            />
-            <LastDeployed>
-              <Dot>•</Dot> Last deployed{" "}
-              {this.readableDate(chart.info.last_deployed)}
-            </LastDeployed>
-          </InfoWrapper>
+  return (
+    <StyledChart
+      onMouseEnter={() => setExpand(true)}
+      onMouseLeave={() => setExpand(false)}
+      expand={expand}
+      onClick={() => {
+        let urlParams = new URLSearchParams(location.search);
+        let cluster = urlParams.get("cluster");
+        let route = `${match.url}/${cluster}/${chart.namespace}/${chart.name}`;
+        pushFiltered({ location, history }, route, ["project_id"]);
+      }}
+    >
+      <Title>
+        <IconWrapper>{renderIcon()}</IconWrapper>
+        {chart.name}
+      </Title>
 
-          <TagWrapper>
-            Namespace
-            <NamespaceTag>{chart.namespace}</NamespaceTag>
-          </TagWrapper>
-        </BottomWrapper>
+      <BottomWrapper>
+        <InfoWrapper>
+          <StatusIndicator
+            controllers={filteredControllers}
+            status={chart.info.status}
+            margin_left={"17px"}
+          />
+          <LastDeployed>
+            <Dot>•</Dot> Last deployed {readableDate(chart.info.last_deployed)}
+          </LastDeployed>
+        </InfoWrapper>
 
-        <Version>v{chart.version}</Version>
-      </StyledChart>
-    );
-  }
-}
+        <TagWrapper>
+          Namespace
+          <NamespaceTag>{chart.namespace}</NamespaceTag>
+        </TagWrapper>
+      </BottomWrapper>
 
-Chart.contextType = Context;
+      <Version>v{chart.version}</Version>
+    </StyledChart>
+  );
+};
 
-export default withRouter(Chart);
+export default Chart;
 
 const BottomWrapper = styled.div`
   display: flex;

+ 5 - 69
dashboard/src/main/home/cluster-dashboard/chart/ChartList.tsx

@@ -10,7 +10,7 @@ import Chart from "./Chart";
 import Loading from "components/Loading";
 import { useWebsockets } from "shared/hooks/useWebsockets";
 
-type PropsType = {
+type Props = {
   currentCluster: ClusterType;
   namespace: string;
   // TODO Convert to enum
@@ -18,7 +18,7 @@ type PropsType = {
   currentView: PorterUrl;
 };
 
-const ChartList: React.FunctionComponent<PropsType> = ({
+const ChartList: React.FunctionComponent<Props> = ({
   namespace,
   sortType,
   currentView,
@@ -30,9 +30,6 @@ const ChartList: React.FunctionComponent<PropsType> = ({
     closeAllWebsockets,
   } = useWebsockets();
   const [charts, setCharts] = useState<ChartType[]>([]);
-  const [chartLookupTable, setChartLookupTable] = useState<
-    Record<string, string>
-  >({});
   const [controllers, setControllers] = useState<
     Record<string, Record<string, any>>
   >({});
@@ -103,8 +100,6 @@ const ChartList: React.FunctionComponent<PropsType> = ({
       console.log(error);
       context.setCurrentError(JSON.stringify(error));
       setIsError(true);
-    } finally {
-      setIsLoading(false);
     }
   };
 
@@ -120,19 +115,10 @@ const ChartList: React.FunctionComponent<PropsType> = ({
         let event = JSON.parse(evt.data);
         let object = event.Object;
         object.metadata.kind = event.Kind;
-        let chartKey = chartLookupTable[object.metadata.uid];
-
-        // ignore if updated object does not belong to any chart in the list.
-        if (!chartKey) {
-          return;
-        }
-
-        let chartControllers = controllers[chartKey];
-        chartControllers[object.metadata.uid] = object;
 
         setControllers((oldControllers) => ({
           ...oldControllers,
-          [chartKey]: chartControllers,
+          [object.metadata.uid]: object,
         }));
       },
       onclose: () => {
@@ -155,53 +141,6 @@ const ChartList: React.FunctionComponent<PropsType> = ({
     });
   };
 
-  const getControllerForChart = async (chart: ChartType) => {
-    try {
-      const { currentCluster, currentProject } = context;
-      const res = await api.getChartControllers(
-        "<token>",
-        {
-          namespace: chart.namespace,
-          cluster_id: currentCluster.id,
-          storage: StorageType.Secret,
-        },
-        {
-          id: currentProject.id,
-          name: chart.name,
-          revision: chart.version,
-        }
-      );
-
-      let chartControllers = {} as Record<string, Record<string, any>>;
-
-      res.data.forEach((c: any) => {
-        c.metadata.kind = c.kind;
-        chartControllers[c.metadata.uid] = c;
-      });
-
-      res.data.forEach(async (c: any) => {
-        setChartLookupTable((oldChartLookupTable) => ({
-          ...oldChartLookupTable,
-          [c.metadata.uid]: `${chart.namespace}-${chart.name}`,
-        }));
-        setControllers((oldControllers) => ({
-          ...oldControllers,
-          [`${chart.namespace}-${chart.name}`]: chartControllers,
-        }));
-      });
-    } catch (error) {
-      context.setCurrentError(JSON.stringify(error));
-    }
-  };
-
-  const getControllers = (charts: any[]) => {
-    charts.forEach(async (chart: any) => {
-      // don't retrieve controllers for chart that failed to even deploy.
-      if (chart.info.status == "failed") return;
-      await getControllerForChart(chart);
-    });
-  };
-
   // Setup basic websockets on start
   useEffect(() => {
     setControllerWebsockets([
@@ -223,7 +162,7 @@ const ChartList: React.FunctionComponent<PropsType> = ({
       updateCharts().then((charts) => {
         if (isSubscribed) {
           setCharts(charts);
-          getControllers(charts);
+          setIsLoading(false);
         }
       });
     }
@@ -258,10 +197,7 @@ const ChartList: React.FunctionComponent<PropsType> = ({
         <Chart
           key={`${chart.namespace}-${chart.name}`}
           chart={chart}
-          controllers={
-            controllers[`${chart.namespace}-${chart.name}`] ||
-            ({} as Record<string, any>)
-          }
+          controllers={controllers || {}}
         />
       );
     });