Skip to content

Commit b8de21e

Browse files
committed
feat: Add latency indicator to the UI
With Tailscale, we now get latency of all regions.
1 parent de219d9 commit b8de21e

File tree

5 files changed

+162
-16
lines changed

5 files changed

+162
-16
lines changed

site/.storybook/main.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ module.exports = {
1414
// addons are official and community plugins to extend Storybook.
1515
//
1616
// SEE: https://storybook.js.org/addons
17-
addons: ["@storybook/addon-links", "@storybook/addon-essentials"],
17+
addons: ["@storybook/addon-links", {
18+
name: "@storybook/addon-essentials",
19+
options: {
20+
actions: false,
21+
},
22+
}],
1823

1924
// Storybook uses babel under the hood, while we currently use ts-loader.
2025
// Sometimes, you may encounter an error in a Storybook that contains syntax
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { Story } from "@storybook/react"
2+
import { ResourceAgentLatency, ResourceAgentLatencyProps } from "./ResourceAgentLatency"
3+
4+
export default {
5+
title: "components/ResourceAgentLatency",
6+
component: ResourceAgentLatency,
7+
}
8+
9+
const Template: Story<ResourceAgentLatencyProps> = (args) => <ResourceAgentLatency {...args} />
10+
11+
export const Single = Template.bind({})
12+
Single.args = {
13+
latency: {
14+
"Coder DERP": {
15+
latency_ms: 100.52,
16+
preferred: true,
17+
},
18+
},
19+
}
20+
21+
export const Multiple = Template.bind({})
22+
Multiple.args = {
23+
latency: {
24+
Chicago: {
25+
latency_ms: 22.25551,
26+
preferred: false,
27+
},
28+
"New York": {
29+
latency_ms: 56.1111,
30+
preferred: true,
31+
},
32+
"San Francisco": {
33+
latency_ms: 125.11,
34+
preferred: false,
35+
},
36+
Tokyo: {
37+
latency_ms: 255,
38+
preferred: false,
39+
},
40+
},
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import { makeStyles } from "@material-ui/core/styles"
2+
import StarIcon from "@material-ui/icons/Star"
3+
import React from "react"
4+
import { WorkspaceAgent } from "../../api/typesGenerated"
5+
import { HelpTooltip, HelpTooltipText } from "../Tooltips/HelpTooltip"
6+
7+
export interface ResourceAgentLatencyProps {
8+
latency: WorkspaceAgent["latency"]
9+
}
10+
11+
export const ResourceAgentLatency: React.FC<ResourceAgentLatencyProps> = (props) => {
12+
const styles = useStyles()
13+
if (Object.keys(props.latency).length === 0) {
14+
return null
15+
}
16+
return (
17+
<div className={styles.root}>
18+
<div className={styles.title}>
19+
Latency
20+
<HelpTooltip size="small">
21+
<HelpTooltipText>
22+
Latency from relay servers, used when connections cannot connect peer-to-peer. Star
23+
indicates the preferred relay.
24+
</HelpTooltipText>
25+
</HelpTooltip>
26+
</div>
27+
{Object.keys(props.latency)
28+
.sort()
29+
.map((region) => {
30+
const value = props.latency[region]
31+
return (
32+
<div key={region} className={styles.region}>
33+
<b>{region}:</b>&nbsp;{Math.round(value.latency_ms * 100) / 100} ms
34+
{value.preferred && <StarIcon className={styles.star} />}
35+
</div>
36+
)
37+
})}
38+
</div>
39+
)
40+
}
41+
42+
const useStyles = makeStyles(() => ({
43+
root: {
44+
display: "grid",
45+
gap: 6,
46+
},
47+
title: {
48+
fontSize: "Something",
49+
display: "flex",
50+
alignItems: "center",
51+
// This ensures that the latency aligns with other columns in the grid.
52+
height: 20,
53+
},
54+
region: {
55+
display: "flex",
56+
alignItems: "center",
57+
},
58+
star: {
59+
width: 14,
60+
height: 14,
61+
marginLeft: 4,
62+
},
63+
}))

site/src/components/Resources/Resources.tsx

+29-14
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { TerminalLink } from "../TerminalLink/TerminalLink"
2020
import { AgentHelpTooltip } from "../Tooltips/AgentHelpTooltip"
2121
import { AgentOutdatedTooltip } from "../Tooltips/AgentOutdatedTooltip"
2222
import { ResourcesHelpTooltip } from "../Tooltips/ResourcesHelpTooltip"
23+
import { ResourceAgentLatency } from "./ResourceAgentLatency"
2324
import { ResourceAvatarData } from "./ResourceAvatarData"
2425

2526
const Language = {
@@ -113,21 +114,28 @@ export const Resources: FC<React.PropsWithChildren<ResourcesProps>> = ({
113114

114115
<TableCell className={styles.agentColumn}>
115116
<TableCellDataPrimary highlight>{agent.name}</TableCellDataPrimary>
116-
<div className={styles.data}>
117-
<div className={styles.dataRow}>
118-
<strong>{Language.statusLabel}</strong>
119-
<span style={{ color: agentStatus.color }} className={styles.status}>
120-
{agentStatus.status}
121-
</span>
117+
<div className={styles.dataWrapper}>
118+
<div className={styles.data}>
119+
<div className={styles.dataRow}>
120+
<strong>{Language.statusLabel}</strong>
121+
<span style={{ color: agentStatus.color }} className={styles.status}>
122+
{agentStatus.status}
123+
</span>
124+
</div>
125+
<div className={styles.dataRow}>
126+
<strong>{Language.osLabel}</strong>
127+
<span className={styles.operatingSystem}>
128+
{agent.operating_system}
129+
</span>
130+
</div>
131+
<div className={styles.dataRow}>
132+
<strong>{Language.versionLabel}</strong>
133+
<span className={styles.agentVersion}>{displayVersion}</span>
134+
<AgentOutdatedTooltip outdated={outdated} />
135+
</div>
122136
</div>
123-
<div className={styles.dataRow}>
124-
<strong>{Language.osLabel}</strong>
125-
<span className={styles.operatingSystem}>{agent.operating_system}</span>
126-
</div>
127-
<div className={styles.dataRow}>
128-
<strong>{Language.versionLabel}</strong>
129-
<span className={styles.agentVersion}>{displayVersion}</span>
130-
<AgentOutdatedTooltip outdated={outdated} />
137+
<div className={styles.data}>
138+
<ResourceAgentLatency latency={agent.latency} />
131139
</div>
132140
</div>
133141
</TableCell>
@@ -224,6 +232,12 @@ const useStyles = makeStyles((theme) => ({
224232
whiteSpace: "nowrap",
225233
},
226234

235+
dataWrapper: {
236+
display: "grid",
237+
gap: theme.spacing(1),
238+
gridAutoFlow: "column",
239+
},
240+
227241
data: {
228242
color: theme.palette.text.secondary,
229243
fontSize: 14,
@@ -232,6 +246,7 @@ const useStyles = makeStyles((theme) => ({
232246
gridAutoFlow: "row",
233247
whiteSpace: "nowrap",
234248
gap: theme.spacing(0.75),
249+
height: "fit-content",
235250
},
236251

237252
dataRow: {

site/src/testHelpers/entities.ts

+23-1
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,12 @@ export const MockWorkspaceAgent: TypesGen.WorkspaceAgent = {
311311
status: "connected",
312312
updated_at: "",
313313
version: MockBuildInfo.version,
314-
latency: {},
314+
latency: {
315+
"Coder Embedded DERP": {
316+
latency_ms: 32.55,
317+
preferred: true,
318+
},
319+
},
315320
}
316321

317322
export const MockWorkspaceAgentDisconnected: TypesGen.WorkspaceAgent = {
@@ -320,6 +325,7 @@ export const MockWorkspaceAgentDisconnected: TypesGen.WorkspaceAgent = {
320325
name: "another-workspace-agent",
321326
status: "disconnected",
322327
version: "",
328+
latency: {},
323329
}
324330

325331
export const MockWorkspaceAgentOutdated: TypesGen.WorkspaceAgent = {
@@ -328,6 +334,21 @@ export const MockWorkspaceAgentOutdated: TypesGen.WorkspaceAgent = {
328334
name: "an-outdated-workspace-agent",
329335
version: "v99.999.9998+abcdef",
330336
operating_system: "Windows",
337+
latency: {
338+
...MockWorkspaceAgent.latency,
339+
Chicago: {
340+
preferred: false,
341+
latency_ms: 95.11,
342+
},
343+
"San Francisco": {
344+
preferred: false,
345+
latency_ms: 111.55,
346+
},
347+
Paris: {
348+
preferred: false,
349+
latency_ms: 221.66,
350+
},
351+
},
331352
}
332353

333354
export const MockWorkspaceAgentConnecting: TypesGen.WorkspaceAgent = {
@@ -336,6 +357,7 @@ export const MockWorkspaceAgentConnecting: TypesGen.WorkspaceAgent = {
336357
name: "another-workspace-agent",
337358
status: "connecting",
338359
version: "",
360+
latency: {},
339361
}
340362

341363
export const MockWorkspaceResource: TypesGen.WorkspaceResource = {

0 commit comments

Comments
 (0)