git_ui: Update Project Diff empty state design (#26554)
Title Release Notes: - N/A --------- Co-authored-by: Cole Miller <m@cole-miller.net>
This commit is contained in:
parent
010c5a2c4e
commit
8d259a9dbe
1 changed files with 288 additions and 3 deletions
|
@ -1,4 +1,7 @@
|
|||
use crate::git_panel::{GitPanel, GitPanelAddon, GitStatusEntry};
|
||||
use crate::{
|
||||
git_panel::{GitPanel, GitPanelAddon, GitStatusEntry},
|
||||
remote_button::{render_publish_button, render_push_button},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus};
|
||||
use collections::HashSet;
|
||||
|
@ -9,8 +12,9 @@ use editor::{
|
|||
};
|
||||
use futures::StreamExt;
|
||||
use git::{
|
||||
repository::Branch, status::FileStatus, Commit, StageAll, StageAndNext, ToggleStaged,
|
||||
UnstageAll, UnstageAndNext,
|
||||
repository::{Branch, Upstream, UpstreamTracking, UpstreamTrackingStatus},
|
||||
status::FileStatus,
|
||||
Commit, StageAll, StageAndNext, ToggleStaged, UnstageAll, UnstageAndNext,
|
||||
};
|
||||
use gpui::{
|
||||
actions, Action, AnyElement, AnyView, App, AppContext as _, AsyncWindowContext, Entity,
|
||||
|
@ -1022,6 +1026,287 @@ impl Render for ProjectDiffToolbar {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(IntoElement, IntoComponent)]
|
||||
#[component(scope = "Version Control")]
|
||||
pub struct ProjectDiffEmptyState {
|
||||
pub no_repo: bool,
|
||||
pub can_push_and_pull: bool,
|
||||
pub focus_handle: Option<FocusHandle>,
|
||||
pub current_branch: Option<Branch>,
|
||||
// has_pending_commits: bool,
|
||||
// ahead_of_remote: bool,
|
||||
// no_git_repository: bool,
|
||||
}
|
||||
|
||||
impl RenderOnce for ProjectDiffEmptyState {
|
||||
fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
|
||||
let status_against_remote = |ahead_by: usize, behind_by: usize| -> bool {
|
||||
match self.current_branch {
|
||||
Some(Branch {
|
||||
upstream:
|
||||
Some(Upstream {
|
||||
tracking:
|
||||
UpstreamTracking::Tracked(UpstreamTrackingStatus {
|
||||
ahead, behind, ..
|
||||
}),
|
||||
..
|
||||
}),
|
||||
..
|
||||
}) if (ahead > 0) == (ahead_by > 0) && (behind > 0) == (behind_by > 0) => true,
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
|
||||
let change_count = |current_branch: &Branch| -> (usize, usize) {
|
||||
match current_branch {
|
||||
Branch {
|
||||
upstream:
|
||||
Some(Upstream {
|
||||
tracking:
|
||||
UpstreamTracking::Tracked(UpstreamTrackingStatus {
|
||||
ahead, behind, ..
|
||||
}),
|
||||
..
|
||||
}),
|
||||
..
|
||||
} => (*ahead as usize, *behind as usize),
|
||||
_ => (0, 0),
|
||||
}
|
||||
};
|
||||
|
||||
let not_ahead_or_behind = status_against_remote(0, 0);
|
||||
let ahead_of_remote = status_against_remote(1, 0);
|
||||
let branch_not_on_remote = if let Some(branch) = self.current_branch.as_ref() {
|
||||
branch.upstream.is_none()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
let has_branch_container = |branch: &Branch| {
|
||||
h_flex()
|
||||
.max_w(px(420.))
|
||||
.bg(cx.theme().colors().text.opacity(0.05))
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.rounded_sm()
|
||||
.gap_8()
|
||||
.px_6()
|
||||
.py_4()
|
||||
.map(|this| {
|
||||
if ahead_of_remote {
|
||||
let ahead_count = change_count(branch).0;
|
||||
let ahead_string = format!("{} Commits Ahead", ahead_count);
|
||||
this.child(
|
||||
v_flex()
|
||||
.child(Headline::new(ahead_string).size(HeadlineSize::Small))
|
||||
.child(
|
||||
Label::new(format!("Push your changes to {}", branch.name))
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
.child(div().child(render_push_button(
|
||||
self.focus_handle,
|
||||
"push".into(),
|
||||
ahead_count as u32,
|
||||
)))
|
||||
} else if branch_not_on_remote {
|
||||
this.child(
|
||||
v_flex()
|
||||
.child(Headline::new("Publish Branch").size(HeadlineSize::Small))
|
||||
.child(
|
||||
Label::new(format!("Create {} on remote", branch.name))
|
||||
.color(Color::Muted),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
div().child(render_publish_button(self.focus_handle, "publish".into())),
|
||||
)
|
||||
} else {
|
||||
this.child(Label::new("Remote status unknown").color(Color::Muted))
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
v_flex().size_full().items_center().justify_center().child(
|
||||
v_flex()
|
||||
.gap_1()
|
||||
.when(self.no_repo, |this| {
|
||||
// TODO: add git init
|
||||
this.text_center()
|
||||
.child(Label::new("No Repository").color(Color::Muted))
|
||||
})
|
||||
.map(|this| {
|
||||
if not_ahead_or_behind && self.current_branch.is_some() {
|
||||
this.text_center()
|
||||
.child(Label::new("No Changes").color(Color::Muted))
|
||||
} else {
|
||||
this.when_some(self.current_branch.as_ref(), |this, branch| {
|
||||
this.child(has_branch_container(&branch))
|
||||
})
|
||||
}
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// .when(self.can_push_and_pull, |this| {
|
||||
// let remote_button = crate::render_remote_button(
|
||||
// "project-diff-remote-button",
|
||||
// &branch,
|
||||
// self.focus_handle.clone(),
|
||||
// false,
|
||||
// );
|
||||
|
||||
// match remote_button {
|
||||
// Some(button) => {
|
||||
// this.child(h_flex().justify_around().child(button))
|
||||
// }
|
||||
// None => this.child(
|
||||
// h_flex()
|
||||
// .justify_around()
|
||||
// .child(Label::new("Remote up to date")),
|
||||
// ),
|
||||
// }
|
||||
// }),
|
||||
//
|
||||
// // .map(|this| {
|
||||
// this.child(h_flex().justify_around().mt_1().child(
|
||||
// Button::new("project-diff-close-button", "Close").when_some(
|
||||
// self.focus_handle.clone(),
|
||||
// |this, focus_handle| {
|
||||
// this.key_binding(KeyBinding::for_action_in(
|
||||
// &CloseActiveItem::default(),
|
||||
// &focus_handle,
|
||||
// window,
|
||||
// cx,
|
||||
// ))
|
||||
// .on_click(move |_, window, cx| {
|
||||
// window.focus(&focus_handle);
|
||||
// window
|
||||
// .dispatch_action(Box::new(CloseActiveItem::default()), cx);
|
||||
// })
|
||||
// },
|
||||
// ),
|
||||
// ))
|
||||
// }),
|
||||
|
||||
mod preview {
|
||||
use git::repository::{
|
||||
Branch, CommitSummary, Upstream, UpstreamTracking, UpstreamTrackingStatus,
|
||||
};
|
||||
use ui::prelude::*;
|
||||
|
||||
use super::ProjectDiffEmptyState;
|
||||
|
||||
// View this component preview using `workspace: open component-preview`
|
||||
impl ComponentPreview for ProjectDiffEmptyState {
|
||||
fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
|
||||
let unknown_upstream: Option<UpstreamTracking> = None;
|
||||
let ahead_of_upstream: Option<UpstreamTracking> = Some(
|
||||
UpstreamTrackingStatus {
|
||||
ahead: 2,
|
||||
behind: 0,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
|
||||
let not_ahead_or_behind_upstream: Option<UpstreamTracking> = Some(
|
||||
UpstreamTrackingStatus {
|
||||
ahead: 0,
|
||||
behind: 0,
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
|
||||
fn branch(upstream: Option<UpstreamTracking>) -> Branch {
|
||||
Branch {
|
||||
is_head: true,
|
||||
name: "some-branch".into(),
|
||||
upstream: upstream.map(|tracking| Upstream {
|
||||
ref_name: "origin/some-branch".into(),
|
||||
tracking,
|
||||
}),
|
||||
most_recent_commit: Some(CommitSummary {
|
||||
sha: "abc123".into(),
|
||||
subject: "Modify stuff".into(),
|
||||
commit_timestamp: 1710932954,
|
||||
has_parent: true,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
let no_repo_state = ProjectDiffEmptyState {
|
||||
no_repo: true,
|
||||
can_push_and_pull: false,
|
||||
focus_handle: None,
|
||||
current_branch: None,
|
||||
};
|
||||
|
||||
let no_changes_state = ProjectDiffEmptyState {
|
||||
no_repo: false,
|
||||
can_push_and_pull: true,
|
||||
focus_handle: None,
|
||||
current_branch: Some(branch(not_ahead_or_behind_upstream)),
|
||||
};
|
||||
|
||||
let ahead_of_upstream_state = ProjectDiffEmptyState {
|
||||
no_repo: false,
|
||||
can_push_and_pull: true,
|
||||
focus_handle: None,
|
||||
current_branch: Some(branch(ahead_of_upstream)),
|
||||
};
|
||||
|
||||
let unknown_upstream_state = ProjectDiffEmptyState {
|
||||
no_repo: false,
|
||||
can_push_and_pull: true,
|
||||
focus_handle: None,
|
||||
current_branch: Some(branch(unknown_upstream)),
|
||||
};
|
||||
|
||||
let (width, height) = (px(480.), px(320.));
|
||||
|
||||
v_flex()
|
||||
.gap_6()
|
||||
.children(vec![example_group(vec![
|
||||
single_example(
|
||||
"No Repo",
|
||||
div()
|
||||
.w(width)
|
||||
.h(height)
|
||||
.child(no_repo_state)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"No Changes",
|
||||
div()
|
||||
.w(width)
|
||||
.h(height)
|
||||
.child(no_changes_state)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Unknown Upstream",
|
||||
div()
|
||||
.w(width)
|
||||
.h(height)
|
||||
.child(unknown_upstream_state)
|
||||
.into_any_element(),
|
||||
),
|
||||
single_example(
|
||||
"Ahead of Remote",
|
||||
div()
|
||||
.w(width)
|
||||
.h(height)
|
||||
.child(ahead_of_upstream_state)
|
||||
.into_any_element(),
|
||||
),
|
||||
])
|
||||
.vertical()])
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue