diff --git a/crates/git_ui/src/blame_ui.rs b/crates/git_ui/src/blame_ui.rs index f67d426ff3bf23df7e32af7aae109ad78642a674..09ab3229bc5b2b7814b89bbb914472407793a52d 100644 --- a/crates/git_ui/src/blame_ui.rs +++ b/crates/git_ui/src/blame_ui.rs @@ -47,11 +47,13 @@ impl BlameRenderer for GitBlameRenderer { let name = util::truncate_and_trailoff(author_name, GIT_BLAME_MAX_AUTHOR_CHARS_DISPLAYED); let avatar = if ProjectSettings::get_global(cx).git.blame.show_avatar { - CommitAvatar::new( - &blame_entry.sha.to_string().into(), - details.as_ref().and_then(|it| it.remote.as_ref()), + Some( + CommitAvatar::new( + &blame_entry.sha.to_string().into(), + details.as_ref().and_then(|it| it.remote.as_ref()), + ) + .render(window, cx), ) - .render(window, cx) } else { None }; @@ -264,7 +266,7 @@ impl BlameRenderer for GitBlameRenderer { .flex_wrap() .border_b_1() .border_color(cx.theme().colors().border_variant) - .children(avatar) + .child(avatar) .child(author) .when(!author_email.is_empty(), |this| { this.child( diff --git a/crates/git_ui/src/commit_tooltip.rs b/crates/git_ui/src/commit_tooltip.rs index 6dfe92427df5b9fd5aa051aeb1635b2e782ad3a4..cf6512b0763e128633cfa65f934d8ed18cd6d022 100644 --- a/crates/git_ui/src/commit_tooltip.rs +++ b/crates/git_ui/src/commit_tooltip.rs @@ -29,11 +29,16 @@ pub struct CommitDetails { pub struct CommitAvatar<'a> { sha: &'a SharedString, remote: Option<&'a GitRemote>, + size: Option, } impl<'a> CommitAvatar<'a> { pub fn new(sha: &'a SharedString, remote: Option<&'a GitRemote>) -> Self { - Self { sha, remote } + Self { + sha, + remote, + size: None, + } } pub fn from_commit_details(details: &'a CommitDetails) -> Self { @@ -43,28 +48,37 @@ impl<'a> CommitAvatar<'a> { .message .as_ref() .and_then(|details| details.remote.as_ref()), + size: None, } } -} -impl<'a> CommitAvatar<'a> { - pub fn render(&'a self, window: &mut Window, cx: &mut App) -> Option> { + pub fn size(mut self, size: IconSize) -> Self { + self.size = Some(size); + self + } + + pub fn render(&'a self, window: &mut Window, cx: &mut App) -> AnyElement { + match self.avatar(window, cx) { + // Loading or no avatar found + None => Icon::new(IconName::Person) + .color(Color::Muted) + .when_some(self.size, |this, size| this.size(size)) + .into_any_element(), + // Found + Some(avatar) => avatar + .when_some(self.size, |this, size| this.size(size.rems())) + .into_any_element(), + } + } + + pub fn avatar(&'a self, window: &mut Window, cx: &mut App) -> Option { let remote = self .remote .filter(|remote| remote.host_supports_avatars())?; - let avatar_url = CommitAvatarAsset::new(remote.clone(), self.sha.clone()); - let element = match window.use_asset::(&avatar_url, cx) { - // Loading or no avatar found - None | Some(None) => Icon::new(IconName::Person) - .color(Color::Muted) - .into_element() - .into_any(), - // Found - Some(Some(url)) => Avatar::new(url.to_string()).into_element().into_any(), - }; - Some(element) + let url = window.use_asset::(&avatar_url, cx)??; + Some(Avatar::new(url.to_string())) } } @@ -253,7 +267,7 @@ impl Render for CommitTooltip { .gap_x_2() .overflow_x_hidden() .flex_wrap() - .children(avatar) + .child(avatar) .child(author) .when(!author_email.is_empty(), |this| { this.child( diff --git a/crates/git_ui/src/commit_view.rs b/crates/git_ui/src/commit_view.rs index 30b4e3d986b12f4aba1c5487fac7500bb5cbe670..b83ad6d8a6ddab467eb32c31cbc67810b9f74247 100644 --- a/crates/git_ui/src/commit_view.rs +++ b/crates/git_ui/src/commit_view.rs @@ -5,8 +5,8 @@ use editor::{Editor, EditorEvent, ExcerptRange, MultiBuffer, multibuffer_context use git::repository::{CommitDetails, CommitDiff, RepoPath}; use git::{GitHostingProviderRegistry, GitRemote, parse_git_remote_url}; use gpui::{ - AnyElement, App, AppContext as _, Asset, AsyncApp, AsyncWindowContext, Context, Element, - Entity, EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement, ParentElement, + AnyElement, App, AppContext as _, AsyncApp, AsyncWindowContext, Context, Element, Entity, + EventEmitter, FocusHandle, Focusable, InteractiveElement, IntoElement, ParentElement, PromptLevel, Render, Styled, Task, WeakEntity, Window, actions, }; use language::{ @@ -21,7 +21,7 @@ use std::{ sync::Arc, }; use theme::ActiveTheme; -use ui::{Avatar, DiffStat, Tooltip, prelude::*}; +use ui::{DiffStat, Tooltip, prelude::*}; use util::{ResultExt, paths::PathStyle, rel_path::RelPath, truncate_and_trailoff}; use workspace::item::TabTooltipContent; use workspace::{ @@ -33,6 +33,7 @@ use workspace::{ searchable::SearchableItemHandle, }; +use crate::commit_tooltip::CommitAvatar; use crate::git_panel::GitPanel; actions!(git, [ApplyCurrentStash, PopCurrentStash, DropCurrentStash,]); @@ -318,17 +319,7 @@ impl CommitView { cx: &mut App, ) -> AnyElement { let size = size.into(); - let remote = self.remote.as_ref().filter(|r| r.host_supports_avatars()); - - if let Some(remote) = remote { - let avatar_asset = CommitAvatarAsset::new(remote.clone(), sha.clone()); - if let Some(Some(url)) = window.use_asset::(&avatar_asset, cx) { - return Avatar::new(url.to_string()) - .size(size) - .into_element() - .into_any(); - } - } + let avatar = CommitAvatar::new(sha, self.remote.as_ref()); v_flex() .w(size) @@ -339,10 +330,15 @@ impl CommitView { .justify_center() .items_center() .child( - Icon::new(IconName::Person) - .color(Color::Muted) - .size(IconSize::Medium) - .into_element(), + avatar + .avatar(window, cx) + .map(|a| a.size(size).into_any_element()) + .unwrap_or_else(|| { + Icon::new(IconName::Person) + .color(Color::Muted) + .size(IconSize::Medium) + .into_any_element() + }), ) .into_any() } @@ -647,54 +643,6 @@ impl CommitView { } } -#[derive(Clone, Debug)] -struct CommitAvatarAsset { - sha: SharedString, - remote: GitRemote, -} - -impl std::hash::Hash for CommitAvatarAsset { - fn hash(&self, state: &mut H) { - self.sha.hash(state); - self.remote.host.name().hash(state); - } -} - -impl CommitAvatarAsset { - fn new(remote: GitRemote, sha: SharedString) -> Self { - Self { remote, sha } - } -} - -impl Asset for CommitAvatarAsset { - type Source = Self; - type Output = Option; - - fn load( - source: Self::Source, - cx: &mut App, - ) -> impl Future + Send + 'static { - let client = cx.http_client(); - async move { - match source - .remote - .host - .commit_author_avatar_url( - &source.remote.owner, - &source.remote.repo, - source.sha.clone(), - client, - ) - .await - { - Ok(Some(url)) => Some(SharedString::from(url.to_string())), - Ok(None) => None, - Err(_) => None, - } - } - } -} - impl language::File for GitBlob { fn as_local(&self) -> Option<&dyn language::LocalFile> { None