Skip to content

Commit

Permalink
[feat] Add automatic refreshing to measurements
Browse files Browse the repository at this point in the history
  • Loading branch information
apata committed Jan 20, 2021
1 parent 1a96f81 commit 668a47d
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 12 deletions.
7 changes: 7 additions & 0 deletions src/components/D3Diagram.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ const D3Diagram = ({ data, deviceColorMap }: D3DiagramProps) => {

useEffect(() => {
drawDiagram(d3Ref, data, deviceColorMap);

const currentD3Ref = d3Ref.current;
return () => {
if (currentD3Ref && currentD3Ref.firstChild) {
currentD3Ref.firstChild.remove();
}
};
}, [data, deviceColorMap]);

return <FullSizeContainer ref={d3Ref} />;
Expand Down
51 changes: 39 additions & 12 deletions src/components/Diagram.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo } from "react";
import React, { useMemo, useState, useEffect } from "react";
import styled from "styled-components";
import DataType from "../../functions/src/models/DataType";
import GetMeasurementsParams from "../../functions/src/models/GetMeasurementsParams";
Expand All @@ -8,11 +8,13 @@ import { FlexColumn } from "../elements/Flex";
import { Heading2 } from "../elements/Typography";
import getMeasurements from "../api/getMeasurements";
import measurementsContainDataPoints from "./measurementsContainDataPoints";
import Overlay from "../elements/Overlay";

interface DiagramProps {
dataType: DataType;
queryParams: GetMeasurementsParams;
deviceColorMap: Map<string, string>;
autoRefreshPeriod: number;
}

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

const DiagramInner = styled.div`
display: flex;
position: relative;
flex-grow: 1;
width: 100%;
background-color: ${({ theme }) => theme.palette.background.paper};
`;

const Diagram = ({ dataType, queryParams, deviceColorMap }: DiagramProps) => {
const Diagram = ({
dataType,
queryParams,
deviceColorMap,
autoRefreshPeriod,
}: DiagramProps) => {
const [updateKey, setUpdateKey] = useState(0);
useEffect(() => {
let interval: any = 0;
if (autoRefreshPeriod !== 0) {
interval = setInterval(
() => setUpdateKey(Date.now()),
autoRefreshPeriod * 1000
);
} else {
clearInterval(interval);
}
return () => clearInterval(interval);
}, [autoRefreshPeriod]);
const query = useMemo(() => {
return getMeasurements(queryParams);
}, [queryParams]);

const { responseData: measurements, error, isLoading } = useQuery({
query,
compare: updateKey,
});

const canShowRealData =
measurements &&
measurements.length &&
measurementsContainDataPoints(measurements);

return (
<DiagramContainer>
<Heading2>
{dataType.name} (unit: {dataType.unit})
{dataType.name} ({dataType.unit})
</Heading2>
<DiagramInner>
<D3Diagram
data={canShowRealData ? measurements! : [["0000000000000000", []]]}
deviceColorMap={deviceColorMap}
/>
{isLoading ? (
"Loading data..."
<Overlay>Loading data...</Overlay>
) : error ? (
"Failed to load data."
) : measurements &&
measurements.length &&
measurementsContainDataPoints(measurements) ? (
<D3Diagram data={measurements} deviceColorMap={deviceColorMap} />
) : (
"No data to display."
)}
<Overlay>Error loading data.</Overlay>
) : !canShowRealData ? (
<Overlay>No data to display</Overlay>
) : null}
</DiagramInner>
</DiagramContainer>
);
Expand Down
17 changes: 17 additions & 0 deletions src/elements/Overlay.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import styled from "styled-components";

const Overlay = styled.div`
backdrop-filter: blur(50px);
opacity: 0.9;
background-color: ${({ theme }) => theme.palette.background.paper};
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
justify-content: center;
`;

export default Overlay;
65 changes: 65 additions & 0 deletions src/elements/Switch.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React from "react";
import styled from "styled-components";

interface SwitchProps {
checked: boolean;
label: string;
onChange: () => void;
}

const HiddenSwitch = styled.input.attrs({
type: "checkbox",
})`
left: -100%;
opacity: 0;
position: absolute;
margin: 0;
padding: 0;
`;

const SwitchContainer = styled.label`
position: relative;
display: inline-flex;
flex-flow: row nowrap;
flex-grow: 0;
overflow: hidden;
padding-top: ${({ theme }) => theme.spacing.d1}px;
padding-bottom: ${({ theme }) => theme.spacing.d1}px;
`;

const SwitchLabel = styled.span`
display: block;
margin-left: ${({ theme }) => theme.spacing.d1}px;
`;

const SwitchBackground = styled.span<Pick<SwitchProps, "checked">>`
display: block;
width: ${({ theme }) => theme.spacing.d4 + theme.spacing.d2}px;
height: ${({ theme }) => theme.spacing.d2}px;
border-radius: ${({ theme }) => theme.shapes.borderRadius}px;
background-color: ${({ theme }) => theme.palette.grey[100]};
`;

const SwitchBullet = styled.span<Pick<SwitchProps, "checked">>`
display: block;
position: absolute;
left: ${({ theme, checked }) => (!checked ? "0" : theme.spacing.d4 + "px")};
transition: all 0.1s ease-in-out;
height: ${({ theme }) => theme.spacing.d2}px;
width: ${({ theme }) => theme.spacing.d2}px;
border-radius: ${({ theme }) => theme.shapes.borderRadius}px;
background-color: ${({ theme, checked }) =>
!checked ? theme.palette.grey[400] : theme.palette.primary.main};
`;

const Switch = ({ checked, label, onChange }: SwitchProps) => (
<SwitchContainer>
<SwitchBackground checked={checked}>
<SwitchBullet checked={checked} />
<HiddenSwitch checked={checked} onChange={onChange} />
</SwitchBackground>
<SwitchLabel>{label}</SwitchLabel>
</SwitchContainer>
);

export default Switch;
16 changes: 16 additions & 0 deletions src/views/MainView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@ import TimeRangeRow from "../components/TimeRangeRow";
import { FlexColumn } from "../elements/Flex";
import GridContainer from "../elements/GridContainer";
import Label from "../elements/Label";
import Switch from "../elements/Switch";
import useQuery from "../hooks/useQuery";

const REFRESH_FREQUENCY = 10;

const MainView = () => {
const { responseData: dataTypes, error, isLoading } = useQuery({
query: getDataTypes,
});

// 0 is false
const [autoRefreshPeriod, setAutoRefreshPeriod] = useState<number>(0);

// empty list state means are all selected
const [selectedDevices, setSelectedDevices] = useState<string[]>([]);

Expand Down Expand Up @@ -46,6 +52,15 @@ const MainView = () => {
setSelectedDevices={setSelectedDevices}
setDeviceColorMap={setDeviceColorMap}
/>
<Switch
checked={!!autoRefreshPeriod}
onChange={() =>
setAutoRefreshPeriod((currentPeriod) =>
!!currentPeriod ? 0 : REFRESH_FREQUENCY
)
}
label={`Automatically refresh measurements every ${REFRESH_FREQUENCY} seconds`}
/>
<GridContainer spacing="d2">
{isLoading ? (
<Label>Loading data types...</Label>
Expand All @@ -59,6 +74,7 @@ const MainView = () => {
deviceColorMap={deviceColorMap}
key={dataType.id}
dataType={dataType}
autoRefreshPeriod={autoRefreshPeriod}
queryParams={{
dataTypes: [dataType.id],
devices: selectedDevices,
Expand Down

0 comments on commit 668a47d

Please sign in to comment.