@@ -20,7 +20,10 @@ use smallvec::{SmallVec, smallvec};
use std::{ops::Range, rc::Rc, sync::Arc, sync::OnceLock};
use theme::{AccentColors, ThemeSettings};
use time::{OffsetDateTime, UtcOffset, format_description::BorrowedFormatItem};
-use ui::{ContextMenu, ScrollableHandle, Table, TableInteractionState, Tooltip, prelude::*};
+use ui::{
+ CommonAnimationExt as _, ContextMenu, ScrollableHandle, Table, TableInteractionState, Tooltip,
+ prelude::*,
+};
use workspace::{
Workspace,
item::{Item, ItemEvent, SerializableItem},
@@ -623,7 +626,7 @@ impl GitGraph {
// This won't overlap with loading commits from the repository because
// we either have all commits or commits loaded in chunks and loading commits
// from the repository event is always adding the last chunk of commits.
- let commits =
+ let (commits, _) =
repository.graph_data(log_source.clone(), log_order, 0..usize::MAX, cx);
graph.add_commits(commits);
});
@@ -674,7 +677,7 @@ impl GitGraph {
let old_count = self.graph_data.commits.len();
repository.update(cx, |repository, cx| {
- let commits = repository.graph_data(
+ let (commits, _) = repository.graph_data(
self.log_source.clone(),
self.log_order,
old_count..*commit_count,
@@ -882,6 +885,15 @@ impl GitGraph {
})
}
+ fn render_loading_spinner(&self, cx: &App) -> AnyElement {
+ let rems = TextSize::Large.rems(cx);
+ Icon::new(IconName::LoadCircle)
+ .size(IconSize::Custom(rems))
+ .color(Color::Accent)
+ .with_rotate_animation(3)
+ .into_any_element()
+ }
+
fn render_commit_detail_panel(
&self,
window: &mut Window,
@@ -1443,35 +1455,45 @@ impl Render for GitGraph {
let author_width_fraction = 0.10;
let commit_width_fraction = 0.06;
- let commit_count = match self.graph_data.max_commit_count {
- AllCommitCount::Loaded(count) => count,
+ let (commit_count, is_loading) = match self.graph_data.max_commit_count {
+ AllCommitCount::Loaded(count) => (count, true),
AllCommitCount::NotLoaded => {
- self.project.update(cx, |project, cx| {
+ let is_loading = self.project.update(cx, |project, cx| {
if let Some(repository) = project.active_repository(cx) {
repository.update(cx, |repository, cx| {
// Start loading the graph data if we haven't started already
- repository.graph_data(
- self.log_source.clone(),
- self.log_order,
- 0..0,
- cx,
- );
+ repository
+ .graph_data(self.log_source.clone(), self.log_order, 0..0, cx)
+ .1
})
+ } else {
+ false
}
- });
+ }) && self.graph_data.commits.is_empty();
- self.graph_data.commits.len()
+ (self.graph_data.commits.len(), is_loading)
}
};
let content = if self.graph_data.commits.is_empty() {
- let message = "No commits found";
+ let message = if is_loading {
+ "Loading"
+ } else {
+ "No commits found"
+ };
+ let label = Label::new(message)
+ .color(Color::Muted)
+ .size(LabelSize::Large);
div()
.size_full()
- .flex()
+ .h_flex()
+ .gap_1()
.items_center()
.justify_center()
- .child(Label::new(message).color(Color::Muted))
+ .child(label)
+ .when(is_loading, |this| {
+ this.child(self.render_loading_spinner(cx))
+ })
} else {
div()
.size_full()
@@ -2349,6 +2371,7 @@ mod tests {
0..usize::MAX,
cx,
)
+ .0
.to_vec()
});
@@ -4240,8 +4240,8 @@ impl Repository {
log_order: LogOrder,
range: Range<usize>,
cx: &mut Context<Self>,
- ) -> &[Arc<InitialGraphCommitData>] {
- let initial_commit_data = &self
+ ) -> (&[Arc<InitialGraphCommitData>], bool) {
+ let (loading_task, initial_commit_data) = self
.initial_graph_data
.entry((log_order, log_source.clone()))
.or_insert_with(|| {
@@ -4267,12 +4267,14 @@ impl Repository {
}),
vec![],
)
- })
- .1;
+ });
let max_start = initial_commit_data.len().saturating_sub(1);
let max_end = initial_commit_data.len();
- &initial_commit_data[range.start.min(max_start)..range.end.min(max_end)]
+ (
+ &initial_commit_data[range.start.min(max_start)..range.end.min(max_end)],
+ !loading_task.is_ready(),
+ )
}
async fn local_git_graph_data(