site: cleanup code in WorkspaceBuildProgress (#4584)

This commit is contained in:
Ammar Bandukwala
2022-10-17 14:45:06 -05:00
committed by GitHub
parent 6f5544e0e4
commit 8acba0ccff

View File

@ -11,23 +11,19 @@ dayjs.extend(duration)
const estimateFinish = (
startedAt: Dayjs,
templateAverage?: number,
buildEstimate: number,
): [number, string] => {
if (templateAverage === undefined) {
return [0, "Unknown"]
}
const realPercentage = dayjs().diff(startedAt) / templateAverage
const realPercentage = dayjs().diff(startedAt) / buildEstimate
// Showing a full bar is frustrating.
const maxPercentage = 0.99
const maxPercentage = 1
if (realPercentage > maxPercentage) {
return [maxPercentage, "Any moment now..."]
return [maxPercentage * 100, "Any moment now..."]
}
return [
realPercentage,
realPercentage * 100,
`~${Math.ceil(
dayjs.duration((1 - realPercentage) * templateAverage).asSeconds(),
dayjs.duration((1 - realPercentage) * buildEstimate).asSeconds(),
)} seconds remaining...`,
]
}
@ -62,49 +58,55 @@ export const WorkspaceBuildProgress: FC<WorkspaceBuildProgressProps> = ({
}) => {
const styles = useStyles()
const job = workspace.latest_build.job
const [progressValue, setProgressValue] = useState(0)
const [progressValue, setProgressValue] = useState<number | undefined>(0)
// By default workspace is updated every second, which can cause visual stutter
// when the build estimate is a few seconds. The timer ensures no observable
// stutter in all cases.
useEffect(() => {
const updateProgress = () => {
if (job.status !== "running") {
setProgressValue(0)
if (job.status !== "running" || buildEstimate === undefined) {
setProgressValue(undefined)
return
}
setProgressValue(
estimateFinish(dayjs(job.started_at), buildEstimate)[0] * 100,
)
const est = estimateFinish(dayjs(job.started_at), buildEstimate)[0]
setProgressValue(est)
}
setTimeout(updateProgress, 100)
setTimeout(updateProgress, 5)
}, [progressValue, job, buildEstimate])
// buildEstimate may be undefined if the template is new or coderd hasn't
// finished initial metrics collection.
if (buildEstimate === undefined) {
return (
<div className={styles.stack}>
<LinearProgress value={0} variant="indeterminate" />
<div className={styles.barHelpers}>
<div className={styles.label}>{`Build ${job.status}`}</div>
<div className={styles.label}>Unknown ETA</div>
</div>
</div>
)
}
return (
<div className={styles.stack}>
<LinearProgress
value={(job.status === "running" && progressValue) || 0}
variant={job.status === "running" ? "determinate" : "indeterminate"}
value={progressValue !== undefined ? progressValue : 0}
variant={
// There is an initial state where progressValue may be undefined
// (e.g. the build isn't yet running). If we flicker from the
// indeterminate bar to the determinate bar, the vigilant user
// perceives the bar jumping from 100% to 0%.
progressValue !== undefined &&
progressValue < 100 &&
buildEstimate !== undefined
? "determinate"
: "indeterminate"
}
// If a transition is set, there is a moment on new load where the
// bar accelerates to progressValue and then rapidly decelerates, which
// is not indicative of true progress.
classes={{ bar: styles.noTransition }}
/>
<div className={styles.barHelpers}>
<div className={styles.label}>{`Build ${job.status}`}</div>
<div className={styles.label}>
{job.status === "running" &&
estimateFinish(dayjs(job.started_at), buildEstimate)[1]}
{(() => {
if (job.status !== "running") {
return ""
} else if (buildEstimate !== undefined) {
return estimateFinish(dayjs(job.started_at), buildEstimate)[1]
} else {
return "Unknown ETA"
}
})()}
</div>
</div>
</div>
@ -116,6 +118,9 @@ const useStyles = makeStyles((theme) => ({
paddingLeft: theme.spacing(0.2),
paddingRight: theme.spacing(0.2),
},
noTransition: {
transition: "none",
},
barHelpers: {
display: "flex",
justifyContent: "space-between",