Message: Show admin and owner marks (#2156)
This commit is contained in:
parent
365a021dda
commit
5c510a91a4
@ -274,10 +274,14 @@ export function buildChatMember(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
userId,
|
userId,
|
||||||
inviterId: 'inviterId' in member ? buildApiPeerId(member.inviterId as BigInt.BigInteger, 'user') : undefined,
|
inviterId: 'inviterId' in member && member.inviterId
|
||||||
|
? buildApiPeerId(member.inviterId as BigInt.BigInteger, 'user')
|
||||||
|
: undefined,
|
||||||
joinedDate: 'date' in member ? member.date : undefined,
|
joinedDate: 'date' in member ? member.date : undefined,
|
||||||
kickedByUserId: 'kickedBy' in member ? buildApiPeerId(member.kickedBy, 'user') : undefined,
|
kickedByUserId: 'kickedBy' in member && member.kickedBy ? buildApiPeerId(member.kickedBy, 'user') : undefined,
|
||||||
promotedByUserId: 'promotedBy' in member ? buildApiPeerId(member.promotedBy, 'user') : undefined,
|
promotedByUserId: 'promotedBy' in member && member.promotedBy
|
||||||
|
? buildApiPeerId(member.promotedBy, 'user')
|
||||||
|
: undefined,
|
||||||
bannedRights: 'bannedRights' in member ? omitVirtualClassFields(member.bannedRights) : undefined,
|
bannedRights: 'bannedRights' in member ? omitVirtualClassFields(member.bannedRights) : undefined,
|
||||||
adminRights: 'adminRights' in member ? omitVirtualClassFields(member.adminRights) : undefined,
|
adminRights: 'adminRights' in member ? omitVirtualClassFields(member.adminRights) : undefined,
|
||||||
customTitle: 'rank' in member ? member.rank : undefined,
|
customTitle: 'rank' in member ? member.rank : undefined,
|
||||||
|
|||||||
@ -210,7 +210,7 @@ export function buildApiMessageWithChatId(chatId: string, mtpMessage: UniversalM
|
|||||||
...(shouldHideKeyboardButtons && { shouldHideKeyboardButtons }),
|
...(shouldHideKeyboardButtons && { shouldHideKeyboardButtons }),
|
||||||
...(mtpMessage.viaBotId && { viaBotId: buildApiPeerId(mtpMessage.viaBotId, 'user') }),
|
...(mtpMessage.viaBotId && { viaBotId: buildApiPeerId(mtpMessage.viaBotId, 'user') }),
|
||||||
...(replies?.comments && { threadInfo: buildThreadInfo(replies, mtpMessage.id, chatId) }),
|
...(replies?.comments && { threadInfo: buildThreadInfo(replies, mtpMessage.id, chatId) }),
|
||||||
...(postAuthor && { adminTitle: postAuthor }),
|
...(postAuthor && { postAuthorTitle: postAuthor }),
|
||||||
isProtected,
|
isProtected,
|
||||||
isForwardingAllowed,
|
isForwardingAllowed,
|
||||||
};
|
};
|
||||||
@ -399,7 +399,7 @@ function buildApiMessageForwardInfo(fwdFrom: GramJs.MessageFwdHeader, isChatWith
|
|||||||
fromMessageId: fwdFrom.savedFromMsgId || fwdFrom.channelPost,
|
fromMessageId: fwdFrom.savedFromMsgId || fwdFrom.channelPost,
|
||||||
senderUserId: fromId,
|
senderUserId: fromId,
|
||||||
hiddenUserName: fwdFrom.fromName,
|
hiddenUserName: fwdFrom.fromName,
|
||||||
adminTitle: fwdFrom.postAuthor,
|
postAuthorTitle: fwdFrom.postAuthor,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -385,7 +385,7 @@ async function getFullChatInfo(chatId: string): Promise<FullChatData | undefined
|
|||||||
...(chatPhoto instanceof GramJs.Photo && { profilePhoto: buildApiPhoto(chatPhoto) }),
|
...(chatPhoto instanceof GramJs.Photo && { profilePhoto: buildApiPhoto(chatPhoto) }),
|
||||||
about,
|
about,
|
||||||
members,
|
members,
|
||||||
adminMembers,
|
adminMembersById: adminMembers ? buildCollectionByKey(adminMembers, 'userId') : undefined,
|
||||||
canViewMembers: true,
|
canViewMembers: true,
|
||||||
botCommands,
|
botCommands,
|
||||||
...(exportedInvite instanceof GramJs.ChatInviteExported && {
|
...(exportedInvite instanceof GramJs.ChatInviteExported && {
|
||||||
@ -463,7 +463,7 @@ async function getFullChannelInfo(
|
|||||||
canViewParticipants && adminRights && await fetchMembers(id, accessHash, 'kicked')
|
canViewParticipants && adminRights && await fetchMembers(id, accessHash, 'kicked')
|
||||||
) || {};
|
) || {};
|
||||||
const { members: adminMembers, users: adminUsers, userStatusesById: adminStatusesById } = (
|
const { members: adminMembers, users: adminUsers, userStatusesById: adminStatusesById } = (
|
||||||
canViewParticipants && adminRights && await fetchMembers(id, accessHash, 'admin')
|
canViewParticipants && await fetchMembers(id, accessHash, 'admin')
|
||||||
) || {};
|
) || {};
|
||||||
const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined;
|
const botCommands = botInfo ? buildApiChatBotCommands(botInfo) : undefined;
|
||||||
|
|
||||||
@ -506,7 +506,7 @@ async function getFullChannelInfo(
|
|||||||
isPreHistoryHidden: hiddenPrehistory,
|
isPreHistoryHidden: hiddenPrehistory,
|
||||||
members,
|
members,
|
||||||
kickedMembers,
|
kickedMembers,
|
||||||
adminMembers,
|
adminMembersById: adminMembers ? buildCollectionByKey(adminMembers, 'userId') : undefined,
|
||||||
groupCallId: call ? String(call.id) : undefined,
|
groupCallId: call ? String(call.id) : undefined,
|
||||||
linkedChatId: linkedChatId ? buildApiPeerId(linkedChatId, 'chat') : undefined,
|
linkedChatId: linkedChatId ? buildApiPeerId(linkedChatId, 'chat') : undefined,
|
||||||
botCommands,
|
botCommands,
|
||||||
|
|||||||
@ -86,7 +86,7 @@ export interface ApiChatFullInfo {
|
|||||||
onlineCount?: number;
|
onlineCount?: number;
|
||||||
members?: ApiChatMember[];
|
members?: ApiChatMember[];
|
||||||
kickedMembers?: ApiChatMember[];
|
kickedMembers?: ApiChatMember[];
|
||||||
adminMembers?: ApiChatMember[];
|
adminMembersById?: Record<string, ApiChatMember>;
|
||||||
canViewMembers?: boolean;
|
canViewMembers?: boolean;
|
||||||
isPreHistoryHidden?: boolean;
|
isPreHistoryHidden?: boolean;
|
||||||
inviteLink?: string;
|
inviteLink?: string;
|
||||||
|
|||||||
@ -293,7 +293,7 @@ export interface ApiMessageForwardInfo {
|
|||||||
senderUserId?: string;
|
senderUserId?: string;
|
||||||
fromMessageId?: number;
|
fromMessageId?: number;
|
||||||
hiddenUserName?: string;
|
hiddenUserName?: string;
|
||||||
adminTitle?: string;
|
postAuthorTitle?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ApiMessageEntityDefault = {
|
export type ApiMessageEntityDefault = {
|
||||||
@ -408,7 +408,7 @@ export interface ApiMessage {
|
|||||||
isKeyboardSingleUse?: boolean;
|
isKeyboardSingleUse?: boolean;
|
||||||
viaBotId?: string;
|
viaBotId?: string;
|
||||||
threadInfo?: ApiThreadInfo;
|
threadInfo?: ApiThreadInfo;
|
||||||
adminTitle?: string;
|
postAuthorTitle?: string;
|
||||||
isScheduled?: boolean;
|
isScheduled?: boolean;
|
||||||
shouldHideKeyboardButtons?: boolean;
|
shouldHideKeyboardButtons?: boolean;
|
||||||
isFromScheduled?: boolean;
|
isFromScheduled?: boolean;
|
||||||
|
|||||||
@ -2,7 +2,9 @@ import React, { useEffect, useCallback, memo } from '../../lib/teact/teact';
|
|||||||
import { getActions, withGlobal } from '../../global';
|
import { getActions, withGlobal } from '../../global';
|
||||||
|
|
||||||
import type { FC } from '../../lib/teact/teact';
|
import type { FC } from '../../lib/teact/teact';
|
||||||
import type { ApiUser, ApiTypingStatus, ApiUserStatus } from '../../api/types';
|
import type {
|
||||||
|
ApiUser, ApiTypingStatus, ApiUserStatus, ApiChatMember,
|
||||||
|
} from '../../api/types';
|
||||||
import type { GlobalState } from '../../global/types';
|
import type { GlobalState } from '../../global/types';
|
||||||
import type { AnimationLevel } from '../../types';
|
import type { AnimationLevel } from '../../types';
|
||||||
import { MediaViewerOrigin } from '../../types';
|
import { MediaViewerOrigin } from '../../types';
|
||||||
@ -34,6 +36,7 @@ type OwnProps = {
|
|||||||
emojiStatusSize?: number;
|
emojiStatusSize?: number;
|
||||||
noStatusOrTyping?: boolean;
|
noStatusOrTyping?: boolean;
|
||||||
noRtl?: boolean;
|
noRtl?: boolean;
|
||||||
|
adminMember?: ApiChatMember;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StateProps =
|
type StateProps =
|
||||||
@ -67,6 +70,7 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
|||||||
animationLevel,
|
animationLevel,
|
||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
serverTimeOffset,
|
serverTimeOffset,
|
||||||
|
adminMember,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
loadFullUser,
|
loadFullUser,
|
||||||
@ -131,6 +135,10 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const customTitle = adminMember
|
||||||
|
? adminMember.customTitle || lang(adminMember.isOwner ? 'GroupInfo.LabelOwner' : 'GroupInfo.LabelAdmin')
|
||||||
|
: undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ChatInfo" dir={!noRtl && lang.isRtl ? 'rtl' : undefined}>
|
<div className="ChatInfo" dir={!noRtl && lang.isRtl ? 'rtl' : undefined}>
|
||||||
<Avatar
|
<Avatar
|
||||||
@ -143,12 +151,15 @@ const PrivateChatInfo: FC<OwnProps & StateProps> = ({
|
|||||||
animationLevel={animationLevel}
|
animationLevel={animationLevel}
|
||||||
/>
|
/>
|
||||||
<div className="info">
|
<div className="info">
|
||||||
<FullNameTitle
|
<div className="info-name-title">
|
||||||
peer={user}
|
<FullNameTitle
|
||||||
withEmojiStatus
|
peer={user}
|
||||||
emojiStatusSize={emojiStatusSize}
|
withEmojiStatus
|
||||||
isSavedMessages={isSavedMessages}
|
emojiStatusSize={emojiStatusSize}
|
||||||
/>
|
isSavedMessages={isSavedMessages}
|
||||||
|
/>
|
||||||
|
{customTitle && <span className="custom-title">{customTitle}</span>}
|
||||||
|
</div>
|
||||||
{(status || (!isSavedMessages && !noStatusOrTyping)) && renderStatusOrTyping()}
|
{(status || (!isSavedMessages && !noStatusOrTyping)) && renderStatusOrTyping()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -45,7 +45,7 @@ import {
|
|||||||
selectTheme,
|
selectTheme,
|
||||||
} from '../../global/selectors';
|
} from '../../global/selectors';
|
||||||
import {
|
import {
|
||||||
getCanPostInChat, getMessageSendingRestrictionReason, isChatChannel, isChatSuperGroup, isUserId,
|
getCanPostInChat, getMessageSendingRestrictionReason, isChatChannel, isChatGroup, isChatSuperGroup, isUserId,
|
||||||
} from '../../global/helpers';
|
} from '../../global/helpers';
|
||||||
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
import captureEscKeyListener from '../../util/captureEscKeyListener';
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
@ -108,6 +108,7 @@ type StateProps = {
|
|||||||
canSubscribe?: boolean;
|
canSubscribe?: boolean;
|
||||||
canStartBot?: boolean;
|
canStartBot?: boolean;
|
||||||
canRestartBot?: boolean;
|
canRestartBot?: boolean;
|
||||||
|
shouldLoadFullChat?: boolean;
|
||||||
activeEmojiInteractions?: ActiveEmojiInteraction[];
|
activeEmojiInteractions?: ActiveEmojiInteraction[];
|
||||||
shouldJoinToSend?: boolean;
|
shouldJoinToSend?: boolean;
|
||||||
shouldSendJoinRequest?: boolean;
|
shouldSendJoinRequest?: boolean;
|
||||||
@ -154,6 +155,7 @@ const MiddleColumn: FC<StateProps> = ({
|
|||||||
activeEmojiInteractions,
|
activeEmojiInteractions,
|
||||||
shouldJoinToSend,
|
shouldJoinToSend,
|
||||||
shouldSendJoinRequest,
|
shouldSendJoinRequest,
|
||||||
|
shouldLoadFullChat,
|
||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
@ -168,6 +170,7 @@ const MiddleColumn: FC<StateProps> = ({
|
|||||||
sendBotCommand,
|
sendBotCommand,
|
||||||
restartBot,
|
restartBot,
|
||||||
showNotification,
|
showNotification,
|
||||||
|
loadFullChat,
|
||||||
} = getActions();
|
} = getActions();
|
||||||
|
|
||||||
const { width: windowWidth } = useWindowSize();
|
const { width: windowWidth } = useWindowSize();
|
||||||
@ -264,6 +267,12 @@ const MiddleColumn: FC<StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [chatId, isPrivate, areChatSettingsLoaded, lastSyncTime, loadChatSettings]);
|
}, [chatId, isPrivate, areChatSettingsLoaded, lastSyncTime, loadChatSettings]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (chatId && shouldLoadFullChat && isReady) {
|
||||||
|
loadFullChat({ chatId });
|
||||||
|
}
|
||||||
|
}, [shouldLoadFullChat, chatId, isReady, loadFullChat]);
|
||||||
|
|
||||||
const handleDragEnter = useCallback((e: React.DragEvent<HTMLDivElement>) => {
|
const handleDragEnter = useCallback((e: React.DragEvent<HTMLDivElement>) => {
|
||||||
if (IS_TOUCH_ENV) {
|
if (IS_TOUCH_ENV) {
|
||||||
return;
|
return;
|
||||||
@ -616,6 +625,7 @@ export default memo(withGlobal(
|
|||||||
const shouldSendJoinRequest = Boolean(chat?.isNotJoined && chat.isJoinRequest);
|
const shouldSendJoinRequest = Boolean(chat?.isNotJoined && chat.isJoinRequest);
|
||||||
const canRestartBot = Boolean(bot && selectIsUserBlocked(global, bot.id));
|
const canRestartBot = Boolean(bot && selectIsUserBlocked(global, bot.id));
|
||||||
const canStartBot = !canRestartBot && isBotNotStarted;
|
const canStartBot = !canRestartBot && isBotNotStarted;
|
||||||
|
const shouldLoadFullChat = Boolean(chat && isChatGroup(chat) && !chat.fullInfo && lastSyncTime);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
@ -645,6 +655,7 @@ export default memo(withGlobal(
|
|||||||
canRestartBot,
|
canRestartBot,
|
||||||
shouldJoinToSend,
|
shouldJoinToSend,
|
||||||
shouldSendJoinRequest,
|
shouldSendJoinRequest,
|
||||||
|
shouldLoadFullChat,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(MiddleColumn));
|
)(MiddleColumn));
|
||||||
|
|||||||
@ -280,7 +280,6 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
addRecentEmoji,
|
addRecentEmoji,
|
||||||
sendInlineBotResult,
|
sendInlineBotResult,
|
||||||
loadSendAs,
|
loadSendAs,
|
||||||
loadFullChat,
|
|
||||||
resetOpenChatWithDraft,
|
resetOpenChatWithDraft,
|
||||||
callAttachBot,
|
callAttachBot,
|
||||||
openLimitReachedModal,
|
openLimitReachedModal,
|
||||||
@ -334,12 +333,6 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [chat, chatId, isReady, lastSyncTime, loadSendAs, sendAsPeerIds]);
|
}, [chat, chatId, isReady, lastSyncTime, loadSendAs, sendAsPeerIds]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (chatId && chat && lastSyncTime && !chat.fullInfo && isReady && isChatSuperGroup(chat)) {
|
|
||||||
loadFullChat({ chatId });
|
|
||||||
}
|
|
||||||
}, [chat, chatId, isReady, lastSyncTime, loadFullChat]);
|
|
||||||
|
|
||||||
const shouldAnimateSendAsButtonRef = useRef(false);
|
const shouldAnimateSendAsButtonRef = useRef(false);
|
||||||
useOnChange(([prevChatId, prevSendAsPeerIds]) => {
|
useOnChange(([prevChatId, prevSendAsPeerIds]) => {
|
||||||
// We only animate send-as button if `sendAsPeerIds` was missing when opening the chat
|
// We only animate send-as button if `sendAsPeerIds` was missing when opening the chat
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import type {
|
|||||||
ApiChat,
|
ApiChat,
|
||||||
ApiThreadInfo,
|
ApiThreadInfo,
|
||||||
ApiAvailableReaction,
|
ApiAvailableReaction,
|
||||||
|
ApiChatMember,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import type {
|
import type {
|
||||||
AnimationLevel, FocusDirection, IAlbum, ISettings,
|
AnimationLevel, FocusDirection, IAlbum, ISettings,
|
||||||
@ -74,7 +75,9 @@ import {
|
|||||||
areReactionsEmpty,
|
areReactionsEmpty,
|
||||||
getMessageHtmlId,
|
getMessageHtmlId,
|
||||||
isGeoLiveExpired,
|
isGeoLiveExpired,
|
||||||
getMessageSingleCustomEmoji, hasMessageText,
|
getMessageSingleCustomEmoji,
|
||||||
|
hasMessageText,
|
||||||
|
isChatGroup,
|
||||||
} from '../../../global/helpers';
|
} from '../../../global/helpers';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import useEnsureMessage from '../../../hooks/useEnsureMessage';
|
import useEnsureMessage from '../../../hooks/useEnsureMessage';
|
||||||
@ -183,6 +186,7 @@ type StateProps = {
|
|||||||
isChatWithSelf?: boolean;
|
isChatWithSelf?: boolean;
|
||||||
isRepliesChat?: boolean;
|
isRepliesChat?: boolean;
|
||||||
isChannel?: boolean;
|
isChannel?: boolean;
|
||||||
|
isGroup?: boolean;
|
||||||
canReply?: boolean;
|
canReply?: boolean;
|
||||||
lastSyncTime?: number;
|
lastSyncTime?: number;
|
||||||
serverTimeOffset: number;
|
serverTimeOffset: number;
|
||||||
@ -211,6 +215,7 @@ type StateProps = {
|
|||||||
isTranscriptionError?: boolean;
|
isTranscriptionError?: boolean;
|
||||||
isPremium: boolean;
|
isPremium: boolean;
|
||||||
animationLevel: AnimationLevel;
|
animationLevel: AnimationLevel;
|
||||||
|
senderAdminMember?: ApiChatMember;
|
||||||
};
|
};
|
||||||
|
|
||||||
type MetaPosition =
|
type MetaPosition =
|
||||||
@ -275,6 +280,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
isChatWithSelf,
|
isChatWithSelf,
|
||||||
isRepliesChat,
|
isRepliesChat,
|
||||||
isChannel,
|
isChannel,
|
||||||
|
isGroup,
|
||||||
canReply,
|
canReply,
|
||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
serverTimeOffset,
|
serverTimeOffset,
|
||||||
@ -301,6 +307,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
hasUnreadReaction,
|
hasUnreadReaction,
|
||||||
memoFirstUnreadIdRef,
|
memoFirstUnreadIdRef,
|
||||||
animationLevel,
|
animationLevel,
|
||||||
|
senderAdminMember,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
toggleMessageSelection,
|
toggleMessageSelection,
|
||||||
@ -615,7 +622,9 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
style = `width: ${calculatedWidth + extraPadding}px`;
|
style = `width: ${calculatedWidth + extraPadding}px`;
|
||||||
}
|
}
|
||||||
|
|
||||||
const signature = (isChannel && message.adminTitle) || (!asForwarded && forwardInfo?.adminTitle) || undefined;
|
const signature = (isChannel && message.postAuthorTitle)
|
||||||
|
|| (!asForwarded && forwardInfo?.postAuthorTitle)
|
||||||
|
|| undefined;
|
||||||
const metaSafeAuthorWidth = useMemo(() => {
|
const metaSafeAuthorWidth = useMemo(() => {
|
||||||
return signature ? calculateAuthorWidth(signature) : undefined;
|
return signature ? calculateAuthorWidth(signature) : undefined;
|
||||||
}, [signature]);
|
}, [signature]);
|
||||||
@ -984,13 +993,23 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
)}
|
)}
|
||||||
{forwardInfo?.isLinkedChannelPost ? (
|
{forwardInfo?.isLinkedChannelPost ? (
|
||||||
<span className="admin-title" dir="auto">{lang('DiscussChannel')}</span>
|
<span className="admin-title" dir="auto">{lang('DiscussChannel')}</span>
|
||||||
) : message.adminTitle && !isChannel ? (
|
) : message.forwardInfo?.postAuthorTitle && isGroup && asForwarded ? (
|
||||||
<span className="admin-title" dir="auto">{message.adminTitle}</span>
|
<span className="admin-title" dir="auto">{message.forwardInfo?.postAuthorTitle}</span>
|
||||||
|
) : message.postAuthorTitle && isGroup && !asForwarded ? (
|
||||||
|
<span className="admin-title" dir="auto">{message.postAuthorTitle}</span>
|
||||||
|
) : senderAdminMember && !asForwarded ? (
|
||||||
|
<span className="admin-title" dir="auto">
|
||||||
|
{senderAdminMember.customTitle || lang(
|
||||||
|
senderAdminMember.isOwner ? 'GroupInfo.LabelOwner' : 'GroupInfo.LabelAdmin',
|
||||||
|
)}
|
||||||
|
</span>
|
||||||
) : undefined}
|
) : undefined}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const forwardAuthor = isGroup && asForwarded ? message.postAuthorTitle : undefined;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@ -1040,7 +1059,10 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
dir="auto"
|
dir="auto"
|
||||||
>
|
>
|
||||||
{asForwarded && !isInDocumentGroupNotFirst && (
|
{asForwarded && !isInDocumentGroupNotFirst && (
|
||||||
<div className="message-title">{lang('ForwardedMessage')}</div>
|
<div className="message-title">
|
||||||
|
{lang('ForwardedMessage')}
|
||||||
|
{forwardAuthor && <span className="admin-title" dir="auto">{forwardAuthor}</span>}
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
{renderContent()}
|
{renderContent()}
|
||||||
{!isInDocumentGroupNotLast && metaPosition === 'standalone' && renderReactionsAndMeta()}
|
{!isInDocumentGroupNotLast && metaPosition === 'standalone' && renderReactionsAndMeta()}
|
||||||
@ -1124,13 +1146,18 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const isChatWithSelf = selectIsChatWithSelf(global, chatId);
|
const isChatWithSelf = selectIsChatWithSelf(global, chatId);
|
||||||
const isRepliesChat = isChatWithRepliesBot(chatId);
|
const isRepliesChat = isChatWithRepliesBot(chatId);
|
||||||
const isChannel = chat && isChatChannel(chat);
|
const isChannel = chat && isChatChannel(chat);
|
||||||
|
const isGroup = chat && isChatGroup(chat);
|
||||||
const chatUsername = chat?.username;
|
const chatUsername = chat?.username;
|
||||||
|
|
||||||
|
const isForwarding = forwardMessages.messageIds && forwardMessages.messageIds.includes(id);
|
||||||
const forceSenderName = !isChatWithSelf && isAnonymousOwnMessage(message);
|
const forceSenderName = !isChatWithSelf && isAnonymousOwnMessage(message);
|
||||||
const canShowSender = withSenderName || withAvatar || forceSenderName;
|
const canShowSender = withSenderName || withAvatar || forceSenderName;
|
||||||
const sender = selectSender(global, message);
|
const sender = selectSender(global, message);
|
||||||
const originSender = selectForwardedSender(global, message);
|
const originSender = selectForwardedSender(global, message);
|
||||||
const botSender = viaBotId ? selectUser(global, viaBotId) : undefined;
|
const botSender = viaBotId ? selectUser(global, viaBotId) : undefined;
|
||||||
|
const senderAdminMember = sender?.id && isGroup
|
||||||
|
? chat.fullInfo?.adminMembersById?.[sender?.id]
|
||||||
|
: undefined;
|
||||||
|
|
||||||
const threadTopMessageId = threadId ? selectThreadTopMessageId(global, chatId, threadId) : undefined;
|
const threadTopMessageId = threadId ? selectThreadTopMessageId(global, chatId, threadId) : undefined;
|
||||||
const isThreadTop = message.id === threadTopMessageId;
|
const isThreadTop = message.id === threadTopMessageId;
|
||||||
@ -1152,8 +1179,6 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
direction: focusDirection, noHighlight: noFocusHighlight, isResizingContainer,
|
direction: focusDirection, noHighlight: noFocusHighlight, isResizingContainer,
|
||||||
} = (isFocused && focusedMessage) || {};
|
} = (isFocused && focusedMessage) || {};
|
||||||
|
|
||||||
const isForwarding = forwardMessages.messageIds && forwardMessages.messageIds.includes(id);
|
|
||||||
|
|
||||||
const { query: highlight } = selectCurrentTextSearch(global) || {};
|
const { query: highlight } = selectCurrentTextSearch(global) || {};
|
||||||
|
|
||||||
const singleEmoji = getMessageSingleRegularEmoji(message);
|
const singleEmoji = getMessageSingleRegularEmoji(message);
|
||||||
@ -1206,6 +1231,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
isChatWithSelf,
|
isChatWithSelf,
|
||||||
isRepliesChat,
|
isRepliesChat,
|
||||||
isChannel,
|
isChannel,
|
||||||
|
isGroup,
|
||||||
canReply,
|
canReply,
|
||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
serverTimeOffset,
|
serverTimeOffset,
|
||||||
@ -1239,6 +1265,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
transcribedText: transcriptionId !== undefined ? global.transcriptions[transcriptionId]?.text : undefined,
|
transcribedText: transcriptionId !== undefined ? global.transcriptions[transcriptionId]?.text : undefined,
|
||||||
isPremium: selectIsCurrentUserPremium(global),
|
isPremium: selectIsCurrentUserPremium(global),
|
||||||
animationLevel: global.settings.byKey.animationLevel,
|
animationLevel: global.settings.byKey.animationLevel,
|
||||||
|
senderAdminMember,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(Message));
|
)(Message));
|
||||||
|
|||||||
@ -85,6 +85,7 @@ type StateProps = {
|
|||||||
canAddMembers?: boolean;
|
canAddMembers?: boolean;
|
||||||
canDeleteMembers?: boolean;
|
canDeleteMembers?: boolean;
|
||||||
members?: ApiChatMember[];
|
members?: ApiChatMember[];
|
||||||
|
adminMembersById?: Record<string, ApiChatMember>;
|
||||||
commonChatIds?: string[];
|
commonChatIds?: string[];
|
||||||
chatsById: Record<string, ApiChat>;
|
chatsById: Record<string, ApiChat>;
|
||||||
usersById: Record<string, ApiUser>;
|
usersById: Record<string, ApiUser>;
|
||||||
@ -126,6 +127,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
|||||||
canDeleteMembers,
|
canDeleteMembers,
|
||||||
commonChatIds,
|
commonChatIds,
|
||||||
members,
|
members,
|
||||||
|
adminMembersById,
|
||||||
usersById,
|
usersById,
|
||||||
userStatusesById,
|
userStatusesById,
|
||||||
chatsById,
|
chatsById,
|
||||||
@ -416,7 +418,7 @@ const Profile: FC<OwnProps & StateProps> = ({
|
|||||||
onClick={() => handleMemberClick(id)}
|
onClick={() => handleMemberClick(id)}
|
||||||
contextActions={getMemberContextAction(id)}
|
contextActions={getMemberContextAction(id)}
|
||||||
>
|
>
|
||||||
<PrivateChatInfo userId={id} forceShowSelf />
|
<PrivateChatInfo userId={id} adminMember={adminMembersById?.[id]} forceShowSelf />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
))
|
))
|
||||||
) : resultType === 'commonChats' ? (
|
) : resultType === 'commonChats' ? (
|
||||||
@ -524,6 +526,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const isChannel = chat && isChatChannel(chat);
|
const isChannel = chat && isChatChannel(chat);
|
||||||
const hasMembersTab = isGroup || (isChannel && isChatAdmin(chat!));
|
const hasMembersTab = isGroup || (isChannel && isChatAdmin(chat!));
|
||||||
const members = chat?.fullInfo?.members;
|
const members = chat?.fullInfo?.members;
|
||||||
|
const adminMembersById = chat?.fullInfo?.adminMembersById;
|
||||||
const areMembersHidden = hasMembersTab && chat
|
const areMembersHidden = hasMembersTab && chat
|
||||||
&& (chat.isForbidden || (chat.fullInfo && !chat.fullInfo.canViewMembers));
|
&& (chat.isForbidden || (chat.fullInfo && !chat.fullInfo.canViewMembers));
|
||||||
const canAddMembers = hasMembersTab && chat
|
const canAddMembers = hasMembersTab && chat
|
||||||
@ -562,7 +565,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
userStatusesById,
|
userStatusesById,
|
||||||
chatsById,
|
chatsById,
|
||||||
isChatProtected: chat?.isProtected,
|
isChatProtected: chat?.isProtected,
|
||||||
...(hasMembersTab && members && { members }),
|
...(hasMembersTab && members && { members, adminMembersById }),
|
||||||
...(hasCommonChatsTab && user && { commonChatIds: user.commonChats?.ids }),
|
...(hasCommonChatsTab && user && { commonChatIds: user.commonChats?.ids }),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|||||||
@ -108,7 +108,7 @@ const ManageChannel: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [progress]);
|
}, [progress]);
|
||||||
|
|
||||||
const adminsCount = (chat?.fullInfo?.adminMembers?.length) || 0;
|
const adminsCount = Object.keys(chat.fullInfo?.adminMembersById || {}).length;
|
||||||
const removedUsersCount = (chat?.fullInfo?.kickedMembers?.length) || 0;
|
const removedUsersCount = (chat?.fullInfo?.kickedMembers?.length) || 0;
|
||||||
|
|
||||||
const handleClickEditType = useCallback(() => {
|
const handleClickEditType = useCallback(() => {
|
||||||
|
|||||||
@ -49,11 +49,11 @@ const ManageChatAdministrators: FC<OwnProps & StateProps> = ({
|
|||||||
}, [onScreenSelect]);
|
}, [onScreenSelect]);
|
||||||
|
|
||||||
const adminMembers = useMemo(() => {
|
const adminMembers = useMemo(() => {
|
||||||
if (!chat.fullInfo || !chat.fullInfo.adminMembers) {
|
if (!chat.fullInfo?.adminMembersById) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return chat.fullInfo.adminMembers.sort((a, b) => {
|
return Object.values(chat.fullInfo.adminMembersById).sort((a, b) => {
|
||||||
if (a.isOwner) {
|
if (a.isOwner) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (b.isOwner) {
|
} else if (b.isOwner) {
|
||||||
|
|||||||
@ -236,7 +236,9 @@ const ManageGroup: FC<OwnProps & StateProps> = ({
|
|||||||
return totalCount;
|
return totalCount;
|
||||||
}, [chat]);
|
}, [chat]);
|
||||||
|
|
||||||
const adminsCount = (chat.fullInfo?.adminMembers?.length) || 0;
|
const adminsCount = useMemo(() => {
|
||||||
|
return Object.keys(chat.fullInfo?.adminMembersById || {}).length;
|
||||||
|
}, [chat.fullInfo?.adminMembersById]);
|
||||||
|
|
||||||
const handleDeleteGroup = useCallback(() => {
|
const handleDeleteGroup = useCallback(() => {
|
||||||
if (isBasicGroup) {
|
if (isBasicGroup) {
|
||||||
|
|||||||
@ -70,7 +70,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
const selectedChatMember = useMemo(() => {
|
const selectedChatMember = useMemo(() => {
|
||||||
const selectedAdminMember = chat.fullInfo?.adminMembers?.find(({ userId }) => userId === selectedUserId);
|
const selectedAdminMember = selectedUserId ? chat.fullInfo?.adminMembersById?.[selectedUserId] : undefined;
|
||||||
|
|
||||||
// If `selectedAdminMember` variable is filled with a value, then we have already saved the administrator,
|
// If `selectedAdminMember` variable is filled with a value, then we have already saved the administrator,
|
||||||
// so now we need to return to the list of administrators
|
// so now we need to return to the list of administrators
|
||||||
@ -91,7 +91,7 @@ const ManageGroupAdminRights: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return selectedAdminMember;
|
return selectedAdminMember;
|
||||||
}, [chat.fullInfo?.adminMembers, defaultRights, isNewAdmin, lang, selectedUserId]);
|
}, [chat.fullInfo?.adminMembersById, defaultRights, isNewAdmin, lang, selectedUserId]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (chat?.fullInfo && selectedUserId && !selectedChatMember) {
|
if (chat?.fullInfo && selectedUserId && !selectedChatMember) {
|
||||||
|
|||||||
@ -37,7 +37,7 @@ type OwnProps = {
|
|||||||
type StateProps = {
|
type StateProps = {
|
||||||
userStatusesById: Record<string, ApiUserStatus>;
|
userStatusesById: Record<string, ApiUserStatus>;
|
||||||
members?: ApiChatMember[];
|
members?: ApiChatMember[];
|
||||||
adminMembers?: ApiChatMember[];
|
adminMembersById?: Record<string, ApiChatMember>;
|
||||||
isChannel?: boolean;
|
isChannel?: boolean;
|
||||||
localContactIds?: string[];
|
localContactIds?: string[];
|
||||||
searchQuery?: string;
|
searchQuery?: string;
|
||||||
@ -52,7 +52,7 @@ type StateProps = {
|
|||||||
const ManageGroupMembers: FC<OwnProps & StateProps> = ({
|
const ManageGroupMembers: FC<OwnProps & StateProps> = ({
|
||||||
noAdmins,
|
noAdmins,
|
||||||
members,
|
members,
|
||||||
adminMembers,
|
adminMembersById,
|
||||||
userStatusesById,
|
userStatusesById,
|
||||||
isChannel,
|
isChannel,
|
||||||
isActive,
|
isActive,
|
||||||
@ -78,8 +78,8 @@ const ManageGroupMembers: FC<OwnProps & StateProps> = ({
|
|||||||
const [deletingUserId, setDeletingUserId] = useState<string | undefined>();
|
const [deletingUserId, setDeletingUserId] = useState<string | undefined>();
|
||||||
|
|
||||||
const adminIds = useMemo(() => {
|
const adminIds = useMemo(() => {
|
||||||
return noAdmins ? adminMembers?.map(({ userId }) => userId) || [] : [];
|
return noAdmins && adminMembersById ? Object.keys(adminMembersById) : [];
|
||||||
}, [adminMembers, noAdmins]);
|
}, [adminMembersById, noAdmins]);
|
||||||
|
|
||||||
const memberIds = useMemo(() => {
|
const memberIds = useMemo(() => {
|
||||||
// No need for expensive global updates on users, so we avoid them
|
// No need for expensive global updates on users, so we avoid them
|
||||||
@ -233,7 +233,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
const { statusesById: userStatusesById } = global.users;
|
const { statusesById: userStatusesById } = global.users;
|
||||||
const members = chat?.fullInfo?.members;
|
const members = chat?.fullInfo?.members;
|
||||||
const adminMembers = chat?.fullInfo?.adminMembers;
|
const adminMembersById = chat?.fullInfo?.adminMembersById;
|
||||||
const isChannel = chat && isChatChannel(chat);
|
const isChannel = chat && isChatChannel(chat);
|
||||||
const { userIds: localContactIds } = global.contactList || {};
|
const { userIds: localContactIds } = global.contactList || {};
|
||||||
|
|
||||||
@ -248,7 +248,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
members,
|
members,
|
||||||
adminMembers,
|
adminMembersById,
|
||||||
userStatusesById,
|
userStatusesById,
|
||||||
isChannel,
|
isChannel,
|
||||||
localContactIds,
|
localContactIds,
|
||||||
|
|||||||
@ -30,11 +30,11 @@ const ManageGroupRecentActions: FC<OwnProps & StateProps> = ({ chat, onClose, is
|
|||||||
});
|
});
|
||||||
|
|
||||||
const adminMembers = useMemo(() => {
|
const adminMembers = useMemo(() => {
|
||||||
if (!chat || !chat.fullInfo || !chat.fullInfo.adminMembers) {
|
if (!chat?.fullInfo?.adminMembersById) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
return chat.fullInfo.adminMembers.sort((a, b) => {
|
return Object.values(chat.fullInfo.adminMembersById).sort((a, b) => {
|
||||||
if (a.isOwner) {
|
if (a.isOwner) {
|
||||||
return -1;
|
return -1;
|
||||||
} else if (b.isOwner) {
|
} else if (b.isOwner) {
|
||||||
|
|||||||
@ -165,6 +165,11 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.info-name-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.info-row,
|
.info-row,
|
||||||
.title,
|
.title,
|
||||||
.subtitle {
|
.subtitle {
|
||||||
@ -211,6 +216,17 @@
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
||||||
|
.custom-title {
|
||||||
|
padding-inline-start: 1rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
margin-inline-start: auto;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.status,
|
.status,
|
||||||
.typing-status {
|
.typing-status {
|
||||||
font-size: 0.875rem;
|
font-size: 0.875rem;
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import {
|
|||||||
} from '../../index';
|
} from '../../index';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiChat, ApiUser, ApiChatFolder, ApiError,
|
ApiChat, ApiUser, ApiChatFolder, ApiError, ApiChatMember,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||||
import { NewChatMembersProgress, ChatCreationProgress, ManagementProgress } from '../../../types';
|
import { NewChatMembersProgress, ChatCreationProgress, ManagementProgress } from '../../../types';
|
||||||
@ -867,24 +867,31 @@ addActionHandler('updateChatAdmin', async (global, actions, payload) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { adminMembers } = chatAfterUpdate.fullInfo;
|
const { adminMembersById } = chatAfterUpdate.fullInfo;
|
||||||
const isDismissed = !Object.keys(adminRights).length;
|
const isDismissed = !Object.keys(adminRights).length;
|
||||||
|
let newAdminMembersById: Record<string, ApiChatMember> | undefined;
|
||||||
|
if (adminMembersById) {
|
||||||
|
if (isDismissed) {
|
||||||
|
const { [userId]: remove, ...rest } = adminMembersById;
|
||||||
|
newAdminMembersById = rest;
|
||||||
|
} else {
|
||||||
|
newAdminMembersById = {
|
||||||
|
...adminMembersById,
|
||||||
|
[userId]: {
|
||||||
|
...adminMembersById[userId],
|
||||||
|
adminRights,
|
||||||
|
customTitle,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
global = getGlobal();
|
global = getGlobal();
|
||||||
|
|
||||||
setGlobal(updateChat(global, chatId, {
|
setGlobal(updateChat(global, chatId, {
|
||||||
fullInfo: {
|
fullInfo: {
|
||||||
...chatAfterUpdate.fullInfo,
|
...chatAfterUpdate.fullInfo,
|
||||||
...(adminMembers && isDismissed && {
|
...(newAdminMembersById && { adminMembersById: newAdminMembersById }),
|
||||||
adminMembers: adminMembers.filter((m) => m.userId !== userId),
|
|
||||||
}),
|
|
||||||
...(adminMembers && !isDismissed && {
|
|
||||||
adminMembers: adminMembers.map((m) => (
|
|
||||||
m.userId === userId
|
|
||||||
? { ...m, adminRights, customTitle }
|
|
||||||
: m
|
|
||||||
)),
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { addActionHandler, getGlobal, setGlobal } from '../../index';
|
|||||||
import { MAIN_THREAD_ID } from '../../../api/types';
|
import { MAIN_THREAD_ID } from '../../../api/types';
|
||||||
|
|
||||||
import { ARCHIVED_FOLDER_ID, MAX_ACTIVE_PINNED_CHATS } from '../../../config';
|
import { ARCHIVED_FOLDER_ID, MAX_ACTIVE_PINNED_CHATS } from '../../../config';
|
||||||
import { pick } from '../../../util/iteratees';
|
import { buildCollectionByKey, pick } from '../../../util/iteratees';
|
||||||
import { closeMessageNotifications, notifyAboutMessage } from '../../../util/notifications';
|
import { closeMessageNotifications, notifyAboutMessage } from '../../../util/notifications';
|
||||||
import {
|
import {
|
||||||
updateChat,
|
updateChat,
|
||||||
@ -326,7 +326,7 @@ addActionHandler('apiUpdate', (global, actions, update) => {
|
|||||||
fullInfo: {
|
fullInfo: {
|
||||||
...targetChat.fullInfo,
|
...targetChat.fullInfo,
|
||||||
members,
|
members,
|
||||||
adminMembers,
|
adminMembersById: buildCollectionByKey(adminMembers, 'userId'),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import type { GlobalState } from '../types';
|
|||||||
import type { ApiChat, ApiChatMember, ApiPhoto } from '../../api/types';
|
import type { ApiChat, ApiChatMember, ApiPhoto } from '../../api/types';
|
||||||
|
|
||||||
import { ARCHIVED_FOLDER_ID } from '../../config';
|
import { ARCHIVED_FOLDER_ID } from '../../config';
|
||||||
import { areSortedArraysEqual, omit } from '../../util/iteratees';
|
import { areSortedArraysEqual, buildCollectionByKey, omit } from '../../util/iteratees';
|
||||||
import { selectChatListType } from '../selectors';
|
import { selectChatListType } from '../selectors';
|
||||||
|
|
||||||
export function replaceChatListIds(
|
export function replaceChatListIds(
|
||||||
@ -242,6 +242,7 @@ export function addChatMembers(global: GlobalState, chat: ApiChat, membersToAdd:
|
|||||||
fullInfo: {
|
fullInfo: {
|
||||||
...chat.fullInfo,
|
...chat.fullInfo,
|
||||||
members: updatedMembers,
|
members: updatedMembers,
|
||||||
|
adminMembersById: buildCollectionByKey(updatedMembers, 'userId'),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user