162 lines
5.6 KiB
Rust
162 lines
5.6 KiB
Rust
use gpui::prelude::*;
|
|
use gpui::{AnyElement, App, Context, Window, div, px};
|
|
use gpui_component::scroll::Scrollbar;
|
|
use gpui_component::spinner::Spinner;
|
|
use gpui_component::{ActiveTheme, Sizable, h_flex, v_flex, v_virtual_list};
|
|
use signed_git::FileCommit;
|
|
use signed_ui::placeholder;
|
|
use utils::relative_time_secs;
|
|
|
|
use super::RepoDetailView;
|
|
|
|
/// Height of one commit row in the virtual list.
|
|
pub(super) const COMMIT_ROW_HEIGHT: f32 = 56.;
|
|
|
|
pub(super) fn commit_row(
|
|
ix: usize,
|
|
commit: &FileCommit,
|
|
on_click: impl Fn(&mut Window, &mut App) + 'static,
|
|
cx: &App,
|
|
) -> AnyElement {
|
|
h_flex()
|
|
.id(ix)
|
|
.px_4()
|
|
.h(px(COMMIT_ROW_HEIGHT))
|
|
.w_full()
|
|
.gap_3()
|
|
.items_center()
|
|
.border_b(px(1.))
|
|
.border_color(cx.theme().border)
|
|
.hover(|this| this.bg(cx.theme().list_hover))
|
|
.child(
|
|
v_flex()
|
|
.flex_1()
|
|
.min_w_0()
|
|
.gap_0p5()
|
|
.justify_center()
|
|
.child(
|
|
h_flex()
|
|
.gap_2()
|
|
.items_center()
|
|
.overflow_hidden()
|
|
.child(
|
|
div()
|
|
.font_family(cx.theme().mono_font_family.clone())
|
|
.text_xs()
|
|
.text_color(cx.theme().muted_foreground)
|
|
.child(commit.id.clone()),
|
|
)
|
|
.child(
|
|
div()
|
|
.flex_1()
|
|
.min_w_0()
|
|
.text_sm()
|
|
.text_ellipsis()
|
|
.whitespace_nowrap()
|
|
.child(commit.summary.clone()),
|
|
),
|
|
)
|
|
.child(
|
|
h_flex()
|
|
.gap_2()
|
|
.text_xs()
|
|
.text_color(cx.theme().muted_foreground)
|
|
.child(commit.author.clone())
|
|
.child(relative_time_secs(commit.time)),
|
|
),
|
|
)
|
|
.on_click(move |_event, window, cx| on_click(window, cx))
|
|
.into_any_element()
|
|
}
|
|
|
|
impl RepoDetailView {
|
|
/// Full-height body of the Commits tab: all commits in a virtual
|
|
/// list, or a status message while loading / when there are none.
|
|
pub(super) fn render_commits_tab(&self, cx: &mut Context<Self>) -> AnyElement {
|
|
let Some(list) = self.all_commits.as_ref() else {
|
|
return if self.loading_all_commits {
|
|
v_flex()
|
|
.size_full()
|
|
.items_center()
|
|
.justify_center()
|
|
.child(Spinner::new().small())
|
|
.into_any_element()
|
|
} else {
|
|
placeholder("Failed to load commits", cx)
|
|
};
|
|
};
|
|
|
|
if list.commits.is_empty() {
|
|
return placeholder("No commits found", cx);
|
|
}
|
|
|
|
// Copy only the values the element tree needs; the list itself is
|
|
// borrowed inside the renderer below instead of being cloned per
|
|
// frame (a full history can be tens of thousands of commits).
|
|
let view = cx.entity().clone();
|
|
let sizes = self.item_sizes.clone();
|
|
let scroll_handle = self.scroll_handle.clone();
|
|
let shown = list.commits.len();
|
|
let total = list.total;
|
|
|
|
v_flex()
|
|
.relative()
|
|
.flex_1()
|
|
.w_full()
|
|
.min_h_0()
|
|
.child(
|
|
v_virtual_list(view, "commits", sizes, move |this, range, _window, cx| {
|
|
let view = cx.entity().downgrade();
|
|
let commits = this
|
|
.all_commits
|
|
.as_ref()
|
|
.map(|list| list.commits.as_slice())
|
|
.unwrap_or(&[]);
|
|
|
|
range
|
|
.map(|ix| {
|
|
let id = commits[ix].id.clone();
|
|
let view = view.clone();
|
|
|
|
commit_row(
|
|
ix,
|
|
&commits[ix],
|
|
move |window, cx| {
|
|
if let Some(view) = view.upgrade() {
|
|
view.update(cx, |this, cx| {
|
|
this.open_commit_diff(&id, window, cx)
|
|
});
|
|
}
|
|
},
|
|
cx,
|
|
)
|
|
})
|
|
.collect()
|
|
})
|
|
.track_scroll(&scroll_handle)
|
|
.size_full(),
|
|
)
|
|
.when(shown < total, |this| {
|
|
// The history is capped; tell the user the list is truncated.
|
|
this.child(
|
|
div()
|
|
.py_2()
|
|
.w_full()
|
|
.text_xs()
|
|
.text_color(cx.theme().muted_foreground)
|
|
.child(format!("Showing {shown} of {total} commits")),
|
|
)
|
|
})
|
|
.child(
|
|
div()
|
|
.absolute()
|
|
.top_0()
|
|
.left_0()
|
|
.right_0()
|
|
.bottom_0()
|
|
.child(Scrollbar::vertical(&self.scroll_handle)),
|
|
)
|
|
.into_any_element()
|
|
}
|
|
}
|