From 472003b6ab4971e183b1d587978e7010e1ca6028 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Fri, 19 Sep 2025 14:35:27 +0200 Subject: [PATCH] Gift Info Modal: Wrap context menu in portal (#6249) --- .../gift/info/GiftInfoModal.module.scss | 2 +- .../modals/gift/info/GiftInfoModal.tsx | 86 ++++++++++++------- 2 files changed, 58 insertions(+), 30 deletions(-) diff --git a/src/components/modals/gift/info/GiftInfoModal.module.scss b/src/components/modals/gift/info/GiftInfoModal.module.scss index 05932d3c1..1921f075e 100644 --- a/src/components/modals/gift/info/GiftInfoModal.module.scss +++ b/src/components/modals/gift/info/GiftInfoModal.module.scss @@ -38,7 +38,7 @@ .modalContent { position: relative; - max-height: min(92vh, 46rem) !important; + max-height: min(92vh, 48rem) !important; } .headerSplitButton { diff --git a/src/components/modals/gift/info/GiftInfoModal.tsx b/src/components/modals/gift/info/GiftInfoModal.tsx index 8a792feae..d9083c900 100644 --- a/src/components/modals/gift/info/GiftInfoModal.tsx +++ b/src/components/modals/gift/info/GiftInfoModal.tsx @@ -1,5 +1,5 @@ -import type { FC, TeactNode } from '../../../../lib/teact/teact'; -import { memo, useMemo, useState } from '../../../../lib/teact/teact'; +import type { TeactNode } from '../../../../lib/teact/teact'; +import { memo, useMemo, useRef, useState } from '../../../../lib/teact/teact'; import { getActions, getGlobal, withGlobal } from '../../../../global'; import type { @@ -27,6 +27,7 @@ import { formatPercent } from '../../../../util/textFormat'; import { getGiftAttributes, getStickerFromGift } from '../../../common/helpers/gifts'; import { renderTextWithEntities } from '../../../common/helpers/renderTextWithEntities'; +import useContextMenuHandlers from '../../../../hooks/useContextMenuHandlers'; import useCurrentOrPrev from '../../../../hooks/useCurrentOrPrev'; import useFlag from '../../../../hooks/useFlag'; import useLang from '../../../../hooks/useLang'; @@ -43,8 +44,8 @@ import SafeLink from '../../../common/SafeLink'; import Button from '../../../ui/Button'; import Checkbox from '../../../ui/Checkbox'; import ConfirmDialog from '../../../ui/ConfirmDialog'; -import DropdownMenu from '../../../ui/DropdownMenu'; import Link from '../../../ui/Link'; +import Menu from '../../../ui/Menu'; import TableInfoModal, { type TableData } from '../../common/TableInfoModal'; import UniqueGiftHeader from '../UniqueGiftHeader'; @@ -106,6 +107,17 @@ const GiftInfoModal = ({ const [isConfirmModalOpen, setIsConfirmModalOpen] = useState(false); const [shouldPayInTon, setShouldPayInTon] = useState(false); + const splitButtonRef = useRef(); + const menuRef = useRef(); + const uniqueGiftHeaderRef = useRef(); + const { + isContextMenuOpen, + contextMenuAnchor, + handleContextMenu, + handleContextMenuClose, + handleContextMenuHide, + } = useContextMenuHandlers(splitButtonRef); + const handleSymbolClick = useLastCallback(() => { if (!gift || !giftAttributes?.pattern) return; @@ -303,8 +315,8 @@ const GiftInfoModal = ({ return gift && getGiftAttributes(gift); }, [gift]); - const SettingsMenuButton: FC<{ onTrigger: () => void; isMenuOpen?: boolean }> = useMemo(() => { - return ({ onTrigger }) => ( + const SettingsMenuButton = useMemo(() => { + return (
); - }, [lang]); + }, [lang, handleContextMenu]); const renderFooterButton = useLastCallback(() => { if (canBuyGift) { @@ -480,22 +493,6 @@ const GiftInfoModal = ({ return canManage ? lang('GiftInfoReceived') : lang('GiftInfoTitle'); } - const uniqueGiftContextMenu = ( - - - - ); - const uniqueGiftModalHeader = (
)} -
- {isOpen && uniqueGiftContextMenu} +
+ {SettingsMenuButton}
+
uniqueGiftHeaderRef.current); + const getTriggerElement = useLastCallback(() => splitButtonRef.current); + const getMenuElement = useLastCallback(() => menuRef.current); + const getLayout = useLastCallback(() => ({ withPortal: true })); + + const uniqueGiftContextMenu = contextMenuAnchor && typeGift && ( + + + + ); + return ( <> + {uniqueGiftContextMenu} {uniqueGift && currentUser && Boolean(confirmPrice) && (