add issue detail

This commit is contained in:
2026-08-21 19:45:25 +07:00
parent 052c30d12b
commit 9bdc3daa7e
8 changed files with 429 additions and 111 deletions
+49 -14
View File
@@ -11,8 +11,9 @@ use crate::backend::{Backend, BackendEvent};
/// events (e.g. per-event `NostrUpdate`s) collapse into one query.
const REFRESH_DEBOUNCE: Duration = Duration::from_millis(300);
/// Per-repository store: announcement, state, issues, patches, PRs and
/// their resolved statuses. Always derived from the local database.
/// Per-repository store: announcement, state, issues, patches, PRs,
/// comments and their resolved statuses. Always derived from the local
/// database.
pub struct RepoStore {
addr: RepoAddr,
pub announcement: Option<Announcement>,
@@ -23,6 +24,8 @@ pub struct RepoStore {
pub issues: Vec<Event>,
pub patches: Vec<Event>,
pub pull_requests: Vec<Event>,
/// Comments on issues / PRs, oldest first.
pub comments: Vec<Event>,
statuses: Vec<Event>,
/// Error of the last action initiated from this store, if any.
pub last_error: Option<String>,
@@ -73,6 +76,7 @@ impl RepoStore {
issues: Vec::new(),
patches: Vec::new(),
pull_requests: Vec::new(),
comments: Vec::new(),
statuses: Vec::new(),
last_error: None,
refreshing: false,
@@ -172,8 +176,8 @@ impl RepoStore {
let all_states = states.into_iter().filter(|e| !deletions.is_deleted(e));
let state = latest(all_states).map(|state| parse_state(&state));
let (mut issues, mut patches, mut pull_requests, mut statuses) =
(Vec::new(), Vec::new(), Vec::new(), Vec::new());
let (mut issues, mut patches, mut pull_requests, mut statuses, mut comments) =
(Vec::new(), Vec::new(), Vec::new(), Vec::new(), Vec::new());
for event in activity {
if deletions.is_deleted(&event) {
@@ -183,6 +187,7 @@ impl RepoStore {
Kind::GitIssue => issues.push(event),
Kind::GitPatch => patches.push(event),
Kind::GitPullRequest | Kind::GitPullRequestUpdate => pull_requests.push(event),
Kind::Comment => comments.push(event),
kind if RepoStatus::from_kind(kind).is_some() => statuses.push(event),
_ => {}
}
@@ -191,6 +196,7 @@ impl RepoStore {
sort_newest_first(&mut issues);
sort_newest_first(&mut patches);
sort_newest_first(&mut pull_requests);
sort_oldest_first(&mut comments);
Ok::<_, Error>((
announcement,
@@ -199,21 +205,23 @@ impl RepoStore {
patches,
pull_requests,
statuses,
comments,
))
});
self.tasks.retain(|task| !task.is_ready());
self.tasks.push(cx.spawn(async move |this, cx| {
let (announcement, state, issues, patches, pull_requests, statuses) = match work.await {
Ok(data) => data,
Err(e) => {
return this.update(cx, |this, cx| {
this.refreshing = false;
this.last_error = Some(e.to_string());
cx.notify();
});
}
};
let (announcement, state, issues, patches, pull_requests, statuses, comments) =
match work.await {
Ok(data) => data,
Err(e) => {
return this.update(cx, |this, cx| {
this.refreshing = false;
this.last_error = Some(e.to_string());
cx.notify();
});
}
};
let again = this.update(cx, |this, cx| {
this.announcement = announcement;
@@ -226,6 +234,7 @@ impl RepoStore {
this.issues = issues;
this.patches = patches;
this.pull_requests = pull_requests;
this.comments = comments;
this.statuses = statuses;
cx.notify();
@@ -297,6 +306,28 @@ impl RepoStore {
self.send(builder, cx);
}
/// Comments on a root event (issue / PR), oldest first.
pub fn comments_of(&self, root: &EventId) -> impl Iterator<Item = &Event> {
self.comments
.iter()
.filter(move |e| signed_core::references_root(e, root))
}
/// Comment on a root event (issue / PR) per NIP-34 (kind 1111).
pub fn comment(&mut self, root: &Event, content: String, cx: &mut Context<Self>) {
let Ok(root_ref) = Tag::parse(["e", &root.id.to_hex(), "", "root"]) else {
return;
};
let builder = EventBuilder::new(Kind::Comment, content).tags([
root_ref,
Tag::public_key(root.pubkey),
Tag::coordinate(self.addr.clone(), None),
]);
self.send(builder, cx);
}
/// Open a pull request on this repository: a root PR event whose content
/// is the `git format-patch` output of the proposed changes.
///
@@ -390,6 +421,10 @@ fn sort_newest_first(events: &mut [Event]) {
events.sort_by_key(|e| std::cmp::Reverse(e.created_at));
}
fn sort_oldest_first(events: &mut [Event]) {
events.sort_by_key(|e| e.created_at);
}
/// The proposed commit of a `git format-patch` output: the `From <commit>`
/// header on its first line.
fn patch_current_commit(patch: &str) -> Option<&str> {