yangzhang75 commented on code in PR #8540:
URL: https://github.com/apache/texera/pull/8540#discussion_r4041094320


##########
frontend/src/app/common/service/workflow-persist/workflow-persist.service.ts:
##########
@@ -68,29 +72,74 @@ export class WorkflowPersistService {
    * before it has landed too. Each request snapshots its payload when asked 
for; it is sent when its
    * turn comes, and its outcome is relayed to that caller alone. A failed 
save fails its own caller
    * and does not hold up the next.
+   *
+   * A response is relayed with the page's current name and description in 
place of its own (see
+   * withLocalEdits): those are the two fields a user edits, and a response 
answers the save it was
+   * sent for, which may be older than an edit made since. Callers feed the 
response back as the
+   * workflow's metadata; without this, a rename made while a save was out 
came back undone.
    */
   private readonly persistQueue = new Subject<{ send: Observable<Workflow>; 
result: Subject<Workflow> }>();
 
+  /** Saves asked for and not yet answered (or failed); see whenSavesDrained. 
*/
+  private pendingSaves = 0;
+  private readonly savesDrained = new Subject<void>();
+
   constructor(
     private http: HttpClient,
-    private notificationService: NotificationService
+    private notificationService: NotificationService,
+    // Looked up lazily, at response time: the persist service is also used by 
the dashboard, where
+    // no workflow is open and constructing the (graph-owning) action service 
would be a side effect.
+    private injector: Injector
   ) {
     this.persistQueue
       .pipe(
         concatMap(({ send, result }) =>
           send.pipe(
+            map(updated => this.withLocalEdits(updated)),
             tap({
               next: updated => result.next(updated),
               error: (err: unknown) => result.error(err),
               complete: () => result.complete(),
             }),
-            catchError(() => EMPTY)
+            catchError(() => EMPTY),
+            finalize(() => this.saveDone())
           )
         )
       )
       .subscribe();
   }
 
+  /**
+   * Emits once every save asked for so far has been answered or has failed; 
at once when none is
+   * pending. For a caller that leaves the page on completion (the view 
switches): its own save
+   * completing is not enough, a save queued behind it (a rename's, a 
description's) would still be
+   * aborted by the page load.
+   */
+  public whenSavesDrained(): Observable<void> {
+    return this.pendingSaves === 0 ? of(undefined) : 
this.savesDrained.pipe(take(1));
+  }
+
+  private saveDone(): void {
+    this.pendingSaves -= 1;
+    if (this.pendingSaves === 0) {
+      this.savesDrained.next();
+    }
+  }
+
+  /**
+   * The response with the page's current name and description: a response 
carries the values the
+   * save was sent with, and an edit made since would be undone by feeding 
them back. Left alone when
+   * another workflow is open by now (nothing local belongs to this response); 
a workflow just created
+   * still carries the default id locally, and its rename made meanwhile is 
kept too.
+   */
+  private withLocalEdits(response: Workflow): Workflow {

Review Comment:
   Folded away in d857341e9 — the guard and the spec that pinned it are gone; 
the rule stays in `withLocalEdits` alone.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to