[Refactoring] Simplify avatars (#3365)
This commit is contained in:
parent
bb31151402
commit
05ff8ce87a
@ -29,14 +29,12 @@ type OwnProps = {
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
user?: ApiUser;
|
||||
chat?: ApiChat;
|
||||
peer?: ApiUser | ApiChat;
|
||||
};
|
||||
|
||||
const GroupCallParticipant: FC<OwnProps & StateProps> = ({
|
||||
participant,
|
||||
user,
|
||||
chat,
|
||||
peer,
|
||||
}) => {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
@ -124,13 +122,13 @@ const GroupCallParticipant: FC<OwnProps & StateProps> = ({
|
||||
isMutedByMe, isRaiseHand, isSelf, hasCustomVolume, isMuted, isSpeaking, participant.about, participant.volume, lang,
|
||||
]);
|
||||
|
||||
if (!user && !chat) {
|
||||
if (!peer) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
leftElement={<Avatar user={user} chat={chat} className={styles.avatar} />}
|
||||
leftElement={<Avatar peer={peer} className={styles.avatar} />}
|
||||
rightElement={<OutlinedMicrophoneIcon participant={participant} className={styles.icon} />}
|
||||
className={styles.root}
|
||||
onClick={handleContextMenu}
|
||||
@ -140,7 +138,7 @@ const GroupCallParticipant: FC<OwnProps & StateProps> = ({
|
||||
ripple
|
||||
ref={ref}
|
||||
>
|
||||
<FullNameTitle peer={user || chat!} withEmojiStatus className={styles.title} />
|
||||
<FullNameTitle peer={peer} withEmojiStatus className={styles.title} />
|
||||
<span className={buildClassName(styles.subtitle, 'subtitle', aboutColor)}>
|
||||
{hasPresentationStream && <i className="icon icon-share-screen" aria-hidden />}
|
||||
{hasVideoStream && <i className="icon icon-video" aria-hidden />}
|
||||
@ -166,8 +164,7 @@ const GroupCallParticipant: FC<OwnProps & StateProps> = ({
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { participant }): StateProps => {
|
||||
return {
|
||||
user: participant.isUser ? selectUser(global, participant.id) : undefined,
|
||||
chat: !participant.isUser ? selectChat(global, participant.id) : undefined,
|
||||
peer: selectUser(global, participant.id) || selectChat(global, participant.id),
|
||||
};
|
||||
},
|
||||
)(GroupCallParticipant));
|
||||
|
||||
@ -27,6 +27,8 @@ type StateProps = {
|
||||
isActive: boolean;
|
||||
};
|
||||
|
||||
const PREVIEW_AVATARS_COUNT = 3;
|
||||
|
||||
const GroupCallTopPane: FC<OwnProps & StateProps> = ({
|
||||
chatId,
|
||||
isActive,
|
||||
@ -58,19 +60,10 @@ const GroupCallTopPane: FC<OwnProps & StateProps> = ({
|
||||
const usersById = getGlobal().users.byId;
|
||||
const chatsById = getGlobal().chats.byId;
|
||||
|
||||
return Object.values(participants).filter((_, i) => i < 3).map(({ id, isUser }) => {
|
||||
if (isUser) {
|
||||
if (!usersById[id]) {
|
||||
return undefined;
|
||||
}
|
||||
return { user: usersById[id] };
|
||||
} else {
|
||||
if (!chatsById[id]) {
|
||||
return undefined;
|
||||
}
|
||||
return { chat: chatsById[id] };
|
||||
}
|
||||
}).filter(Boolean);
|
||||
return Object.values(participants)
|
||||
.slice(0, PREVIEW_AVATARS_COUNT)
|
||||
.map(({ id }) => usersById[id] || chatsById[id])
|
||||
.filter(Boolean);
|
||||
}, [participants]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -107,17 +100,12 @@ const GroupCallTopPane: FC<OwnProps & StateProps> = ({
|
||||
<span className="participants">{lang('Participants', groupCall.participantsCount || 0, 'i')}</span>
|
||||
</div>
|
||||
<div className="avatars">
|
||||
{fetchedParticipants.map((p) => {
|
||||
if (!p) return undefined;
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
key={p.user ? p.user.id : p.chat.id}
|
||||
chat={p.chat}
|
||||
user={p.user}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
{fetchedParticipants.map((peer) => (
|
||||
<Avatar
|
||||
key={peer.id}
|
||||
peer={peer}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<Button round className="join">
|
||||
{lang('VoipChatJoin')}
|
||||
|
||||
@ -234,7 +234,7 @@ const PhoneCall: FC<StateProps> = ({
|
||||
dialogRef={containerRef}
|
||||
>
|
||||
<Avatar
|
||||
user={user}
|
||||
peer={user}
|
||||
size="jumbo"
|
||||
className={hasVideo || hasPresentation ? styles.blurred : ''}
|
||||
/>
|
||||
|
||||
@ -7,16 +7,16 @@
|
||||
width: 3.375rem;
|
||||
height: 3.375rem;
|
||||
border-radius: var(--radius);
|
||||
background: linear-gradient(var(--color-white) -125%, var(--color-user));
|
||||
background-image: linear-gradient(var(--color-white) -125%, var(--color-user));
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
white-space: nowrap;
|
||||
user-select: none;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&__media {
|
||||
border-radius: var(--radius);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@ -124,29 +124,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.online::after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
bottom: 0.0625rem;
|
||||
right: 0.0625rem;
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--color-background);
|
||||
background-color: #0ac630;
|
||||
flex-shrink: 0;
|
||||
|
||||
opacity: 0.5;
|
||||
transform: scale(0);
|
||||
transition: opacity 200ms, transform 200ms;
|
||||
}
|
||||
|
||||
&.online-open::after {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
&.interactive {
|
||||
cursor: var(--custom-cursor, pointer);
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import type { MouseEvent as ReactMouseEvent } from 'react';
|
||||
import React, {
|
||||
memo, useMemo, useRef,
|
||||
memo, useRef,
|
||||
} from '../../lib/teact/teact';
|
||||
|
||||
import type { FC, TeactNode } from '../../lib/teact/teact';
|
||||
import type {
|
||||
ApiChat, ApiPhoto, ApiUser, ApiUserStatus,
|
||||
ApiChat, ApiPhoto, ApiUser,
|
||||
} from '../../api/types';
|
||||
import type { ObserveFn } from '../../hooks/useIntersectionObserver';
|
||||
import { ApiMediaFormat } from '../../api/types';
|
||||
@ -19,7 +19,6 @@ import {
|
||||
isUserId,
|
||||
isChatWithRepliesBot,
|
||||
isDeletedUser,
|
||||
isUserOnline,
|
||||
} from '../../global/helpers';
|
||||
import { getFirstLetters } from '../../util/textFormat';
|
||||
import buildClassName, { createClassNameBuilder } from '../../util/buildClassName';
|
||||
@ -44,10 +43,8 @@ cn.icon = cn('icon');
|
||||
type OwnProps = {
|
||||
className?: string;
|
||||
size?: 'micro' | 'tiny' | 'mini' | 'small' | 'small-mobile' | 'medium' | 'large' | 'jumbo';
|
||||
chat?: ApiChat;
|
||||
user?: ApiUser;
|
||||
peer?: ApiChat | ApiUser;
|
||||
photo?: ApiPhoto;
|
||||
userStatus?: ApiUserStatus;
|
||||
text?: string;
|
||||
isSavedMessages?: boolean;
|
||||
withVideo?: boolean;
|
||||
@ -60,10 +57,8 @@ type OwnProps = {
|
||||
const Avatar: FC<OwnProps> = ({
|
||||
className,
|
||||
size = 'large',
|
||||
chat,
|
||||
user,
|
||||
peer,
|
||||
photo,
|
||||
userStatus,
|
||||
text,
|
||||
isSavedMessages,
|
||||
withVideo,
|
||||
@ -74,8 +69,10 @@ const Avatar: FC<OwnProps> = ({
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const videoLoopCountRef = useRef(0);
|
||||
const user = peer && isUserId(peer.id) ? peer as ApiUser : undefined;
|
||||
const chat = peer && !isUserId(peer.id) ? peer as ApiChat : undefined;
|
||||
const isDeleted = user && isDeletedUser(user);
|
||||
const isReplies = user && isChatWithRepliesBot(user.id);
|
||||
const isReplies = peer && isChatWithRepliesBot(peer.id);
|
||||
const isForum = chat?.isForum;
|
||||
let imageHash: string | undefined;
|
||||
let videoHash: string | undefined;
|
||||
@ -84,10 +81,8 @@ const Avatar: FC<OwnProps> = ({
|
||||
|
||||
const shouldFetchBig = size === 'jumbo';
|
||||
if (!isSavedMessages && !isDeleted) {
|
||||
if (user && !noPersonalPhoto) {
|
||||
imageHash = getChatAvatarHash(user, shouldFetchBig ? 'big' : undefined);
|
||||
} else if (chat) {
|
||||
imageHash = getChatAvatarHash(chat, shouldFetchBig ? 'big' : undefined);
|
||||
if ((user && !noPersonalPhoto) || chat) {
|
||||
imageHash = getChatAvatarHash(peer!, shouldFetchBig ? 'big' : undefined);
|
||||
} else if (photo) {
|
||||
imageHash = `photo${photo.id}?size=m`;
|
||||
if (photo.isVideo && withVideo) {
|
||||
@ -104,12 +99,6 @@ const Avatar: FC<OwnProps> = ({
|
||||
|
||||
const transitionClassNames = useMediaTransition(hasBlobUrl);
|
||||
|
||||
const isOnline = !isSavedMessages && user && userStatus && isUserOnline(user, userStatus);
|
||||
const onlineTransitionClassNames = useMediaTransition(isOnline);
|
||||
const onlineClassNamesPrefixed = useMemo(() => {
|
||||
return onlineTransitionClassNames.split(' ').map((c) => (c === 'shown' ? 'online' : `online-${c}`)).join(' ');
|
||||
}, [onlineTransitionClassNames]);
|
||||
|
||||
const handleVideoEnded = useLastCallback((e) => {
|
||||
const video = e.currentTarget;
|
||||
if (!videoBlobUrl) return;
|
||||
@ -200,12 +189,11 @@ const Avatar: FC<OwnProps> = ({
|
||||
const fullClassName = buildClassName(
|
||||
`Avatar size-${size}`,
|
||||
className,
|
||||
`color-bg-${getUserColorKey(user || chat)}`,
|
||||
`color-bg-${getUserColorKey(peer)}`,
|
||||
isSavedMessages && 'saved-messages',
|
||||
isDeleted && 'deleted-account',
|
||||
isReplies && 'replies-bot-account',
|
||||
isForum && 'forum',
|
||||
onlineClassNamesPrefixed,
|
||||
onClick && 'interactive',
|
||||
(!isSavedMessages && !imgBlobUrl) && 'no-photo',
|
||||
);
|
||||
@ -218,13 +206,11 @@ const Avatar: FC<OwnProps> = ({
|
||||
}
|
||||
});
|
||||
|
||||
const senderId = (user || chat) && (user || chat)!.id;
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={ref}
|
||||
className={fullClassName}
|
||||
data-test-sender-id={IS_TEST ? senderId : undefined}
|
||||
data-test-sender-id={IS_TEST ? peer?.id : undefined}
|
||||
aria-label={typeof content === 'string' ? author : undefined}
|
||||
onClick={handleClick}
|
||||
onMouseDown={handleMouseDown}
|
||||
|
||||
@ -123,7 +123,7 @@ const DeleteChatModal: FC<OwnProps & StateProps> = ({
|
||||
<div className="modal-header" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
<Avatar
|
||||
size="tiny"
|
||||
chat={chat}
|
||||
peer={chat}
|
||||
isSavedMessages={isChatWithSelf}
|
||||
/>
|
||||
<h3 className="modal-title">{lang(renderTitle())}</h3>
|
||||
|
||||
@ -182,7 +182,7 @@ const GroupChatInfo: FC<OwnProps & StateProps> = ({
|
||||
<Avatar
|
||||
key={chat.id}
|
||||
size={avatarSize}
|
||||
chat={chat}
|
||||
peer={chat}
|
||||
onClick={withMediaViewer ? handleAvatarViewerOpen : undefined}
|
||||
/>
|
||||
)}
|
||||
|
||||
@ -56,11 +56,10 @@ const PickerSelectedItem: FC<OwnProps & StateProps> = ({
|
||||
);
|
||||
|
||||
titleText = title;
|
||||
} else if (chat || user) {
|
||||
} else if (user || chat) {
|
||||
iconElement = (
|
||||
<Avatar
|
||||
chat={chat}
|
||||
user={user}
|
||||
peer={user || chat}
|
||||
size="small"
|
||||
isSavedMessages={user?.isSelf}
|
||||
/>
|
||||
|
||||
@ -175,7 +175,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
||||
<Avatar
|
||||
key={user.id}
|
||||
size={avatarSize}
|
||||
user={user}
|
||||
peer={user}
|
||||
isSavedMessages={isSavedMessages}
|
||||
onClick={withMediaViewer ? handleAvatarViewerOpen : undefined}
|
||||
/>
|
||||
|
||||
@ -222,6 +222,27 @@
|
||||
transition: opacity var(--layer-transition);
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-online {
|
||||
position: absolute;
|
||||
bottom: 0.0625rem;
|
||||
right: 0.0625rem;
|
||||
width: 0.875rem;
|
||||
height: 0.875rem;
|
||||
border-radius: 50%;
|
||||
border: 2px solid var(--color-background);
|
||||
background-color: #0ac630;
|
||||
flex-shrink: 0;
|
||||
|
||||
opacity: 0.5;
|
||||
transform: scale(0);
|
||||
transition: opacity 200ms, transform 200ms;
|
||||
|
||||
&.avatar-online-shown {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
|
||||
@ -21,6 +21,7 @@ import {
|
||||
getMessageAction,
|
||||
getPrivateChatUserId,
|
||||
isUserId,
|
||||
isUserOnline,
|
||||
selectIsChatMuted,
|
||||
} from '../../../global/helpers';
|
||||
import {
|
||||
@ -49,6 +50,7 @@ import useFlag from '../../../hooks/useFlag';
|
||||
import useChatListEntry from './hooks/useChatListEntry';
|
||||
import { useIsIntersecting } from '../../../hooks/useIntersectionObserver';
|
||||
import useAppLayout from '../../../hooks/useAppLayout';
|
||||
import useShowTransition from '../../../hooks/useShowTransition';
|
||||
|
||||
import ListItem from '../../ui/ListItem';
|
||||
import Avatar from '../../common/Avatar';
|
||||
@ -225,10 +227,15 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
}, [chat, chatId, isForum, isIntersecting]);
|
||||
|
||||
const isOnline = user && userStatus && isUserOnline(user, userStatus);
|
||||
const { hasShownClass: isAvatarOnlineShown } = useShowTransition(isOnline);
|
||||
|
||||
if (!chat) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const peer = user || chat;
|
||||
|
||||
const className = buildClassName(
|
||||
'Chat chat-item-clickable',
|
||||
isUserId(chatId) ? 'private' : 'group',
|
||||
@ -251,12 +258,11 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
>
|
||||
<div className="status">
|
||||
<Avatar
|
||||
chat={chat}
|
||||
user={user}
|
||||
userStatus={userStatus}
|
||||
peer={peer}
|
||||
isSavedMessages={user?.isSelf}
|
||||
/>
|
||||
<div className="avatar-badge-wrapper">
|
||||
<div className={buildClassName('avatar-online', isAvatarOnlineShown && 'avatar-online-shown')} />
|
||||
<ChatBadge chat={chat} isMuted={isMuted} shouldShowOnlyMostImportant forceHidden={getIsForumPanelClosed} />
|
||||
</div>
|
||||
{chat.isCallActive && chat.isCallNotEmpty && (
|
||||
@ -266,7 +272,7 @@ const Chat: FC<OwnProps & StateProps> = ({
|
||||
<div className="info">
|
||||
<div className="info-row">
|
||||
<FullNameTitle
|
||||
peer={user || chat}
|
||||
peer={peer}
|
||||
withEmojiStatus
|
||||
isSavedMessages={chatId === user?.id && user?.isSelf}
|
||||
observeIntersection={observeIntersection}
|
||||
|
||||
@ -72,6 +72,8 @@ const ChatMessage: FC<OwnProps & StateProps> = ({
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const peer = privateChatUser || chat;
|
||||
|
||||
return (
|
||||
<ListItem
|
||||
className="ChatMessage chat-item-clickable"
|
||||
@ -80,14 +82,13 @@ const ChatMessage: FC<OwnProps & StateProps> = ({
|
||||
buttonRef={buttonRef}
|
||||
>
|
||||
<Avatar
|
||||
chat={chat}
|
||||
user={privateChatUser}
|
||||
peer={peer}
|
||||
isSavedMessages={privateChatUser?.isSelf}
|
||||
/>
|
||||
<div className="info">
|
||||
<div className="info-row">
|
||||
<FullNameTitle
|
||||
peer={privateChatUser || chat}
|
||||
peer={peer}
|
||||
withEmojiStatus
|
||||
isSavedMessages={chatId === privateChatUser?.id && privateChatUser?.isSelf}
|
||||
/>
|
||||
|
||||
@ -83,7 +83,7 @@ const RecentContacts: FC<OwnProps & StateProps> = ({
|
||||
onClick={() => handleClick(userId)}
|
||||
dir={lang.isRtl ? 'rtl' : undefined}
|
||||
>
|
||||
<Avatar user={usersById[userId]} />
|
||||
<Avatar peer={usersById[userId]} />
|
||||
<div className="top-peer-name">{renderText(getUserFirstOrLastName(usersById[userId]) || NBSP)}</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@ -75,7 +75,7 @@ const SettingsActiveWebsite: FC<OwnProps & StateProps> = ({
|
||||
>
|
||||
<Avatar
|
||||
className={styles.avatar}
|
||||
user={renderingBot}
|
||||
peer={renderingBot}
|
||||
size="large"
|
||||
/>
|
||||
{renderingBot && <FullNameTitle className={styles.title} peer={renderingBot} />}
|
||||
|
||||
@ -110,7 +110,7 @@ const SettingsActiveWebsites: FC<OwnProps & StateProps> = ({
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleOpenSessionModal(session.hash)}
|
||||
>
|
||||
<Avatar className={styles.avatar} user={bot} size="tiny" />
|
||||
<Avatar className={styles.avatar} peer={bot} size="tiny" />
|
||||
<div className="multiline-menu-item full-size" dir="auto">
|
||||
<span className="date">{formatPastTimeShort(lang, session.dateActive * 1000)}</span>
|
||||
{bot && <FullNameTitle className={styles.title} peer={bot} />}
|
||||
|
||||
@ -68,9 +68,9 @@ const SettingsPrivacyBlockedUsers: FC<OwnProps & StateProps> = ({
|
||||
|
||||
function renderContact(contactId: string, i: number, viewportOffset: number) {
|
||||
const isPrivate = isUserId(contactId);
|
||||
const user = isPrivate ? usersByIds[contactId] : undefined;
|
||||
const chat = !isPrivate ? chatsByIds[contactId] : undefined;
|
||||
const userOrChat = user || chat;
|
||||
const user = usersByIds[contactId];
|
||||
const chat = chatsByIds[contactId];
|
||||
const peer = user || chat;
|
||||
|
||||
const className = buildClassName(
|
||||
'Chat chat-item-clickable blocked-list-item small-icon',
|
||||
@ -96,11 +96,10 @@ const SettingsPrivacyBlockedUsers: FC<OwnProps & StateProps> = ({
|
||||
>
|
||||
<Avatar
|
||||
size="medium"
|
||||
user={user}
|
||||
chat={chat}
|
||||
peer={peer}
|
||||
/>
|
||||
<div className="contact-info" dir="auto">
|
||||
{userOrChat && <FullNameTitle peer={userOrChat} />}
|
||||
{peer && <FullNameTitle peer={peer} />}
|
||||
{user?.phoneNumber && (
|
||||
<div className="contact-phone" dir="auto">{formatPhoneNumberWithCode(phoneCodeList, user.phoneNumber)}</div>
|
||||
)}
|
||||
|
||||
@ -122,7 +122,7 @@ const NewContactModal: FC<OwnProps & StateProps> = ({
|
||||
<div className="NewContactModal__profile" dir={lang.isRtl ? 'rtl' : undefined}>
|
||||
<Avatar
|
||||
size="jumbo"
|
||||
user={renderingUser}
|
||||
peer={renderingUser}
|
||||
text={`${firstName} ${lastName}`}
|
||||
/>
|
||||
<div className="NewContactModal__profile-info">
|
||||
|
||||
@ -125,7 +125,7 @@ const GiftPremiumModal: FC<OwnProps & StateProps> = ({
|
||||
<i className="icon icon-close" />
|
||||
</Button>
|
||||
<Avatar
|
||||
user={renderedUser}
|
||||
peer={renderedUser}
|
||||
size="jumbo"
|
||||
className={styles.avatar}
|
||||
/>
|
||||
|
||||
@ -77,11 +77,7 @@ const SenderInfo: FC<OwnProps & StateProps> = ({
|
||||
|
||||
return (
|
||||
<div className="SenderInfo" onClick={handleFocusMessage}>
|
||||
{isUserId(sender.id) ? (
|
||||
<Avatar key={sender.id} size="medium" user={sender as ApiUser} />
|
||||
) : (
|
||||
<Avatar key={sender.id} size="medium" chat={sender as ApiChat} />
|
||||
)}
|
||||
<Avatar key={sender.id} size="medium" peer={sender} />
|
||||
<div className="meta">
|
||||
<div className="title" dir="auto">
|
||||
{senderTitle && renderText(senderTitle)}
|
||||
|
||||
@ -1,20 +1,17 @@
|
||||
import type { FC } from '../../lib/teact/teact';
|
||||
import React, {
|
||||
memo, useMemo, useEffect, useState, useRef,
|
||||
memo, useEffect, useMemo, useRef, useState,
|
||||
} from '../../lib/teact/teact';
|
||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||
|
||||
import type { ApiAvailableReaction, ApiMessage, ApiReaction } from '../../api/types';
|
||||
import { LoadMoreDirection } from '../../types';
|
||||
|
||||
import {
|
||||
selectChatMessage,
|
||||
selectTabState,
|
||||
} from '../../global/selectors';
|
||||
import { selectChatMessage, selectTabState } from '../../global/selectors';
|
||||
import buildClassName from '../../util/buildClassName';
|
||||
import { formatIntegerCompact } from '../../util/textFormat';
|
||||
import { unique } from '../../util/iteratees';
|
||||
import { isSameReaction, getReactionUniqueKey } from '../../global/helpers';
|
||||
import { getReactionUniqueKey, isSameReaction } from '../../global/helpers';
|
||||
import { formatDateAtTime } from '../../util/dateFormat';
|
||||
|
||||
import useLang from '../../hooks/useLang';
|
||||
@ -63,6 +60,7 @@ const ReactorListModal: FC<OwnProps & StateProps> = ({
|
||||
|
||||
// No need for expensive global updates on users, so we avoid them
|
||||
const usersById = getGlobal().users.byId;
|
||||
const chatsById = getGlobal().chats.byId;
|
||||
|
||||
const lang = useLang();
|
||||
const [isClosing, startClosing, stopClosing] = useFlag(false);
|
||||
@ -187,25 +185,26 @@ const ReactorListModal: FC<OwnProps & StateProps> = ({
|
||||
onLoadMore={getMore}
|
||||
>
|
||||
{viewportIds?.flatMap(
|
||||
(userId) => {
|
||||
const user = usersById[userId];
|
||||
const userReactions = reactors?.reactions.filter((reactor) => reactor.userId === userId);
|
||||
(peerId) => {
|
||||
const peer = usersById[peerId] || chatsById[peerId];
|
||||
|
||||
const userReactions = reactors?.reactions.filter((reactor) => reactor.userId === peerId);
|
||||
const items: React.ReactNode[] = [];
|
||||
const seenByUser = seenByDates?.[userId];
|
||||
const seenByUser = seenByDates?.[peerId];
|
||||
|
||||
userReactions?.forEach((r) => {
|
||||
if (chosenTab && !isSameReaction(r.reaction, chosenTab)) return;
|
||||
|
||||
items.push(
|
||||
<ListItem
|
||||
key={`${userId}-${getReactionUniqueKey(r.reaction)}`}
|
||||
key={`${peerId}-${getReactionUniqueKey(r.reaction)}`}
|
||||
className="chat-item-clickable reactors-list-item"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleClick(userId)}
|
||||
onClick={() => handleClick(peerId)}
|
||||
>
|
||||
<Avatar user={user} size="medium" />
|
||||
<Avatar peer={peer} size="medium" />
|
||||
<div className="info">
|
||||
<FullNameTitle peer={user} withEmojiStatus />
|
||||
<FullNameTitle peer={peer} withEmojiStatus />
|
||||
<span className="status" dir="auto">
|
||||
<i className="icon icon-heart-outline status-icon" />
|
||||
{formatDateAtTime(lang, r.addedDate * 1000)}
|
||||
@ -225,13 +224,13 @@ const ReactorListModal: FC<OwnProps & StateProps> = ({
|
||||
if (!chosenTab && !userReactions?.length) {
|
||||
items.push(
|
||||
<ListItem
|
||||
key={`${userId}-seen-by`}
|
||||
key={`${peerId}-seen-by`}
|
||||
className="chat-item-clickable scroll-item small-icon"
|
||||
// eslint-disable-next-line react/jsx-no-bind
|
||||
onClick={() => handleClick(userId)}
|
||||
onClick={() => handleClick(peerId)}
|
||||
>
|
||||
<PrivateChatInfo
|
||||
userId={userId}
|
||||
userId={peerId}
|
||||
noStatusOrTyping
|
||||
avatarSize="medium"
|
||||
status={seenByUser ? formatDateAtTime(lang, seenByUser * 1000) : undefined}
|
||||
|
||||
@ -36,7 +36,7 @@ const BotCommand: FC<OwnProps> = ({
|
||||
focus={focus}
|
||||
>
|
||||
{withAvatar && (
|
||||
<Avatar size="small" user={bot} />
|
||||
<Avatar size="small" peer={bot} />
|
||||
)}
|
||||
<div className="content-inner">
|
||||
<span className="title">/{botCommand.command}</span>
|
||||
|
||||
@ -1383,8 +1383,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
className={buildClassName('send-as-button', shouldAnimateSendAsButtonRef.current && 'appear-animation')}
|
||||
>
|
||||
<Avatar
|
||||
user={sendAsUser}
|
||||
chat={sendAsChat}
|
||||
peer={sendAsUser || sendAsChat}
|
||||
size="tiny"
|
||||
/>
|
||||
</Button>
|
||||
|
||||
@ -7,7 +7,6 @@ import type { ApiSendAsPeerId } from '../../../api/types';
|
||||
import { IS_TOUCH_ENV } from '../../../util/windowEnvironment';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import setTooltipItemVisible from '../../../util/setTooltipItemVisible';
|
||||
import { isUserId } from '../../../global/helpers';
|
||||
|
||||
import useLastCallback from '../../../hooks/useLastCallback';
|
||||
import { useKeyboardNavigation } from './hooks/useKeyboardNavigation';
|
||||
@ -95,9 +94,9 @@ const SendAsMenu: FC<OwnProps> = ({
|
||||
>
|
||||
<div className="send-as-title" dir="auto">{lang('SendMessageAsTitle')}</div>
|
||||
{usersById && chatsById && sendAsPeerIds?.map(({ id, isPremium }, index) => {
|
||||
const user = isUserId(id) ? usersById[id] : undefined;
|
||||
const chat = !user ? chatsById[id] : undefined;
|
||||
const userOrChat = user || chat;
|
||||
const user = usersById[id];
|
||||
const chat = chatsById[id];
|
||||
const peer = user || chat;
|
||||
|
||||
const handleClick = () => {
|
||||
if (!isPremium || isCurrentUserPremium) {
|
||||
@ -128,12 +127,11 @@ const SendAsMenu: FC<OwnProps> = ({
|
||||
>
|
||||
<Avatar
|
||||
size="small"
|
||||
chat={chat}
|
||||
user={user}
|
||||
peer={peer}
|
||||
className={avatarClassName}
|
||||
/>
|
||||
<div className="info">
|
||||
{userOrChat && <FullNameTitle peer={userOrChat} noFake />}
|
||||
{peer && <FullNameTitle peer={peer} noFake />}
|
||||
<span className="subtitle">{user
|
||||
? lang('VoipGroupPersonalAccount')
|
||||
: lang('Subscribers', chat?.membersCount, 'i')}
|
||||
|
||||
@ -282,7 +282,7 @@ const StickerPicker: FC<OwnProps & StateProps> = ({
|
||||
) : stickerSet.id === FAVORITE_SYMBOL_SET_ID ? (
|
||||
<i className="icon icon-favorite" />
|
||||
) : stickerSet.id === CHAT_STICKER_SET_ID ? (
|
||||
<Avatar chat={chat} size="small" />
|
||||
<Avatar peer={chat} size="small" />
|
||||
) : (
|
||||
<StickerSetCover
|
||||
stickerSet={stickerSet as ApiStickerSet}
|
||||
|
||||
@ -3,7 +3,7 @@ import React, { memo, useMemo } from '../../../lib/teact/teact';
|
||||
import { getActions, getGlobal } from '../../../global';
|
||||
|
||||
import type {
|
||||
ApiChat, ApiThreadInfo, ApiUser,
|
||||
ApiThreadInfo,
|
||||
} from '../../../api/types';
|
||||
|
||||
import { isUserId } from '../../../global/helpers';
|
||||
@ -57,14 +57,13 @@ const CommentButton: FC<OwnProps> = ({
|
||||
|
||||
function renderRecentRepliers() {
|
||||
return (
|
||||
recentRepliers && recentRepliers.length > 0 && (
|
||||
Boolean(recentRepliers?.length) && (
|
||||
<div className="recent-repliers" dir={lang.isRtl ? 'rtl' : 'ltr'}>
|
||||
{recentRepliers.map((user) => (
|
||||
{recentRepliers!.map((peer) => (
|
||||
<Avatar
|
||||
key={user.id}
|
||||
key={peer.id}
|
||||
size="small"
|
||||
user={isUserId(user.id) ? user as ApiUser : undefined}
|
||||
chat={!isUserId(user.id) ? user as ApiChat : undefined}
|
||||
peer={peer}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -49,7 +49,7 @@ const Contact: FC<OwnProps & StateProps> = ({
|
||||
>
|
||||
<Avatar
|
||||
size="large"
|
||||
user={user}
|
||||
peer={user}
|
||||
text={firstName || lastName}
|
||||
/>
|
||||
<div className="contact-info">
|
||||
|
||||
@ -12,7 +12,6 @@ import {
|
||||
getMessageLocation,
|
||||
buildStaticMapHash,
|
||||
isGeoLiveExpired,
|
||||
isUserId,
|
||||
} from '../../../global/helpers';
|
||||
import { formatCountdownShort, formatLastUpdated } from '../../../util/dateFormat';
|
||||
import {
|
||||
@ -87,10 +86,6 @@ const Location: FC<OwnProps> = ({
|
||||
const prevMediaBlobUrl = usePrevious(mediaBlobUrl);
|
||||
const mapBlobUrl = mediaBlobUrl || prevMediaBlobUrl;
|
||||
|
||||
const isPeerUser = peer && isUserId(peer.id);
|
||||
const avatarUser = (peer && isPeerUser) ? peer as ApiUser : undefined;
|
||||
const avatarChat = (peer && !isPeerUser) ? peer as ApiChat : undefined;
|
||||
|
||||
const accuracyRadiusPx = useMemo(() => {
|
||||
if (type !== 'geoLive' || !point.accuracyRadius) {
|
||||
return 0;
|
||||
@ -213,7 +208,7 @@ const Location: FC<OwnProps> = ({
|
||||
if (type === 'geoLive') {
|
||||
return (
|
||||
<div className={pinClassName} dangerouslySetInnerHTML={SVG_PIN}>
|
||||
<Avatar chat={avatarChat} user={avatarUser} className="location-avatar" />
|
||||
<Avatar peer={peer} className="location-avatar" />
|
||||
{location.heading !== undefined && (
|
||||
<div className="direction" style={`--direction: ${location.heading}deg`} />
|
||||
)}
|
||||
|
||||
@ -774,18 +774,14 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
function renderAvatar() {
|
||||
const isAvatarPeerUser = avatarPeer && isUserId(avatarPeer.id);
|
||||
const avatarUser = (avatarPeer && isAvatarPeerUser) ? avatarPeer as ApiUser : undefined;
|
||||
const avatarChat = (avatarPeer && !isAvatarPeerUser) ? avatarPeer as ApiChat : undefined;
|
||||
const hiddenName = (!avatarPeer && forwardInfo) ? forwardInfo.hiddenUserName : undefined;
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
size={isMobile ? 'small-mobile' : 'small'}
|
||||
user={avatarUser}
|
||||
chat={avatarChat}
|
||||
peer={avatarPeer}
|
||||
text={hiddenName}
|
||||
onClick={(avatarUser || avatarChat) ? handleAvatarClick : undefined}
|
||||
onClick={avatarPeer ? handleAvatarClick : undefined}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@ -415,7 +415,7 @@ const MessageContextMenu: FC<OwnProps> = ({
|
||||
{seenByRecentUsers?.map((user) => (
|
||||
<Avatar
|
||||
size="micro"
|
||||
user={user}
|
||||
peer={user}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -226,7 +226,7 @@ const Poll: FC<OwnProps & StateProps> = ({
|
||||
<Avatar
|
||||
key={user.id}
|
||||
size="micro"
|
||||
user={user}
|
||||
peer={user}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -84,7 +84,7 @@ const ReactionButton: FC<{
|
||||
{recentReactors.map((user) => (
|
||||
<Avatar
|
||||
key={user.id}
|
||||
user={user}
|
||||
peer={user}
|
||||
size="micro"
|
||||
/>
|
||||
))}
|
||||
|
||||
@ -13,7 +13,6 @@ import {
|
||||
selectChat,
|
||||
selectCurrentTextSearch,
|
||||
} from '../../global/selectors';
|
||||
import { isChatChannel } from '../../global/helpers';
|
||||
import { disableDirectTextInput, enableDirectTextInput } from '../../util/directInputManager';
|
||||
import { renderMessageSummary } from '../common/helpers/renderMessageText';
|
||||
import useLang from '../../hooks/useLang';
|
||||
@ -37,7 +36,6 @@ export type OwnProps = {
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
chat?: ApiChat;
|
||||
messagesById?: Record<number, ApiMessage>;
|
||||
query?: string;
|
||||
totalCount?: number;
|
||||
@ -48,7 +46,6 @@ const RightSearch: FC<OwnProps & StateProps> = ({
|
||||
chatId,
|
||||
threadId,
|
||||
isActive,
|
||||
chat,
|
||||
messagesById,
|
||||
query,
|
||||
totalCount,
|
||||
@ -96,26 +93,29 @@ const RightSearch: FC<OwnProps & StateProps> = ({
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const senderUser = message.senderId ? selectUser(getGlobal(), message.senderId) : undefined;
|
||||
const global = getGlobal();
|
||||
|
||||
let senderChat;
|
||||
if (chat && isChatChannel(chat)) {
|
||||
senderChat = chat;
|
||||
} else if (message.forwardInfo) {
|
||||
let senderPeer = message.senderId
|
||||
? selectUser(global, message.senderId) || selectChat(global, message.senderId)
|
||||
: undefined;
|
||||
|
||||
if (!senderPeer && message.forwardInfo) {
|
||||
const { isChannelPost, fromChatId } = message.forwardInfo;
|
||||
senderChat = isChannelPost && fromChatId ? selectChat(getGlobal(), fromChatId) : undefined;
|
||||
} else {
|
||||
senderChat = message.senderId ? selectChat(getGlobal(), message.senderId) : undefined;
|
||||
const originalSender = isChannelPost && fromChatId ? selectChat(global, fromChatId) : undefined;
|
||||
if (originalSender) senderPeer = originalSender;
|
||||
}
|
||||
|
||||
if (!senderPeer) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
message,
|
||||
senderUser,
|
||||
senderChat,
|
||||
senderPeer: senderPeer!,
|
||||
onClick: () => focusMessage({ chatId, threadId, messageId: id }),
|
||||
};
|
||||
}).filter(Boolean);
|
||||
}, [query, viewportIds, messagesById, chat, focusMessage, chatId, threadId]);
|
||||
}, [query, viewportIds, messagesById, focusMessage, chatId, threadId]);
|
||||
|
||||
const handleKeyDown = useKeyboardListNavigation(containerRef, true, (index) => {
|
||||
const foundResult = viewportResults?.[index === -1 ? 0 : index];
|
||||
@ -125,11 +125,10 @@ const RightSearch: FC<OwnProps & StateProps> = ({
|
||||
}, '.ListItem-button', true);
|
||||
|
||||
const renderSearchResult = ({
|
||||
message, senderUser, senderChat, onClick,
|
||||
message, senderPeer, onClick,
|
||||
}: {
|
||||
message: ApiMessage;
|
||||
senderUser?: ApiUser;
|
||||
senderChat?: ApiChat;
|
||||
senderPeer: ApiUser | ApiChat;
|
||||
onClick: NoneToVoidFunction;
|
||||
}) => {
|
||||
const text = renderMessageSummary(lang, message, undefined, query);
|
||||
@ -142,12 +141,11 @@ const RightSearch: FC<OwnProps & StateProps> = ({
|
||||
onClick={onClick}
|
||||
>
|
||||
<Avatar
|
||||
chat={senderChat}
|
||||
user={senderUser}
|
||||
peer={senderPeer}
|
||||
/>
|
||||
<div className="info">
|
||||
<div className="search-result-message-top">
|
||||
<FullNameTitle peer={(senderUser || senderChat)!} withEmojiStatus />
|
||||
<FullNameTitle peer={senderPeer} withEmojiStatus />
|
||||
<LastMessageMeta message={message} />
|
||||
</div>
|
||||
<div className="subtitle" dir="auto">
|
||||
@ -189,9 +187,8 @@ const RightSearch: FC<OwnProps & StateProps> = ({
|
||||
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global, { chatId }): StateProps => {
|
||||
const chat = selectChat(global, chatId);
|
||||
const messagesById = chat && selectChatMessages(global, chat.id);
|
||||
if (!chat || !messagesById) {
|
||||
const messagesById = selectChatMessages(global, chatId);
|
||||
if (!messagesById) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@ -199,7 +196,6 @@ export default memo(withGlobal<OwnProps>(
|
||||
const { totalCount, foundIds } = results || {};
|
||||
|
||||
return {
|
||||
chat,
|
||||
messagesById,
|
||||
query,
|
||||
totalCount,
|
||||
|
||||
@ -67,7 +67,7 @@ const JoinRequest: FC<OwnProps & StateProps> = ({
|
||||
<Avatar
|
||||
key={userId}
|
||||
size="medium"
|
||||
user={user}
|
||||
peer={user}
|
||||
/>
|
||||
<div className={buildClassName('user-info')}>
|
||||
<div className={buildClassName('user-name')}>{fullName}</div>
|
||||
|
||||
@ -118,7 +118,7 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
||||
<div className="modal-header">
|
||||
<Avatar
|
||||
size="tiny"
|
||||
chat={linkedChat}
|
||||
peer={linkedChat}
|
||||
/>
|
||||
<div className="modal-title">
|
||||
{lang(isChannel ? 'DiscussionUnlinkGroup' : 'DiscussionUnlinkChannel')}
|
||||
@ -136,7 +136,7 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
|
||||
<div className="modal-header">
|
||||
<Avatar
|
||||
size="tiny"
|
||||
chat={linkedGroup}
|
||||
peer={linkedGroup}
|
||||
/>
|
||||
<div className="modal-title">
|
||||
{lang('Channel.DiscussionGroup.LinkGroup')}
|
||||
|
||||
@ -230,7 +230,7 @@ const ManageUser: FC<OwnProps & StateProps> = ({
|
||||
<Avatar
|
||||
photo={notPersonalPhoto}
|
||||
noPersonalPhoto
|
||||
user={user}
|
||||
peer={user}
|
||||
size="mini"
|
||||
className="personal-photo"
|
||||
/>
|
||||
|
||||
@ -27,7 +27,7 @@ const StatisticsPublicForward: FC<OwnProps> = ({ data }) => {
|
||||
|
||||
return (
|
||||
<div className="StatisticsPublicForward" onClick={handleClick}>
|
||||
<Avatar size="medium" chat={data.chat} />
|
||||
<Avatar size="medium" peer={data.chat} />
|
||||
|
||||
<div className="StatisticsPublicForward__info">
|
||||
<div className="StatisticsPublicForward__title">
|
||||
|
||||
@ -171,6 +171,10 @@ export function isUserOnline(user: ApiUser, userStatus?: ApiUserStatus) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (user.isSelf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return userStatus.type === 'userStatusOnline' && type !== 'userTypeBot';
|
||||
}
|
||||
|
||||
|
||||
@ -178,7 +178,7 @@ class TelegramClient {
|
||||
return topic.id < offsetTopicId;
|
||||
}
|
||||
return true;
|
||||
}).filter((_, i) => i < limit),
|
||||
}).slice(0, limit),
|
||||
users: [],
|
||||
chats: [],
|
||||
messages: [],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user