Gift Info Modal: Wrap context menu in portal (#6249)
This commit is contained in:
parent
c2badd4a4b
commit
472003b6ab
@ -38,7 +38,7 @@
|
|||||||
|
|
||||||
.modalContent {
|
.modalContent {
|
||||||
position: relative;
|
position: relative;
|
||||||
max-height: min(92vh, 46rem) !important;
|
max-height: min(92vh, 48rem) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.headerSplitButton {
|
.headerSplitButton {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import type { FC, TeactNode } from '../../../../lib/teact/teact';
|
import type { TeactNode } from '../../../../lib/teact/teact';
|
||||||
import { memo, useMemo, useState } from '../../../../lib/teact/teact';
|
import { memo, useMemo, useRef, useState } from '../../../../lib/teact/teact';
|
||||||
import { getActions, getGlobal, withGlobal } from '../../../../global';
|
import { getActions, getGlobal, withGlobal } from '../../../../global';
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
@ -27,6 +27,7 @@ import { formatPercent } from '../../../../util/textFormat';
|
|||||||
import { getGiftAttributes, getStickerFromGift } from '../../../common/helpers/gifts';
|
import { getGiftAttributes, getStickerFromGift } from '../../../common/helpers/gifts';
|
||||||
import { renderTextWithEntities } from '../../../common/helpers/renderTextWithEntities';
|
import { renderTextWithEntities } from '../../../common/helpers/renderTextWithEntities';
|
||||||
|
|
||||||
|
import useContextMenuHandlers from '../../../../hooks/useContextMenuHandlers';
|
||||||
import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev';
|
import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev';
|
||||||
import useFlag from '../../../../hooks/useFlag';
|
import useFlag from '../../../../hooks/useFlag';
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
@ -43,8 +44,8 @@ import SafeLink from '../../../common/SafeLink';
|
|||||||
import Button from '../../../ui/Button';
|
import Button from '../../../ui/Button';
|
||||||
import Checkbox from '../../../ui/Checkbox';
|
import Checkbox from '../../../ui/Checkbox';
|
||||||
import ConfirmDialog from '../../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../../ui/ConfirmDialog';
|
||||||
import DropdownMenu from '../../../ui/DropdownMenu';
|
|
||||||
import Link from '../../../ui/Link';
|
import Link from '../../../ui/Link';
|
||||||
|
import Menu from '../../../ui/Menu';
|
||||||
import TableInfoModal, { type TableData } from '../../common/TableInfoModal';
|
import TableInfoModal, { type TableData } from '../../common/TableInfoModal';
|
||||||
import UniqueGiftHeader from '../UniqueGiftHeader';
|
import UniqueGiftHeader from '../UniqueGiftHeader';
|
||||||
|
|
||||||
@ -106,6 +107,17 @@ const GiftInfoModal = ({
|
|||||||
const [isConfirmModalOpen, setIsConfirmModalOpen] = useState<boolean>(false);
|
const [isConfirmModalOpen, setIsConfirmModalOpen] = useState<boolean>(false);
|
||||||
const [shouldPayInTon, setShouldPayInTon] = useState<boolean>(false);
|
const [shouldPayInTon, setShouldPayInTon] = useState<boolean>(false);
|
||||||
|
|
||||||
|
const splitButtonRef = useRef<HTMLDivElement>();
|
||||||
|
const menuRef = useRef<HTMLDivElement>();
|
||||||
|
const uniqueGiftHeaderRef = useRef<HTMLDivElement>();
|
||||||
|
const {
|
||||||
|
isContextMenuOpen,
|
||||||
|
contextMenuAnchor,
|
||||||
|
handleContextMenu,
|
||||||
|
handleContextMenuClose,
|
||||||
|
handleContextMenuHide,
|
||||||
|
} = useContextMenuHandlers(splitButtonRef);
|
||||||
|
|
||||||
const handleSymbolClick = useLastCallback(() => {
|
const handleSymbolClick = useLastCallback(() => {
|
||||||
if (!gift || !giftAttributes?.pattern) return;
|
if (!gift || !giftAttributes?.pattern) return;
|
||||||
|
|
||||||
@ -303,8 +315,8 @@ const GiftInfoModal = ({
|
|||||||
return gift && getGiftAttributes(gift);
|
return gift && getGiftAttributes(gift);
|
||||||
}, [gift]);
|
}, [gift]);
|
||||||
|
|
||||||
const SettingsMenuButton: FC<{ onTrigger: () => void; isMenuOpen?: boolean }> = useMemo(() => {
|
const SettingsMenuButton = useMemo(() => {
|
||||||
return ({ onTrigger }) => (
|
return (
|
||||||
<div
|
<div
|
||||||
className={buildClassName(
|
className={buildClassName(
|
||||||
styles.headerButton,
|
styles.headerButton,
|
||||||
@ -314,7 +326,8 @@ const GiftInfoModal = ({
|
|||||||
role="button"
|
role="button"
|
||||||
aria-haspopup="menu"
|
aria-haspopup="menu"
|
||||||
aria-label={lang('AriaMoreButton')}
|
aria-label={lang('AriaMoreButton')}
|
||||||
onClick={onTrigger}
|
onContextMenu={handleContextMenu}
|
||||||
|
onClick={handleContextMenu}
|
||||||
>
|
>
|
||||||
<Icon
|
<Icon
|
||||||
name="more"
|
name="more"
|
||||||
@ -322,7 +335,7 @@ const GiftInfoModal = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}, [lang]);
|
}, [lang, handleContextMenu]);
|
||||||
|
|
||||||
const renderFooterButton = useLastCallback(() => {
|
const renderFooterButton = useLastCallback(() => {
|
||||||
if (canBuyGift) {
|
if (canBuyGift) {
|
||||||
@ -480,22 +493,6 @@ const GiftInfoModal = ({
|
|||||||
return canManage ? lang('GiftInfoReceived') : lang('GiftInfoTitle');
|
return canManage ? lang('GiftInfoReceived') : lang('GiftInfoTitle');
|
||||||
}
|
}
|
||||||
|
|
||||||
const uniqueGiftContextMenu = (
|
|
||||||
<DropdownMenu
|
|
||||||
className="with-menu-transitions"
|
|
||||||
trigger={SettingsMenuButton}
|
|
||||||
positionX="right"
|
|
||||||
>
|
|
||||||
<GiftMenuItems
|
|
||||||
peerId={renderingModal!.peerId!}
|
|
||||||
gift={typeGift}
|
|
||||||
canManage={canManage}
|
|
||||||
collectibleEmojiStatuses={collectibleEmojiStatuses}
|
|
||||||
currentUserEmojiStatus={currentUserEmojiStatus}
|
|
||||||
/>
|
|
||||||
</DropdownMenu>
|
|
||||||
);
|
|
||||||
|
|
||||||
const uniqueGiftModalHeader = (
|
const uniqueGiftModalHeader = (
|
||||||
<div
|
<div
|
||||||
className={styles.modalHeader}
|
className={styles.modalHeader}
|
||||||
@ -513,8 +510,8 @@ const GiftInfoModal = ({
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={styles.headerSplitButton}>
|
<div className={styles.headerSplitButton} ref={splitButtonRef}>
|
||||||
{isOpen && uniqueGiftContextMenu}
|
{SettingsMenuButton}
|
||||||
<div
|
<div
|
||||||
className={buildClassName(
|
className={buildClassName(
|
||||||
styles.headerButton,
|
styles.headerButton,
|
||||||
@ -539,7 +536,7 @@ const GiftInfoModal = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const uniqueGiftHeader = isGiftUnique && (
|
const uniqueGiftHeader = isGiftUnique && (
|
||||||
<div className={buildClassName(styles.header, styles.uniqueGift)}>
|
<div ref={uniqueGiftHeaderRef} className={buildClassName(styles.header, styles.uniqueGift)}>
|
||||||
<UniqueGiftHeader
|
<UniqueGiftHeader
|
||||||
backdropAttribute={giftAttributes!.backdrop!}
|
backdropAttribute={giftAttributes!.backdrop!}
|
||||||
patternAttribute={giftAttributes!.pattern!}
|
patternAttribute={giftAttributes!.pattern!}
|
||||||
@ -875,12 +872,42 @@ const GiftInfoModal = ({
|
|||||||
typeGift, savedGift, renderingTargetPeer, giftSticker, lang,
|
typeGift, savedGift, renderingTargetPeer, giftSticker, lang,
|
||||||
canManage, hasConvertOption, isSender, oldLang, tonExplorerUrl,
|
canManage, hasConvertOption, isSender, oldLang, tonExplorerUrl,
|
||||||
gift, giftAttributes, renderFooterButton, isTargetChat,
|
gift, giftAttributes, renderFooterButton, isTargetChat,
|
||||||
SettingsMenuButton, isGiftUnique, renderingModal,
|
SettingsMenuButton, isGiftUnique, saleDateInfo,
|
||||||
collectibleEmojiStatuses, currentUserEmojiStatus, saleDateInfo,
|
canBuyGift, giftOwnerTitle, resellPrice, giftSubtitle,
|
||||||
canBuyGift, giftOwnerTitle, isOpen, resellPrice, giftSubtitle,
|
|
||||||
releasedByPeer, handleSymbolClick, handleBackdropClick, handleModelClick,
|
releasedByPeer, handleSymbolClick, handleBackdropClick, handleModelClick,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const getRootElement = useLastCallback(() => uniqueGiftHeaderRef.current);
|
||||||
|
const getTriggerElement = useLastCallback(() => splitButtonRef.current);
|
||||||
|
const getMenuElement = useLastCallback(() => menuRef.current);
|
||||||
|
const getLayout = useLastCallback(() => ({ withPortal: true }));
|
||||||
|
|
||||||
|
const uniqueGiftContextMenu = contextMenuAnchor && typeGift && (
|
||||||
|
<Menu
|
||||||
|
ref={menuRef}
|
||||||
|
isOpen={isContextMenuOpen}
|
||||||
|
anchor={contextMenuAnchor}
|
||||||
|
className="gift-context-menu with-menu-transitions"
|
||||||
|
autoClose
|
||||||
|
withPortal
|
||||||
|
onClose={handleContextMenuClose}
|
||||||
|
onCloseAnimationEnd={handleContextMenuHide}
|
||||||
|
positionX="right"
|
||||||
|
getTriggerElement={getTriggerElement}
|
||||||
|
getRootElement={getRootElement}
|
||||||
|
getMenuElement={getMenuElement}
|
||||||
|
getLayout={getLayout}
|
||||||
|
>
|
||||||
|
<GiftMenuItems
|
||||||
|
peerId={renderingModal!.peerId!}
|
||||||
|
gift={typeGift}
|
||||||
|
canManage={canManage}
|
||||||
|
collectibleEmojiStatuses={collectibleEmojiStatuses}
|
||||||
|
currentUserEmojiStatus={currentUserEmojiStatus}
|
||||||
|
/>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<TableInfoModal
|
<TableInfoModal
|
||||||
@ -897,6 +924,7 @@ const GiftInfoModal = ({
|
|||||||
currencyInBalanceBar={confirmPrice?.currency}
|
currencyInBalanceBar={confirmPrice?.currency}
|
||||||
isLowStackPriority
|
isLowStackPriority
|
||||||
/>
|
/>
|
||||||
|
{uniqueGiftContextMenu}
|
||||||
{uniqueGift && currentUser && Boolean(confirmPrice) && (
|
{uniqueGift && currentUser && Boolean(confirmPrice) && (
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={isConfirmModalOpen}
|
isOpen={isConfirmModalOpen}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user