TelegramPWA/src/components/middle/message/ReactionPickerLimited.tsx

124 lines
4.3 KiB
TypeScript

import type { FC } from '../../../lib/teact/teact';
import React, { memo, useMemo, useRef } from '../../../lib/teact/teact';
import { withGlobal } from '../../../global';
import type { ApiAvailableReaction, ApiChatReactions, ApiReaction } from '../../../api/types';
import { getReactionUniqueKey, sortReactions } from '../../../global/helpers';
import { selectChatFullInfo } from '../../../global/selectors';
import buildClassName from '../../../util/buildClassName';
import { REM } from '../../common/helpers/mediaDimensions';
import useAppLayout from '../../../hooks/useAppLayout';
import useWindowSize from '../../../hooks/useWindowSize';
import ReactionEmoji from '../../common/ReactionEmoji';
import styles from './ReactionPickerLimited.module.scss';
type OwnProps = {
chatId: string;
loadAndPlay: boolean;
onReactionSelect?: (reaction: ApiReaction) => void;
selectedReactionIds?: string[];
};
type StateProps = {
enabledReactions?: ApiChatReactions;
availableReactions?: ApiAvailableReaction[];
topReactions: ApiReaction[];
canAnimate?: boolean;
isSavedMessages?: boolean;
isCurrentUserPremium?: boolean;
};
const REACTION_SIZE = 36;
const GRID_GAP_THRESHOLD = 600;
const MODAL_PADDING_SIZE_REM = 0.5;
const MODAL_MAX_HEIGHT_REM = 18;
const MODAL_MAX_WIDTH_REM = 26.25;
const GRID_GAP_DESKTOP_REM = 0.625;
const GRID_GAP_MOBILE_REM = 0.5;
const ReactionPickerLimited: FC<OwnProps & StateProps> = ({
loadAndPlay,
enabledReactions,
availableReactions,
topReactions,
selectedReactionIds,
onReactionSelect,
}) => {
// eslint-disable-next-line no-null/no-null
const sharedCanvasRef = useRef<HTMLCanvasElement>(null);
// eslint-disable-next-line no-null/no-null
const sharedCanvasHqRef = useRef<HTMLCanvasElement>(null);
const { width: windowWidth } = useWindowSize();
const { isTouchScreen } = useAppLayout();
const allAvailableReactions = useMemo(() => {
if (!enabledReactions) {
return [];
}
if (enabledReactions.type === 'all') {
return sortReactions((availableReactions || []).map(({ reaction }) => reaction), topReactions);
}
return sortReactions(enabledReactions.allowed, topReactions);
}, [availableReactions, enabledReactions, topReactions]);
const pickerHeight = useMemo(() => {
const pickerWidth = Math.min(MODAL_MAX_WIDTH_REM * REM, windowWidth);
const gapWidth = (windowWidth > GRID_GAP_THRESHOLD ? GRID_GAP_DESKTOP_REM : GRID_GAP_MOBILE_REM) * REM;
const availableWidth = pickerWidth - MODAL_PADDING_SIZE_REM * REM;
const itemsInRow = Math.floor((availableWidth + gapWidth) / (REACTION_SIZE + gapWidth));
const rowsCount = Math.ceil(allAvailableReactions.length / itemsInRow);
const pickerMaxHeight = rowsCount * REACTION_SIZE + (rowsCount - 1) * gapWidth + MODAL_PADDING_SIZE_REM * REM * 2;
return Math.min(MODAL_MAX_HEIGHT_REM * REM, pickerMaxHeight);
}, [allAvailableReactions.length, windowWidth]);
return (
<div className={styles.root} style={`height: ${pickerHeight}px`}>
<div className={buildClassName(styles.wrapper, isTouchScreen ? 'no-scrollbar' : 'custom-scroll')}>
<div className="symbol-set-container shared-canvas-container">
<canvas ref={sharedCanvasRef} className="shared-canvas" />
<canvas ref={sharedCanvasHqRef} className="shared-canvas" />
{allAvailableReactions.map((reaction) => {
const reactionId = getReactionUniqueKey(reaction);
const isSelected = reactionId ? selectedReactionIds?.includes(reactionId) : undefined;
return (
<ReactionEmoji
key={reactionId}
reaction={reaction}
isSelected={isSelected}
loadAndPlay={loadAndPlay}
availableReactions={availableReactions}
onClick={onReactionSelect!}
sharedCanvasRef={sharedCanvasRef}
sharedCanvasHqRef={sharedCanvasHqRef}
/>
);
})}
</div>
</div>
</div>
);
};
export default memo(withGlobal<OwnProps>(
(global, { chatId }): StateProps => {
const { availableReactions, topReactions } = global;
const { enabledReactions } = selectChatFullInfo(global, chatId) || {};
return {
enabledReactions,
availableReactions,
topReactions,
};
},
)(ReactionPickerLimited));