Message: Allow downloading animated emojis (#6340)

This commit is contained in:
zubiden 2025-10-08 12:33:50 +02:00 committed by Alexander Zinchuk
parent a1d0d55c51
commit 091ea45a1a
9 changed files with 44 additions and 46 deletions

View File

@ -1 +1 @@
10.9.75
12.0.0

View File

@ -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: {

View File

@ -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;

View File

@ -826,7 +826,7 @@ const Message: FC<OwnProps & StateProps> = ({
});
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)

View File

@ -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');

View File

@ -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);
}

View File

@ -274,19 +274,18 @@ export function updateChatMessage<T extends GlobalState>(
}
}
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 extends GlobalState>(
): 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,
};

View File

@ -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<T extends GlobalState>(global: T, message: ApiMessage) {
const { isSensitiveEnabled } = selectSettingsKeys(global);
@ -46,19 +46,14 @@ export function selectIsMediaNsfw<T extends GlobalState>(global: T, message: Api
}
export function selectMessageDownloadableMedia<T extends GlobalState>(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)

View File

@ -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<ApiFormattedText> = {
export function addTimestampEntities<T extends ApiFormattedText>(apiText: T): T {
const resultText: Required<ApiFormattedText> & T = {
...apiText,
text: apiText.text,
entities: apiText.entities?.filter((e) => e.type !== ApiMessageEntityTypes.Timestamp) || [],
};