diff --git a/public/version.txt b/public/version.txt index e539c5db4..4044f9086 100644 --- a/public/version.txt +++ b/public/version.txt @@ -1 +1 @@ -10.9.75 +12.0.0 diff --git a/src/api/gramjs/apiBuilders/messages.ts b/src/api/gramjs/apiBuilders/messages.ts index 1caeb1ea6..c8543c5db 100644 --- a/src/api/gramjs/apiBuilders/messages.ts +++ b/src/api/gramjs/apiBuilders/messages.ts @@ -226,6 +226,8 @@ export function buildApiMessageWithChatId( const isProtected = mtpMessage.noforwards || isInvoiceMedia; const isForwardingAllowed = !mtpMessage.noforwards; const emojiOnlyCount = getEmojiOnlyCountForMessage(content, groupedId); + if (content.text && emojiOnlyCount) content.text.emojiOnlyCount = emojiOnlyCount; + const hasComments = mtpMessage.replies?.comments; const senderBoosts = mtpMessage.fromBoostsApplied; const factCheck = mtpMessage.factcheck && buildApiFactCheck(mtpMessage.factcheck); @@ -252,7 +254,6 @@ export function buildApiMessageWithChatId( isSilent: mtpMessage.silent, isPinned: mtpMessage.pinned, reactions: mtpMessage.reactions && buildMessageReactions(mtpMessage.reactions), - emojiOnlyCount, ...(mtpMessage.replyTo && { replyInfo: buildApiReplyInfo(mtpMessage.replyTo, mtpMessage) }), ...(mtpMessage.suggestedPost && { suggestedPostInfo: buildApiSuggestedPost(mtpMessage.suggestedPost) }), forwardInfo, @@ -453,7 +454,9 @@ export function buildLocalMessage( const localPoll = poll && buildNewPoll(poll, localId); const localTodo = todo && buildNewTodo(todo); - const formattedText = text ? addTimestampEntities({ text, entities }) : undefined; + const formattedText = text ? addTimestampEntities( + { text, entities, emojiOnlyCount: undefined }, + ) : undefined; const message = { id: localId, @@ -486,14 +489,10 @@ export function buildLocalMessage( } satisfies ApiMessage; const emojiOnlyCount = getEmojiOnlyCountForMessage(message.content, message.groupedId); - - const finalMessage: ApiMessage = { - ...message, - ...(emojiOnlyCount && { emojiOnlyCount }), - }; + if (emojiOnlyCount && message.content.text) message.content.text.emojiOnlyCount = emojiOnlyCount; return { - message: finalMessage, + message, poll: localPoll, }; } @@ -542,6 +541,7 @@ export function buildLocalForwardedMessage({ } : content.text; const textWithTimestamps = strippedText && addTimestampEntities(strippedText); const emojiOnlyCount = getEmojiOnlyCountForMessage(content, groupedId); + if (emojiOnlyCount && textWithTimestamps) textWithTimestamps.emojiOnlyCount = emojiOnlyCount; const updatedContent = { ...content, @@ -572,7 +572,6 @@ export function buildLocalForwardedMessage({ isInvertedMedia, ...(toThreadId && toChat?.isForum && { isTopicReply: true }), - ...(emojiOnlyCount && { emojiOnlyCount }), // Forward info doesn't get added when user forwards own messages and when forwarding audio ...(message.chatId !== currentUserId && !isAudio && !noAuthors && { forwardInfo: { diff --git a/src/api/types/messages.ts b/src/api/types/messages.ts index c494705c6..439469361 100644 --- a/src/api/types/messages.ts +++ b/src/api/types/messages.ts @@ -577,8 +577,12 @@ export interface ApiFormattedText { entities?: ApiMessageEntity[]; } +export interface ApiFormattedTextWithEmojiOnlyCount extends ApiFormattedText { + emojiOnlyCount?: number; +} + export type MediaContent = { - text?: ApiFormattedText; + text?: ApiFormattedTextWithEmojiOnlyCount; photo?: ApiPhoto; video?: ApiVideo; altVideos?: ApiVideo[]; @@ -661,7 +665,6 @@ export interface ApiMessage { isForwardingAllowed?: boolean; transcriptionId?: string; isTranscriptionError?: boolean; - emojiOnlyCount?: number; reactors?: { nextOffset?: string; count: number; diff --git a/src/components/middle/message/Message.tsx b/src/components/middle/message/Message.tsx index faf941c09..9d68cd82a 100644 --- a/src/components/middle/message/Message.tsx +++ b/src/components/middle/message/Message.tsx @@ -826,7 +826,7 @@ const Message: FC = ({ }); const withAppendix = contentClassName.includes('has-appendix'); - const emojiSize = getCustomEmojiSize(message.emojiOnlyCount); + const emojiSize = getCustomEmojiSize(text?.emojiOnlyCount); const paidMessageStarsInMeta = !isChatWithUser ? (isAlbum && paidMessageStars ? album.messages.length * paidMessageStars : paidMessageStars) diff --git a/src/components/middle/message/helpers/buildContentClassName.ts b/src/components/middle/message/helpers/buildContentClassName.ts index b5d7422cb..b4644fad3 100644 --- a/src/components/middle/message/helpers/buildContentClassName.ts +++ b/src/components/middle/message/helpers/buildContentClassName.ts @@ -81,10 +81,10 @@ export function buildContentClassName( classNames.push(peerColorClass); } - if (!isMedia && message.emojiOnlyCount) { + if (!isMedia && text?.emojiOnlyCount) { classNames.push('emoji-only'); - if (message.emojiOnlyCount <= EMOJI_SIZES) { - classNames.push(`emoji-only-${message.emojiOnlyCount}`); + if (text.emojiOnlyCount <= EMOJI_SIZES) { + classNames.push(`emoji-only-${text.emojiOnlyCount}`); } } else if (hasText) { classNames.push('text'); diff --git a/src/global/helpers/messages.ts b/src/global/helpers/messages.ts index 75d1cc4ae..132891908 100644 --- a/src/global/helpers/messages.ts +++ b/src/global/helpers/messages.ts @@ -119,29 +119,31 @@ export function getMessageCustomShape(message: ApiMessage): boolean { const hasOtherFormatting = text?.entities?.some((entity) => entity.type !== ApiMessageEntityTypes.CustomEmoji); - return Boolean(message.emojiOnlyCount && !hasOtherFormatting); + return Boolean(text.emojiOnlyCount && !hasOtherFormatting); } -export function getMessageSingleRegularEmoji(message: ApiMessage) { +export function getMessageSingleRegularEmoji(message: MediaContainer) { const { text } = message.content; - if (text?.entities?.length || message.emojiOnlyCount !== 1) { + if (!text || text.entities?.length || text.emojiOnlyCount !== 1) { return undefined; } - return text!.text; + return text.text; } -export function getMessageSingleCustomEmoji(message: ApiMessage): string | undefined { +export function getMessageSingleCustomEmoji(message: MediaContainer): string | undefined { const { text } = message.content; + const firstEntity = text?.entities?.[0]; if (text?.entities?.length !== 1 - || text.entities[0].type !== ApiMessageEntityTypes.CustomEmoji - || message.emojiOnlyCount !== 1) { + || firstEntity?.type !== ApiMessageEntityTypes.CustomEmoji + || firstEntity.offset !== 0 + || firstEntity.length !== text.text.length) { return undefined; } - return text.entities[0].documentId; + return firstEntity.documentId; } export function getFirstLinkInMessage(message: ApiMessage) { @@ -250,7 +252,7 @@ export function isMessageTranslatable(message: ApiMessage, allowOutgoing?: boole const isServiceNotification = isServiceNotificationMessage(message); const isAction = isActionMessage(message); - return Boolean(text?.text.length && !message.emojiOnlyCount && !game && (allowOutgoing || !message.isOutgoing) + return Boolean(text?.text.length && !text.emojiOnlyCount && !game && (allowOutgoing || !message.isOutgoing) && !isLocal && !isServiceNotification && !isAction && !message.isScheduled); } diff --git a/src/global/reducers/messages.ts b/src/global/reducers/messages.ts index 1b12b51f8..cc5b69654 100644 --- a/src/global/reducers/messages.ts +++ b/src/global/reducers/messages.ts @@ -274,19 +274,18 @@ export function updateChatMessage( } } - let emojiOnlyCount = message?.emojiOnlyCount; let text = message?.content?.text; if (messageUpdate.content) { - emojiOnlyCount = getEmojiOnlyCountForMessage( + const emojiOnlyCount = getEmojiOnlyCountForMessage( messageUpdate.content, message?.groupedId || messageUpdate.groupedId, ); text = messageUpdate.content.text ? addTimestampEntities(messageUpdate.content.text) : text; + if (text) text.emojiOnlyCount = emojiOnlyCount; } const updatedMessage = omitUndefined({ ...message, ...messageUpdate, - emojiOnlyCount, text, }); @@ -305,19 +304,18 @@ export function updateScheduledMessage( ): T { const message = selectScheduledMessage(global, chatId, messageId)!; - let emojiOnlyCount = message?.emojiOnlyCount; let text = message?.content?.text; if (messageUpdate.content) { - emojiOnlyCount = getEmojiOnlyCountForMessage( + const emojiOnlyCount = getEmojiOnlyCountForMessage( messageUpdate.content, message?.groupedId || messageUpdate.groupedId, ); text = messageUpdate.content.text ? addTimestampEntities(messageUpdate.content.text) : text; + if (text) text.emojiOnlyCount = emojiOnlyCount; } const updatedMessage = { ...message, ...messageUpdate, - emojiOnlyCount, text, }; diff --git a/src/global/selectors/media.ts b/src/global/selectors/media.ts index 8a3be2c2f..30fa4cce5 100644 --- a/src/global/selectors/media.ts +++ b/src/global/selectors/media.ts @@ -3,7 +3,6 @@ import type { } from '../types'; import { type ApiMessage, - ApiMessageEntityTypes, type MediaContainer, type SizeTarget, } from '../../api/types'; @@ -16,8 +15,9 @@ import { getMessageInvoice, getMessageMediaHash, getMessagePhoto, + getMessageSingleCustomEmoji, + getMessageSingleRegularEmoji, getMessageSticker, - getMessageText, getMessageVideo, getMessageVoice, getWebPageAudio, @@ -31,7 +31,7 @@ import { selectWebPageFromMessage, } from './messages'; import { selectSettingsKeys } from './settings'; -import { selectCustomEmoji } from './symbols'; +import { selectAnimatedEmoji, selectCustomEmoji } from './symbols'; export function selectIsMediaNsfw(global: T, message: ApiMessage) { const { isSensitiveEnabled } = selectSettingsKeys(global); @@ -46,19 +46,14 @@ export function selectIsMediaNsfw(global: T, message: Api } export function selectMessageDownloadableMedia(global: T, message: MediaContainer) { - const text = getMessageText(message); - const firstEntity = text?.entities?.[0]; - const isSingleCustomEmoji = firstEntity - && text.entities?.length === 1 - && firstEntity.type === ApiMessageEntityTypes.CustomEmoji - && firstEntity.offset === 0 - && firstEntity.length === text.text.length; - - const customEmoji = isSingleCustomEmoji ? selectCustomEmoji(global, firstEntity.documentId) : undefined; + const singleEmoji = getMessageSingleRegularEmoji(message); + const animatedEmoji = singleEmoji && selectAnimatedEmoji(global, singleEmoji); + const animatedCustomEmojiId = getMessageSingleCustomEmoji(message); + const customEmoji = animatedCustomEmojiId && selectCustomEmoji(global, animatedCustomEmojiId); const webPage = selectWebPageFromMessage(global, message); return ( - customEmoji + customEmoji || animatedEmoji || getMessagePhoto(message) || getMessageVideo(message) || getMessageDocument(message) diff --git a/src/util/dates/timestamp.ts b/src/util/dates/timestamp.ts index 6a59df1a7..c0b665867 100644 --- a/src/util/dates/timestamp.ts +++ b/src/util/dates/timestamp.ts @@ -8,8 +8,9 @@ import { getSeconds } from './units'; const TIMESTAMP_RE = /\b(?:(\d{1,2}):)?([0-5]?\d):([0-5]\d)\b/g; -export function addTimestampEntities(apiText: ApiFormattedText): ApiFormattedText { - const resultText: Required = { +export function addTimestampEntities(apiText: T): T { + const resultText: Required & T = { + ...apiText, text: apiText.text, entities: apiText.entities?.filter((e) => e.type !== ApiMessageEntityTypes.Timestamp) || [], };