Files
novem/src/screen/dashboard/pull_request_view.rs

157 lines
4.6 KiB
Rust
Raw Normal View History

2026-05-11 00:32:12 +08:00
use gpui::{AppContext, IntoElement, ParentElement, Styled, div, prelude::FluentBuilder};
use crate::{
api::{self, issues::PullRequest},
app,
component::{
font_icon::{FontIcon, font_icon},
markdown::{self, MarkdownText},
text::text,
},
query::{self, QueryStatus, read_query, use_query},
};
pub(crate) struct PullRequestView {
markdown_viewer: Option<gpui::Entity<MarkdownText>>,
pull_request_query: Option<query::Entity<api::issues::FetchPullRequest>>,
}
pub fn new(cx: &mut gpui::Context<PullRequestView>) -> PullRequestView {
PullRequestView {
markdown_viewer: None,
pull_request_query: None,
}
}
impl PullRequestView {
pub(crate) fn change_displayed_pull_request(
&mut self,
id: api::issues::Id,
cx: &mut gpui::Context<Self>,
) {
let query = use_query(api::issues::FetchPullRequest { id }, cx);
self.pull_request_query = Some(query.clone());
_ = cx
.observe(&query.clone(), move |this, _, cx| {
let maybe_content = {
let data = read_query(&query, cx);
if let QueryStatus::Loaded(pr) = data {
Some(gpui::SharedString::new(pr.body.as_str()))
} else {
None
}
};
this.markdown_viewer =
maybe_content.map(|content| cx.new(|cx| markdown::new(content, cx)))
})
.detach();
cx.notify();
}
fn pr_content(
&self,
pr: &api::issues::DetailedPullRequest,
cx: &gpui::Context<Self>,
) -> gpui::Div {
let theme = app::current_theme(cx);
let mut status_pill = div()
.flex()
.flex_row()
.items_center()
.gap_1()
.px_2()
.rounded_full();
match pr.state {
| api::issues::PullRequestState::Open => {
status_pill = status_pill
.bg(theme.colors.success)
.child(
font_icon(FontIcon::PullRequestArrow)
.size_3()
.text_color(theme.colors.accent_text),
)
.child(text("Open").text_color(theme.colors.accent_text).text_xs());
}
| api::issues::PullRequestState::Closed => {
status_pill = status_pill
.bg(theme.colors.danger)
.child(
font_icon(FontIcon::PullRequestClosed)
.size_3()
.text_color(theme.colors.accent_text),
)
.child(
text("Closed")
.text_color(theme.colors.accent_text)
.text_xs(),
);
}
| api::issues::PullRequestState::Merged => {
status_pill = status_pill.bg(theme.colors.accent).child(
text("Merged")
.text_color(theme.colors.accent_text)
.text_xs(),
);
}
}
let author_pill = div()
.px_2()
.border_1()
.border_color(theme.colors.border)
.rounded_full()
.bg(theme.colors.surface_elevated)
.child(text("kennethnym").text_xs());
let row = div()
.flex()
.flex_row()
.gap_2()
.child(status_pill)
.child(author_pill);
div()
.size_full()
.flex()
.flex_col()
.child(
div()
.w_full()
.px_3p5()
.py_3()
.border_b_1()
.border_color(theme.colors.border)
.child(text(pr.title.clone()).w_full().text_xl().mb_2())
.child(row),
)
.when_some(self.markdown_viewer.as_ref(), |it, viewer| {
it.child(div().h_full().p_3p5().child(viewer.clone()))
})
}
}
impl gpui::Render for PullRequestView {
fn render(
&mut self,
window: &mut gpui::Window,
cx: &mut gpui::Context<Self>,
) -> impl gpui::IntoElement {
match &self.pull_request_query {
| Some(q) => match read_query(q, cx) {
| QueryStatus::Loaded(pr) => self.pr_content(pr, cx),
| QueryStatus::Err(e) => div().child(format!("{:?}", e)),
| QueryStatus::Loading => div().child("loading pr content"),
},
| None => div().child("no pr selected"),
}
}
}