Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 6b21f00

Browse files
committedOct 16, 2022
Remove visual stutter
1 parent de58d53 commit 6b21f00

File tree

3 files changed

+61
-48
lines changed

3 files changed

+61
-48
lines changed
 

‎site/src/components/Workspace/Workspace.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ import { WorkspaceSection } from "../WorkspaceSection/WorkspaceSection"
2020
import { WorkspaceStats } from "../WorkspaceStats/WorkspaceStats"
2121
import { AlertBanner } from "../AlertBanner/AlertBanner"
2222
import { useTranslation } from "react-i18next"
23-
import { WorkspaceBuildProgress } from "components/WorkspaceBuildProgress/WorkspaceBuildProgress"
23+
import {
24+
EstimateTransitionTime,
25+
WorkspaceBuildProgress,
26+
} from "components/WorkspaceBuildProgress/WorkspaceBuildProgress"
2427

2528
export enum WorkspaceErrors {
2629
GET_RESOURCES_ERROR = "getResourcesError",
@@ -115,6 +118,15 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
115118
/>
116119
)
117120

121+
let buildTimeEstimate: number | undefined = undefined
122+
let isTransitioning: boolean | undefined = undefined
123+
if (template !== undefined) {
124+
;[buildTimeEstimate, isTransitioning] = EstimateTransitionTime(
125+
template,
126+
workspace,
127+
)
128+
}
129+
118130
return (
119131
<Margins>
120132
<PageHeader
@@ -186,7 +198,12 @@ export const Workspace: FC<React.PropsWithChildren<WorkspaceProps>> = ({
186198

187199
<WorkspaceStats workspace={workspace} handleUpdate={handleUpdate} />
188200

189-
<WorkspaceBuildProgress workspace={workspace} template={template} />
201+
{isTransitioning !== undefined && isTransitioning && (
202+
<WorkspaceBuildProgress
203+
workspace={workspace}
204+
buildEstimate={buildTimeEstimate}
205+
/>
206+
)}
190207

191208
{typeof resources !== "undefined" && resources.length > 0 && (
192209
<Resources

‎site/src/components/WorkspaceBuildProgress/WorkspaceBuildProgress.stories.tsx

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import dayjs from "dayjs"
33
import {
44
MockProvisionerJob,
55
MockStartingWorkspace,
6-
MockTemplate,
76
MockWorkspaceBuild,
87
} from "../../testHelpers/renderHelpers"
98
import {
@@ -22,12 +21,7 @@ const Template: Story<WorkspaceBuildProgressProps> = (args) => (
2221

2322
export const Starting = Template.bind({})
2423
Starting.args = {
25-
template: {
26-
...MockTemplate,
27-
build_time_stats: {
28-
start_ms: 10000,
29-
},
30-
},
24+
buildEstimate: 10000,
3125
workspace: {
3226
...MockStartingWorkspace,
3327
latest_build: {
@@ -45,21 +39,11 @@ Starting.args = {
4539
export const StartingUnknown = Template.bind({})
4640
StartingUnknown.args = {
4741
...Starting.args,
48-
template: {
49-
...MockTemplate,
50-
build_time_stats: {
51-
start_ms: undefined,
52-
},
53-
},
42+
buildEstimate: undefined,
5443
}
5544

5645
export const StartingPassedEstimate = Template.bind({})
5746
StartingPassedEstimate.args = {
5847
...Starting.args,
59-
template: {
60-
...MockTemplate,
61-
build_time_stats: {
62-
start_ms: 1000,
63-
},
64-
},
48+
buildEstimate: 1000,
6549
}

‎site/src/components/WorkspaceBuildProgress/WorkspaceBuildProgress.tsx

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,21 @@ import LinearProgress from "@material-ui/core/LinearProgress"
22
import makeStyles from "@material-ui/core/styles/makeStyles"
33
import { Template, Workspace } from "api/typesGenerated"
44
import dayjs, { Dayjs } from "dayjs"
5-
import { FC } from "react"
5+
import { FC, useEffect, useState } from "react"
66
import { MONOSPACE_FONT_FAMILY } from "theme/constants"
77

88
import duration from "dayjs/plugin/duration"
9+
import { Transition } from "xstate"
910

1011
dayjs.extend(duration)
1112

1213
const estimateFinish = (
1314
startedAt: Dayjs,
14-
templateAverage: number,
15+
templateAverage?: number,
1516
): [number, string] => {
17+
if (templateAverage === undefined) {
18+
return [0, "Unknown"]
19+
}
1620
const realPercentage = dayjs().diff(startedAt) / templateAverage
1721

1822
// Showing a full bar is frustrating.
@@ -30,37 +34,49 @@ const estimateFinish = (
3034

3135
export interface WorkspaceBuildProgressProps {
3236
workspace: Workspace
33-
template?: Template
37+
buildEstimate?: number
3438
}
3539

36-
export const WorkspaceBuildProgress: FC<WorkspaceBuildProgressProps> = ({
37-
workspace,
38-
template,
39-
}) => {
40-
const styles = useStyles()
41-
42-
// Template stats not loaded or non-existent
43-
if (!template) {
44-
return <></>
45-
}
46-
47-
let buildEstimate: number | undefined = 0
40+
// EstimateTransitionTime gets the build estimate for the workspace,
41+
// if it is in a transition state.
42+
export const EstimateTransitionTime = (
43+
template: Template,
44+
workspace: Workspace,
45+
): [number | undefined, boolean] => {
4846
switch (workspace.latest_build.status) {
4947
case "starting":
50-
buildEstimate = template.build_time_stats.start_ms
51-
break
48+
return [template.build_time_stats.start_ms, true]
5249
case "stopping":
53-
buildEstimate = template.build_time_stats.stop_ms
54-
break
50+
return [template.build_time_stats.stop_ms, true]
5551
case "deleting":
56-
buildEstimate = template.build_time_stats.delete_ms
57-
break
52+
return [template.build_time_stats.delete_ms, true]
5853
default:
5954
// Not in a transition state
60-
return <></>
55+
return [undefined, false]
6156
}
57+
}
58+
59+
export const WorkspaceBuildProgress: FC<WorkspaceBuildProgressProps> = ({
60+
workspace,
61+
buildEstimate,
62+
}) => {
63+
const styles = useStyles()
6264

6365
const job = workspace.latest_build.job
66+
67+
const [progressValue, setProgressValue] = useState(0)
68+
// By default workspace is updated every second, which can cause visual stutter
69+
// when the build estimate is a few seconds. The timer ensures no observable
70+
// stutter in all cases.
71+
useEffect(() => {
72+
const updateProgress = () => {
73+
setProgressValue(
74+
estimateFinish(dayjs(job.started_at), buildEstimate)[0] * 100,
75+
)
76+
}
77+
setTimeout(updateProgress, 100)
78+
}, [progressValue, job.started_at, buildEstimate])
79+
6480
if (buildEstimate === undefined) {
6581
return (
6682
<div className={styles.stack}>
@@ -76,11 +92,7 @@ export const WorkspaceBuildProgress: FC<WorkspaceBuildProgressProps> = ({
7692
return (
7793
<div className={styles.stack}>
7894
<LinearProgress
79-
value={
80-
(job.status === "running" &&
81-
estimateFinish(dayjs(job.started_at), buildEstimate)[0] * 100) ||
82-
0
83-
}
95+
value={(job.status === "running" && progressValue) || 0}
8496
variant={job.status === "running" ? "determinate" : "indeterminate"}
8597
/>
8698
<div className={styles.barHelpers}>

0 commit comments

Comments
 (0)
Failed to load comments.