diff --git a/crates/signed_state/src/repo.rs b/crates/signed_state/src/repo.rs index 4be8861..e2548ad 100644 --- a/crates/signed_state/src/repo.rs +++ b/crates/signed_state/src/repo.rs @@ -87,7 +87,7 @@ pub struct RepoStore { /// /// Avoids re-running the maintainer Auto sync on every refresh. synced_maintainers: HashSet, - /// In-flight refresh tasks. + /// In-flight tasks, cancelled when the store drops. tasks: Vec>>, /// Backend subscription of an announced repository. `None` while local-only. _subscription: Option, @@ -770,9 +770,9 @@ impl RepoStore { .collect() }; - cx.spawn(async move |this, cx| { - // The PR references the root patch event. - // Viewers can then find the patch without carrying it inline. + let task: Task> = cx.spawn(async move |this, cx| { + // The PR references the root patch, + // viewers can then find the patch without carrying it inline. let root_patch = match publish_patch_series( &this, cx, @@ -838,24 +838,17 @@ impl RepoStore { }; let builder = this.update(cx, |this, _cx| { - // NIP-34 PRs carry at least one clone URL. - // The tip commit is downloadable from it. - // The author's `/prs/` URLs come first. - // They are author-controlled and most likely alive. - // The announced mirrors follow. - // The list is fixed before signing. - // The pushed ref name embeds the event id. - // Every candidate URL is listed up front. - // Dead URLs are inert, the linked patch stays the source of truth. let prs_urls: Vec = author_targets .iter() .filter_map(|(url, _)| Url::parse(url).ok()) .collect(); + let base_clone = this .announcement .as_ref() .map(|a| a.clone.clone()) .unwrap_or_default(); + let clone = pr_clone_urls(prs_urls, base_clone); let builder = GitPullRequest { @@ -880,8 +873,6 @@ impl RepoStore { })?; // Sign before publishing. - // The tip is pushed to the grasp servers under `refs/nostr/`. - // Nak's convention, readers fetch that ref for the commit behind the `c` tag. let event = cx .background_spawn({ let signer = signer.clone(); @@ -892,20 +883,22 @@ impl RepoStore { if let Some(path) = push_from.as_ref() { let tip = current_commit.to_string(); let reference = format!("refs/nostr/{}", event.id.to_hex()); + let (pushed, failures) = cx .background_spawn({ let path = path.clone(); let tip = tip.clone(); let reference = reference.clone(); // Author servers first, then the announced base grasp servers. - // The extra targets are best-effort redundancy. let targets: Vec<(String, String)> = author_targets .into_iter() .chain(base_targets) .collect(); + async move { let mut failures = Vec::new(); let mut pushed = 0; + for (url, label) in &targets { match signed_git::push_commit_ref( &path, url, &tip, &reference, @@ -914,6 +907,7 @@ impl RepoStore { Err(e) => failures.push(format!("{label}: {e}")), } } + (pushed, failures) } }) @@ -957,8 +951,8 @@ impl RepoStore { } Ok(()) - }) - .detach(); + }); + self.tasks.push(task); } /// Generate the patch between `merge_base` and `compare_ref` in `repo_path`, @@ -1086,7 +1080,7 @@ impl RepoStore { .map(|a| a.clone.clone()) .unwrap_or_default(); - cx.spawn(async move |this, cx| { + let task: Task> = cx.spawn(async move |this, cx| { if let Err(e) = publish_patch_series( &this, cx, @@ -1145,8 +1139,8 @@ impl RepoStore { } Ok(()) - }) - .detach(); + }); + self.tasks.push(task); } /// Set the status of a root event. @@ -1272,7 +1266,7 @@ impl RepoStore { } Ok(()) }); - task.detach(); + self.tasks.push(task); } /// The latest announcement of this repository, @@ -1588,7 +1582,7 @@ impl RepoStore { Ok(()) }); - task.detach(); + self.tasks.push(task); } } diff --git a/crates/workspace/src/views/commit_diff/mod.rs b/crates/workspace/src/views/commit_diff/mod.rs index b907adb..17799c8 100644 --- a/crates/workspace/src/views/commit_diff/mod.rs +++ b/crates/workspace/src/views/commit_diff/mod.rs @@ -303,6 +303,7 @@ pub struct CommitDiffView { loading: bool, error: Option, pane: Entity, + tasks: Vec>>, } impl CommitDiffView { @@ -336,6 +337,7 @@ impl CommitDiffView { loading: true, error: None, pane, + tasks: Vec::new(), } } @@ -383,7 +385,7 @@ impl CommitDiffView { Ok(()) }); - task.detach(); + self.tasks.push(task); } fn render_header(&self, cx: &mut Context) -> AnyElement { diff --git a/crates/workspace/src/views/pull_requests/detail.rs b/crates/workspace/src/views/pull_requests/detail.rs index 89dd0e4..d1385c3 100644 --- a/crates/workspace/src/views/pull_requests/detail.rs +++ b/crates/workspace/src/views/pull_requests/detail.rs @@ -79,8 +79,7 @@ pub struct PullRequestDetailView { pane: Entity, commit_item_sizes: Rc>>, commit_scroll_handle: VirtualListScrollHandle, - /// The dock caches item panels, so without this observer a panel opened - /// before the store loaded would stay on its placeholder. + tasks: Vec>>, _subscription: Subscription, } @@ -124,6 +123,7 @@ impl PullRequestDetailView { pane, commit_item_sizes: Rc::new(Vec::new()), commit_scroll_handle: VirtualListScrollHandle::new(), + tasks: Vec::new(), _subscription: subscription, } } @@ -327,7 +327,7 @@ impl PullRequestDetailView { Ok(()) }); - task.detach(); + self.tasks.push(task); } /// Open the diff of `commit_id` in the bottom dock of the area. diff --git a/crates/workspace/src/views/pull_requests/new.rs b/crates/workspace/src/views/pull_requests/new.rs index a3e3ef8..4d29ba1 100644 --- a/crates/workspace/src/views/pull_requests/new.rs +++ b/crates/workspace/src/views/pull_requests/new.rs @@ -68,6 +68,7 @@ pub struct NewPullRequestView { pane: Entity, scroll_handle: VirtualListScrollHandle, item_sizes: Rc>>, + tasks: Vec>>, _subscriptions: Vec, } @@ -323,6 +324,7 @@ impl NewPullRequestView { pane, scroll_handle: VirtualListScrollHandle::new(), item_sizes: Rc::new(Vec::new()), + tasks: Vec::new(), _subscriptions: subscriptions, } } @@ -390,7 +392,8 @@ impl NewPullRequestView { Ok(()) }); - task.detach(); + + self.tasks.push(task); } /// Branches and the current branch are read off the UI thread, then applied. @@ -419,7 +422,8 @@ impl NewPullRequestView { Ok(()) }); - task.detach(); + + self.tasks.push(task); } fn apply_checkout( @@ -636,7 +640,8 @@ impl NewPullRequestView { Ok(()) }); - task.detach(); + + self.tasks.push(task); } #[allow(clippy::too_many_arguments)] @@ -830,7 +835,7 @@ impl NewPullRequestView { Ok(()) }); - task.detach(); + self.tasks.push(task); } fn submit(&mut self, window: &mut Window, cx: &mut Context) { @@ -911,7 +916,7 @@ impl NewPullRequestView { Ok(()) }); - task.detach(); + self.tasks.push(task); } fn open_commit_diff(&mut self, commit_id: &str, window: &mut Window, cx: &mut Context) {