Skip to content

Commit 668a47d

Browse files
committed
[feat] Add automatic refreshing to measurements
1 parent 1a96f81 commit 668a47d

File tree

5 files changed

+144
-12
lines changed

5 files changed

+144
-12
lines changed

src/components/D3Diagram.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@ const D3Diagram = ({ data, deviceColorMap }: D3DiagramProps) => {
1313

1414
useEffect(() => {
1515
drawDiagram(d3Ref, data, deviceColorMap);
16+
17+
const currentD3Ref = d3Ref.current;
18+
return () => {
19+
if (currentD3Ref && currentD3Ref.firstChild) {
20+
currentD3Ref.firstChild.remove();
21+
}
22+
};
1623
}, [data, deviceColorMap]);
1724

1825
return <FullSizeContainer ref={d3Ref} />;

src/components/Diagram.tsx

Lines changed: 39 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useMemo } from "react";
1+
import React, { useMemo, useState, useEffect } from "react";
22
import styled from "styled-components";
33
import DataType from "../../functions/src/models/DataType";
44
import GetMeasurementsParams from "../../functions/src/models/GetMeasurementsParams";
@@ -8,11 +8,13 @@ import { FlexColumn } from "../elements/Flex";
88
import { Heading2 } from "../elements/Typography";
99
import getMeasurements from "../api/getMeasurements";
1010
import measurementsContainDataPoints from "./measurementsContainDataPoints";
11+
import Overlay from "../elements/Overlay";
1112

1213
interface DiagramProps {
1314
dataType: DataType;
1415
queryParams: GetMeasurementsParams;
1516
deviceColorMap: Map<string, string>;
17+
autoRefreshPeriod: number;
1618
}
1719

1820
const DiagramContainer = styled(FlexColumn)`
@@ -26,37 +28,62 @@ const DiagramContainer = styled(FlexColumn)`
2628

2729
const DiagramInner = styled.div`
2830
display: flex;
31+
position: relative;
2932
flex-grow: 1;
3033
width: 100%;
3134
background-color: ${({ theme }) => theme.palette.background.paper};
3235
`;
3336

34-
const Diagram = ({ dataType, queryParams, deviceColorMap }: DiagramProps) => {
37+
const Diagram = ({
38+
dataType,
39+
queryParams,
40+
deviceColorMap,
41+
autoRefreshPeriod,
42+
}: DiagramProps) => {
43+
const [updateKey, setUpdateKey] = useState(0);
44+
useEffect(() => {
45+
let interval: any = 0;
46+
if (autoRefreshPeriod !== 0) {
47+
interval = setInterval(
48+
() => setUpdateKey(Date.now()),
49+
autoRefreshPeriod * 1000
50+
);
51+
} else {
52+
clearInterval(interval);
53+
}
54+
return () => clearInterval(interval);
55+
}, [autoRefreshPeriod]);
3556
const query = useMemo(() => {
3657
return getMeasurements(queryParams);
3758
}, [queryParams]);
3859

3960
const { responseData: measurements, error, isLoading } = useQuery({
4061
query,
62+
compare: updateKey,
4163
});
4264

65+
const canShowRealData =
66+
measurements &&
67+
measurements.length &&
68+
measurementsContainDataPoints(measurements);
69+
4370
return (
4471
<DiagramContainer>
4572
<Heading2>
46-
{dataType.name} (unit: {dataType.unit})
73+
{dataType.name} ({dataType.unit})
4774
</Heading2>
4875
<DiagramInner>
76+
<D3Diagram
77+
data={canShowRealData ? measurements! : [["0000000000000000", []]]}
78+
deviceColorMap={deviceColorMap}
79+
/>
4980
{isLoading ? (
50-
"Loading data..."
81+
<Overlay>Loading data...</Overlay>
5182
) : error ? (
52-
"Failed to load data."
53-
) : measurements &&
54-
measurements.length &&
55-
measurementsContainDataPoints(measurements) ? (
56-
<D3Diagram data={measurements} deviceColorMap={deviceColorMap} />
57-
) : (
58-
"No data to display."
59-
)}
83+
<Overlay>Error loading data.</Overlay>
84+
) : !canShowRealData ? (
85+
<Overlay>No data to display</Overlay>
86+
) : null}
6087
</DiagramInner>
6188
</DiagramContainer>
6289
);

src/elements/Overlay.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import styled from "styled-components";
2+
3+
const Overlay = styled.div`
4+
backdrop-filter: blur(50px);
5+
opacity: 0.9;
6+
background-color: ${({ theme }) => theme.palette.background.paper};
7+
position: absolute;
8+
top: 0;
9+
bottom: 0;
10+
left: 0;
11+
right: 0;
12+
display: flex;
13+
align-items: center;
14+
justify-content: center;
15+
`;
16+
17+
export default Overlay;

src/elements/Switch.tsx

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React from "react";
2+
import styled from "styled-components";
3+
4+
interface SwitchProps {
5+
checked: boolean;
6+
label: string;
7+
onChange: () => void;
8+
}
9+
10+
const HiddenSwitch = styled.input.attrs({
11+
type: "checkbox",
12+
})`
13+
left: -100%;
14+
opacity: 0;
15+
position: absolute;
16+
margin: 0;
17+
padding: 0;
18+
`;
19+
20+
const SwitchContainer = styled.label`
21+
position: relative;
22+
display: inline-flex;
23+
flex-flow: row nowrap;
24+
flex-grow: 0;
25+
overflow: hidden;
26+
padding-top: ${({ theme }) => theme.spacing.d1}px;
27+
padding-bottom: ${({ theme }) => theme.spacing.d1}px;
28+
`;
29+
30+
const SwitchLabel = styled.span`
31+
display: block;
32+
margin-left: ${({ theme }) => theme.spacing.d1}px;
33+
`;
34+
35+
const SwitchBackground = styled.span<Pick<SwitchProps, "checked">>`
36+
display: block;
37+
width: ${({ theme }) => theme.spacing.d4 + theme.spacing.d2}px;
38+
height: ${({ theme }) => theme.spacing.d2}px;
39+
border-radius: ${({ theme }) => theme.shapes.borderRadius}px;
40+
background-color: ${({ theme }) => theme.palette.grey[100]};
41+
`;
42+
43+
const SwitchBullet = styled.span<Pick<SwitchProps, "checked">>`
44+
display: block;
45+
position: absolute;
46+
left: ${({ theme, checked }) => (!checked ? "0" : theme.spacing.d4 + "px")};
47+
transition: all 0.1s ease-in-out;
48+
height: ${({ theme }) => theme.spacing.d2}px;
49+
width: ${({ theme }) => theme.spacing.d2}px;
50+
border-radius: ${({ theme }) => theme.shapes.borderRadius}px;
51+
background-color: ${({ theme, checked }) =>
52+
!checked ? theme.palette.grey[400] : theme.palette.primary.main};
53+
`;
54+
55+
const Switch = ({ checked, label, onChange }: SwitchProps) => (
56+
<SwitchContainer>
57+
<SwitchBackground checked={checked}>
58+
<SwitchBullet checked={checked} />
59+
<HiddenSwitch checked={checked} onChange={onChange} />
60+
</SwitchBackground>
61+
<SwitchLabel>{label}</SwitchLabel>
62+
</SwitchContainer>
63+
);
64+
65+
export default Switch;

src/views/MainView.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,19 @@ import TimeRangeRow from "../components/TimeRangeRow";
66
import { FlexColumn } from "../elements/Flex";
77
import GridContainer from "../elements/GridContainer";
88
import Label from "../elements/Label";
9+
import Switch from "../elements/Switch";
910
import useQuery from "../hooks/useQuery";
1011

12+
const REFRESH_FREQUENCY = 10;
13+
1114
const MainView = () => {
1215
const { responseData: dataTypes, error, isLoading } = useQuery({
1316
query: getDataTypes,
1417
});
1518

19+
// 0 is false
20+
const [autoRefreshPeriod, setAutoRefreshPeriod] = useState<number>(0);
21+
1622
// empty list state means are all selected
1723
const [selectedDevices, setSelectedDevices] = useState<string[]>([]);
1824

@@ -46,6 +52,15 @@ const MainView = () => {
4652
setSelectedDevices={setSelectedDevices}
4753
setDeviceColorMap={setDeviceColorMap}
4854
/>
55+
<Switch
56+
checked={!!autoRefreshPeriod}
57+
onChange={() =>
58+
setAutoRefreshPeriod((currentPeriod) =>
59+
!!currentPeriod ? 0 : REFRESH_FREQUENCY
60+
)
61+
}
62+
label={`Automatically refresh measurements every ${REFRESH_FREQUENCY} seconds`}
63+
/>
4964
<GridContainer spacing="d2">
5065
{isLoading ? (
5166
<Label>Loading data types...</Label>
@@ -59,6 +74,7 @@ const MainView = () => {
5974
deviceColorMap={deviceColorMap}
6075
key={dataType.id}
6176
dataType={dataType}
77+
autoRefreshPeriod={autoRefreshPeriod}
6278
queryParams={{
6379
dataTypes: [dataType.id],
6480
devices: selectedDevices,

0 commit comments

Comments
 (0)