1
- import { registerDestructor } from ' @ember/destroyable' ;
2
1
import { fn } from ' @ember/helper' ;
3
2
import { on } from ' @ember/modifier' ;
4
3
import { action } from ' @ember/object' ;
@@ -14,9 +13,7 @@ import Component from '@glimmer/component';
14
13
import { tracked , cached } from ' @glimmer/tracking' ;
15
14
16
15
import ExclamationCircle from ' @cardstack/boxel-icons/exclamation-circle' ;
17
- import { formatDistanceToNow } from ' date-fns' ;
18
16
import {
19
- task ,
20
17
restartableTask ,
21
18
timeout ,
22
19
waitForProperty ,
@@ -124,18 +121,11 @@ export default class OperatorModeStackItem extends Component<Signature> {
124
121
125
122
// @tracked private selectedCards = new TrackedArray<CardDef>([]);
126
123
@tracked private selectedCards = new TrackedArray <CardDefOrId >([]);
127
- @tracked private isSaving = false ;
128
124
@tracked private animationType:
129
125
| ' opening'
130
126
| ' closing'
131
127
| ' movingForward'
132
128
| undefined = ' opening' ;
133
- @tracked private hasUnsavedChanges = false ;
134
- @tracked private lastSaved: number | undefined ;
135
- @tracked private lastSavedMsg: string | undefined ;
136
- @tracked private lastSaveError: Error | undefined ;
137
- private refreshSaveMsg: number | undefined ;
138
- private subscribedCard: CardDef | undefined ;
139
129
private contentEl: HTMLElement | undefined ;
140
130
private containerEl: HTMLElement | undefined ;
141
131
private itemEl: HTMLElement | undefined ;
@@ -162,7 +152,6 @@ export default class OperatorModeStackItem extends Component<Signature> {
162
152
constructor (owner : Owner , args : Signature [' Args' ]) {
163
153
super (owner , args );
164
154
this .loadCard .perform ();
165
- this .subscribeToCard .perform ();
166
155
this .args .setupStackItem (this .args .item , {
167
156
clearSelections: this .clearSelections ,
168
157
doWithStableScroll: this .doWithStableScroll .perform ,
@@ -376,86 +365,12 @@ export default class OperatorModeStackItem extends Component<Signature> {
376
365
}
377
366
});
378
367
379
- private subscribeToCard = task (async () => {
380
- await this .args .item .ready ();
381
- // TODO how do we make sure that this is called after the error is cleared?
382
- // Address this as part of SSE support for card errors
383
- if (! this .cardError ) {
384
- this .subscribedCard = this .card ;
385
- let api = this .args .item .api ;
386
- registerDestructor (this , this .cleanup );
387
- api .subscribeToChanges (this .subscribedCard , this .onCardChange );
388
- this .refreshSaveMsg = setInterval (
389
- () => this .calculateLastSavedMsg (),
390
- 10 * 1000 ,
391
- ) as unknown as number ;
392
- }
393
- });
394
-
395
- private cleanup = () => {
396
- if (this .subscribedCard ) {
397
- let api = this .args .item .api ;
398
- api .unsubscribeFromChanges (this .subscribedCard , this .onCardChange );
399
- clearInterval (this .refreshSaveMsg );
400
- }
401
- };
402
-
403
- private onCardChange = () => {
404
- this .initiateAutoSaveTask .perform ();
405
- };
406
-
407
- private initiateAutoSaveTask = restartableTask (async () => {
408
- this .hasUnsavedChanges = true ;
409
- await timeout (this .environmentService .autoSaveDelayMs );
410
- try {
411
- this .isSaving = true ;
412
- this .lastSaveError = undefined ;
413
- await timeout (25 );
414
- await this .args .saveCard (this .card );
415
- this .hasUnsavedChanges = false ;
416
- this .lastSaved = Date .now ();
417
- } catch (error ) {
418
- // error will already be logged in CardService
419
- this .lastSaveError = error as Error ;
420
- } finally {
421
- this .isSaving = false ;
422
- this .calculateLastSavedMsg ();
423
- }
424
- });
425
-
426
- private calculateLastSavedMsg() {
427
- let savedMessage: string | undefined ;
428
- if (this .lastSaveError ) {
429
- savedMessage = ` Failed to save: ${this .getErrorMessage (
430
- this .lastSaveError ,
431
- )} ` ;
432
- } else if (this .lastSaved ) {
433
- savedMessage = ` Saved ${formatDistanceToNow (this .lastSaved , {
434
- addSuffix: true ,
435
- })} ` ;
436
- }
437
- // runs frequently, so only change a tracked property if the value has changed
438
- if (this .lastSavedMsg != savedMessage ) {
439
- this .lastSavedMsg = savedMessage ;
440
- }
441
- }
442
-
443
- private getErrorMessage(error : Error ) {
444
- if ((error as any ).responseHeaders ?.get (' x-blocked-by-waf-rule' )) {
445
- return ' Rejected by firewall' ;
446
- }
447
- if (error .message ) {
448
- return error .message ;
449
- }
450
- return ' Unknown error' ;
451
- }
452
-
453
368
private doneEditing = restartableTask (async () => {
454
369
let item = this .args .item ;
455
370
let { request } = item ;
456
371
// if the card is actually different do the save and dismiss, otherwise
457
372
// just change the stack item's format to isolated
458
- if (this .hasUnsavedChanges ) {
373
+ if (item . autoSaveState ? .hasUnsavedChanges ) {
459
374
// we dont want to have the user wait for the save to complete before
460
375
// dismissing edit mode so intentionally not awaiting here
461
376
let updatedCard = await this .args .saveCard (item .card );
@@ -675,9 +590,9 @@ export default class OperatorModeStackItem extends Component<Signature> {
675
590
<CardHeader
676
591
@ cardTypeDisplayName ={{this .headerTitle }}
677
592
@ cardTypeIcon ={{cardTypeIcon @ item.card}}
678
- @ isSaving ={{this .isSaving }}
593
+ @ isSaving ={{@ item.autoSaveState .isSaving }}
679
594
@ isTopCard ={{this .isTopCard }}
680
- @ lastSavedMessage ={{this .lastSavedMsg }}
595
+ @ lastSavedMessage ={{@ item.autoSaveState.lastSavedErrorMsg }}
681
596
@ moreOptionsMenuItems ={{this .moreOptionsMenuItems }}
682
597
@ realmInfo ={{realmInfo }}
683
598
@ onEdit ={{if this . canEdit ( fn @ publicAPI.editCard this . card) }}
0 commit comments