Skip to content

Commit 77391d5

Browse files
Apply to Avatar too
1 parent 5509367 commit 77391d5

10 files changed

Lines changed: 90 additions & 73 deletions

File tree

src/components/Dialog/components/ContextMenu.tsx

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import React, {
1212
useRef,
1313
useState,
1414
} from 'react';
15-
import { Avatar, type AvatarProps } from '../../Avatar';
15+
import { type AvatarProps, Avatar as DefaultAvatar } from '../../Avatar';
1616
import { IconChevronLeft, IconChevronRight } from '../../Icons';
1717
import type { PopperLikePlacement } from '../hooks';
1818
import { useDialogIsOpen, useDialogOnNearestManager } from '../hooks';
@@ -141,23 +141,26 @@ export const UserContextMenuButton = ({
141141
role = 'menuitem',
142142
userName,
143143
...props
144-
}: UserContextMenuButtonProps) => (
145-
<button
146-
{...props}
147-
className={clsx(
148-
'str-chat__context-menu__button str-chat__user-context-menu__button',
149-
className,
150-
)}
151-
role={role}
152-
type='button'
153-
>
154-
{/* The avatar is decorative here: the button already carries the user's name as its
155-
label, so exposing the avatar (its fallback initials, title, and role="button")
156-
to assistive tech only adds noise to the option's accessible name. */}
157-
<Avatar aria-hidden imageUrl={imageUrl} size='sm' userName={userName} />
158-
<div className='str-chat__context-menu__button__label'>{children ?? userName}</div>
159-
</button>
160-
);
144+
}: UserContextMenuButtonProps) => {
145+
const { Avatar = DefaultAvatar } = useComponentContext();
146+
return (
147+
<button
148+
{...props}
149+
className={clsx(
150+
'str-chat__context-menu__button str-chat__user-context-menu__button',
151+
className,
152+
)}
153+
role={role}
154+
type='button'
155+
>
156+
{/* The avatar is decorative here: the button already carries the user's name as its
157+
label, so exposing the avatar (its fallback initials, title, and role="button")
158+
to assistive tech only adds noise to the option's accessible name. */}
159+
<Avatar aria-hidden imageUrl={imageUrl} size='sm' userName={userName} />
160+
<div className='str-chat__context-menu__button__label'>{children ?? userName}</div>
161+
</button>
162+
);
163+
};
161164

162165
export type EmojiContextMenuButtonProps = { emoji: string } & Pick<
163166
BaseContextMenuButtonProps,

src/components/Message/MessageUI.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ import { PinIndicator as DefaultPinIndicator } from './PinIndicator';
5151
import { QuotedMessage as DefaultQuotedMessage } from './QuotedMessage';
5252
import { MessageBubble } from './MessageBubble';
5353
import { ErrorBadge } from '../Badge';
54+
import { extractDisplayInfo as defaultExtractDisplayInfo } from '../Avatar/utils';
5455

5556
type MessageUIWithContextProps = MessageContextValue;
5657

@@ -79,6 +80,7 @@ const MessageUIWithContext = ({
7980
const {
8081
Attachment = DefaultAttachment,
8182
Avatar = DefaultAvatar,
83+
extractDisplayInfo = defaultExtractDisplayInfo,
8284
MessageActions = DefaultMessageActions,
8385
MessageAlsoSentInChannelIndicator = DefaultMessageAlsoSentInChannelIndicator,
8486
MessageBlocked = DefaultMessageBlocked,
@@ -210,12 +212,11 @@ const MessageUIWithContext = ({
210212
<MessageTranslationIndicator message={message} />
211213
{message.user && (
212214
<Avatar
215+
{...extractDisplayInfo({ user: message.user ?? undefined })}
213216
className='str-chat__avatar--with-border'
214-
imageUrl={message.user.image}
215217
onClick={onUserClick}
216218
onMouseOver={onUserHover}
217219
size='md'
218-
userName={message.user.name || message.user.id}
219220
/>
220221
)}
221222
<div

src/components/Poll/PollVote.tsx

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,13 @@
11
import React, { useState } from 'react';
2-
import { Avatar } from '../Avatar';
2+
import { Avatar as DefaultAvatar } from '../Avatar';
3+
import { extractDisplayInfo as defaultExtractDisplayInfo } from '../Avatar/utils';
34
import { PopperTooltip } from '../Tooltip';
45
import { useEnterLeaveHandlers } from '../Tooltip/hooks';
5-
import { useChatContext, useTranslationContext } from '../../context';
6+
import {
7+
useChatContext,
8+
useComponentContext,
9+
useTranslationContext,
10+
} from '../../context';
611

712
import type { PollVote as PollVoteType } from 'stream-chat';
813

@@ -41,6 +46,8 @@ type PollVoteAuthor = PollVoteProps;
4146
const PollVoteAuthor = ({ vote }: PollVoteAuthor) => {
4247
const { t } = useTranslationContext();
4348
const { client } = useChatContext();
49+
const { Avatar = DefaultAvatar, extractDisplayInfo = defaultExtractDisplayInfo } =
50+
useComponentContext();
4451
const displayName =
4552
client.user?.id && client.user.id === vote.user?.id
4653
? t('You')
@@ -50,11 +57,10 @@ const PollVoteAuthor = ({ vote }: PollVoteAuthor) => {
5057
<div className='str-chat__poll-vote__author'>
5158
{vote.user && (
5259
<Avatar
60+
{...extractDisplayInfo({ user: vote.user })}
5361
className='str-chat__avatar--poll-vote-author'
54-
imageUrl={vote.user.image}
5562
key={`poll-vote-${vote.id}-avatar-${vote.user.id}`}
5663
size='md'
57-
userName={vote.user.name}
5864
/>
5965
)}
6066
<div className='str-chat__poll-vote__author__name'>{displayName}</div>

src/components/Reactions/MessageReactionsDetail.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { ReactionSummary, ReactionType } from './types';
44

55
import { useFetchReactions } from './hooks/useFetchReactions';
66
import { Avatar as DefaultAvatar } from '../Avatar';
7+
import { extractDisplayInfo as defaultExtractDisplayInfo } from '../Avatar/utils';
78
import type { MessageContextValue } from '../../context';
89
import {
910
useChatContext,
@@ -66,6 +67,7 @@ export const MessageReactionsDetail: MessageReactionsDetailInterface = ({
6667
const { client } = useChatContext();
6768
const {
6869
Avatar = DefaultAvatar,
70+
extractDisplayInfo = defaultExtractDisplayInfo,
6971
LoadingIndicator = MessageReactionsDetailLoadingIndicator,
7072
reactionOptions = defaultReactionOptions,
7173
ReactionSelectorExtendedList = ReactionSelector.ExtendedList,
@@ -212,11 +214,10 @@ export const MessageReactionsDetail: MessageReactionsDetailInterface = ({
212214
key={`${user?.id}-${type}`}
213215
>
214216
<Avatar
217+
{...extractDisplayInfo({ user: user ?? undefined })}
215218
className='str-chat__avatar--with-border'
216219
data-testid='avatar'
217-
imageUrl={user?.image as string | undefined}
218220
size='md'
219-
userName={user?.name || user?.id}
220221
/>
221222
<div className='str-chat__message-reactions-detail__user-list-item-info'>
222223
<span

src/components/Search/SearchResults/SearchResultItem.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import type { ComponentType } from 'react';
44
import type { Channel, MessageResponse, User } from 'stream-chat';
55

66
import { useSearchContext } from '../SearchContext';
7-
import { Avatar } from '../../../components/Avatar';
7+
import { Avatar as DefaultAvatar } from '../../../components/Avatar';
8+
import { extractDisplayInfo as defaultExtractDisplayInfo } from '../../../components/Avatar/utils';
89
import { ChannelListItem } from '../../../components/ChannelListItem';
910
import {
1011
useChannelListContext,
1112
useChatContext,
13+
useComponentContext,
1214
useTranslationContext,
1315
} from '../../../context';
1416
import { DEFAULT_JUMP_TO_PAGE_SIZE } from '../../../constants/limits';
@@ -99,6 +101,8 @@ export const UserSearchResultItem = ({ item }: UserSearchResultItemProps) => {
99101
const { setChannels } = useChannelListContext();
100102
const { directMessagingChannelType } = useSearchContext();
101103
const { t } = useTranslationContext();
104+
const { Avatar = DefaultAvatar, extractDisplayInfo = defaultExtractDisplayInfo } =
105+
useComponentContext();
102106

103107
const onClick = useCallback(() => {
104108
const newChannel = client.channel(directMessagingChannelType, {
@@ -121,10 +125,9 @@ export const UserSearchResultItem = ({ item }: UserSearchResultItemProps) => {
121125
role='option'
122126
>
123127
<Avatar
124-
imageUrl={item.image}
128+
{...extractDisplayInfo({ user: item })}
125129
isOnline={item.online}
126130
size='xl'
127-
userName={item.name || item.id}
128131
/>
129132
<div className='str-chat__search-result-data'>
130133
<div className='str-chat__search-result__display-name'>

src/components/TextareaComposer/SuggestionList/MentionItem/UserItem.tsx

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import clsx from 'clsx';
22
import React, { useMemo } from 'react';
3-
import type { UserSuggestion } from 'stream-chat';
4-
import { Avatar } from '../../../Avatar';
3+
import type { UserResponse, UserSuggestion } from 'stream-chat';
4+
import { Avatar as DefaultAvatar } from '../../../Avatar';
5+
import { extractDisplayInfo as defaultExtractDisplayInfo } from '../../../Avatar/utils';
56
import { ListItemLayout } from '../../../ListItemLayout';
7+
import { useComponentContext } from '../../../../context';
68
import type { MentionItemComponentProps } from './types';
79
import {
810
type TokenizedSuggestionDisplayName,
@@ -25,24 +27,20 @@ export type UserItemProps = MentionItemComponentProps<
2527
* UI component for mentions rendered in suggestion list
2628
*/
2729
export const UserItem = ({ entity, focused, ...buttonProps }: UserItemProps) => {
30+
const { Avatar = DefaultAvatar, extractDisplayInfo = defaultExtractDisplayInfo } =
31+
useComponentContext();
2832
const hasEntity = !!Object.keys(entity).length;
2933

3034
const titleAttribute = entity.name || entity.id || '';
35+
const displayInfo = extractDisplayInfo({ user: entity });
3136
const LeadingSlot = useMemo(
3237
() =>
3338
function UserItemAvatar() {
3439
// Decorative: the option's accessible name already carries the user's name (title +
3540
// tokenized display name), so the avatar's fallback initials/role would only add noise.
36-
return (
37-
<Avatar
38-
aria-hidden
39-
imageUrl={entity.image}
40-
size='md'
41-
userName={titleAttribute}
42-
/>
43-
);
41+
return <Avatar {...displayInfo} aria-hidden size='md' />;
4442
},
45-
[entity.image, titleAttribute],
43+
[Avatar, displayInfo],
4644
);
4745

4846
if (!hasEntity) return null;

src/plugins/ChannelDetail/Views/ChannelMediaView/ChannelMediaView.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ import {
77
useTranslationContext,
88
} from '../../../../context';
99
import { formatTime } from '../../../../components/AudioPlayback';
10-
import { Avatar } from '../../../../components/Avatar';
10+
import { Avatar as DefaultAvatar } from '../../../../components/Avatar';
11+
import { extractDisplayInfo as defaultExtractDisplayInfo } from '../../../../components/Avatar/utils';
1112
import { Badge } from '../../../../components/Badge';
1213
import {
1314
type BaseImageProps,
@@ -51,6 +52,8 @@ const ChannelMediaGridItem = ({
5152
onClick,
5253
}: ChannelMediaGridItemProps) => {
5354
const { t } = useTranslationContext('ChannelMediaView');
55+
const { Avatar = DefaultAvatar, extractDisplayInfo = defaultExtractDisplayInfo } =
56+
useComponentContext();
5457
const displayName = getUserDisplayName(item.user);
5558
const mediaSrc =
5659
item.type === 'video'
@@ -84,11 +87,10 @@ const ChannelMediaGridItem = ({
8487
</div>
8588
)}
8689
<Avatar
90+
{...extractDisplayInfo({ user: item.user })}
8791
aria-hidden='true'
8892
className='str-chat__channel-detail__media-view__item__avatar'
89-
imageUrl={item.user?.image}
9093
size='sm'
91-
userName={displayName}
9294
/>
9395
{item.type === 'video' && (
9496
<Badge

src/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersAddView.tsx

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
import { type SearchSourceState, type UserResponse, UserSearchSource } from 'stream-chat';
22
import React, { useCallback, useEffect, useMemo, useState } from 'react';
33

4-
import { useChatContext, useTranslationContext } from '../../../../context';
4+
import {
5+
useChatContext,
6+
useComponentContext,
7+
useTranslationContext,
8+
} from '../../../../context';
59
import { useStateStore } from '../../../../store';
6-
import { Avatar } from '../../../../components/Avatar';
10+
import { Avatar as DefaultAvatar } from '../../../../components/Avatar';
11+
import { extractDisplayInfo as defaultExtractDisplayInfo } from '../../../../components/Avatar/utils';
712
import { Checkbox } from '../../../../components/Form';
813
import { IconMute } from '../../../../components/Icons';
914
import { ListItemLayout } from '../../../../components/ListItemLayout';
@@ -57,21 +62,17 @@ const ChannelMembersAddViewItem = ({
5762
user: UserResponse;
5863
}) => {
5964
const { t } = useTranslationContext();
65+
const { Avatar = DefaultAvatar, extractDisplayInfo = defaultExtractDisplayInfo } =
66+
useComponentContext();
6067
const displayName = getUserDisplayName(user);
68+
const displayInfo = extractDisplayInfo({ user });
6169

6270
const LeadingSlot = useMemo(
6371
() =>
6472
function MemberAvatar() {
65-
return (
66-
<Avatar
67-
imageUrl={user.image}
68-
isOnline={user.online}
69-
size='md'
70-
userName={displayName}
71-
/>
72-
);
73+
return <Avatar {...displayInfo} isOnline={user.online} size='md' />;
7374
},
74-
[displayName, user.image, user.online],
75+
[Avatar, displayInfo, user.online],
7576
);
7677

7778
const SelectableTrailingSlot = useMemo(

src/plugins/ChannelDetail/Views/ChannelMembersView/ChannelMembersBrowseView.tsx

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import type { ChannelMemberResponse } from 'stream-chat';
22
import React, { useCallback, useMemo } from 'react';
33

4-
import { useChatContext, useTranslationContext } from '../../../../context';
5-
import { Avatar } from '../../../../components/Avatar';
4+
import {
5+
useChatContext,
6+
useComponentContext,
7+
useTranslationContext,
8+
} from '../../../../context';
9+
import { Avatar as DefaultAvatar } from '../../../../components/Avatar';
10+
import { extractDisplayInfo as defaultExtractDisplayInfo } from '../../../../components/Avatar/utils';
611
import { IconMute } from '../../../../components/Icons';
712
import { ListItemLayout } from '../../../../components/ListItemLayout';
813
import { VirtualizedList } from '../../VirtualizedList';
914
import { Prompt } from '../../../../components/Dialog';
10-
import {
11-
getMemberDisplayName,
12-
getMemberUserId,
13-
getUserDisplayName,
14-
} from './ChannelMembersView.utils';
15+
import { getMemberDisplayName, getMemberUserId } from './ChannelMembersView.utils';
1516
import { ChannelDetailEmptyList } from '../../ChannelDetailEmptyList';
1617
import { ChannelDetailListLoadingIndicator } from '../../ChannelDetailListLoadingIndicator';
1718
import { ChannelDetailSearchInput } from '../../ChannelDetailSearchInput';
@@ -56,23 +57,19 @@ const ChannelMembersBrowseViewItem = ({
5657
onMemberSelect?: (member: ChannelMemberResponse) => void;
5758
}) => {
5859
const { t } = useTranslationContext();
60+
const { Avatar = DefaultAvatar, extractDisplayInfo = defaultExtractDisplayInfo } =
61+
useComponentContext();
5962
const user = member.user;
6063
const displayName = getMemberDisplayName(member);
6164
const roleTranslation = getMemberRoleTranslation(member, t);
65+
const displayInfo = extractDisplayInfo({ user: user ?? undefined });
6266

6367
const LeadingSlot = useMemo(
6468
() =>
6569
function MemberAvatar() {
66-
return (
67-
<Avatar
68-
imageUrl={user?.image}
69-
isOnline={user?.online}
70-
size='md'
71-
userName={getUserDisplayName(user)}
72-
/>
73-
);
70+
return <Avatar {...displayInfo} isOnline={user?.online} size='md' />;
7471
},
75-
[user],
72+
[Avatar, displayInfo, user],
7673
);
7774

7875
const TrailingSlot = useMemo(

src/plugins/ChannelDetail/Views/PinnedMessagesView/PinnedMessagesView.tsx

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ import React, { useCallback, useMemo } from 'react';
44
import {
55
useChannelActionContext,
66
useChatContext,
7+
useComponentContext,
78
useModalContext,
89
useTranslationContext,
910
} from '../../../../context';
1011
import { getDateString, isDate } from '../../../../i18n/utils';
11-
import { Avatar } from '../../../../components/Avatar';
12+
import { Avatar as DefaultAvatar } from '../../../../components/Avatar';
13+
import { extractDisplayInfo as defaultExtractDisplayInfo } from '../../../../components/Avatar/utils';
1214
import { ListItemLayout } from '../../../../components/ListItemLayout';
1315
import { VirtualizedList } from '../../VirtualizedList';
1416
import { Prompt } from '../../../../components/Dialog';
@@ -82,14 +84,17 @@ const PinnedMessagesViewItem = ({
8284
onSelect: (message: PinnedMessage) => void;
8385
}) => {
8486
const { t } = useTranslationContext();
87+
const { Avatar = DefaultAvatar, extractDisplayInfo = defaultExtractDisplayInfo } =
88+
useComponentContext();
8589
const displayName = getUserDisplayName(message.user ?? undefined);
90+
const displayInfo = extractDisplayInfo({ user: message.user ?? undefined });
8691

8792
const LeadingSlot = useMemo(
8893
() =>
8994
function MessageAuthorAvatar() {
90-
return <Avatar imageUrl={message.user?.image} size='md' userName={displayName} />;
95+
return <Avatar {...displayInfo} size='md' />;
9196
},
92-
[displayName, message.user?.image],
97+
[Avatar, displayInfo],
9398
);
9499

95100
const TrailingSlot = useMemo(

0 commit comments

Comments
 (0)