Send As: Fix error in some chats (#5966)

This commit is contained in:
zubiden 2025-06-04 20:41:33 +02:00 committed by Alexander Zinchuk
parent f1a538ed8e
commit 91b8463b35
22 changed files with 142 additions and 81 deletions

View File

@ -52,67 +52,77 @@ function buildApiChatFieldsFromPeerEntity(
peerEntity: Entity,
isSupport = false,
): PeerEntityApiChatFields {
const user = peerEntity instanceof GramJs.User ? peerEntity : undefined;
const channel = peerEntity instanceof GramJs.Channel ? peerEntity : undefined;
const userOrChannel = user || channel;
// Shared fields
const isMin = Boolean('min' in peerEntity && peerEntity.min);
const accessHash = ('accessHash' in peerEntity) ? String(peerEntity.accessHash) : undefined;
const hasVideoAvatar = 'photo' in peerEntity && peerEntity.photo && 'hasVideo' in peerEntity.photo
&& peerEntity.photo.hasVideo;
const avatarPhotoId = ('photo' in peerEntity) && peerEntity.photo ? buildAvatarPhotoId(peerEntity.photo) : undefined;
const areSignaturesShown = Boolean('signatures' in peerEntity && peerEntity.signatures);
const hasPrivateLink = Boolean('hasLink' in peerEntity && peerEntity.hasLink);
const isScam = Boolean('scam' in peerEntity && peerEntity.scam);
const isFake = Boolean('fake' in peerEntity && peerEntity.fake);
const isJoinToSend = Boolean('joinToSend' in peerEntity && peerEntity.joinToSend);
const isJoinRequest = Boolean('joinRequest' in peerEntity && peerEntity.joinRequest);
const hasUsername = Boolean('username' in peerEntity && peerEntity.username);
const usernames = buildApiUsernames(peerEntity);
const isForum = Boolean('forum' in peerEntity && peerEntity.forum);
const areStoriesHidden = Boolean('storiesHidden' in peerEntity && peerEntity.storiesHidden);
const maxStoryId = 'storiesMaxId' in peerEntity ? peerEntity.storiesMaxId : undefined;
const botVerificationIconId = 'botVerificationIcon' in peerEntity
? peerEntity.botVerificationIcon?.toString() : undefined;
const storiesUnavailable = Boolean('storiesUnavailable' in peerEntity && peerEntity.storiesUnavailable);
const color = ('color' in peerEntity && peerEntity.color) ? buildApiPeerColor(peerEntity.color) : undefined;
const emojiStatus = ('emojiStatus' in peerEntity && peerEntity.emojiStatus)
? buildApiEmojiStatus(peerEntity.emojiStatus) : undefined;
const boostLevel = ('level' in peerEntity) ? peerEntity.level : undefined;
const areProfilesShown = Boolean('signatureProfiles' in peerEntity && peerEntity.signatureProfiles);
const subscriptionUntil = 'subscriptionUntilDate' in peerEntity ? peerEntity.subscriptionUntilDate : undefined;
const paidMessagesStars = 'sendPaidMessagesStars' in peerEntity ? peerEntity.sendPaidMessagesStars : undefined;
// Chat and channel shared fields
const isCallActive = 'callActive' in peerEntity && peerEntity.callActive;
const isCallNotEmpty = 'callNotEmpty' in peerEntity && peerEntity.callNotEmpty;
const creationDate = 'date' in peerEntity ? peerEntity.date : undefined;
const membersCount = 'participantsCount' in peerEntity ? peerEntity.participantsCount : undefined;
const isProtected = 'noforwards' in peerEntity && peerEntity.noforwards;
const isCreator = 'creator' in peerEntity && peerEntity.creator;
// User and channel shared fields
const isScam = userOrChannel?.scam;
const isFake = userOrChannel?.fake;
const areStoriesHidden = userOrChannel?.storiesHidden;
const maxStoryId = userOrChannel?.storiesMaxId;
const botVerificationIconId = userOrChannel?.botVerificationIcon?.toString();
const storiesUnavailable = userOrChannel?.storiesUnavailable;
const color = userOrChannel?.color ? buildApiPeerColor(userOrChannel.color) : undefined;
const emojiStatus = userOrChannel?.emojiStatus ? buildApiEmojiStatus(userOrChannel.emojiStatus) : undefined;
const paidMessagesStars = userOrChannel?.sendPaidMessagesStars;
const isVerified = userOrChannel?.verified;
return {
isMin,
hasPrivateLink,
areSignaturesShown,
areProfilesShown,
isLinkedInDiscussion: channel?.hasLink,
areSignaturesShown: channel?.signatures,
areProfilesShown: channel?.signatureProfiles,
usernames,
accessHash,
hasVideoAvatar,
avatarPhotoId,
...('verified' in peerEntity && { isVerified: peerEntity.verified }),
...('callActive' in peerEntity && { isCallActive: peerEntity.callActive }),
...('callNotEmpty' in peerEntity && { isCallNotEmpty: peerEntity.callNotEmpty }),
...('date' in peerEntity && { creationDate: peerEntity.date }),
...('participantsCount' in peerEntity && peerEntity.participantsCount !== undefined && {
membersCount: peerEntity.participantsCount,
}),
...('noforwards' in peerEntity && { isProtected: Boolean(peerEntity.noforwards) }),
isVerified,
isCallActive,
isCallNotEmpty,
creationDate,
hasUsername,
...(membersCount !== undefined && { membersCount }),
isProtected,
isSupport: isSupport || undefined,
...buildApiChatPermissions(peerEntity),
...('creator' in peerEntity && { isCreator: peerEntity.creator }),
...buildApiChatRestrictions(peerEntity),
...buildApiChatMigrationInfo(peerEntity),
isCreator,
fakeType: isScam ? 'scam' : (isFake ? 'fake' : undefined),
color,
isJoinToSend,
isJoinRequest,
isForum,
isJoinToSend: channel?.joinToSend,
isJoinRequest: channel?.joinRequest,
isForum: channel?.forum,
areStoriesHidden,
maxStoryId,
hasStories: Boolean(maxStoryId) && !storiesUnavailable,
emojiStatus,
boostLevel,
boostLevel: channel?.level,
botVerificationIconId,
subscriptionUntil,
hasGeo: channel?.hasGeo,
subscriptionUntil: channel?.subscriptionUntilDate,
paidMessagesStars: paidMessagesStars?.toJSNumber(),
...buildApiChatPermissions(peerEntity),
...buildApiChatRestrictions(peerEntity),
...buildApiChatMigrationInfo(peerEntity),
};
}

View File

@ -96,7 +96,7 @@ export function buildApiUser(mtpUser: GramJs.TypeUser): ApiUser | undefined {
const {
id, firstName, lastName, fake, scam, support, closeFriend, storiesUnavailable, storiesMaxId,
bot, botActiveUsers, botVerificationIcon, botInlinePlaceholder, botAttachMenu, botCanEdit,
sendPaidMessagesStars,
sendPaidMessagesStars, username,
} = mtpUser;
const hasVideoAvatar = mtpUser.photo instanceof GramJs.UserProfilePhoto ? Boolean(mtpUser.photo.hasVideo) : undefined;
const avatarPhotoId = mtpUser.photo && buildAvatarPhotoId(mtpUser.photo);
@ -121,6 +121,7 @@ export function buildApiUser(mtpUser: GramJs.TypeUser): ApiUser | undefined {
canEditBot: botCanEdit,
...(userType === 'userTypeBot' && { canBeInvitedToGroup: !mtpUser.botNochats }),
...(usernames && { usernames }),
hasUsername: Boolean(username),
phoneNumber: mtpUser.phone || '',
noStatus: !mtpUser.status,
...(mtpUser.accessHash && { accessHash: String(mtpUser.accessHash) }),

View File

@ -29,12 +29,14 @@ export interface ApiChat {
isVerified?: true;
areSignaturesShown?: boolean;
areProfilesShown?: boolean;
hasPrivateLink?: boolean;
isLinkedInDiscussion?: boolean;
hasGeo?: boolean;
accessHash?: string;
isMin?: boolean;
hasVideoAvatar?: boolean;
avatarPhotoId?: string;
usernames?: ApiUsername[];
hasUsername?: boolean;
membersCount?: number;
creationDate?: number;
isSupport?: true;

View File

@ -20,6 +20,7 @@ export interface ApiUser {
lastName?: string;
noStatus?: boolean;
usernames?: ApiUsername[];
hasUsername?: boolean;
phoneNumber: string;
accessHash?: string;
hasVideoAvatar?: boolean;

View File

@ -1304,6 +1304,7 @@
"ComposerSilentPostingEnabledTootlip" = "Subscribers will receive a silent notification.";
"ComposerSilentPostingDisabledTootlip" = "Subscribers will be notified when you post.";
"ComposerPlaceholder" = "Message";
"ComposerPlaceholderAnonymous" = "Send anonymously";
"ComposerPlaceholderBroadcast" = "Broadcast";
"ComposerPlaceholderBroadcastSilent" = "Silent Broadcast";
"ComposerPlaceholderTopic" = "Message in {topic}";

View File

@ -21,6 +21,7 @@ import type {
ApiMessage,
ApiMessageEntity,
ApiNewPoll,
ApiPeer,
ApiQuickReply,
ApiReaction,
ApiStealthMode,
@ -62,6 +63,7 @@ import {
getStoryKey,
isChatAdmin,
isChatChannel,
isChatPublic,
isChatSuperGroup,
isSameReaction,
isSystemBot,
@ -253,8 +255,7 @@ type StateProps =
inlineBots?: Record<string, false | InlineBotSettings>;
botCommands?: ApiBotCommand[] | false;
botMenuButton?: ApiBotMenuButton;
sendAsUser?: ApiUser;
sendAsChat?: ApiChat;
sendAsPeer?: ApiPeer;
sendAsId?: string;
editingDraft?: ApiFormattedText;
requestedDraft?: ApiFormattedText;
@ -374,8 +375,7 @@ const Composer: FC<OwnProps & StateProps> = ({
inlineBots,
isInlineBotLoading,
botCommands,
sendAsUser,
sendAsChat,
sendAsPeer,
sendAsId,
editingDraft,
requestedDraft,
@ -472,8 +472,7 @@ const Composer: FC<OwnProps & StateProps> = ({
const isInMessageList = type === 'messageList';
const isInStoryViewer = type === 'story';
const sendAsPeerIds = isInMessageList ? chat?.sendAsPeerIds : undefined;
const canShowSendAs = sendAsPeerIds
&& (sendAsPeerIds.length > 1 || !sendAsPeerIds.some((peer) => peer.id === currentUserId!));
const canShowSendAs = Boolean(sendAsPeerIds?.length);
// Prevent Symbol Menu from closing when calendar is open
const [isSymbolMenuForced, forceShowSymbolMenu, cancelForceShowSymbolMenu] = useFlag();
const sendMessageAction = useSendMessageAction(chatId, threadId);
@ -518,7 +517,9 @@ const Composer: FC<OwnProps & StateProps> = ({
useEffect(() => {
const isChannelWithProfiles = isChannel && chat?.areProfilesShown;
if (chatId && chat && !sendAsPeerIds && isReady && (isChatSuperGroup(chat) || isChannelWithProfiles)) {
const isChatWithSendAs = chat && isChatSuperGroup(chat)
&& Boolean(isChatPublic(chat) || chat.isLinkedInDiscussion || chat.hasGeo);
if (!sendAsPeerIds && isReady && (isChatWithSendAs || isChannelWithProfiles)) {
loadSendAs({ chatId });
}
}, [chat, chatId, isChannel, isReady, loadSendAs, sendAsPeerIds]);
@ -1586,6 +1587,11 @@ const Composer: FC<OwnProps & StateProps> = ({
withNodes: true,
});
}
if (chat?.adminRights?.anonymous) {
return lang('ComposerPlaceholderAnonymous');
}
if (chat?.isForum && chat?.isForumAsMessages && threadId === MAIN_THREAD_ID) {
return replyToTopic
? lang('ComposerPlaceholderTopic', { topic: replyToTopic.title })
@ -1978,7 +1984,7 @@ const Composer: FC<OwnProps & StateProps> = ({
<Icon name="bot-commands-filled" />
</ResponsiveHoverButton>
)}
{canShowSendAs && (sendAsUser || sendAsChat) && (
{canShowSendAs && sendAsPeer && (
<Button
round
color="translucent"
@ -1991,7 +1997,7 @@ const Composer: FC<OwnProps & StateProps> = ({
)}
>
<Avatar
peer={sendAsUser || sendAsChat}
peer={sendAsPeer}
size="tiny"
/>
</Button>
@ -2363,13 +2369,8 @@ export default memo(withGlobal<OwnProps>(
const { currentUserId } = global;
const currentUser = selectUser(global, currentUserId!)!;
const defaultSendAsId = chatFullInfo ? chatFullInfo?.sendAsId || currentUserId : undefined;
const sendAsId = chat?.sendAsPeerIds && defaultSendAsId && (
chat.sendAsPeerIds.some((peer) => peer.id === defaultSendAsId)
? defaultSendAsId
: (chat?.adminRights?.anonymous ? chat?.id : undefined)
);
const sendAsUser = sendAsId ? selectUser(global, sendAsId) : undefined;
const sendAsChat = !sendAsUser && sendAsId ? selectChat(global, sendAsId) : undefined;
const sendAsId = defaultSendAsId;
const sendAsPeer = sendAsId ? selectPeer(global, sendAsId) : undefined;
const requestedDraft = selectRequestedDraft(global, chatId);
const requestedDraftFiles = selectRequestedDraftFiles(global, chatId);
@ -2467,8 +2468,7 @@ export default memo(withGlobal<OwnProps>(
isInlineBotLoading: tabState.inlineBots.isLoading,
botCommands: userFullInfo ? (userFullInfo.botInfo?.commands || false) : undefined,
botMenuButton: userFullInfo?.botInfo?.menuButton,
sendAsUser,
sendAsChat,
sendAsPeer,
sendAsId,
editingDraft,
requestedDraft,

View File

@ -7,7 +7,7 @@ import type { CustomPeerType, UniqueCustomPeer } from '../../../types';
import { DEBUG } from '../../../config';
import { requestMeasure } from '../../../lib/fasterdom/fasterdom';
import { getGroupStatus, getUserStatus, isUserOnline } from '../../../global/helpers';
import { getGroupStatus, getMainUsername, getUserStatus, isUserOnline } from '../../../global/helpers';
import { getPeerTypeKey, isApiPeerChat } from '../../../global/helpers/peers';
import { selectPeer, selectUserStatus } from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
@ -279,7 +279,7 @@ const PeerPicker = <CategoryType extends string = CustomPeerType>({
if (!peer) return undefined;
if (withPeerUsernames) {
const username = peer.usernames?.[0]?.username;
const username = getMainUsername(peer);
if (username) {
return [`@${username}`];
}

View File

@ -9,6 +9,7 @@ import type {
} from '../../../api/types';
import type { Signal } from '../../../util/signals';
import { getMainUsername } from '../../../global/helpers';
import buildClassName from '../../../util/buildClassName';
import freezeWhenClosed from '../../../util/hoc/freezeWhenClosed';
import setTooltipItemVisible from '../../../util/setTooltipItemVisible';
@ -63,7 +64,7 @@ const ChatCommandTooltip: FC<OwnProps> = ({
const bot = usersById[botId];
sendBotCommand({
command: `/${command}${withUsername && bot ? `@${bot.usernames![0].username}` : ''}`,
command: `/${command}${withUsername && bot ? `@${getMainUsername(bot)}` : ''}`,
});
onClick();
});

View File

@ -102,7 +102,7 @@ export default function useMentionTooltip(
forceFocus = false,
insertAtEnd = false,
) => {
if (!peer.usernames && !getPeerTitle(lang, peer)) {
if (!peer.hasUsername && !getPeerTitle(lang, peer)) {
return;
}

View File

@ -9,6 +9,7 @@ import {
TME_LINK_PREFIX,
} from '../../../config';
import {
getMainUsername,
getMessageInvoice, getMessageText, isChatChannel,
} from '../../../global/helpers';
import { getPeerTitle } from '../../../global/helpers/peers';
@ -386,10 +387,9 @@ const ActionMessageText = ({
if (isAttachMenu) return lang('ActionAttachMenuBotAllowed');
if (isFromRequest) return lang('ActionWebappBotAllowed');
if (app) {
const link = sender?.usernames?.length
&& `${TME_LINK_PREFIX + sender.usernames[0].username}/${app.shortName}`;
const senderUsername = sender && getMainUsername(sender);
const link = senderUsername && `${TME_LINK_PREFIX + senderUsername}/${app.shortName}`;
const appLink = link
? <Link onClick={() => openTelegramLink({ url: link })}>{app.title}</Link>
: lang('ActionBotAppPlaceholder');
return lang('ActionBotAllowedFromApp', { app: appLink }, { withNodes: true });

View File

@ -47,6 +47,7 @@ import { EMOJI_STATUS_LOOP_LIMIT, MESSAGE_APPEARANCE_DELAY } from '../../../conf
import {
areReactionsEmpty,
getIsDownloading,
getMainUsername,
getMessageContent,
getMessageCustomShape,
getMessageDownloadableMedia,
@ -1499,7 +1500,7 @@ const Message: FC<OwnProps & StateProps> = ({
const senderIsPremium = senderPeer && 'isPremium' in senderPeer && senderPeer.isPremium;
const shouldRenderForwardAvatar = asForwarded && senderPeer;
const hasBotSenderUsername = botSender?.usernames?.length;
const hasBotSenderUsername = botSender?.hasUsername;
return (
<div className="message-title" dir="ltr">
{(senderTitle || asForwarded) ? (
@ -1543,14 +1544,14 @@ const Message: FC<OwnProps & StateProps> = ({
) : !botSender ? (
NBSP
) : undefined}
{Boolean(botSender?.usernames?.length) && (
{botSender?.hasUsername && (
<span className="interactive">
<span className="via">{lang('ViaBot')}</span>
<span
className="sender-title"
onClick={handleViaBotClick}
>
{renderText(`@${botSender.usernames[0].username}`)}
{renderText(`@${getMainUsername(botSender)}`)}
</span>
</span>
)}

View File

@ -3,6 +3,7 @@ import { getActions, withGlobal } from '../../../global';
import type { TabState } from '../../../global/types';
import { getMainUsername } from '../../../global/helpers';
import { selectUser } from '../../../global/selectors';
import { formatDateToString } from '../../../util/dates/dateFormat';
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
@ -130,9 +131,8 @@ export default memo(withGlobal<OwnProps>(
(global): StateProps => {
const freezeUntilDate = global.appConfig?.freezeUntilDate;
const freezeAppealUrl = global.appConfig?.freezeAppealUrl;
const botFreezeAppealId = global.botFreezeAppealId;
const botFreezeAppealUsername = botFreezeAppealId
? selectUser(global, botFreezeAppealId)?.usernames?.[0]?.username : undefined;
const botFreezeAppeal = global.botFreezeAppealId ? selectUser(global, global.botFreezeAppealId) : undefined;
const botFreezeAppealUsername = botFreezeAppeal && getMainUsername(botFreezeAppeal);
return {
freezeUntilDate,

View File

@ -8,7 +8,7 @@ import type { ApiChat } from '../../../api/types';
import { ManagementScreens } from '../../../types';
import { STICKER_SIZE_DISCUSSION_GROUPS } from '../../../config';
import { isChatChannel } from '../../../global/helpers';
import { isChatChannel, isChatPublic } from '../../../global/helpers';
import { selectChat, selectChatFullInfo } from '../../../global/selectors';
import { LOCAL_TGS_URLS } from '../../common/helpers/animatedAssets';
import renderText from '../../common/helpers/renderText';
@ -152,7 +152,7 @@ const ManageDiscussion: FC<OwnProps & StateProps> = ({
const linkedGroup = chatsByIds[linkedGroupId];
if (!linkedGroup) return undefined;
if (linkedGroup.hasPrivateLink) {
if (isChatPublic(linkedGroup)) {
return renderText(
`Do you want to make **${linkedGroup.title}** the discussion board for **${chat!.title}**?`,
['br', 'simple_markdown'],

View File

@ -100,9 +100,9 @@ const ManageInvites: FC<OwnProps & StateProps> = ({
const primaryInvite = exportedInvites?.find(({ isPermanent }) => isPermanent);
const primaryInviteLink = chatMainUsername ? `${TME_LINK_PREFIX}${chatMainUsername}` : primaryInvite?.link;
const temporalInvites = useMemo(() => {
const invites = chat?.usernames ? exportedInvites : exportedInvites?.filter(({ isPermanent }) => !isPermanent);
const invites = chat?.hasUsername ? exportedInvites : exportedInvites?.filter(({ isPermanent }) => !isPermanent);
return invites?.sort(inviteComparator);
}, [chat?.usernames, exportedInvites]);
}, [chat?.hasUsername, exportedInvites]);
const editInvite = (invite: ApiExportedInvite) => {
setEditingExportedInvite({ chatId, invite });

View File

@ -216,7 +216,7 @@ function Story({
isLoadedStory
&& story.isPublic
&& !isChangelog
&& peer?.usernames?.length,
&& peer?.hasUsername,
);
const canShare = Boolean(

View File

@ -27,6 +27,7 @@ import { debounce } from '../../../util/schedulers';
import { getServerTime } from '../../../util/serverTime';
import { extractCurrentThemeParams } from '../../../util/themeStyle';
import { callApi } from '../../../api/gramjs';
import { getMainUsername } from '../../helpers';
import {
getWebAppKey,
} from '../../helpers/bots';
@ -377,7 +378,7 @@ addActionHandler('switchBotInline', (global, actions, payload): ActionReturnType
actions.openChatWithDraft({
text: {
text: `@${botSender.usernames![0].username} ${query}`,
text: `@${getMainUsername(botSender)} ${query}`,
},
chatId: isSamePeer ? chat.id : undefined,
filter,

View File

@ -36,6 +36,7 @@ import {
import { copyTextToClipboard } from '../../../util/clipboard';
import { formatShareText, processDeepLink } from '../../../util/deeplink';
import { isDeepLink } from '../../../util/deepLinkParser';
import { isUserId } from '../../../util/entities/ids';
import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { getOrderedIds } from '../../../util/folderManager';
import {
@ -564,6 +565,30 @@ addActionHandler('loadFullChat', (global, actions, payload): ActionReturnType =>
}
});
addActionHandler('invalidateFullInfo', (global, actions, payload): ActionReturnType => {
const { peerId } = payload;
const isUser = isUserId(peerId);
if (isUser) {
return {
...global,
users: {
...global.users,
fullInfoById: omit(global.users.fullInfoById, [peerId]),
},
};
}
return {
...global,
chats: {
...global.chats,
fullInfoById: omit(global.chats.fullInfoById, [peerId]),
},
};
});
addActionHandler('loadTopChats', (): ActionReturnType => {
runThrottledForLoadTopChats(() => {
loadChats('active', undefined, true);

View File

@ -1,4 +1,4 @@
import type { ApiMessage, ApiUpdateChat } from '../../../api/types';
import type { ApiChat, ApiMessage, ApiUpdateChat } from '../../../api/types';
import type { ActionReturnType } from '../../types';
import { MAIN_THREAD_ID } from '../../../api/types';
@ -43,6 +43,10 @@ import {
} from '../../selectors';
const TYPING_STATUS_CLEAR_DELAY = 6000; // 6 seconds
const INVALIDATE_FULL_CHAT_FIELDS = new Set<keyof ApiChat>([
'boostLevel', 'isForum', 'isLinkedInDiscussion', 'fakeType', 'restrictionReason', 'isJoinToSend', 'isJoinRequest',
'type',
]);
addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
switch (update['@type']) {
@ -93,6 +97,15 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
}
});
if (localChat) {
const chatUpdate = update.chat;
const changedFields = (Object.keys(chatUpdate) as (keyof ApiChat)[])
.filter((key) => localChat[key] !== chatUpdate[key]);
if (changedFields.some((key) => INVALIDATE_FULL_CHAT_FIELDS.has(key))) {
actions.invalidateFullInfo({ peerId: update.id });
}
}
return undefined;
}

View File

@ -328,7 +328,7 @@ export function getFolderDescriptionText(lang: OldLangFn, folder: ApiChatFolder,
}
export function isChatPublic(chat: ApiChat) {
return chat.usernames?.some(({ isActive }) => isActive);
return chat.hasUsername;
}
export function getOrderedTopics(

View File

@ -14,6 +14,7 @@ import {
getHasAdminRight,
getPrivateChatUserId,
isChatChannel,
isChatPublic,
isChatSuperGroup,
isHistoryClearMessage,
isUserBot,
@ -256,7 +257,7 @@ export function selectCanInviteToChat<T extends GlobalState>(global: T, chatId:
// https://github.com/TelegramMessenger/Telegram-iOS/blob/5126be83b3b9578fb014eb52ca553da9e7a8b83a/submodules/TelegramCore/Sources/TelegramEngine/Peers/Communities.swift#L6
return !chat.migratedTo && Boolean(!isUserId(chatId) && ((isChatChannel(chat) || isChatSuperGroup(chat)) ? (
chat.isCreator || getHasAdminRight(chat, 'inviteUsers')
|| (chat.usernames?.length && !chat.isJoinRequest)
|| (isChatPublic(chat) && !chat.isJoinRequest)
) : (chat.isCreator || getHasAdminRight(chat, 'inviteUsers'))));
}

View File

@ -1154,6 +1154,9 @@ export interface ActionPayloads {
withPhotos?: boolean;
force?: boolean;
};
invalidateFullInfo: {
peerId: string;
};
updateChatPhoto: {
chatId: string;
photo: ApiPhoto;

View File

@ -1112,6 +1112,7 @@ export interface LangPair {
'ComposerSilentPostingEnabledTootlip': undefined;
'ComposerSilentPostingDisabledTootlip': undefined;
'ComposerPlaceholder': undefined;
'ComposerPlaceholderAnonymous': undefined;
'ComposerPlaceholderBroadcast': undefined;
'ComposerPlaceholderBroadcastSilent': undefined;
'ComposerPlaceholderTopicGeneral': undefined;