From dcb8aacaf190800becb1092459d3255cb578c55a Mon Sep 17 00:00:00 2001
From: Adam Horodyski <ad.horodyski@gmail.com>
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) <noreply@anthropic.com>
---
 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 (
-        <PopoverWithMeasuredContent
-            shouldHandleNavigationBack={isMobileChrome()}
-            isVisible={isEmojiPickerVisible}
-            onClose={hideEmojiPicker}
-            onModalShow={focusEmojiSearchInput}
-            onModalHide={handleModalHide}
-            shouldSetModalVisibility={false}
-            anchorPosition={{
-                vertical: emojiPopoverAnchorPosition.vertical,
-                horizontal: emojiPopoverAnchorPosition.horizontal,
-            }}
-            anchorRef={getEmojiPopoverAnchor() as RefObject<View | HTMLDivElement>}
-            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}
-        >
-            <FocusTrapForModal active={isEmojiPickerVisible}>
-                <View>
-                    <EmojiPickerMenu
-                        onEmojiSelected={selectEmoji}
-                        activeEmoji={activeEmoji.current}
-                        ref={(el) => {
-                            emojiSearchInput.current = el;
-                        }}
-                    />
-                </View>
-            </FocusTrapForModal>
-        </PopoverWithMeasuredContent>
+        <Activity mode={isEmojiPickerVisible ? 'visible' : 'hidden'}>
+            <PopoverWithMeasuredContent
+                shouldHandleNavigationBack={isMobileChrome()}
+                isVisible={isEmojiPickerVisible}
+                onClose={hideEmojiPicker}
+                onModalShow={focusEmojiSearchInput}
+                onModalHide={handleModalHide}
+                shouldSetModalVisibility={false}
+                anchorPosition={{
+                    vertical: emojiPopoverAnchorPosition.vertical,
+                    horizontal: emojiPopoverAnchorPosition.horizontal,
+                }}
+                anchorRef={getEmojiPopoverAnchor() as RefObject<View | HTMLDivElement>}
+                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}
+            >
+                <FocusTrapForModal active={isEmojiPickerVisible}>
+                    <View>
+                        <EmojiPickerMenu
+                            onEmojiSelected={selectEmoji}
+                            activeEmoji={activeEmoji.current}
+                            ref={(el) => {
+                                emojiSearchInput.current = el;
+                            }}
+                        />
+                    </View>
+                </FocusTrapForModal>
+            </PopoverWithMeasuredContent>
+        </Activity>
     );
 }
 
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({
                                 )}
                             </View>
                         </View>
-                        <PopoverMenu
-                            animationInTiming={menuItems.length * 50}
-                            // The menu should close 2/3 of the time it took to open
-                            animationOutTiming={menuItems.length * 50 * 0.66}
-                            isVisible={isMenuVisible && isFocused}
-                            onClose={onPopoverMenuClose}
-                            onItemSelected={(item, index) => {
-                                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;
+                        <Activity mode={isMenuVisible && isFocused ? 'visible' : 'hidden'}>
+                            <PopoverMenu
+                                animationInTiming={menuItems.length * 50}
+                                // The menu should close 2/3 of the time it took to open
+                                animationOutTiming={menuItems.length * 50 * 0.66}
+                                isVisible={isMenuVisible && isFocused}
+                                onClose={onPopoverMenuClose}
+                                onItemSelected={(item, index) => {
+                                    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}
+                            />
+                        </Activity>
                     </>
                 );
             }}

From 868cc849a1a6dee5752560b4f89aef58d83b9cba Mon Sep 17 00:00:00 2001
From: Adam Horodyski <ad.horodyski@gmail.com>
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) <noreply@anthropic.com>
---
 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 (
-        <Activity mode={isEmojiPickerVisible ? 'visible' : 'hidden'}>
-            <PopoverWithMeasuredContent
-                shouldHandleNavigationBack={isMobileChrome()}
-                isVisible={isEmojiPickerVisible}
-                onClose={hideEmojiPicker}
-                onModalShow={focusEmojiSearchInput}
-                onModalHide={handleModalHide}
-                shouldSetModalVisibility={false}
-                anchorPosition={{
-                    vertical: emojiPopoverAnchorPosition.vertical,
-                    horizontal: emojiPopoverAnchorPosition.horizontal,
-                }}
-                anchorRef={getEmojiPopoverAnchor() as RefObject<View | HTMLDivElement>}
-                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}
-            >
-                <FocusTrapForModal active={isEmojiPickerVisible}>
+        <PopoverWithMeasuredContent
+            shouldHandleNavigationBack={isMobileChrome()}
+            isVisible={isEmojiPickerVisible}
+            onClose={hideEmojiPicker}
+            onModalShow={focusEmojiSearchInput}
+            onModalHide={handleModalHide}
+            shouldSetModalVisibility={false}
+            anchorPosition={{
+                vertical: emojiPopoverAnchorPosition.vertical,
+                horizontal: emojiPopoverAnchorPosition.horizontal,
+            }}
+            anchorRef={getEmojiPopoverAnchor() as RefObject<View | HTMLDivElement>}
+            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}
+        >
+            <FocusTrapForModal active={isEmojiPickerVisible}>
+                <Activity mode={isEmojiPickerVisible ? 'visible' : 'hidden'}>
                     <View>
                         <EmojiPickerMenu
                             onEmojiSelected={selectEmoji}
@@ -275,9 +275,9 @@ function EmojiPicker({viewportOffsetTop, ref}: EmojiPickerProps) {
                             }}
                         />
                     </View>
-                </FocusTrapForModal>
-            </PopoverWithMeasuredContent>
-        </Activity>
+                </Activity>
+            </FocusTrapForModal>
+        </PopoverWithMeasuredContent>
     );
 }
 
