@@ -86,10 +86,9 @@ pub fn SetRemotePopup(
8686 let workflows = LocalResource :: new ( move || {
8787 let target = debounced_target. get ( ) ;
8888 async move {
89- match target {
90- Some ( ( host, bucket) ) => Some ( commands:: get_bucket_workflows ( host, bucket) . await ) ,
91- None => None ,
92- }
89+ let ( host, bucket) = target?;
90+ let res = commands:: get_bucket_workflows ( host. clone ( ) , bucket. clone ( ) ) . await ;
91+ Some ( ( ( host, bucket) , res) )
9392 }
9493 } ) ;
9594
@@ -98,9 +97,16 @@ pub fn SetRemotePopup(
9897 // bucket default is preselected. `None` here means "no target / still
9998 // loading"; a fetch error maps to the `Unavailable` view.
10099 let wf_view = Memo :: new ( move |_| {
101- workflows. get ( ) . flatten ( ) . map ( |res| match res {
102- Ok ( cw) => build_workflow_view ( & cw, None ) ,
103- Err ( _) => build_workflow_view ( & CommitWorkflows :: Unavailable , None ) ,
100+ workflows. get ( ) . flatten ( ) . and_then ( |( target, res) | {
101+ // Self-keying: ignore a result whose target no longer matches the
102+ // typed one. Covers BOTH the debounce-pending window and the
103+ // in-flight-refetch window (the resource keeps the previous bucket's
104+ // value while re-running), so the view is `None` (loading) until the
105+ // fetch for the *current* bucket resolves.
106+ ( Some ( target) == valid_target. get ( ) ) . then ( || match res {
107+ Ok ( cw) => build_workflow_view ( & cw, None ) ,
108+ Err ( _) => build_workflow_view ( & CommitWorkflows :: Unavailable , None ) ,
109+ } )
104110 } )
105111 } ) ;
106112
@@ -116,15 +122,13 @@ pub fn SetRemotePopup(
116122 // No previous revision on a first push, so the divergence note never shows.
117123 let workflow_note = Memo :: new ( |_| None :: < String > ) ;
118124
119- // True while a valid target's workflow config isn't ready yet — the fetch is
120- // in flight (`wf_view` is `None`) OR the debounce hasn't caught up so the
121- // shown view still belongs to the previous bucket (`valid_target` has moved
122- // ahead of `debounced_target`). Blocking Save on both keeps the submitted
123- // intent matched to the currently-typed bucket, never a stale one.
124- let workflow_loading = Memo :: new ( move |_| {
125- valid_target. get ( ) . is_some ( )
126- && ( wf_view. get ( ) . is_none ( ) || valid_target. get ( ) != debounced_target. get ( ) )
127- } ) ;
125+ // True while a valid target's workflow config isn't ready. Because `wf_view`
126+ // is self-keyed to the current `valid_target` (above), a `None` here covers
127+ // every not-yet-current window — debounce pending AND in-flight refetch —
128+ // so blocking Save on it keeps the submitted intent matched to the
129+ // currently-typed bucket, never a stale one.
130+ let workflow_loading =
131+ Memo :: new ( move |_| valid_target. get ( ) . is_some ( ) && wf_view. get ( ) . is_none ( ) ) ;
128132 // Disable Save while submitting or while the workflow config is loading, so
129133 // the submitted intent always corresponds to the displayed bucket's config
130134 // and never a stale one from an earlier keystroke.
0 commit comments