@@ -2,17 +2,21 @@ import LinearProgress from "@material-ui/core/LinearProgress"
2
2
import makeStyles from "@material-ui/core/styles/makeStyles"
3
3
import { Template , Workspace } from "api/typesGenerated"
4
4
import dayjs , { Dayjs } from "dayjs"
5
- import { FC } from "react"
5
+ import { FC , useEffect , useState } from "react"
6
6
import { MONOSPACE_FONT_FAMILY } from "theme/constants"
7
7
8
8
import duration from "dayjs/plugin/duration"
9
+ import { Transition } from "xstate"
9
10
10
11
dayjs . extend ( duration )
11
12
12
13
const estimateFinish = (
13
14
startedAt : Dayjs ,
14
- templateAverage : number ,
15
+ templateAverage ? : number ,
15
16
) : [ number , string ] => {
17
+ if ( templateAverage === undefined ) {
18
+ return [ 0 , "Unknown" ]
19
+ }
16
20
const realPercentage = dayjs ( ) . diff ( startedAt ) / templateAverage
17
21
18
22
// Showing a full bar is frustrating.
@@ -30,37 +34,49 @@ const estimateFinish = (
30
34
31
35
export interface WorkspaceBuildProgressProps {
32
36
workspace : Workspace
33
- template ?: Template
37
+ buildEstimate ?: number
34
38
}
35
39
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 ] => {
48
46
switch ( workspace . latest_build . status ) {
49
47
case "starting" :
50
- buildEstimate = template . build_time_stats . start_ms
51
- break
48
+ return [ template . build_time_stats . start_ms , true ]
52
49
case "stopping" :
53
- buildEstimate = template . build_time_stats . stop_ms
54
- break
50
+ return [ template . build_time_stats . stop_ms , true ]
55
51
case "deleting" :
56
- buildEstimate = template . build_time_stats . delete_ms
57
- break
52
+ return [ template . build_time_stats . delete_ms , true ]
58
53
default :
59
54
// Not in a transition state
60
- return < > </ >
55
+ return [ undefined , false ]
61
56
}
57
+ }
58
+
59
+ export const WorkspaceBuildProgress : FC < WorkspaceBuildProgressProps > = ( {
60
+ workspace,
61
+ buildEstimate,
62
+ } ) => {
63
+ const styles = useStyles ( )
62
64
63
65
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
+
64
80
if ( buildEstimate === undefined ) {
65
81
return (
66
82
< div className = { styles . stack } >
@@ -76,11 +92,7 @@ export const WorkspaceBuildProgress: FC<WorkspaceBuildProgressProps> = ({
76
92
return (
77
93
< div className = { styles . stack } >
78
94
< 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 }
84
96
variant = { job . status === "running" ? "determinate" : "indeterminate" }
85
97
/>
86
98
< div className = { styles . barHelpers } >
0 commit comments