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