@@ -40,6 +40,27 @@ const moreBuildsAvailable = (
40
40
return event . data . latest_build . updated_at !== latestBuildInTimeline . updated_at
41
41
}
42
42
43
+ const updateWorkspaceStatus = (
44
+ status : TypesGen . WorkspaceStatus ,
45
+ workspace ?: TypesGen . Workspace ,
46
+ ) => {
47
+ if ( ! workspace ) {
48
+ throw new Error ( "Workspace not defined" )
49
+ }
50
+
51
+ return {
52
+ ...workspace ,
53
+ latest_build : {
54
+ ...workspace . latest_build ,
55
+ status,
56
+ } ,
57
+ }
58
+ }
59
+
60
+ const isUpdated = ( newDateStr : string , oldDateStr : string ) : boolean => {
61
+ return new Date ( oldDateStr ) . getTime ( ) - new Date ( newDateStr ) . getTime ( ) > 0
62
+ }
63
+
43
64
const Language = {
44
65
getTemplateWarning :
45
66
"Error updating workspace: latest template could not be fetched." ,
@@ -252,6 +273,7 @@ export const workspaceMachine = createMachine(
252
273
on : {
253
274
REFRESH_WORKSPACE : {
254
275
actions : [ "refreshWorkspace" ] ,
276
+ cond : "hasUpdates" ,
255
277
} ,
256
278
EVENT_SOURCE_ERROR : {
257
279
target : "error" ,
@@ -325,7 +347,7 @@ export const workspaceMachine = createMachine(
325
347
} ,
326
348
} ,
327
349
requestingStart : {
328
- entry : "clearBuildError" ,
350
+ entry : [ "clearBuildError" , "updateStatusToStarting" ] ,
329
351
invoke : {
330
352
src : "startWorkspace" ,
331
353
id : "startWorkspace" ,
@@ -344,7 +366,7 @@ export const workspaceMachine = createMachine(
344
366
} ,
345
367
} ,
346
368
requestingStop : {
347
- entry : "clearBuildError" ,
369
+ entry : [ "clearBuildError" , "updateStatusToStopping" ] ,
348
370
invoke : {
349
371
src : "stopWorkspace" ,
350
372
id : "stopWorkspace" ,
@@ -363,7 +385,7 @@ export const workspaceMachine = createMachine(
363
385
} ,
364
386
} ,
365
387
requestingDelete : {
366
- entry : "clearBuildError" ,
388
+ entry : [ "clearBuildError" , "updateStatusToDeleting" ] ,
367
389
invoke : {
368
390
src : "deleteWorkspace" ,
369
391
id : "deleteWorkspace" ,
@@ -382,7 +404,11 @@ export const workspaceMachine = createMachine(
382
404
} ,
383
405
} ,
384
406
requestingCancel : {
385
- entry : [ "clearCancellationMessage" , "clearCancellationError" ] ,
407
+ entry : [
408
+ "clearCancellationMessage" ,
409
+ "clearCancellationError" ,
410
+ "updateStatusToCanceling" ,
411
+ ] ,
386
412
invoke : {
387
413
src : "cancelWorkspace" ,
388
414
id : "cancelWorkspace" ,
@@ -430,9 +456,7 @@ export const workspaceMachine = createMachine(
430
456
on : {
431
457
REFRESH_TIMELINE : {
432
458
target : "#workspaceState.ready.timeline.gettingBuilds" ,
433
- cond : {
434
- type : "moreBuildsAvailable" ,
435
- } ,
459
+ cond : "moreBuildsAvailable" ,
436
460
} ,
437
461
} ,
438
462
} ,
@@ -599,9 +623,46 @@ export const workspaceMachine = createMachine(
599
623
} ) ,
600
624
{ to : "scheduleBannerMachine" } ,
601
625
) ,
626
+ // Optimistically updates. So when the user clicks on stop, we can show
627
+ // the "stopping" state right away without having to wait 0.5s ~ 2s to
628
+ // display the visual feedback to the user.
629
+ updateStatusToStarting : assign ( {
630
+ workspace : ( { workspace } ) =>
631
+ updateWorkspaceStatus ( "starting" , workspace ) ,
632
+ } ) ,
633
+ updateStatusToStopping : assign ( {
634
+ workspace : ( { workspace } ) =>
635
+ updateWorkspaceStatus ( "stopping" , workspace ) ,
636
+ } ) ,
637
+ updateStatusToDeleting : assign ( {
638
+ workspace : ( { workspace } ) =>
639
+ updateWorkspaceStatus ( "deleting" , workspace ) ,
640
+ } ) ,
641
+ updateStatusToCanceling : assign ( {
642
+ workspace : ( { workspace } ) =>
643
+ updateWorkspaceStatus ( "canceling" , workspace ) ,
644
+ } ) ,
602
645
} ,
603
646
guards : {
604
647
moreBuildsAvailable,
648
+ // We only want to update the workspace when there are changes to it to
649
+ // avoid re-renderings and allow optimistically updates to improve the UI.
650
+ // When updating the workspace every second, the optimistic updates that
651
+ // were applied before get lost since it will be rewrite.
652
+ hasUpdates : ( { workspace } , event : { data : TypesGen . Workspace } ) => {
653
+ if ( ! workspace ) {
654
+ throw new Error ( "Workspace not defined" )
655
+ }
656
+ const isWorkspaceUpdated = isUpdated (
657
+ event . data . updated_at ,
658
+ workspace . updated_at ,
659
+ )
660
+ const isBuildUpdated = isUpdated (
661
+ event . data . latest_build . updated_at ,
662
+ workspace . latest_build . updated_at ,
663
+ )
664
+ return isWorkspaceUpdated || isBuildUpdated
665
+ } ,
605
666
} ,
606
667
services : {
607
668
getWorkspace : async ( _ , event ) => {
0 commit comments