1
1
import { assign , createMachine , send } from "xstate"
2
- import { pure } from "xstate/lib/actions"
3
2
import * as API from "../../api/api"
4
3
import * as Types from "../../api/types"
5
4
import * as TypesGen from "../../api/typesGenerated"
@@ -12,8 +11,31 @@ const latestBuild = (builds: TypesGen.WorkspaceBuild[]) => {
12
11
} ) [ 0 ]
13
12
}
14
13
14
+ const moreBuildsAvailable = (
15
+ context : WorkspaceContext ,
16
+ event : {
17
+ type : "REFRESH_TIMELINE"
18
+ checkRefresh ?: boolean
19
+ data ?: TypesGen . ServerSentEvent [ "data" ]
20
+ } ,
21
+ ) => {
22
+ // No need to refresh the timeline if it is not loaded
23
+ if ( ! context . builds ) {
24
+ return false
25
+ }
26
+
27
+ if ( ! event . checkRefresh ) {
28
+ return true
29
+ }
30
+
31
+ // After we refresh a workspace, we want to check if the latest
32
+ // build was updated before refreshing the timeline so as to not over fetch the builds
33
+ const latestBuildInTimeline = latestBuild ( context . builds )
34
+ return event . data . latest_build . updated_at !== latestBuildInTimeline . updated_at
35
+ }
36
+
15
37
const Language = {
16
- refreshTemplateWarning : "Error updating workspace: latest template could not be fetched." ,
38
+ getTemplateWarning : "Error updating workspace: latest template could not be fetched." ,
17
39
buildError : "Workspace action failed." ,
18
40
}
19
41
@@ -28,7 +50,7 @@ export interface WorkspaceContext {
28
50
getWorkspaceError ?: Error | unknown
29
51
// these are labeled as warnings because they don't make the page unusable
30
52
refreshWorkspaceWarning ?: Error | unknown
31
- refreshTemplateWarning : Error | unknown
53
+ getTemplateWarning : Error | unknown
32
54
// Builds
33
55
builds ?: TypesGen . WorkspaceBuild [ ]
34
56
getBuildsError ?: Error | unknown
@@ -51,8 +73,7 @@ export type WorkspaceEvent =
51
73
| { type : "CANCEL_DELETE" }
52
74
| { type : "UPDATE" }
53
75
| { type : "CANCEL" }
54
- | { type : "CHECK_REFRESH_TIMELINE" ; data : TypesGen . ServerSentEvent [ "data" ] }
55
- | { type : "REFRESH_TIMELINE" }
76
+ | { type : "REFRESH_TIMELINE" ; checkRefresh ?: boolean ; data ?: TypesGen . ServerSentEvent [ "data" ] }
56
77
| { type : "EVENT_SOURCE_ERROR" ; error : Error | unknown }
57
78
58
79
export const checks = {
@@ -139,7 +160,7 @@ export const workspaceMachine = createMachine(
139
160
onDone : [
140
161
{
141
162
actions : "assignWorkspace" ,
142
- target : "refreshingTemplate " ,
163
+ target : "gettingTemplate " ,
143
164
} ,
144
165
] ,
145
166
onError : [
@@ -151,11 +172,11 @@ export const workspaceMachine = createMachine(
151
172
} ,
152
173
tags : "loading" ,
153
174
} ,
154
- refreshingTemplate : {
155
- entry : "clearRefreshTemplateWarning " ,
175
+ gettingTemplate : {
176
+ entry : "clearGettingTemplateWarning " ,
156
177
invoke : {
157
178
src : "getTemplate" ,
158
- id : "refreshTemplate " ,
179
+ id : "getTemplate " ,
159
180
onDone : [
160
181
{
161
182
actions : "assignTemplate" ,
@@ -164,7 +185,7 @@ export const workspaceMachine = createMachine(
164
185
] ,
165
186
onError : [
166
187
{
167
- actions : [ "assignRefreshTemplateWarning " , "displayRefreshTemplateWarning " ] ,
188
+ actions : [ "assignGetTemplateWarning " , "displayGetTemplateWarning " ] ,
168
189
target : "error" ,
169
190
} ,
170
191
] ,
@@ -210,9 +231,6 @@ export const workspaceMachine = createMachine(
210
231
EVENT_SOURCE_ERROR : {
211
232
target : "error" ,
212
233
} ,
213
- CHECK_REFRESH_TIMELINE : {
214
- actions : [ "refreshTimeline" ] ,
215
- } ,
216
234
} ,
217
235
} ,
218
236
error : {
@@ -254,7 +272,7 @@ export const workspaceMachine = createMachine(
254
272
src : "startWorkspaceWithLatestTemplate" ,
255
273
onDone : {
256
274
target : "idle" ,
257
- actions : [ "assignBuild" , "refreshTimeline" ] ,
275
+ actions : [ "assignBuild" ] ,
258
276
} ,
259
277
onError : {
260
278
target : "idle" ,
@@ -269,7 +287,7 @@ export const workspaceMachine = createMachine(
269
287
id : "startWorkspace" ,
270
288
onDone : [
271
289
{
272
- actions : [ "assignBuild" , "refreshTimeline" ] ,
290
+ actions : [ "assignBuild" ] ,
273
291
target : "idle" ,
274
292
} ,
275
293
] ,
@@ -288,7 +306,7 @@ export const workspaceMachine = createMachine(
288
306
id : "stopWorkspace" ,
289
307
onDone : [
290
308
{
291
- actions : [ "assignBuild" , "refreshTimeline" ] ,
309
+ actions : [ "assignBuild" ] ,
292
310
target : "idle" ,
293
311
} ,
294
312
] ,
@@ -307,7 +325,7 @@ export const workspaceMachine = createMachine(
307
325
id : "deleteWorkspace" ,
308
326
onDone : [
309
327
{
310
- actions : [ "assignBuild" , "refreshTimeline" ] ,
328
+ actions : [ "assignBuild" ] ,
311
329
target : "idle" ,
312
330
} ,
313
331
] ,
@@ -326,11 +344,7 @@ export const workspaceMachine = createMachine(
326
344
id : "cancelWorkspace" ,
327
345
onDone : [
328
346
{
329
- actions : [
330
- "assignCancellationMessage" ,
331
- "displayCancellationMessage" ,
332
- "refreshTimeline" ,
333
- ] ,
347
+ actions : [ "assignCancellationMessage" , "displayCancellationMessage" ] ,
334
348
target : "idle" ,
335
349
} ,
336
350
] ,
@@ -342,31 +356,11 @@ export const workspaceMachine = createMachine(
342
356
] ,
343
357
} ,
344
358
} ,
345
- refreshingTemplate : {
346
- entry : "clearRefreshTemplateWarning" ,
347
- invoke : {
348
- src : "getTemplate" ,
349
- id : "refreshTemplate" ,
350
- onDone : [
351
- {
352
- actions : "assignTemplate" ,
353
- target : "requestingStart" ,
354
- } ,
355
- ] ,
356
- onError : [
357
- {
358
- actions : [ "assignRefreshTemplateWarning" , "displayRefreshTemplateWarning" ] ,
359
- target : "idle" ,
360
- } ,
361
- ] ,
362
- } ,
363
- } ,
364
359
} ,
365
360
} ,
366
361
timeline : {
367
362
initial : "gettingBuilds" ,
368
363
states : {
369
- idle : { } ,
370
364
gettingBuilds : {
371
365
entry : "clearGetBuildsError" ,
372
366
invoke : {
@@ -380,19 +374,17 @@ export const workspaceMachine = createMachine(
380
374
onError : [
381
375
{
382
376
actions : "assignGetBuildsError" ,
383
- target : "idle " ,
377
+ target : "loadedBuilds " ,
384
378
} ,
385
379
] ,
386
380
} ,
387
381
} ,
388
382
loadedBuilds : {
389
- initial : "idle" ,
390
- states : {
391
- idle : {
392
- on : {
393
- REFRESH_TIMELINE : {
394
- target : "#workspaceState.ready.timeline.gettingBuilds" ,
395
- } ,
383
+ on : {
384
+ REFRESH_TIMELINE : {
385
+ target : "#workspaceState.ready.timeline.gettingBuilds" ,
386
+ cond : {
387
+ type : "moreBuildsAvailable" ,
396
388
} ,
397
389
} ,
398
390
} ,
@@ -483,14 +475,14 @@ export const workspaceMachine = createMachine(
483
475
clearRefreshWorkspaceWarning : assign ( {
484
476
refreshWorkspaceWarning : ( _ ) => undefined ,
485
477
} ) ,
486
- assignRefreshTemplateWarning : assign ( {
487
- refreshTemplateWarning : ( _ , event ) => event . data ,
478
+ assignGetTemplateWarning : assign ( {
479
+ getTemplateWarning : ( _ , event ) => event . data ,
488
480
} ) ,
489
- displayRefreshTemplateWarning : ( ) => {
490
- displayError ( Language . refreshTemplateWarning )
481
+ displayGetTemplateWarning : ( ) => {
482
+ displayError ( Language . getTemplateWarning )
491
483
} ,
492
- clearRefreshTemplateWarning : assign ( {
493
- refreshTemplateWarning : ( _ ) => undefined ,
484
+ clearGettingTemplateWarning : assign ( {
485
+ getTemplateWarning : ( _ ) => undefined ,
494
486
} ) ,
495
487
// Timeline
496
488
assignBuilds : assign ( {
@@ -502,24 +494,9 @@ export const workspaceMachine = createMachine(
502
494
clearGetBuildsError : assign ( {
503
495
getBuildsError : ( _ ) => undefined ,
504
496
} ) ,
505
- refreshTimeline : pure ( ( context , event ) => {
506
- // No need to refresh the timeline if it is not loaded
507
- if ( ! context . builds ) {
508
- return
509
- }
510
-
511
- // When it is a CHECK_REFRESH_TIMELINE workspace event, we want to check if the latest
512
- // build was updated to not over fetch the builds
513
- if ( event . type === "CHECK_REFRESH_TIMELINE" ) {
514
- const latestBuildInTimeline = latestBuild ( context . builds )
515
- const isUpdated = event . data ?. latest_build . updated_at !== latestBuildInTimeline . updated_at
516
- if ( isUpdated ) {
517
- return send ( { type : "REFRESH_TIMELINE" } )
518
- }
519
- } else {
520
- return send ( { type : "REFRESH_TIMELINE" } )
521
- }
522
- } ) ,
497
+ } ,
498
+ guards : {
499
+ moreBuildsAvailable,
523
500
} ,
524
501
services : {
525
502
getWorkspace : async ( _ , event ) => {
@@ -534,40 +511,55 @@ export const workspaceMachine = createMachine(
534
511
throw Error ( "Cannot get template without workspace" )
535
512
}
536
513
} ,
537
- startWorkspaceWithLatestTemplate : async ( context ) => {
514
+ startWorkspaceWithLatestTemplate : ( context ) => async ( send ) => {
538
515
if ( context . workspace && context . template ) {
539
- return await API . startWorkspace ( context . workspace . id , context . template . active_version_id )
516
+ const startWorkspacePromise = await API . startWorkspace (
517
+ context . workspace . id ,
518
+ context . template . active_version_id ,
519
+ )
520
+ send ( { type : "REFRESH_TIMELINE" } )
521
+ return startWorkspacePromise
540
522
} else {
541
523
throw Error ( "Cannot start workspace without workspace id" )
542
524
}
543
525
} ,
544
- startWorkspace : async ( context ) => {
526
+ startWorkspace : ( context ) => async ( send ) => {
545
527
if ( context . workspace ) {
546
- return await API . startWorkspace (
528
+ const startWorkspacePromise = await API . startWorkspace (
547
529
context . workspace . id ,
548
530
context . workspace . latest_build . template_version_id ,
549
531
)
532
+ send ( { type : "REFRESH_TIMELINE" } )
533
+ return startWorkspacePromise
550
534
} else {
551
535
throw Error ( "Cannot start workspace without workspace id" )
552
536
}
553
537
} ,
554
- stopWorkspace : async ( context ) => {
538
+ stopWorkspace : ( context ) => async ( send ) => {
555
539
if ( context . workspace ) {
556
- return await API . stopWorkspace ( context . workspace . id )
540
+ const stopWorkspacePromise = await API . stopWorkspace ( context . workspace . id )
541
+ send ( { type : "REFRESH_TIMELINE" } )
542
+ return stopWorkspacePromise
557
543
} else {
558
544
throw Error ( "Cannot stop workspace without workspace id" )
559
545
}
560
546
} ,
561
547
deleteWorkspace : async ( context ) => {
562
548
if ( context . workspace ) {
563
- return await API . deleteWorkspace ( context . workspace . id )
549
+ const deleteWorkspacePromise = await API . deleteWorkspace ( context . workspace . id )
550
+ send ( { type : "REFRESH_TIMELINE" } )
551
+ return deleteWorkspacePromise
564
552
} else {
565
553
throw Error ( "Cannot delete workspace without workspace id" )
566
554
}
567
555
} ,
568
- cancelWorkspace : async ( context ) => {
556
+ cancelWorkspace : ( context ) => async ( send ) => {
569
557
if ( context . workspace ) {
570
- return await API . cancelWorkspaceBuild ( context . workspace . latest_build . id )
558
+ const cancelWorkspacePromise = await API . cancelWorkspaceBuild (
559
+ context . workspace . latest_build . id ,
560
+ )
561
+ send ( { type : "REFRESH_TIMELINE" } )
562
+ return cancelWorkspacePromise
571
563
} else {
572
564
throw Error ( "Cannot cancel workspace without build id" )
573
565
}
@@ -582,7 +574,7 @@ export const workspaceMachine = createMachine(
582
574
// refresh our workspace with each SSE
583
575
send ( { type : "REFRESH_WORKSPACE" , data : JSON . parse ( event . data ) } )
584
576
// refresh our timeline
585
- send ( { type : "CHECK_REFRESH_TIMELINE" , data : JSON . parse ( event . data ) } )
577
+ send ( { type : "REFRESH_TIMELINE" , checkRefresh : true , data : JSON . parse ( event . data ) } )
586
578
} )
587
579
588
580
// handle any error events returned by our sse
0 commit comments