add avatar to tab
This commit is contained in:
+3
-1
@@ -6,10 +6,12 @@
|
|||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- Show an avatar in each panel's tab, using the repository owner's profile picture when set and a pixel avatar otherwise
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Migrate the GPUI foundation to the published `gpui-pre` crates and GPUI Kit 0.6, off the zed and gpui-component git pins
|
- Migrate the GPUI foundation to the published `gpui-pre` crates and GPUI Kit 0.6, off the zed and gpui-component git pins
|
||||||
- Redesign the dock tab bar: the active tab is a muted grey, each tab holds a close button that shows on hover or when active, double-clicking a tab zooms its panel, and the trailing panel toolbar is gone
|
- Redesign the dock tab bar, using muted grey active tab, added close buttons, double-click to zoom, and removed panel toolbar
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
|||||||
@@ -18,10 +18,12 @@ use gpui_component::{
|
|||||||
ActiveTheme, Sizable, StyledExt, VirtualListScrollHandle, h_flex, v_flex, v_virtual_list,
|
ActiveTheme, Sizable, StyledExt, VirtualListScrollHandle, h_flex, v_flex, v_virtual_list,
|
||||||
};
|
};
|
||||||
use signed_git::{CommitDiff, DiffHunk, DiffLine, DiffLineKind, DiffStatus, FileCommit, FileDiff};
|
use signed_git::{CommitDiff, DiffHunk, DiffLine, DiffLineKind, DiffStatus, FileCommit, FileDiff};
|
||||||
|
use signed_state::RepoStore;
|
||||||
use signed_ui::{placeholder, tree_row};
|
use signed_ui::{placeholder, tree_row};
|
||||||
use utils::relative_time_secs;
|
use utils::relative_time_secs;
|
||||||
|
|
||||||
use crate::views::tree::{build_tree_items, tree_items};
|
use crate::views::tree::{build_tree_items, tree_items};
|
||||||
|
use crate::views::{repo_tab_avatar, tab_title};
|
||||||
|
|
||||||
const TREE_WIDTH: f32 = 260.;
|
const TREE_WIDTH: f32 = 260.;
|
||||||
|
|
||||||
@@ -293,6 +295,7 @@ impl Render for DiffPane {
|
|||||||
|
|
||||||
pub struct CommitDiffView {
|
pub struct CommitDiffView {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
|
store: Entity<RepoStore>,
|
||||||
worktree: PathBuf,
|
worktree: PathBuf,
|
||||||
repo_name: SharedString,
|
repo_name: SharedString,
|
||||||
commit: FileCommit,
|
commit: FileCommit,
|
||||||
@@ -304,6 +307,7 @@ pub struct CommitDiffView {
|
|||||||
|
|
||||||
impl CommitDiffView {
|
impl CommitDiffView {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
|
store: Entity<RepoStore>,
|
||||||
worktree: PathBuf,
|
worktree: PathBuf,
|
||||||
repo_name: SharedString,
|
repo_name: SharedString,
|
||||||
commit_id: String,
|
commit_id: String,
|
||||||
@@ -319,6 +323,7 @@ impl CommitDiffView {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
|
store,
|
||||||
worktree,
|
worktree,
|
||||||
repo_name,
|
repo_name,
|
||||||
commit: FileCommit {
|
commit: FileCommit {
|
||||||
@@ -465,11 +470,11 @@ impl BasePanel for CommitDiffView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Panel for CommitDiffView {
|
impl Panel for CommitDiffView {
|
||||||
fn title(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
fn title(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
div().text_sm().child(SharedString::from(format!(
|
let avatar = repo_tab_avatar(self.store.read(cx), cx);
|
||||||
"{}/{}",
|
let label = SharedString::from(format!("{}/{}", self.repo_name, self.commit.id));
|
||||||
self.repo_name, self.commit.id
|
|
||||||
)))
|
tab_title(avatar, label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ use signed_state::{
|
|||||||
use signed_ui::{CountBadge, UserAvatar};
|
use signed_ui::{CountBadge, UserAvatar};
|
||||||
use utils::relative_time;
|
use utils::relative_time;
|
||||||
|
|
||||||
use super::{RepoItem, open_repo_item};
|
use super::{RepoItem, open_repo_item, panel_avatar, tab_title};
|
||||||
|
|
||||||
const LIST_OVERDRAW: Pixels = px(400.);
|
const LIST_OVERDRAW: Pixels = px(400.);
|
||||||
const MAX_SUB_ACTIVITIES: usize = 5;
|
const MAX_SUB_ACTIVITIES: usize = 5;
|
||||||
@@ -619,7 +619,7 @@ impl BasePanel for InboxView {
|
|||||||
|
|
||||||
impl Panel for InboxView {
|
impl Panel for InboxView {
|
||||||
fn title(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
fn title(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
div().text_sm().child(SharedString::from("Inbox"))
|
tab_title(panel_avatar("inbox"), SharedString::from("Inbox"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ use signed_ui::{UserAvatar, placeholder, status_badge};
|
|||||||
use utils::relative_time;
|
use utils::relative_time;
|
||||||
|
|
||||||
use crate::views::discussion::{comment_form, comments_section, issue_roots, sidebar_section};
|
use crate::views::discussion::{comment_form, comments_section, issue_roots, sidebar_section};
|
||||||
|
use crate::views::{repo_tab_avatar, tab_title};
|
||||||
|
|
||||||
pub struct IssueDetailView {
|
pub struct IssueDetailView {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
@@ -65,7 +66,9 @@ impl Panel for IssueDetailView {
|
|||||||
})
|
})
|
||||||
.unwrap_or_else(|| SharedString::from("Issue"));
|
.unwrap_or_else(|| SharedString::from("Issue"));
|
||||||
|
|
||||||
div().text_sm().child(short_id)
|
let avatar = repo_tab_avatar(self.store.read(cx), cx);
|
||||||
|
|
||||||
|
tab_title(avatar, short_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ pub(super) mod detail;
|
|||||||
|
|
||||||
use self::detail::IssueDetailView;
|
use self::detail::IssueDetailView;
|
||||||
use super::status_list::{StatusCounts, filter_by_status};
|
use super::status_list::{StatusCounts, filter_by_status};
|
||||||
|
use crate::views::{repo_tab_avatar, tab_title};
|
||||||
|
|
||||||
const ISSUE_ROW_HEIGHT: f32 = 73.;
|
const ISSUE_ROW_HEIGHT: f32 = 73.;
|
||||||
|
|
||||||
@@ -333,8 +334,11 @@ impl BasePanel for IssuesView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Panel for IssuesView {
|
impl Panel for IssuesView {
|
||||||
fn title(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
fn title(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
div().child(SharedString::from(format!("{}/issues", self.repo_name)))
|
let avatar = repo_tab_avatar(self.store.read(cx), cx);
|
||||||
|
let label = SharedString::from(format!("{}/issues", self.repo_name));
|
||||||
|
|
||||||
|
tab_title(avatar, label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,8 +11,53 @@ pub(crate) mod sidebar;
|
|||||||
mod status_list;
|
mod status_list;
|
||||||
pub(crate) mod tree;
|
pub(crate) mod tree;
|
||||||
|
|
||||||
|
use gpui::prelude::*;
|
||||||
|
use gpui::{AnyElement, App, div};
|
||||||
|
use gpui_component::{Sizable as _, h_flex};
|
||||||
pub use inbox::InboxView;
|
pub use inbox::InboxView;
|
||||||
pub use repo::RepoDetailView;
|
pub use repo::RepoDetailView;
|
||||||
pub(crate) use repo::{RepoItem, open_repo_item, open_repo_panel};
|
pub(crate) use repo::{RepoItem, open_repo_item, open_repo_panel};
|
||||||
pub use repo_list::RepoListView;
|
pub use repo_list::RepoListView;
|
||||||
pub use sidebar::SidebarPanel;
|
pub use sidebar::SidebarPanel;
|
||||||
|
use signed_state::{ProfileStore, RepoStore};
|
||||||
|
use signed_ui::{PixelAvatar, UserAvatar};
|
||||||
|
|
||||||
|
pub(crate) fn tab_title(avatar: AnyElement, label: impl IntoElement) -> impl IntoElement {
|
||||||
|
h_flex()
|
||||||
|
.gap_1()
|
||||||
|
.items_center()
|
||||||
|
.child(div().flex_shrink_0().child(avatar))
|
||||||
|
.child(label)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn panel_avatar(seed: impl AsRef<str>) -> AnyElement {
|
||||||
|
PixelAvatar::new(seed).xsmall().into_any_element()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn repo_tab_avatar(store: &RepoStore, cx: &App) -> AnyElement {
|
||||||
|
let seed = store
|
||||||
|
.announcement
|
||||||
|
.as_ref()
|
||||||
|
.map(|announcement| format!("{}:{}", announcement.owner, announcement.id))
|
||||||
|
.or_else(|| store.addr().map(|addr| addr.to_string()))
|
||||||
|
.or_else(|| {
|
||||||
|
store
|
||||||
|
.path
|
||||||
|
.as_ref()
|
||||||
|
.map(|path| path.to_string_lossy().into_owned())
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let owner = store
|
||||||
|
.announcement
|
||||||
|
.as_ref()
|
||||||
|
.map(|announcement| ProfileStore::global(cx).read(cx).get(&announcement.owner));
|
||||||
|
|
||||||
|
match owner.and_then(|profile| profile.picture().map(|picture| (profile.name(), picture))) {
|
||||||
|
Some((name, picture)) => UserAvatar::new(name)
|
||||||
|
.picture(Some(picture))
|
||||||
|
.xsmall()
|
||||||
|
.into_any_element(),
|
||||||
|
None => PixelAvatar::new(seed).xsmall().into_any_element(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ use utils::{relative_time, relative_time_secs};
|
|||||||
|
|
||||||
use crate::views::commit_diff::{CommitDiffView, DiffPane};
|
use crate::views::commit_diff::{CommitDiffView, DiffPane};
|
||||||
use crate::views::discussion::{comment_form, comments_section, pr_roots, sidebar_section};
|
use crate::views::discussion::{comment_form, comments_section, pr_roots, sidebar_section};
|
||||||
|
use crate::views::{repo_tab_avatar, tab_title};
|
||||||
|
|
||||||
const ROW_HEIGHT: f32 = 37.;
|
const ROW_HEIGHT: f32 = 37.;
|
||||||
|
|
||||||
@@ -343,6 +344,7 @@ impl PullRequestDetailView {
|
|||||||
|
|
||||||
let panel = cx.new(|cx| {
|
let panel = cx.new(|cx| {
|
||||||
CommitDiffView::new(
|
CommitDiffView::new(
|
||||||
|
self.store.clone(),
|
||||||
worktree,
|
worktree,
|
||||||
self.repo_name.clone(),
|
self.repo_name.clone(),
|
||||||
commit_id.into(),
|
commit_id.into(),
|
||||||
@@ -788,15 +790,17 @@ impl BasePanel for PullRequestDetailView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Panel for PullRequestDetailView {
|
impl Panel for PullRequestDetailView {
|
||||||
fn title(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
fn title(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let avatar = repo_tab_avatar(self.store.read(cx), cx);
|
||||||
let hex = self.pr_id.to_hex();
|
let hex = self.pr_id.to_hex();
|
||||||
let id = SharedString::from(&hex[..8]);
|
let id = SharedString::from(&hex[..8]);
|
||||||
let title = if self.repo_name.is_empty() {
|
let label = if self.repo_name.is_empty() {
|
||||||
id
|
id
|
||||||
} else {
|
} else {
|
||||||
SharedString::from(format!("{}/{}", self.repo_name, id))
|
SharedString::from(format!("{}/{}", self.repo_name, id))
|
||||||
};
|
};
|
||||||
div().text_sm().child(title)
|
|
||||||
|
tab_title(avatar, label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ use self::new::open_new_pull_panel;
|
|||||||
use super::send_patch::open_send_patch_panel;
|
use super::send_patch::open_send_patch_panel;
|
||||||
use super::status_list::{StatusCounts, filter_by_status};
|
use super::status_list::{StatusCounts, filter_by_status};
|
||||||
use crate::views::repo::RepoAction;
|
use crate::views::repo::RepoAction;
|
||||||
|
use crate::views::{repo_tab_avatar, tab_title};
|
||||||
|
|
||||||
const ROW_HEIGHT: f32 = 73.;
|
const ROW_HEIGHT: f32 = 73.;
|
||||||
|
|
||||||
@@ -333,11 +334,11 @@ impl BasePanel for PullRequestsView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Panel for PullRequestsView {
|
impl Panel for PullRequestsView {
|
||||||
fn title(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
fn title(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
div().child(SharedString::from(format!(
|
let avatar = repo_tab_avatar(self.store.read(cx), cx);
|
||||||
"{}/pull-requests",
|
let label = SharedString::from(format!("{}/pulls", self.repo_name));
|
||||||
self.repo_name
|
|
||||||
)))
|
tab_title(avatar, label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ use signed_state::{
|
|||||||
use signed_ui::{CountBadge, placeholder, ref_selector_trigger};
|
use signed_ui::{CountBadge, placeholder, ref_selector_trigger};
|
||||||
|
|
||||||
use crate::views::commit_diff::{COMMIT_ROW_HEIGHT, CommitDiffView, DiffPane, commit_row};
|
use crate::views::commit_diff::{COMMIT_ROW_HEIGHT, CommitDiffView, DiffPane, commit_row};
|
||||||
|
use crate::views::{repo_tab_avatar, tab_title};
|
||||||
|
|
||||||
pub struct NewPullRequestView {
|
pub struct NewPullRequestView {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
@@ -924,6 +925,7 @@ impl NewPullRequestView {
|
|||||||
|
|
||||||
let panel = cx.new(|cx| {
|
let panel = cx.new(|cx| {
|
||||||
CommitDiffView::new(
|
CommitDiffView::new(
|
||||||
|
self.store.clone(),
|
||||||
repo_path,
|
repo_path,
|
||||||
self.repo_name.clone(),
|
self.repo_name.clone(),
|
||||||
commit_id.into(),
|
commit_id.into(),
|
||||||
@@ -1323,11 +1325,11 @@ impl BasePanel for NewPullRequestView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Panel for NewPullRequestView {
|
impl Panel for NewPullRequestView {
|
||||||
fn title(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
fn title(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
div().child(SharedString::from(format!(
|
let avatar = repo_tab_avatar(self.store.read(cx), cx);
|
||||||
"{}/new-pull-request",
|
let label = SharedString::from(format!("{}/new-pull-request", self.repo_name));
|
||||||
self.repo_name
|
|
||||||
)))
|
tab_title(avatar, label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -106,8 +106,10 @@ impl RepoHistoryView {
|
|||||||
// Same display name as the repo detail panel's title.
|
// Same display name as the repo detail panel's title.
|
||||||
let repo_name = repo_display_name(self.store.read(cx));
|
let repo_name = repo_display_name(self.store.read(cx));
|
||||||
|
|
||||||
let panel =
|
let store = self.store.clone();
|
||||||
cx.new(|cx| CommitDiffView::new(worktree, repo_name, commit_id.into(), window, cx));
|
let panel = cx.new(|cx| {
|
||||||
|
CommitDiffView::new(store, worktree, repo_name, commit_id.into(), window, cx)
|
||||||
|
});
|
||||||
|
|
||||||
dock_area.update(cx, |dock_area, cx| {
|
dock_area.update(cx, |dock_area, cx| {
|
||||||
add_center_panel(dock_area, panel_handle(panel), window, cx);
|
add_center_panel(dock_area, panel_handle(panel), window, cx);
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ use crate::views::pull_requests::new::open_new_pull_panel;
|
|||||||
use crate::views::repo::about::open_about_dialog;
|
use crate::views::repo::about::open_about_dialog;
|
||||||
use crate::views::send_patch::open_send_patch_panel;
|
use crate::views::send_patch::open_send_patch_panel;
|
||||||
use crate::views::tree::{TreeItemSeed, build_tree_items, sorted_worktree_paths};
|
use crate::views::tree::{TreeItemSeed, build_tree_items, sorted_worktree_paths};
|
||||||
|
use crate::views::{repo_tab_avatar, tab_title};
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
enum RefKind {
|
enum RefKind {
|
||||||
@@ -2054,7 +2055,11 @@ impl BasePanel for RepoDetailView {
|
|||||||
|
|
||||||
impl Panel for RepoDetailView {
|
impl Panel for RepoDetailView {
|
||||||
fn title(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
fn title(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
repo_display_name(self.store.read(cx))
|
let store = self.store.read(cx);
|
||||||
|
let name = repo_display_name(store);
|
||||||
|
let avatar = repo_tab_avatar(store, cx);
|
||||||
|
|
||||||
|
tab_title(avatar, name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use signed_state::{ProfileStore, RepoListStore, Timestamp};
|
|||||||
use signed_ui::{SegmentButton, UserAvatar};
|
use signed_ui::{SegmentButton, UserAvatar};
|
||||||
use utils::relative_time;
|
use utils::relative_time;
|
||||||
|
|
||||||
use super::open_repo_panel;
|
use super::{open_repo_panel, panel_avatar, tab_title};
|
||||||
|
|
||||||
const COLUMNS: usize = 2;
|
const COLUMNS: usize = 2;
|
||||||
const CARD_HEIGHT: f32 = 40. + 64. + 48. + 2. + 6.;
|
const CARD_HEIGHT: f32 = 40. + 64. + 48. + 2. + 6.;
|
||||||
@@ -389,7 +389,7 @@ impl BasePanel for RepoListView {
|
|||||||
|
|
||||||
impl Panel for RepoListView {
|
impl Panel for RepoListView {
|
||||||
fn title(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
fn title(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
div().text_sm().child(SharedString::from("Explore"))
|
tab_title(panel_avatar("explore"), SharedString::from("Explore"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ use gpui_component::spinner::Spinner;
|
|||||||
use gpui_component::{ActiveTheme, Icon, IconName, Sizable, h_flex, v_flex};
|
use gpui_component::{ActiveTheme, Icon, IconName, Sizable, h_flex, v_flex};
|
||||||
use signed_state::RepoStore;
|
use signed_state::RepoStore;
|
||||||
|
|
||||||
|
use crate::views::{repo_tab_avatar, tab_title};
|
||||||
|
|
||||||
pub struct SendPatchView {
|
pub struct SendPatchView {
|
||||||
focus_handle: FocusHandle,
|
focus_handle: FocusHandle,
|
||||||
dock_area: WeakEntity<DockArea>,
|
dock_area: WeakEntity<DockArea>,
|
||||||
@@ -205,8 +207,11 @@ impl BasePanel for SendPatchView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Panel for SendPatchView {
|
impl Panel for SendPatchView {
|
||||||
fn title(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
fn title(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
div().child(SharedString::from(format!("{}/send-patch", self.repo_name)))
|
let avatar = repo_tab_avatar(self.store.read(cx), cx);
|
||||||
|
let label = SharedString::from(format!("{}/send-patch", self.repo_name));
|
||||||
|
|
||||||
|
tab_title(avatar, label)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user