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


##########
frontend/src/app/workspace/component/workflow-form/workflow-form.component.ts:
##########
@@ -1301,40 +1545,47 @@ export class WorkflowFormComponent implements OnInit, 
OnDestroy {
    * workflow when the payload has no id, so saving whatever the graph holds 
would spawn
    * stray "Untitled workflow" rows when the page is left before its workflow 
loaded.
    */
-  private save(): void {
+  private save(afterwards?: () => void): void {
     // A read-only viewer can open and run the form (execution is gated on 
computing-unit access,
     // not workflow access) but must never persist: every such save is a 
guaranteed 403 that would
     // spam "Could not save" on each debounce. Their inputs are non-editable, 
so nothing is lost.
+    // `afterwards` runs once the save has completed, or at once when there is 
nothing to save;
+    // it does not run when the save fails, so a caller that navigates on it 
stays put instead.
     if (!this.canEdit) {
+      afterwards?.();
       return;
     }
     if (!this.userService.isLogin() || 
!this.workflowPersistService.isWorkflowPersistEnabled()) {
+      afterwards?.();
       return;
     }
     const workflow = this.workflowActionService.getWorkflow();
     if (workflow.wid === undefined || workflow.wid !== this.wid) {
+      afterwards?.();
       return;
     }
+    // Snapshot now, not when the request's turn comes: on the way out 
ngOnDestroy clears the graph
+    // right after asking for this save, and the queue may only reach it after 
that.
     const preserved: Workflow = {
       ...workflow,
       content: { ...workflow.content, operatorPositions: 
this.positionsToSave(workflow.content) },
     };
-    // On the way out the subscription must NOT be tied to this component: 
ngOnDestroy
-    // calls save(), and untilDestroyed would tear the subscription down as 
part of the
-    // very same destroy sequence, aborting the request that was the point of 
the call.
-    const persist = this.workflowPersistService.persistWorkflow(preserved);
-    // The `destroyed` branch deliberately omits untilDestroyed (see above); 
the persist call
-    // is a one-shot HTTP request that completes on its own, so it needs no 
teardown operator.
-    // eslint-disable-next-line rxjs-angular/prefer-takeuntil
-    (this.destroyed ? persist : persist.pipe(untilDestroyed(this))).subscribe({
-      // Feed the saved workflow back, exactly as the operator canvas does: 
this advances
-      // lastModifiedTime (and the normalised name), and the metadata 
subscription then repaints
-      // the title bar -- so "Saved at ..." moves past the moment the page 
opened.
-      next: updatedWorkflow => 
this.workflowActionService.setWorkflowMetadata(updatedWorkflow),
-      // A save that fails silently is the worst thing this page can do: the 
author walks
-      // away believing the form they just built is stored.
-      error: () => this.notificationService.error("Could not save. Your latest 
changes are not stored yet."),
-    });
+    this.persistQueue.next({ workflow: preserved, afterwards });
+  }
+
+  /** One persist of the given snapshot, reporting its outcome; the queue 
orders them. */
+  private persistNow(preserved: Workflow): Observable<Workflow> {
+    return this.workflowPersistService.persistWorkflow(preserved).pipe(
+      tap({
+        // Feed the saved workflow back, exactly as the operator canvas does: 
this advances
+        // lastModifiedTime (and the normalised name), and the metadata 
subscription then repaints
+        // the title bar -- so "Saved at ..." moves past the moment the page 
opened.
+        next: updatedWorkflow => 
this.workflowActionService.setWorkflowMetadata(updatedWorkflow),

Review Comment:
   Right on both counts. c77e15748: the response feeds back the server-owned 
metadata (the timestamp, and the normalised name when nothing changed), but if 
the name differs from the snapshot that was sent (a rename since, whose own 
save is already queued) the current name is kept; and once the page is gone 
nothing is fed back, so the cleared singleton is not refilled. Specs: an older 
save's response does not undo a rename made meanwhile; no metadata repaint 
after destroy. Deleting either guard turns its spec red.



##########
frontend/src/app/workspace/component/workflow-form/workflow-form.component.ts:
##########
@@ -316,7 +346,11 @@ export class WorkflowFormComponent implements OnInit, 
OnDestroy {
       .getViewResultOperatorsChangedStream()
       .pipe(untilDestroyed(this))
       .subscribe(() => {
+        // An eye toggled on the canvas changes both what shows (a 
newly-viewed step) and what the
+        // author can pick, so rebuild the picker here too -- otherwise a 
just-eyed step would not
+        // appear as an option (and an un-eyed one would linger) until the 
next full re-read.
         this.refreshShownResults();
+        this.rebuildResultChoices();

Review Comment:
   Agreed. Compilation does re-read the config after those edits (it merges the 
same add/delete/link/disabled streams), but debounced and held while the reader 
is typing, so the card and the pill could lag. c77e15748 subscribes to the 
graph's operator add/delete, link add/delete and disabled-changed streams 
directly and refreshes the two derived collections at once (nothing rebuilds 
the inputs). Spec: a shown final step disabled by a co-editor drops from the 
cards and the picker on that event alone; deleting the subscription turns it 
red.



-- 
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