Emoji Modal: Fix sticker set card list modal (#4764)

This commit is contained in:
Alexander Zinchuk 2024-08-06 20:06:12 +02:00
parent 527e4644e2
commit 66d54b2f63
7 changed files with 75 additions and 50 deletions

View File

@ -40,7 +40,6 @@
.sets { .sets {
position: relative; position: relative;
width: 100%; width: 100%;
min-height: 19rem;
overflow-y: auto; overflow-y: auto;
padding: 0 0.5rem; padding: 0 0.5rem;
text-align: left; text-align: left;

View File

@ -1,15 +1,19 @@
import type { FC } from '../../lib/teact/teact'; import type { FC } from '../../lib/teact/teact';
import React, { import React, {
memo, useCallback, useRef, memo,
useMemo,
useRef,
} from '../../lib/teact/teact'; } from '../../lib/teact/teact';
import { getGlobal } from '../../lib/teact/teactn';
import { getActions, withGlobal } from '../../global'; import { getActions, withGlobal } from '../../global';
import type { ApiSticker, ApiStickerSet } from '../../api/types'; import type { ApiSticker } from '../../api/types';
import { selectCanPlayAnimatedEmojis } from '../../global/selectors'; import { selectCanPlayAnimatedEmojis } from '../../global/selectors';
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
import { useIntersectionObserver } from '../../hooks/useIntersectionObserver'; import { useIntersectionObserver } from '../../hooks/useIntersectionObserver';
import useLastCallback from '../../hooks/useLastCallback';
import useOldLang from '../../hooks/useOldLang'; import useOldLang from '../../hooks/useOldLang';
import usePrevious from '../../hooks/usePrevious'; import usePrevious from '../../hooks/usePrevious';
@ -24,18 +28,21 @@ export type OwnProps = {
}; };
type StateProps = { type StateProps = {
customEmojiSets?: ApiStickerSet[];
canPlayAnimatedEmojis?: boolean; canPlayAnimatedEmojis?: boolean;
}; };
const CustomEmojiSetsModal: FC<OwnProps & StateProps> = ({ const CustomEmojiSetsModal: FC<OwnProps & StateProps> = ({
customEmojiSets, customEmojiSetIds,
canPlayAnimatedEmojis, canPlayAnimatedEmojis,
onClose, onClose,
}) => { }) => {
const { openStickerSet } = getActions(); const { openStickerSet } = getActions();
const lang = useOldLang(); const lang = useOldLang();
const customEmojiSets = useMemo(() => {
return customEmojiSetIds?.map((id) => getGlobal().stickers.setsById[id]);
}, [customEmojiSetIds]);
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
const customEmojiModalRef = useRef<HTMLDivElement>(null); const customEmojiModalRef = useRef<HTMLDivElement>(null);
const { observe: observeIntersectionForCovers } = useIntersectionObserver({ const { observe: observeIntersectionForCovers } = useIntersectionObserver({
@ -45,11 +52,11 @@ const CustomEmojiSetsModal: FC<OwnProps & StateProps> = ({
const prevCustomEmojiSets = usePrevious(customEmojiSets); const prevCustomEmojiSets = usePrevious(customEmojiSets);
const renderingCustomEmojiSets = customEmojiSets || prevCustomEmojiSets; const renderingCustomEmojiSets = customEmojiSets || prevCustomEmojiSets;
const handleSetClick = useCallback((sticker: ApiSticker) => { const handleSetClick = useLastCallback((sticker: ApiSticker) => {
openStickerSet({ openStickerSet({
stickerSetInfo: sticker.stickerSetInfo, stickerSetInfo: sticker.stickerSetInfo,
}); });
}, [openStickerSet]); });
return ( return (
<Modal <Modal
@ -60,27 +67,26 @@ const CustomEmojiSetsModal: FC<OwnProps & StateProps> = ({
title={lang('lng_custom_emoji_used_sets')} title={lang('lng_custom_emoji_used_sets')}
> >
<div className={buildClassName(styles.sets, 'custom-scroll')} ref={customEmojiModalRef} teactFastList> <div className={buildClassName(styles.sets, 'custom-scroll')} ref={customEmojiModalRef} teactFastList>
{renderingCustomEmojiSets?.map((customEmojiSet) => ( {renderingCustomEmojiSets?.map((customEmojiSet) => {
<StickerSetCard return (
key={customEmojiSet.id} <StickerSetCard
className={styles.setCard} key={customEmojiSet.id}
stickerSet={customEmojiSet} className={styles.setCard}
onClick={handleSetClick} stickerSet={customEmojiSet}
observeIntersection={observeIntersectionForCovers} onClick={handleSetClick}
noPlay={!canPlayAnimatedEmojis} observeIntersection={observeIntersectionForCovers}
/> noPlay={!canPlayAnimatedEmojis}
))} />
);
})}
</div> </div>
</Modal> </Modal>
); );
}; };
export default memo(withGlobal<OwnProps>( export default memo(withGlobal<OwnProps>(
(global, { customEmojiSetIds }): StateProps => { (global): StateProps => {
const customEmojiSets = customEmojiSetIds?.map((id) => global.stickers.setsById[id]);
return { return {
customEmojiSets,
canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global), canPlayAnimatedEmojis: selectCanPlayAnimatedEmojis(global),
}; };
}, },

View File

@ -1,5 +1,5 @@
import type { FC } from '../../lib/teact/teact'; import type { FC } from '../../lib/teact/teact';
import React, { memo, useCallback } from '../../lib/teact/teact'; import React, { memo } from '../../lib/teact/teact';
import type { ApiSticker, ApiStickerSet } from '../../api/types'; import type { ApiSticker, ApiStickerSet } from '../../api/types';
import type { ObserveFn } from '../../hooks/useIntersectionObserver'; import type { ObserveFn } from '../../hooks/useIntersectionObserver';
@ -7,6 +7,7 @@ import type { ObserveFn } from '../../hooks/useIntersectionObserver';
import { CHAT_HEIGHT_PX, STICKER_SIZE_GENERAL_SETTINGS } from '../../config'; import { CHAT_HEIGHT_PX, STICKER_SIZE_GENERAL_SETTINGS } from '../../config';
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
import useLastCallback from '../../hooks/useLastCallback';
import useOldLang from '../../hooks/useOldLang'; import useOldLang from '../../hooks/useOldLang';
import StickerSetCover from '../middle/composer/StickerSetCover'; import StickerSetCover from '../middle/composer/StickerSetCover';
@ -35,11 +36,11 @@ const StickerSetCard: FC<OwnProps> = ({
const firstSticker = stickerSet?.stickers?.[0]; const firstSticker = stickerSet?.stickers?.[0];
const handleCardClick = useCallback(() => { const handleCardClick = useLastCallback(() => {
if (firstSticker) onClick(firstSticker); if (firstSticker) onClick(firstSticker);
}, [firstSticker, onClick]); });
if (!stickerSet || !stickerSet.stickers) { if (!stickerSet?.stickers) {
return undefined; return undefined;
} }

View File

@ -4,18 +4,17 @@ import React, {
} from '../../../lib/teact/teact'; } from '../../../lib/teact/teact';
import { getActions, getGlobal, withGlobal } from '../../../global'; import { getActions, getGlobal, withGlobal } from '../../../global';
import type {
ApiAvailableReaction,
ApiChatReactions,
ApiMessage,
ApiReaction,
ApiStickerSet, ApiStickerSetInfo,
ApiThreadInfo,
} from '../../../api/types';
import type { ActiveDownloads, MessageListType } from '../../../global/types'; import type { ActiveDownloads, MessageListType } from '../../../global/types';
import type { IAlbum, IAnchorPosition, ThreadId } from '../../../types'; import type { IAlbum, IAnchorPosition, ThreadId } from '../../../types';
import { import { MAIN_THREAD_ID } from '../../../api/types';
type ApiAvailableReaction,
type ApiChatReactions,
type ApiMessage,
type ApiReaction,
type ApiStickerSet,
type ApiStickerSetInfo,
type ApiThreadInfo,
MAIN_THREAD_ID,
} from '../../../api/types';
import { PREVIEW_AVATAR_COUNT, SERVICE_NOTIFICATIONS_USER_ID } from '../../../config'; import { PREVIEW_AVATAR_COUNT, SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
import { import {
@ -45,8 +44,7 @@ import {
selectIsMessageProtected, selectIsMessageProtected,
selectIsMessageUnread, selectIsMessageUnread,
selectIsPremiumPurchaseBlocked, selectIsPremiumPurchaseBlocked,
selectIsReactionPickerOpen, selectIsReactionPickerOpen, selectMessageCustomEmojiSets,
selectMessageCustomEmojiSets,
selectMessageTranslations, selectMessageTranslations,
selectRequestedChatTranslationLanguage, selectRequestedChatTranslationLanguage,
selectRequestedMessageTranslationLanguage, selectRequestedMessageTranslationLanguage,
@ -88,8 +86,6 @@ type StateProps = {
availableReactions?: ApiAvailableReaction[]; availableReactions?: ApiAvailableReaction[];
topReactions?: ApiReaction[]; topReactions?: ApiReaction[];
defaultTagReactions?: ApiReaction[]; defaultTagReactions?: ApiReaction[];
customEmojiSetsInfo?: ApiStickerSetInfo[];
customEmojiSets?: ApiStickerSet[];
noOptions?: boolean; noOptions?: boolean;
canSendNow?: boolean; canSendNow?: boolean;
canReschedule?: boolean; canReschedule?: boolean;
@ -98,6 +94,8 @@ type StateProps = {
canShowReactionsCount?: boolean; canShowReactionsCount?: boolean;
canBuyPremium?: boolean; canBuyPremium?: boolean;
canShowReactionList?: boolean; canShowReactionList?: boolean;
customEmojiSetsInfo?: ApiStickerSetInfo[];
customEmojiSets?: ApiStickerSet[];
canUnpin?: boolean; canUnpin?: boolean;
canDelete?: boolean; canDelete?: boolean;
canReport?: boolean; canReport?: boolean;
@ -694,12 +692,12 @@ export default memo(withGlobal<OwnProps>(
const isMessageUnread = selectIsMessageUnread(global, message); const isMessageUnread = selectIsMessageUnread(global, message);
const canLoadReadDate = Boolean( const canLoadReadDate = Boolean(
isPrivate isPrivate
&& isOwn && isOwn
&& !isMessageUnread && !isMessageUnread
&& readDateExpiresAt && readDateExpiresAt
&& message.date > Date.now() / 1000 - readDateExpiresAt && message.date > Date.now() / 1000 - readDateExpiresAt
&& !userStatus?.isReadDateRestricted && !userStatus?.isReadDateRestricted
&& messageListType !== 'scheduled', && messageListType !== 'scheduled',
); );
const shouldRenderShowWhen = Boolean( const shouldRenderShowWhen = Boolean(
canLoadReadDate && isPrivate && selectUserStatus(global, chat.id)?.isReadDateRestrictedByMe, canLoadReadDate && isPrivate && selectUserStatus(global, chat.id)?.isReadDateRestrictedByMe,

View File

@ -214,7 +214,9 @@ const MessageContextMenu: FC<OwnProps> = ({
onShowOriginal, onShowOriginal,
onSelectLanguage, onSelectLanguage,
}) => { }) => {
const { showNotification, openStickerSet, openCustomEmojiSets } = getActions(); const {
showNotification, openStickerSet, openCustomEmojiSets, loadStickers,
} = getActions();
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
const menuRef = useRef<HTMLDivElement>(null); const menuRef = useRef<HTMLDivElement>(null);
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
@ -244,6 +246,19 @@ const MessageContextMenu: FC<OwnProps> = ({
} }
}, [onClose, isOpen, isReactionPickerOpen, areItemsHidden]); }, [onClose, isOpen, isReactionPickerOpen, areItemsHidden]);
useEffect(() => {
if (customEmojiSets?.length) {
customEmojiSets.map((customEmojiSet) => {
return loadStickers({
stickerSetInfo: {
id: customEmojiSet.id,
accessHash: customEmojiSet.accessHash,
},
});
});
}
}, [customEmojiSets, openCustomEmojiSets]);
const handleOpenCustomEmojiSets = useLastCallback(() => { const handleOpenCustomEmojiSets = useLastCallback(() => {
if (!customEmojiSets) return; if (!customEmojiSets) return;
if (customEmojiSets.length === 1) { if (customEmojiSets.length === 1) {

View File

@ -1,5 +1,11 @@
import type { import type {
ApiAttachment, ApiChat, ApiMessage, ApiMessageEntityTextUrl, ApiPeer, ApiStory, ApiUser, ApiAttachment,
ApiChat,
ApiMessage,
ApiMessageEntityTextUrl,
ApiPeer,
ApiStory,
ApiUser,
} from '../../api/types'; } from '../../api/types';
import type { MediaContent } from '../../api/types/messages'; import type { MediaContent } from '../../api/types/messages';
import type { LangFn } from '../../hooks/useOldLang'; import type { LangFn } from '../../hooks/useOldLang';

View File

@ -722,7 +722,7 @@ export function selectAllowedMessageActions<T extends GlobalState>(global: T, me
const canSelect = !isLocal && !isAction; const canSelect = !isLocal && !isAction;
const canDownload = Boolean(content.webPage?.document || content.webPage?.video || content.webPage?.photo const canDownload = Boolean(content.webPage?.document || content.webPage?.video || content.webPage?.photo
|| content.audio || content.voice || content.photo || content.video || content.document || content.sticker) || content.audio || content.voice || content.photo || content.video || content.document || content.sticker)
&& !hasTtl; && !hasTtl;
const canSaveGif = message.content.video?.isGif; const canSaveGif = message.content.video?.isGif;
@ -1210,7 +1210,7 @@ export function selectLastServiceNotification<T extends GlobalState>(global: T)
export function selectIsMessageProtected<T extends GlobalState>(global: T, message?: ApiMessage) { export function selectIsMessageProtected<T extends GlobalState>(global: T, message?: ApiMessage) {
return Boolean(message && ( return Boolean(message && (
message.isProtected || selectIsChatProtected(global, message.chatId) || hasMessageTtl(message) message.isProtected || selectIsChatProtected(global, message.chatId) || hasMessageTtl(message)
|| getMessagePaidMedia(message) || getMessagePaidMedia(message)
)); ));
} }
@ -1246,7 +1246,7 @@ export function selectCanForwardMessages<T extends GlobalState>(global: T, chatI
return messageIds return messageIds
.map((id) => messages[id]) .map((id) => messages[id])
.every((message) => !hasMessageTtl(message) .every((message) => !hasMessageTtl(message)
&& (message.isForwardingAllowed || isServiceNotificationMessage(message))); && (message.isForwardingAllowed || isServiceNotificationMessage(message)));
} }
export function selectSponsoredMessage<T extends GlobalState>(global: T, chatId: string) { export function selectSponsoredMessage<T extends GlobalState>(global: T, chatId: string) {