@@ -9,9 +9,9 @@ import { type Theme, makeStyles } from '@material-ui/core';
9
9
import { useId } from '../../hooks/hookPolyfills' ;
10
10
11
11
import { useCoderAppConfig } from '../CoderProvider' ;
12
- import { isWorkspaceOnline } from '../../api' ;
12
+ import { getWorkspaceAgentStatuses } from '../../api' ;
13
13
14
- import type { Workspace } from '../../typesConstants' ;
14
+ import type { Workspace , WorkspaceStatus } from '../../typesConstants' ;
15
15
import { WorkspacesListIcon } from './WorkspacesListIcon' ;
16
16
import { VisuallyHidden } from '../VisuallyHidden' ;
17
17
@@ -24,7 +24,7 @@ type StyleKey =
24
24
| 'button' ;
25
25
26
26
type UseStyleInputs = Readonly < {
27
- isOnline : boolean ;
27
+ isAvailable : boolean ;
28
28
} > ;
29
29
30
30
const useStyles = makeStyles < Theme , UseStyleInputs , StyleKey > ( theme => ( {
@@ -84,7 +84,7 @@ const useStyles = makeStyles<Theme, UseStyleInputs, StyleKey>(theme => ({
84
84
fontSize : '16px' ,
85
85
} ,
86
86
87
- onlineStatusLight : ( { isOnline } ) => ( {
87
+ onlineStatusLight : ( { isAvailable } ) => ( {
88
88
display : 'block' ,
89
89
width : theme . spacing ( 1 ) ,
90
90
height : theme . spacing ( 1 ) ,
@@ -93,8 +93,10 @@ const useStyles = makeStyles<Theme, UseStyleInputs, StyleKey>(theme => ({
93
93
borderStyle : 'solid' ,
94
94
95
95
// Border color helps increase color contrast in light mode
96
- borderColor : isOnline ? 'hsl(130deg,100%,40%)' : theme . palette . common . black ,
97
- backgroundColor : isOnline
96
+ borderColor : isAvailable
97
+ ? 'hsl(130deg,100%,40%)'
98
+ : theme . palette . common . black ,
99
+ backgroundColor : isAvailable
98
100
? 'hsl(135deg,100%,77%)'
99
101
: theme . palette . common . black ,
100
102
} ) ,
@@ -142,8 +144,11 @@ export const WorkspacesListItem = ({
142
144
const { accessUrl } = useCoderAppConfig ( ) . deployment ;
143
145
const anchorElementRef = useRef < HTMLAnchorElement > ( null ) ;
144
146
145
- const isOnline = isWorkspaceOnline ( workspace ) ;
146
- const styles = useStyles ( { isOnline } ) ;
147
+ const availabilityStatus = getAvailabilityStatus ( workspace ) ;
148
+ const styles = useStyles ( {
149
+ isAvailable :
150
+ availabilityStatus === 'online' || availabilityStatus === 'pending' ,
151
+ } ) ;
147
152
148
153
const { name, owner_name, template_icon } = workspace ;
149
154
const onlineStatusId = `${ hookId } -online-status` ;
@@ -205,8 +210,15 @@ export const WorkspacesListItem = ({
205
210
/>
206
211
207
212
< VisuallyHidden > Workspace is </ VisuallyHidden >
208
- { isOnline ? 'Online' : 'Offline' }
209
- < VisuallyHidden > .</ VisuallyHidden >
213
+ { availabilityStatus === 'deleting' ||
214
+ availabilityStatus === 'pending' ? (
215
+ < > { toUppercase ( availabilityStatus ) } …</ >
216
+ ) : (
217
+ < >
218
+ { toUppercase ( availabilityStatus ) }
219
+ < VisuallyHidden > .</ VisuallyHidden >
220
+ </ >
221
+ ) }
210
222
</ span >
211
223
</ div >
212
224
@@ -226,6 +238,55 @@ export const WorkspacesListItem = ({
226
238
) ;
227
239
} ;
228
240
241
+ const deletingStatuses : readonly WorkspaceStatus [ ] = [ 'deleting' , 'deleted' ] ;
242
+ const offlineStatuses : readonly WorkspaceStatus [ ] = [
243
+ 'stopped' ,
244
+ 'stopping' ,
245
+ 'pending' ,
246
+ 'canceling' ,
247
+ 'canceled' ,
248
+ ] ;
249
+
250
+ type AvailabilityStatus =
251
+ | 'online'
252
+ | 'offline'
253
+ | 'pending'
254
+ | 'failed'
255
+ | 'deleting' ;
256
+
257
+ function getAvailabilityStatus ( workspace : Workspace ) : AvailabilityStatus {
258
+ const currentStatus = workspace . latest_build . status ;
259
+
260
+ if ( currentStatus === 'failed' ) {
261
+ return 'failed' ;
262
+ }
263
+
264
+ // When a workspace is being deleted, there is a good chance that the agents
265
+ // will still show as connected/connecting. If this check isn't done before
266
+ // looking at the agent statuses, a deleting workspace might show up as online
267
+ if ( deletingStatuses . includes ( currentStatus ) ) {
268
+ return 'deleting' ;
269
+ }
270
+
271
+ if ( offlineStatuses . includes ( currentStatus ) ) {
272
+ return 'offline' ;
273
+ }
274
+
275
+ const uniqueStatuses = getWorkspaceAgentStatuses ( workspace ) ;
276
+ const isPending =
277
+ currentStatus === 'starting' ||
278
+ uniqueStatuses . some ( status => status === 'connecting' ) ;
279
+
280
+ if ( isPending ) {
281
+ return 'pending' ;
282
+ }
283
+
284
+ // .every will still make workspaces with no agents show as online
285
+ return uniqueStatuses . every ( status => status === 'connected' )
286
+ ? 'online'
287
+ : 'offline' ;
288
+ }
289
+
229
290
function stopClickEventBubbling ( event : MouseEvent | KeyboardEvent ) : void {
230
291
const { nativeEvent } = event ;
231
292
const shouldStopBubbling =
@@ -236,3 +297,7 @@ function stopClickEventBubbling(event: MouseEvent | KeyboardEvent): void {
236
297
event . stopPropagation ( ) ;
237
298
}
238
299
}
300
+
301
+ function toUppercase ( s : string ) : string {
302
+ return s . slice ( 0 , 1 ) . toUpperCase ( ) + s . slice ( 1 ) . toLowerCase ( ) ;
303
+ }
0 commit comments