Reaction Picker: Show current reactions (#4822)

This commit is contained in:
Alexander Zinchuk 2024-08-06 20:06:37 +02:00
parent eddce446de
commit a03db12474
3 changed files with 61 additions and 30 deletions

View File

@ -38,7 +38,7 @@ export type OwnProps = {
}; };
interface StateProps { interface StateProps {
withCustomReactions?: boolean; shouldUseFullPicker?: boolean;
message?: ApiMessage; message?: ApiMessage;
story?: ApiStory | ApiStorySkipped; story?: ApiStory | ApiStorySkipped;
isCurrentUserPremium?: boolean; isCurrentUserPremium?: boolean;
@ -61,7 +61,7 @@ const ReactionPicker: FC<OwnProps & StateProps> = ({
position, position,
isTranslucent, isTranslucent,
isCurrentUserPremium, isCurrentUserPremium,
withCustomReactions, shouldUseFullPicker,
sendAsMessage, sendAsMessage,
chatId, chatId,
isForEffects, isForEffects,
@ -91,10 +91,10 @@ const ReactionPicker: FC<OwnProps & StateProps> = ({
} }
return { return {
x: storedPosition.x + (withCustomReactions ? FULL_PICKER_SHIFT_DELTA.x : LIMITED_PICKER_SHIFT_DELTA.x), x: storedPosition.x + (shouldUseFullPicker ? FULL_PICKER_SHIFT_DELTA.x : LIMITED_PICKER_SHIFT_DELTA.x),
y: storedPosition.y + (withCustomReactions ? FULL_PICKER_SHIFT_DELTA.y : LIMITED_PICKER_SHIFT_DELTA.y), y: storedPosition.y + (shouldUseFullPicker ? FULL_PICKER_SHIFT_DELTA.y : LIMITED_PICKER_SHIFT_DELTA.y),
}; };
}, [renderedStoryId, storedPosition, withCustomReactions]); }, [renderedStoryId, storedPosition, shouldUseFullPicker]);
const getMenuElement = useLastCallback(() => menuRef.current); const getMenuElement = useLastCallback(() => menuRef.current);
const getLayout = useLastCallback(() => ({ const getLayout = useLastCallback(() => ({
@ -208,7 +208,7 @@ const ReactionPicker: FC<OwnProps & StateProps> = ({
className={buildClassName(styles.menu, 'ReactionPicker')} className={buildClassName(styles.menu, 'ReactionPicker')}
bubbleClassName={buildClassName( bubbleClassName={buildClassName(
styles.menuContent, styles.menuContent,
!withCustomReactions && !renderedStoryId && styles.onlyReactions, !shouldUseFullPicker && !renderedStoryId && styles.onlyReactions,
renderedStoryId && styles.storyMenu, renderedStoryId && styles.storyMenu,
)} )}
withPortal withPortal
@ -225,7 +225,7 @@ const ReactionPicker: FC<OwnProps & StateProps> = ({
<StickerPicker <StickerPicker
className="" className=""
isHidden={!isOpen} isHidden={!isOpen}
loadAndPlay={Boolean(isOpen && withCustomReactions)} loadAndPlay={Boolean(isOpen && shouldUseFullPicker)}
idPrefix="message-effect" idPrefix="message-effect"
canSendStickers={false} canSendStickers={false}
noContextMenus={false} noContextMenus={false}
@ -239,21 +239,22 @@ const ReactionPicker: FC<OwnProps & StateProps> = ({
<CustomEmojiPicker <CustomEmojiPicker
chatId={renderedChatId} chatId={renderedChatId}
idPrefix="message-emoji-set-" idPrefix="message-emoji-set-"
isHidden={!isOpen || !(withCustomReactions || renderedStoryId)} isHidden={!isOpen || !(shouldUseFullPicker || renderedStoryId)}
loadAndPlay={Boolean(isOpen && withCustomReactions)} loadAndPlay={Boolean(isOpen && shouldUseFullPicker)}
isReactionPicker isReactionPicker
className={!withCustomReactions && !renderedStoryId ? styles.hidden : undefined} className={!shouldUseFullPicker && !renderedStoryId ? styles.hidden : undefined}
selectedReactionIds={selectedReactionIds} selectedReactionIds={selectedReactionIds}
isTranslucent={isTranslucent} isTranslucent={isTranslucent}
onCustomEmojiSelect={renderedStoryId ? handleStoryReactionSelect : handleToggleCustomReaction} onCustomEmojiSelect={renderedStoryId ? handleStoryReactionSelect : handleToggleCustomReaction}
onReactionSelect={renderedStoryId ? handleStoryReactionSelect : handleToggleReaction} onReactionSelect={renderedStoryId ? handleStoryReactionSelect : handleToggleReaction}
/> />
{!withCustomReactions && Boolean(renderedChatId) && ( {!shouldUseFullPicker && Boolean(renderedChatId) && (
<ReactionPickerLimited <ReactionPickerLimited
chatId={renderedChatId} chatId={renderedChatId}
loadAndPlay={isOpen} loadAndPlay={isOpen}
onReactionSelect={renderedStoryId ? handleStoryReactionSelect : handleToggleReaction} onReactionSelect={renderedStoryId ? handleStoryReactionSelect : handleToggleReaction}
selectedReactionIds={selectedReactionIds} selectedReactionIds={selectedReactionIds}
message={message}
/> />
)} )}
</> </>
@ -276,16 +277,20 @@ export default memo(withGlobal<OwnProps>((global): StateProps => {
const message = chatId && messageId ? selectChatMessage(global, chatId, messageId) : undefined; const message = chatId && messageId ? selectChatMessage(global, chatId, messageId) : undefined;
const isPrivateChat = isUserId(chatId || storyPeerId || ''); const isPrivateChat = isUserId(chatId || storyPeerId || '');
const areSomeReactionsAllowed = chatFullInfo?.enabledReactions?.type === 'some'; const areSomeReactionsAllowed = chatFullInfo?.enabledReactions?.type === 'some';
const areCustomReactionsAllowed = chatFullInfo?.enabledReactions?.type === 'all' const { maxUniqueReactions } = global.appConfig || {};
&& chatFullInfo?.enabledReactions?.areCustomAllowed; const areAllReactionsAllowed = chatFullInfo?.enabledReactions?.type === 'all'
&& chatFullInfo?.enabledReactions?.areCustomAllowed;
const currentReactions = message?.reactions?.results;
const shouldUseCurrentReactions = Boolean(maxUniqueReactions && currentReactions
&& currentReactions.length >= maxUniqueReactions);
return { return {
message, message,
story, story,
position, position,
withCustomReactions: chat?.isForbidden || areSomeReactionsAllowed shouldUseFullPicker: (chat?.isForbidden || areSomeReactionsAllowed || shouldUseCurrentReactions) ? false
? false : (areAllReactionsAllowed || isPrivateChat),
: areCustomReactionsAllowed || isPrivateChat,
isTranslucent: selectIsContextMenuTranslucent(global), isTranslucent: selectIsContextMenuTranslucent(global),
isCurrentUserPremium: selectIsCurrentUserPremium(global), isCurrentUserPremium: selectIsCurrentUserPremium(global),
sendAsMessage, sendAsMessage,

View File

@ -1,10 +1,18 @@
import type { FC } from '../../../../lib/teact/teact'; import type { FC } from '../../../../lib/teact/teact';
import React, { memo, useMemo, useRef } from '../../../../lib/teact/teact'; import React, {
memo,
useMemo, useRef,
} from '../../../../lib/teact/teact';
import { withGlobal } from '../../../../global'; import { withGlobal } from '../../../../global';
import type { ApiAvailableReaction, ApiChatReactions, ApiReaction } from '../../../../api/types'; import type {
ApiAvailableReaction, ApiChatReactions, ApiMessage,
ApiReaction,
} from '../../../../api/types';
import { getReactionKey, sortReactions } from '../../../../global/helpers'; import {
getReactionKey, sortReactions,
} from '../../../../global/helpers';
import { selectChatFullInfo } from '../../../../global/selectors'; import { selectChatFullInfo } from '../../../../global/selectors';
import buildClassName from '../../../../util/buildClassName'; import buildClassName from '../../../../util/buildClassName';
import { REM } from '../../../common/helpers/mediaDimensions'; import { REM } from '../../../common/helpers/mediaDimensions';
@ -21,6 +29,7 @@ type OwnProps = {
loadAndPlay: boolean; loadAndPlay: boolean;
onReactionSelect?: (reaction: ApiReaction) => void; onReactionSelect?: (reaction: ApiReaction) => void;
selectedReactionIds?: string[]; selectedReactionIds?: string[];
message?: ApiMessage;
}; };
type StateProps = { type StateProps = {
@ -29,6 +38,7 @@ type StateProps = {
topReactions: ApiReaction[]; topReactions: ApiReaction[];
canAnimate?: boolean; canAnimate?: boolean;
isSavedMessages?: boolean; isSavedMessages?: boolean;
reactionsLimit?: number;
isCurrentUserPremium?: boolean; isCurrentUserPremium?: boolean;
}; };
@ -47,6 +57,8 @@ const ReactionPickerLimited: FC<OwnProps & StateProps> = ({
topReactions, topReactions,
selectedReactionIds, selectedReactionIds,
onReactionSelect, onReactionSelect,
message,
reactionsLimit,
}) => { }) => {
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
const sharedCanvasRef = useRef<HTMLCanvasElement>(null); const sharedCanvasRef = useRef<HTMLCanvasElement>(null);
@ -55,7 +67,15 @@ const ReactionPickerLimited: FC<OwnProps & StateProps> = ({
const { width: windowWidth } = useWindowSize(); const { width: windowWidth } = useWindowSize();
const { isTouchScreen } = useAppLayout(); const { isTouchScreen } = useAppLayout();
const currentReactions = message?.reactions?.results;
const shouldUseCurrentReactions = reactionsLimit && currentReactions
&& currentReactions.length >= reactionsLimit;
const allAvailableReactions = useMemo(() => { const allAvailableReactions = useMemo(() => {
if (shouldUseCurrentReactions) {
return currentReactions.map(({ reaction }) => reaction);
}
if (!enabledReactions) { if (!enabledReactions) {
return []; return [];
} }
@ -65,7 +85,7 @@ const ReactionPickerLimited: FC<OwnProps & StateProps> = ({
} }
return sortReactions(enabledReactions.allowed, topReactions); return sortReactions(enabledReactions.allowed, topReactions);
}, [availableReactions, enabledReactions, topReactions]); }, [availableReactions, enabledReactions, topReactions, shouldUseCurrentReactions, currentReactions]);
const pickerHeight = useMemo(() => { const pickerHeight = useMemo(() => {
const pickerWidth = Math.min(MODAL_MAX_WIDTH_REM * REM, windowWidth); const pickerWidth = Math.min(MODAL_MAX_WIDTH_REM * REM, windowWidth);
@ -112,12 +132,15 @@ const ReactionPickerLimited: FC<OwnProps & StateProps> = ({
export default memo(withGlobal<OwnProps>( export default memo(withGlobal<OwnProps>(
(global, { chatId }): StateProps => { (global, { chatId }): StateProps => {
const { availableReactions, topReactions } = global.reactions; const { availableReactions, topReactions } = global.reactions;
const { maxUniqueReactions } = global.appConfig || {};
const { enabledReactions } = selectChatFullInfo(global, chatId) || {}; const { enabledReactions } = selectChatFullInfo(global, chatId) || {};
return { return {
enabledReactions, enabledReactions,
availableReactions, availableReactions,
topReactions, topReactions,
reactionsLimit: maxUniqueReactions,
}; };
}, },
)(ReactionPickerLimited)); )(ReactionPickerLimited));

View File

@ -75,10 +75,17 @@ const ReactionSelector: FC<OwnProps> = ({
const areReactionsLocked = isInSavedMessages && !isCurrentUserPremium && !isInStoryViewer; const areReactionsLocked = isInSavedMessages && !isCurrentUserPremium && !isInStoryViewer;
const shouldUseCurrentReactions = Boolean(maxUniqueReactions
&& currentReactions && currentReactions.length >= maxUniqueReactions);
const availableReactions = useMemo(() => { const availableReactions = useMemo(() => {
const reactions = isForEffects ? effectReactions : isInSavedMessages ? defaultTagReactions const reactions = (() => {
: (enabledReactions?.type === 'some' ? enabledReactions.allowed if (shouldUseCurrentReactions) return currentReactions?.map((reaction) => reaction.reaction);
: allAvailableReactions?.map((reaction) => reaction.reaction)); if (isForEffects) return effectReactions;
if (isInSavedMessages) return defaultTagReactions;
if (enabledReactions?.type === 'some') return enabledReactions.allowed;
return allAvailableReactions?.map((reaction) => reaction.reaction);
})();
const filteredReactions = reactions?.map((reaction) => { const filteredReactions = reactions?.map((reaction) => {
const isCustomReaction = 'documentId' in reaction; const isCustomReaction = 'documentId' in reaction;
const availableReaction = allAvailableReactions?.find((r) => isSameReaction(r.reaction, reaction)); const availableReaction = allAvailableReactions?.find((r) => isSameReaction(r.reaction, reaction));
@ -87,12 +94,8 @@ const ReactionSelector: FC<OwnProps> = ({
if ((!isCustomReaction && !availableReaction) || availableReaction?.isInactive) return undefined; if ((!isCustomReaction && !availableReaction) || availableReaction?.isInactive) return undefined;
if (!isPrivate && (!enabledReactions || !canSendReaction(reaction, enabledReactions))) { if (!isPrivate && !shouldUseCurrentReactions
return undefined; && (!enabledReactions || !canSendReaction(reaction, enabledReactions))) {
}
if (maxUniqueReactions && currentReactions && currentReactions.length >= maxUniqueReactions
&& !currentReactions.some(({ reaction: currentReaction }) => isSameReaction(reaction, currentReaction))) {
return undefined; return undefined;
} }
@ -102,7 +105,7 @@ const ReactionSelector: FC<OwnProps> = ({
return sortReactions(filteredReactions, topReactions); return sortReactions(filteredReactions, topReactions);
}, [ }, [
allAvailableReactions, currentReactions, defaultTagReactions, enabledReactions, isInSavedMessages, isPrivate, allAvailableReactions, currentReactions, defaultTagReactions, enabledReactions, isInSavedMessages, isPrivate,
maxUniqueReactions, topReactions, isForEffects, effectReactions, topReactions, isForEffects, effectReactions, shouldUseCurrentReactions,
]); ]);
const reactionsToRender = useMemo(() => { const reactionsToRender = useMemo(() => {