@@ -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(
@@ -29,11 +29,16 @@ pub struct CommitDetails {
pub struct CommitAvatar<'a> {
sha: &'a SharedString,
remote: Option<&'a GitRemote>,
+ size: Option<IconSize>,
}
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<impl IntoElement + use<>> {
+ 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<Avatar> {
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::<CommitAvatarAsset>(&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::<CommitAvatarAsset>(&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(
@@ -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::<CommitAvatarAsset>(&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<H: std::hash::Hasher>(&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<SharedString>;
-
- fn load(
- source: Self::Source,
- cx: &mut App,
- ) -> impl Future<Output = Self::Output> + 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