From 91b8463b359f6199025c39b56efd4818ef711cb6 Mon Sep 17 00:00:00 2001 From: zubiden <19638254+zubiden@users.noreply.github.com> Date: Wed, 4 Jun 2025 20:41:33 +0200 Subject: [PATCH] Send As: Fix error in some chats (#5966) --- src/api/gramjs/apiBuilders/chats.ts | 88 +++++++++++-------- src/api/gramjs/apiBuilders/users.ts | 3 +- src/api/types/chats.ts | 4 +- src/api/types/users.ts | 1 + src/assets/localization/fallback.strings | 1 + src/components/common/Composer.tsx | 36 ++++---- src/components/common/pickers/PeerPicker.tsx | 4 +- .../middle/composer/ChatCommandTooltip.tsx | 3 +- .../composer/hooks/useMentionTooltip.ts | 2 +- .../middle/message/ActionMessageText.tsx | 6 +- src/components/middle/message/Message.tsx | 7 +- .../frozenAccount/FrozenAccountModal.tsx | 6 +- .../right/management/ManageDiscussion.tsx | 4 +- .../right/management/ManageInvites.tsx | 4 +- src/components/story/Story.tsx | 2 +- src/global/actions/api/bots.ts | 3 +- src/global/actions/api/chats.ts | 25 ++++++ src/global/actions/apiUpdaters/chats.ts | 15 +++- src/global/helpers/chats.ts | 2 +- src/global/selectors/chats.ts | 3 +- src/global/types/actions.ts | 3 + src/types/language.d.ts | 1 + 22 files changed, 142 insertions(+), 81 deletions(-) diff --git a/src/api/gramjs/apiBuilders/chats.ts b/src/api/gramjs/apiBuilders/chats.ts index 4cbecbf35..5c5f66c21 100644 --- a/src/api/gramjs/apiBuilders/chats.ts +++ b/src/api/gramjs/apiBuilders/chats.ts @@ -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), }; } diff --git a/src/api/gramjs/apiBuilders/users.ts b/src/api/gramjs/apiBuilders/users.ts index 274f1cb5d..32d30f738 100644 --- a/src/api/gramjs/apiBuilders/users.ts +++ b/src/api/gramjs/apiBuilders/users.ts @@ -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) }), diff --git a/src/api/types/chats.ts b/src/api/types/chats.ts index 5434da51c..9f52657ea 100644 --- a/src/api/types/chats.ts +++ b/src/api/types/chats.ts @@ -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; diff --git a/src/api/types/users.ts b/src/api/types/users.ts index 89549e4c7..4605adc5e 100644 --- a/src/api/types/users.ts +++ b/src/api/types/users.ts @@ -20,6 +20,7 @@ export interface ApiUser { lastName?: string; noStatus?: boolean; usernames?: ApiUsername[]; + hasUsername?: boolean; phoneNumber: string; accessHash?: string; hasVideoAvatar?: boolean; diff --git a/src/assets/localization/fallback.strings b/src/assets/localization/fallback.strings index 1eb8ee89b..29b05a961 100644 --- a/src/assets/localization/fallback.strings +++ b/src/assets/localization/fallback.strings @@ -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}"; diff --git a/src/components/common/Composer.tsx b/src/components/common/Composer.tsx index f69ef1f72..c75dd7f20 100644 --- a/src/components/common/Composer.tsx +++ b/src/components/common/Composer.tsx @@ -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; botCommands?: ApiBotCommand[] | false; botMenuButton?: ApiBotMenuButton; - sendAsUser?: ApiUser; - sendAsChat?: ApiChat; + sendAsPeer?: ApiPeer; sendAsId?: string; editingDraft?: ApiFormattedText; requestedDraft?: ApiFormattedText; @@ -374,8 +375,7 @@ const Composer: FC = ({ inlineBots, isInlineBotLoading, botCommands, - sendAsUser, - sendAsChat, + sendAsPeer, sendAsId, editingDraft, requestedDraft, @@ -472,8 +472,7 @@ const Composer: FC = ({ 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 = ({ 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 = ({ 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 = ({ )} - {canShowSendAs && (sendAsUser || sendAsChat) && ( + {canShowSendAs && sendAsPeer && ( @@ -2363,13 +2369,8 @@ export default memo(withGlobal( 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( isInlineBotLoading: tabState.inlineBots.isLoading, botCommands: userFullInfo ? (userFullInfo.botInfo?.commands || false) : undefined, botMenuButton: userFullInfo?.botInfo?.menuButton, - sendAsUser, - sendAsChat, + sendAsPeer, sendAsId, editingDraft, requestedDraft, diff --git a/src/components/common/pickers/PeerPicker.tsx b/src/components/common/pickers/PeerPicker.tsx index 2d584a58d..cfde81a49 100644 --- a/src/components/common/pickers/PeerPicker.tsx +++ b/src/components/common/pickers/PeerPicker.tsx @@ -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 = ({ if (!peer) return undefined; if (withPeerUsernames) { - const username = peer.usernames?.[0]?.username; + const username = getMainUsername(peer); if (username) { return [`@${username}`]; } diff --git a/src/components/middle/composer/ChatCommandTooltip.tsx b/src/components/middle/composer/ChatCommandTooltip.tsx index 77714e5f3..6b34dd06c 100644 --- a/src/components/middle/composer/ChatCommandTooltip.tsx +++ b/src/components/middle/composer/ChatCommandTooltip.tsx @@ -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 = ({ const bot = usersById[botId]; sendBotCommand({ - command: `/${command}${withUsername && bot ? `@${bot.usernames![0].username}` : ''}`, + command: `/${command}${withUsername && bot ? `@${getMainUsername(bot)}` : ''}`, }); onClick(); }); diff --git a/src/components/middle/composer/hooks/useMentionTooltip.ts b/src/components/middle/composer/hooks/useMentionTooltip.ts index 1634c55d9..80b7e346b 100644 --- a/src/components/middle/composer/hooks/useMentionTooltip.ts +++ b/src/components/middle/composer/hooks/useMentionTooltip.ts @@ -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; } diff --git a/src/components/middle/message/ActionMessageText.tsx b/src/components/middle/message/ActionMessageText.tsx index 8d73e9dd5..dec16a76f 100644 --- a/src/components/middle/message/ActionMessageText.tsx +++ b/src/components/middle/message/ActionMessageText.tsx @@ -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 - ? openTelegramLink({ url: link })}>{app.title} : lang('ActionBotAppPlaceholder'); return lang('ActionBotAllowedFromApp', { app: appLink }, { withNodes: true }); diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx index 0cd631c02..0a926ce6e 100644 --- a/src/components/middle/message/Message.tsx +++ b/src/components/middle/message/Message.tsx @@ -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 = ({ const senderIsPremium = senderPeer && 'isPremium' in senderPeer && senderPeer.isPremium; const shouldRenderForwardAvatar = asForwarded && senderPeer; - const hasBotSenderUsername = botSender?.usernames?.length; + const hasBotSenderUsername = botSender?.hasUsername; return (
{(senderTitle || asForwarded) ? ( @@ -1543,14 +1544,14 @@ const Message: FC = ({ ) : !botSender ? ( NBSP ) : undefined} - {Boolean(botSender?.usernames?.length) && ( + {botSender?.hasUsername && ( {lang('ViaBot')} - {renderText(`@${botSender.usernames[0].username}`)} + {renderText(`@${getMainUsername(botSender)}`)} )} diff --git a/src/components/modals/frozenAccount/FrozenAccountModal.tsx b/src/components/modals/frozenAccount/FrozenAccountModal.tsx index 0654f688e..80d69e476 100644 --- a/src/components/modals/frozenAccount/FrozenAccountModal.tsx +++ b/src/components/modals/frozenAccount/FrozenAccountModal.tsx @@ -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( (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, diff --git a/src/components/right/management/ManageDiscussion.tsx b/src/components/right/management/ManageDiscussion.tsx index 3877023ad..72128d6d2 100644 --- a/src/components/right/management/ManageDiscussion.tsx +++ b/src/components/right/management/ManageDiscussion.tsx @@ -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 = ({ 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'], diff --git a/src/components/right/management/ManageInvites.tsx b/src/components/right/management/ManageInvites.tsx index 4c8fa7db2..95bf1c047 100644 --- a/src/components/right/management/ManageInvites.tsx +++ b/src/components/right/management/ManageInvites.tsx @@ -100,9 +100,9 @@ const ManageInvites: FC = ({ 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 }); diff --git a/src/components/story/Story.tsx b/src/components/story/Story.tsx index 3d4fcf76c..b0b95edbb 100644 --- a/src/components/story/Story.tsx +++ b/src/components/story/Story.tsx @@ -216,7 +216,7 @@ function Story({ isLoadedStory && story.isPublic && !isChangelog - && peer?.usernames?.length, + && peer?.hasUsername, ); const canShare = Boolean( diff --git a/src/global/actions/api/bots.ts b/src/global/actions/api/bots.ts index c8030a11f..6742955cb 100644 --- a/src/global/actions/api/bots.ts +++ b/src/global/actions/api/bots.ts @@ -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, diff --git a/src/global/actions/api/chats.ts b/src/global/actions/api/chats.ts index 4974d0698..87b2092c5 100644 --- a/src/global/actions/api/chats.ts +++ b/src/global/actions/api/chats.ts @@ -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); diff --git a/src/global/actions/apiUpdaters/chats.ts b/src/global/actions/apiUpdaters/chats.ts index 8523a2d73..c7252c4fc 100644 --- a/src/global/actions/apiUpdaters/chats.ts +++ b/src/global/actions/apiUpdaters/chats.ts @@ -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([ + '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; } diff --git a/src/global/helpers/chats.ts b/src/global/helpers/chats.ts index e1ab78458..5f838c49c 100644 --- a/src/global/helpers/chats.ts +++ b/src/global/helpers/chats.ts @@ -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( diff --git a/src/global/selectors/chats.ts b/src/global/selectors/chats.ts index 0c0f98a2b..55bc44de7 100644 --- a/src/global/selectors/chats.ts +++ b/src/global/selectors/chats.ts @@ -14,6 +14,7 @@ import { getHasAdminRight, getPrivateChatUserId, isChatChannel, + isChatPublic, isChatSuperGroup, isHistoryClearMessage, isUserBot, @@ -256,7 +257,7 @@ export function selectCanInviteToChat(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')))); } diff --git a/src/global/types/actions.ts b/src/global/types/actions.ts index 9868af4f6..b1ff26f2b 100644 --- a/src/global/types/actions.ts +++ b/src/global/types/actions.ts @@ -1154,6 +1154,9 @@ export interface ActionPayloads { withPhotos?: boolean; force?: boolean; }; + invalidateFullInfo: { + peerId: string; + }; updateChatPhoto: { chatId: string; photo: ApiPhoto; diff --git a/src/types/language.d.ts b/src/types/language.d.ts index 1f079bdd9..0e5e9bd89 100644 --- a/src/types/language.d.ts +++ b/src/types/language.d.ts @@ -1112,6 +1112,7 @@ export interface LangPair { 'ComposerSilentPostingEnabledTootlip': undefined; 'ComposerSilentPostingDisabledTootlip': undefined; 'ComposerPlaceholder': undefined; + 'ComposerPlaceholderAnonymous': undefined; 'ComposerPlaceholderBroadcast': undefined; 'ComposerPlaceholderBroadcastSilent': undefined; 'ComposerPlaceholderTopicGeneral': undefined;