From dcb8aacaf190800becb1092459d3255cb578c55a Mon Sep 17 00:00:00 2001 From: Adam Horodyski Date: Wed, 8 Apr 2026 16:31:08 +0200 Subject: [PATCH 1/2] Wrap PopoverMenu and EmojiPicker in Activity to skip hidden subtree reconciliation Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/EmojiPicker/EmojiPicker.tsx | 84 ++++++++++--------- .../AttachmentPickerWithMenuItems.tsx | 64 +++++++------- 2 files changed, 76 insertions(+), 72 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.tsx b/src/components/EmojiPicker/EmojiPicker.tsx index 40d3534736e3..97e8c9ad9933 100644 --- a/src/components/EmojiPicker/EmojiPicker.tsx +++ b/src/components/EmojiPicker/EmojiPicker.tsx @@ -1,4 +1,4 @@ -import React, {useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; +import React, {Activity, useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import type {ForwardedRef, RefObject} from 'react'; import {Dimensions, View} from 'react-native'; import type {Emoji} from '@assets/emojis/types'; @@ -236,46 +236,48 @@ function EmojiPicker({viewportOffsetTop, ref}: EmojiPickerProps) { }, [isEmojiPickerVisible, shouldUseNarrowLayout, emojiPopoverAnchorOrigin, getEmojiPopoverAnchor, hideEmojiPicker]); return ( - } - withoutOverlay={isWithoutOverlay} - popoverDimensions={{ - width: CONST.EMOJI_PICKER_SIZE.WIDTH, - height: CONST.EMOJI_PICKER_SIZE.HEIGHT, - }} - anchorAlignment={emojiPopoverAnchorOrigin} - outerStyle={StyleUtils.getOuterModalStyle(windowHeight, viewportOffsetTop)} - innerContainerStyle={styles.popoverInnerContainer} - anchorDimensions={emojiAnchorDimension.current} - avoidKeyboard - shouldSwitchPositionIfOverflow - shouldEnableNewFocusManagement - restoreFocusType={CONST.MODAL.RESTORE_FOCUS_TYPE.DELETE} - shouldSkipRemeasurement - shouldWrapModalChildrenInScrollViewIfBottomDockedInLandscapeMode={false} - > - - - { - emojiSearchInput.current = el; - }} - /> - - - + + } + withoutOverlay={isWithoutOverlay} + popoverDimensions={{ + width: CONST.EMOJI_PICKER_SIZE.WIDTH, + height: CONST.EMOJI_PICKER_SIZE.HEIGHT, + }} + anchorAlignment={emojiPopoverAnchorOrigin} + outerStyle={StyleUtils.getOuterModalStyle(windowHeight, viewportOffsetTop)} + innerContainerStyle={styles.popoverInnerContainer} + anchorDimensions={emojiAnchorDimension.current} + avoidKeyboard + shouldSwitchPositionIfOverflow + shouldEnableNewFocusManagement + restoreFocusType={CONST.MODAL.RESTORE_FOCUS_TYPE.DELETE} + shouldSkipRemeasurement + shouldWrapModalChildrenInScrollViewIfBottomDockedInLandscapeMode={false} + > + + + { + emojiSearchInput.current = el; + }} + /> + + + + ); } diff --git a/src/pages/inbox/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx b/src/pages/inbox/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx index 949ffdebca88..46d92156765e 100644 --- a/src/pages/inbox/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx +++ b/src/pages/inbox/report/ReportActionCompose/AttachmentPickerWithMenuItems.tsx @@ -1,6 +1,6 @@ import {useIsFocused} from '@react-navigation/native'; import {accountIDSelector} from '@selectors/Session'; -import React, {useCallback, useEffect, useMemo, useState} from 'react'; +import React, {Activity, useCallback, useEffect, useMemo, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import AttachmentPicker from '@components/AttachmentPicker'; @@ -531,37 +531,39 @@ function AttachmentPickerWithMenuItems({ )} - { - setMenuVisibility(false); - onItemSelected(); - - // In order for the file picker to open dynamically, the click - // function must be called from within a event handler that was initiated - // by the user on Safari. - if (index === menuItems.length - 1) { - if (isSafari()) { - triggerAttachmentPicker(); - return; + + { + setMenuVisibility(false); + onItemSelected(); + + // In order for the file picker to open dynamically, the click + // function must be called from within a event handler that was initiated + // by the user on Safari. + if (index === menuItems.length - 1) { + if (isSafari()) { + triggerAttachmentPicker(); + return; + } + close(() => { + triggerAttachmentPicker(); + }); } - close(() => { - triggerAttachmentPicker(); - }); - } - }} - anchorPosition={popoverAnchorPosition ?? {horizontal: 0, vertical: 0}} - anchorAlignment={{ - horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, - vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, - }} - menuItems={menuItems} - anchorRef={actionButtonRef} - /> + }} + anchorPosition={popoverAnchorPosition ?? {horizontal: 0, vertical: 0}} + anchorAlignment={{ + horizontal: CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT, + vertical: CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.BOTTOM, + }} + menuItems={menuItems} + anchorRef={actionButtonRef} + /> + ); }} From 868cc849a1a6dee5752560b4f89aef58d83b9cba Mon Sep 17 00:00:00 2001 From: Adam Horodyski Date: Thu, 9 Apr 2026 15:32:16 +0200 Subject: [PATCH 2/2] Move Activity inside popover to preserve onModalHide lifecycle Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/EmojiPicker/EmojiPicker.tsx | 66 +++++++++++----------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/components/EmojiPicker/EmojiPicker.tsx b/src/components/EmojiPicker/EmojiPicker.tsx index 97e8c9ad9933..642bbba7c635 100644 --- a/src/components/EmojiPicker/EmojiPicker.tsx +++ b/src/components/EmojiPicker/EmojiPicker.tsx @@ -236,36 +236,36 @@ function EmojiPicker({viewportOffsetTop, ref}: EmojiPickerProps) { }, [isEmojiPickerVisible, shouldUseNarrowLayout, emojiPopoverAnchorOrigin, getEmojiPopoverAnchor, hideEmojiPicker]); return ( - - } - withoutOverlay={isWithoutOverlay} - popoverDimensions={{ - width: CONST.EMOJI_PICKER_SIZE.WIDTH, - height: CONST.EMOJI_PICKER_SIZE.HEIGHT, - }} - anchorAlignment={emojiPopoverAnchorOrigin} - outerStyle={StyleUtils.getOuterModalStyle(windowHeight, viewportOffsetTop)} - innerContainerStyle={styles.popoverInnerContainer} - anchorDimensions={emojiAnchorDimension.current} - avoidKeyboard - shouldSwitchPositionIfOverflow - shouldEnableNewFocusManagement - restoreFocusType={CONST.MODAL.RESTORE_FOCUS_TYPE.DELETE} - shouldSkipRemeasurement - shouldWrapModalChildrenInScrollViewIfBottomDockedInLandscapeMode={false} - > - + } + withoutOverlay={isWithoutOverlay} + popoverDimensions={{ + width: CONST.EMOJI_PICKER_SIZE.WIDTH, + height: CONST.EMOJI_PICKER_SIZE.HEIGHT, + }} + anchorAlignment={emojiPopoverAnchorOrigin} + outerStyle={StyleUtils.getOuterModalStyle(windowHeight, viewportOffsetTop)} + innerContainerStyle={styles.popoverInnerContainer} + anchorDimensions={emojiAnchorDimension.current} + avoidKeyboard + shouldSwitchPositionIfOverflow + shouldEnableNewFocusManagement + restoreFocusType={CONST.MODAL.RESTORE_FOCUS_TYPE.DELETE} + shouldSkipRemeasurement + shouldWrapModalChildrenInScrollViewIfBottomDockedInLandscapeMode={false} + > + + - - - + + + ); }