Message: Allow downloading single custom emoji (#6173)
This commit is contained in:
parent
6ad9b8575f
commit
201c510b11
@ -80,6 +80,7 @@ import {
|
||||
selectChatMessage,
|
||||
selectChatType,
|
||||
selectCurrentMessageList,
|
||||
selectCustomEmoji,
|
||||
selectDraft,
|
||||
selectEditingDraft,
|
||||
selectEditingMessage,
|
||||
@ -1783,7 +1784,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
if (reaction.type === 'custom') {
|
||||
const sticker = getGlobal().customEmojis.byId[reaction.documentId];
|
||||
const sticker = selectCustomEmoji(getGlobal(), reaction.documentId);
|
||||
if (!sticker) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import {
|
||||
import {
|
||||
selectChat,
|
||||
selectCurrentMessageList,
|
||||
selectCustomEmoji,
|
||||
selectPeerPhotos,
|
||||
selectTabState,
|
||||
selectThreadMessagesCount,
|
||||
@ -409,7 +410,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const { animationLevel } = selectSharedSettings(global);
|
||||
|
||||
const emojiStatus = (user || chat)?.emojiStatus;
|
||||
const emojiStatusSticker = emojiStatus ? global.customEmojis.byId[emojiStatus.documentId] : undefined;
|
||||
const emojiStatusSticker = emojiStatus ? selectCustomEmoji(global, emojiStatus.documentId) : undefined;
|
||||
const emojiStatusSlug = emojiStatus?.type === 'collectible' ? emojiStatus.slug : undefined;
|
||||
|
||||
return {
|
||||
|
||||
@ -4,7 +4,7 @@ import { getGlobal } from '../../../global';
|
||||
import type { ApiSticker } from '../../../api/types';
|
||||
import type { GlobalState } from '../../../global/types';
|
||||
|
||||
import { selectCanPlayAnimatedEmojis } from '../../../global/selectors';
|
||||
import { selectCanPlayAnimatedEmojis, selectCustomEmoji } from '../../../global/selectors';
|
||||
import { addCustomEmojiCallback, removeCustomEmojiCallback } from '../../../util/emoji/customEmojiManager';
|
||||
|
||||
import useEnsureCustomEmoji from '../../../hooks/useEnsureCustomEmoji';
|
||||
@ -12,7 +12,7 @@ import useLastCallback from '../../../hooks/useLastCallback';
|
||||
|
||||
export default function useCustomEmoji(documentId?: string) {
|
||||
const [customEmoji, setCustomEmoji] = useState<ApiSticker | undefined>(
|
||||
documentId ? getGlobal().customEmojis.byId[documentId] : undefined,
|
||||
documentId ? selectCustomEmoji(getGlobal(), documentId) : undefined,
|
||||
);
|
||||
const [canPlay, setCanPlay] = useState(selectCanPlayAnimatedEmojis(getGlobal()));
|
||||
|
||||
@ -36,7 +36,7 @@ export default function useCustomEmoji(documentId?: string) {
|
||||
return () => {
|
||||
removeCustomEmojiCallback(handleGlobalChange);
|
||||
};
|
||||
}, [customEmoji, documentId, handleGlobalChange]);
|
||||
}, [documentId]);
|
||||
|
||||
return { customEmoji, canPlay };
|
||||
}
|
||||
|
||||
@ -16,7 +16,13 @@ import type { LangPair } from '../../../types/language';
|
||||
|
||||
import { PREMIUM_FEATURE_SECTIONS, TME_LINK_PREFIX } from '../../../config';
|
||||
import { getUserFullName } from '../../../global/helpers';
|
||||
import { selectIsCurrentUserPremium, selectStickerSet, selectTabState, selectUser } from '../../../global/selectors';
|
||||
import {
|
||||
selectCustomEmoji,
|
||||
selectIsCurrentUserPremium,
|
||||
selectStickerSet,
|
||||
selectTabState,
|
||||
selectUser,
|
||||
} from '../../../global/selectors';
|
||||
import { selectPremiumLimit } from '../../../global/selectors/limits';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { formatCurrency } from '../../../util/formatCurrency';
|
||||
@ -508,7 +514,7 @@ export default memo(withGlobal<OwnProps>((global): StateProps => {
|
||||
} = selectTabState(global);
|
||||
|
||||
const fromUser = premiumModal?.fromUserId ? selectUser(global, premiumModal.fromUserId) : undefined;
|
||||
const fromUserStatusEmoji = fromUser?.emojiStatus ? global.customEmojis.byId[fromUser.emojiStatus.documentId]
|
||||
const fromUserStatusEmoji = fromUser?.emojiStatus ? selectCustomEmoji(global, fromUser.emojiStatus.documentId)
|
||||
: undefined;
|
||||
const fromUserStatusSet = fromUserStatusEmoji ? selectStickerSet(global, fromUserStatusEmoji.stickerSetInfo)
|
||||
: undefined;
|
||||
|
||||
@ -23,6 +23,7 @@ import {
|
||||
import {
|
||||
selectChat,
|
||||
selectChatMessage,
|
||||
selectCustomEmoji,
|
||||
selectIsChatWithSelf,
|
||||
selectIsInSelectMode,
|
||||
selectIsRightColumnShown,
|
||||
@ -403,7 +404,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const typingStatus = selectThreadParam(global, chatId, threadId, 'typingStatus');
|
||||
|
||||
const emojiStatus = peer?.emojiStatus;
|
||||
const emojiStatusSticker = emojiStatus && global.customEmojis.byId[emojiStatus.documentId];
|
||||
const emojiStatusSticker = emojiStatus && selectCustomEmoji(global, emojiStatus.documentId);
|
||||
const emojiStatusSlug = emojiStatus?.type === 'collectible' ? emojiStatus.slug : undefined;
|
||||
|
||||
const isSavedDialog = getIsSavedDialog(chatId, threadId, global.currentUserId);
|
||||
|
||||
@ -4,6 +4,7 @@ import type { ApiMessageEntityCustomEmoji, ApiSticker } from '../../../../api/ty
|
||||
import { ApiMessageEntityTypes } from '../../../../api/types';
|
||||
|
||||
import { EMOJI_SIZES } from '../../../../config';
|
||||
import { selectCustomEmoji } from '../../../../global/selectors';
|
||||
import buildClassName from '../../../../util/buildClassName';
|
||||
import { getInputCustomEmojiParams } from '../../../../util/emoji/customEmojiManager';
|
||||
import { REM } from '../../../common/helpers/mediaDimensions';
|
||||
@ -29,7 +30,7 @@ export function buildCustomEmojiHtml(emoji: ApiSticker) {
|
||||
}
|
||||
|
||||
export function buildCustomEmojiHtmlFromEntity(rawText: string, entity: ApiMessageEntityCustomEmoji) {
|
||||
const customEmoji = getGlobal().customEmojis.byId[entity.documentId];
|
||||
const customEmoji = selectCustomEmoji(getGlobal(), entity.documentId);
|
||||
const [isPlaceholder, src, uniqueId] = getInputCustomEmojiParams(customEmoji);
|
||||
|
||||
const className = buildClassName(
|
||||
|
||||
@ -10,7 +10,7 @@ import type { Signal } from '../../../../util/signals';
|
||||
|
||||
import { requestMeasure } from '../../../../lib/fasterdom/fasterdom';
|
||||
import { ensureRLottie } from '../../../../lib/rlottie/RLottie.async';
|
||||
import { selectIsAlwaysHighPriorityEmoji } from '../../../../global/selectors';
|
||||
import { selectCustomEmoji, selectIsAlwaysHighPriorityEmoji } from '../../../../global/selectors';
|
||||
import AbsoluteVideo from '../../../../util/AbsoluteVideo';
|
||||
import {
|
||||
addCustomEmojiInputRenderCallback,
|
||||
@ -97,7 +97,7 @@ export default function useInputCustomEmojis(
|
||||
return;
|
||||
}
|
||||
|
||||
const customEmoji = global.customEmojis.byId[documentId];
|
||||
const customEmoji = selectCustomEmoji(global, documentId);
|
||||
if (!customEmoji) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import {
|
||||
selectAnimatedEmojiEffect,
|
||||
selectAnimatedEmojiSound,
|
||||
selectCanPlayAnimatedEmojis,
|
||||
selectCustomEmoji,
|
||||
} from '../../../global/selectors';
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { LIKE_STICKER_ID } from '../../common/helpers/mediaDimensions';
|
||||
@ -78,7 +79,7 @@ const AnimatedCustomEmoji: FC<OwnProps & StateProps> = ({
|
||||
};
|
||||
|
||||
export default memo(withGlobal<OwnProps>((global, { customEmojiId, withEffects }) => {
|
||||
const sticker = global.customEmojis.byId[customEmojiId];
|
||||
const sticker = selectCustomEmoji(global, customEmojiId);
|
||||
|
||||
return {
|
||||
sticker,
|
||||
|
||||
@ -115,6 +115,7 @@ import {
|
||||
selectCurrentChat,
|
||||
selectCurrentMessageList,
|
||||
selectCurrentViewedStory,
|
||||
selectCustomEmoji,
|
||||
selectDraft,
|
||||
selectEditingId,
|
||||
selectEditingMessage,
|
||||
@ -1552,7 +1553,7 @@ addActionHandler('transcribeAudio', async (global, actions, payload): Promise<vo
|
||||
addActionHandler('loadCustomEmojis', async (global, actions, payload): Promise<void> => {
|
||||
const { ids, ignoreCache } = payload;
|
||||
const newCustomEmojiIds = ignoreCache ? ids
|
||||
: unique(ids.filter((documentId) => !global.customEmojis.byId[documentId]));
|
||||
: unique(ids.filter((documentId) => !selectCustomEmoji(global, documentId)));
|
||||
const customEmoji = await callApi('fetchCustomEmoji', {
|
||||
documentId: newCustomEmojiIds,
|
||||
});
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
import type {
|
||||
ApiMessage,
|
||||
MediaContainer,
|
||||
SizeTarget,
|
||||
} from '../../api/types';
|
||||
import type {
|
||||
GlobalState,
|
||||
} from '../types';
|
||||
import {
|
||||
type ApiMessage,
|
||||
ApiMessageEntityTypes,
|
||||
type MediaContainer,
|
||||
type SizeTarget,
|
||||
} from '../../api/types';
|
||||
|
||||
import { NSFW_RESTRICTION_REASON } from '../../config';
|
||||
import {
|
||||
@ -16,6 +17,7 @@ import {
|
||||
getMessageMediaHash,
|
||||
getMessagePhoto,
|
||||
getMessageSticker,
|
||||
getMessageText,
|
||||
getMessageVideo,
|
||||
getMessageVoice,
|
||||
getWebPageAudio,
|
||||
@ -29,6 +31,7 @@ import {
|
||||
selectWebPageFromMessage,
|
||||
} from './messages';
|
||||
import { selectSettingsKeys } from './settings';
|
||||
import { selectCustomEmoji } from './symbols';
|
||||
|
||||
export function selectIsMediaNsfw<T extends GlobalState>(global: T, message: ApiMessage) {
|
||||
const { isSensitiveEnabled } = selectSettingsKeys(global);
|
||||
@ -43,9 +46,20 @@ 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 webPage = selectWebPageFromMessage(global, message);
|
||||
return (
|
||||
getMessagePhoto(message)
|
||||
customEmoji
|
||||
|| getMessagePhoto(message)
|
||||
|| getMessageVideo(message)
|
||||
|| getMessageDocument(message)
|
||||
|| getMessageSticker(message)
|
||||
|
||||
@ -80,9 +80,10 @@ import {
|
||||
selectRequestedChatTranslationLanguage,
|
||||
} from './chats';
|
||||
import { selectCurrentLimit } from './limits';
|
||||
import { selectMessageDownloadableMedia } from './media';
|
||||
import { selectPeer, selectPeerPaidMessagesStars } from './peers';
|
||||
import { selectPeerStory } from './stories';
|
||||
import { selectIsStickerFavorite } from './symbols';
|
||||
import { selectCustomEmoji, selectIsStickerFavorite } from './symbols';
|
||||
import { selectTabState } from './tabs';
|
||||
import { selectTopic } from './topics';
|
||||
import {
|
||||
@ -682,7 +683,6 @@ export function selectAllowedMessageActionsSlow<T extends GlobalState>(
|
||||
const isDocumentSticker = isMessageDocumentSticker(message);
|
||||
const isBoostMessage = message.content.action?.type === 'boostApply';
|
||||
const isMonoforum = chat.isMonoforum;
|
||||
const webPage = selectFullWebPageFromMessage(global, message);
|
||||
|
||||
const hasChatPinPermission = (chat.isCreator
|
||||
|| (!isChannel && !isUserRightBanned(chat, 'pinMessages'))
|
||||
@ -762,9 +762,7 @@ export function selectAllowedMessageActionsSlow<T extends GlobalState>(
|
||||
const canCopyLink = !isLocal && !isAction && (isChannel || isSuperGroup) && !isMonoforum;
|
||||
const canSelect = !isLocal && !isAction;
|
||||
|
||||
const canDownload = Boolean(webPage?.document || webPage?.video || webPage?.photo
|
||||
|| content.audio || content.voice || content.photo || content.video || content.document || content.sticker)
|
||||
&& !hasTtl;
|
||||
const canDownload = selectMessageDownloadableMedia(global, message) && !hasTtl;
|
||||
|
||||
const canSaveGif = message.content.video?.isGif;
|
||||
|
||||
@ -1412,7 +1410,7 @@ export function selectMessageCustomEmojiSets<T extends GlobalState>(
|
||||
): ApiStickerSetInfo[] | undefined {
|
||||
const customEmojis = selectCustomEmojis(message);
|
||||
if (!customEmojis) return MEMO_EMPTY_ARRAY;
|
||||
const documents = customEmojis.map((entity) => global.customEmojis.byId[entity.documentId]);
|
||||
const documents = customEmojis.map((entity) => selectCustomEmoji(global, entity.documentId));
|
||||
// If some emoji still loading, do not return empty array
|
||||
if (!documents.every(Boolean)) return undefined;
|
||||
const sets = documents.map((doc) => doc.stickerSetInfo);
|
||||
|
||||
@ -218,3 +218,7 @@ export function selectGiftStickerForTon<T extends GlobalState>(global: T, amount
|
||||
|
||||
return stickers.find((sticker) => sticker.emoji === emoji) || stickers[0];
|
||||
}
|
||||
|
||||
export function selectCustomEmoji<T extends GlobalState>(global: T, documentId: string) {
|
||||
return global.customEmojis.byId[documentId];
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ const REQUEST_THROTTLE = 500;
|
||||
const loadFromQueue = throttle(() => {
|
||||
const queue = Array.from(PEER_ID_QUEUE);
|
||||
const queueToLoad = queue.slice(0, LIMIT_PER_REQUEST);
|
||||
const otherQueue = queue.slice(LIMIT_PER_REQUEST + 1);
|
||||
const otherQueue = queue.slice(LIMIT_PER_REQUEST);
|
||||
|
||||
getActions().loadStoriesMaxIds({
|
||||
peerIds: queueToLoad,
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import { getActions, getGlobal } from '../global';
|
||||
|
||||
import { selectCustomEmoji } from '../global/selectors';
|
||||
import { addCustomEmojiInputRenderCallback } from '../util/emoji/customEmojiManager';
|
||||
import { throttle } from '../util/schedulers';
|
||||
|
||||
@ -12,7 +13,7 @@ const loadFromQueue = throttle(() => {
|
||||
const queue = [...LOAD_QUEUE];
|
||||
|
||||
const queueToLoad = queue.slice(0, LIMIT_PER_REQUEST);
|
||||
const otherQueue = queue.slice(LIMIT_PER_REQUEST + 1);
|
||||
const otherQueue = queue.slice(LIMIT_PER_REQUEST);
|
||||
|
||||
getActions().loadCustomEmojis({
|
||||
ids: queueToLoad,
|
||||
@ -45,7 +46,7 @@ export default function useEnsureCustomEmoji(id?: string) {
|
||||
if (!id) return;
|
||||
notifyCustomEmojiRender(id);
|
||||
|
||||
if (getGlobal().customEmojis.byId[id]) {
|
||||
if (selectCustomEmoji(getGlobal(), id)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import { ApiMediaFormat } from '../../api/types';
|
||||
|
||||
import { requestMutation } from '../../lib/fasterdom/fasterdom';
|
||||
import { getStickerHashById } from '../../global/helpers';
|
||||
import { selectCanPlayAnimatedEmojis } from '../../global/selectors';
|
||||
import { selectCanPlayAnimatedEmojis, selectCustomEmoji } from '../../global/selectors';
|
||||
import { IS_WEBM_SUPPORTED } from '../browser/windowEnvironment';
|
||||
import { createCallbackManager } from '../callbacks';
|
||||
import generateUniqueId from '../generateUniqueId';
|
||||
@ -36,7 +36,7 @@ addCallback((global: GlobalState) => {
|
||||
) {
|
||||
for (const entry of handlers) {
|
||||
const [handler, id] = entry;
|
||||
if (global.customEmojis.byId[id]) {
|
||||
if (selectCustomEmoji(global, id)) {
|
||||
handler(global.customEmojis);
|
||||
}
|
||||
}
|
||||
@ -61,7 +61,7 @@ const callInputRenderHandlers = throttle(renderCallbacks.runCallbacks, DOM_PROCE
|
||||
function processDomForCustomEmoji() {
|
||||
const emojis = document.querySelectorAll<HTMLImageElement>('.custom-emoji.placeholder');
|
||||
emojis.forEach((emoji) => {
|
||||
const customEmoji = getGlobal().customEmojis.byId[emoji.dataset.documentId!];
|
||||
const customEmoji = selectCustomEmoji(getGlobal(), emoji.dataset.documentId!);
|
||||
if (!customEmoji) {
|
||||
INPUT_WAITING_CUSTOM_EMOJI_IDS.add(emoji.dataset.documentId!);
|
||||
return;
|
||||
|
||||
@ -18,6 +18,7 @@ import { getIsChatMuted, getIsChatSilent, getShouldShowMessagePreview } from '..
|
||||
import { getMessageSenderName } from '../global/helpers/peers';
|
||||
import {
|
||||
selectCurrentMessageList,
|
||||
selectCustomEmoji,
|
||||
selectIsChatWithSelf,
|
||||
selectNotifyDefaults,
|
||||
selectNotifyException,
|
||||
@ -183,7 +184,7 @@ export async function unsubscribe() {
|
||||
// Load custom emoji from the api if it's not cached already
|
||||
async function loadCustomEmoji(id: string) {
|
||||
let global = getGlobal();
|
||||
if (global.customEmojis.byId[id]) return;
|
||||
if (selectCustomEmoji(global, id)) return;
|
||||
const customEmoji = await callApi('fetchCustomEmoji', {
|
||||
documentId: [id],
|
||||
});
|
||||
@ -352,7 +353,7 @@ function getReactionEmoji(reaction: ApiPeerReaction) {
|
||||
}
|
||||
|
||||
if (reaction.reaction.type === 'custom') {
|
||||
emoji = getGlobal().customEmojis.byId[reaction.reaction.documentId]?.emoji;
|
||||
emoji = selectCustomEmoji(getGlobal(), reaction.reaction.documentId)?.emoji;
|
||||
}
|
||||
return emoji || '❤️';
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user