Skip to content

Commit 3c99041

Browse files
committed
ref(ui): Improve member loading in actor avatar
Right now if the member isn't present in the store when the avatar renders we'll never end up showing the users avatar
1 parent f29fcd4 commit 3c99041

File tree

2 files changed

+53
-5
lines changed

2 files changed

+53
-5
lines changed

static/app/components/avatar/actorAvatar.spec.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,4 +96,32 @@ describe('ActorAvatar', function () {
9696
expect(await screen.findByText('CT')).toBeInTheDocument();
9797
expect(mockRequest).toHaveBeenCalled();
9898
});
99+
100+
it('should fetch a user not in the store', async function () {
101+
const organization = OrganizationFixture();
102+
103+
OrganizationStore.onUpdate(organization, {replace: true});
104+
105+
const user2 = UserFixture({id: '2', name: 'COOL USER'});
106+
107+
const mockRequest = MockApiClient.addMockResponse({
108+
url: `/organizations/${organization.slug}/members/`,
109+
method: 'GET',
110+
body: [{user: user2}],
111+
});
112+
113+
render(
114+
<ActorAvatar
115+
actor={{
116+
id: user2.id,
117+
name: user2.name,
118+
email: user2.email,
119+
type: 'user',
120+
}}
121+
/>
122+
);
123+
124+
expect(await screen.findByText('CU')).toBeInTheDocument();
125+
expect(mockRequest).toHaveBeenCalled();
126+
});
99127
});

static/app/components/avatar/actorAvatar.tsx

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
import {useMemo} from 'react';
12
import * as Sentry from '@sentry/react';
23

34
import TeamAvatar from 'sentry/components/avatar/teamAvatar';
45
import UserAvatar from 'sentry/components/avatar/userAvatar';
5-
import LoadingIndicator from 'sentry/components/loadingIndicator';
6-
import MemberListStore from 'sentry/stores/memberListStore';
6+
import Placeholder from 'sentry/components/placeholder';
77
import type {Actor} from 'sentry/types';
8+
import {useMembers} from 'sentry/utils/useMembers';
89
import {useTeamsById} from 'sentry/utils/useTeamsById';
910

1011
import type {BaseAvatarProps} from './baseAvatar';
@@ -24,12 +25,32 @@ function LoadTeamAvatar({
2425
const team = teams.find(t => t.id === teamId);
2526

2627
if (isLoading) {
27-
return <LoadingIndicator mini />;
28+
const size = `${props.size}px`;
29+
return <Placeholder width={size} height={size} />;
2830
}
2931

3032
return <TeamAvatar team={team} {...props} />;
3133
}
3234

35+
/**
36+
* Wrapper to assist loading the user from api or store
37+
*/
38+
function LoadMemberAvatar({
39+
userId,
40+
...props
41+
}: {userId: string} & Omit<React.ComponentProps<typeof UserAvatar>, 'team'>) {
42+
const ids = useMemo(() => [userId], [userId]);
43+
const {members, fetching} = useMembers({ids});
44+
const user = members.find(u => u.id === userId);
45+
46+
if (fetching) {
47+
const size = `${props.size}px`;
48+
return <Placeholder shape="circle" width={size} height={size} />;
49+
}
50+
51+
return <UserAvatar user={user} {...props} />;
52+
}
53+
3354
function ActorAvatar({size = 24, hasTooltip = true, actor, ...props}: Props) {
3455
const otherProps = {
3556
size,
@@ -38,8 +59,7 @@ function ActorAvatar({size = 24, hasTooltip = true, actor, ...props}: Props) {
3859
};
3960

4061
if (actor.type === 'user') {
41-
const user = actor.id ? MemberListStore.getById(actor.id) ?? actor : actor;
42-
return <UserAvatar user={user} {...otherProps} />;
62+
return <LoadMemberAvatar userId={actor.id} {...otherProps} />;
4363
}
4464

4565
if (actor.type === 'team') {

0 commit comments

Comments
 (0)