commit view: Reuse avatar asset (#44554)
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user