From 21d67d1d9f46b7d7f2e59e330c29752a1816f186 Mon Sep 17 00:00:00 2001
From: Bernhard Owen Josephus <bernhard.josephus@gmail.com>
Date: Thu, 23 Jan 2025 14:42:57 +0800
Subject: [PATCH 1/2] remove withNavigationFocus HOC

---
 src/components/AvatarWithImagePicker.tsx      |  9 ++----
 .../SignInButtons/AppleSignIn/index.tsx       | 19 +++++-------
 src/components/withNavigationFocus.tsx        | 29 -------------------
 .../ReportActionCompose.perf-test.tsx         | 19 +-----------
 tests/perf-test/SearchRouter.perf-test.tsx    | 18 ------------
 5 files changed, 11 insertions(+), 83 deletions(-)
 delete mode 100644 src/components/withNavigationFocus.tsx

diff --git a/src/components/AvatarWithImagePicker.tsx b/src/components/AvatarWithImagePicker.tsx
index 0230d1a42a02..c88e1be06d04 100644
--- a/src/components/AvatarWithImagePicker.tsx
+++ b/src/components/AvatarWithImagePicker.tsx
@@ -1,3 +1,4 @@
+import {useIsFocused} from '@react-navigation/native';
 import React, {useCallback, useEffect, useRef, useState} from 'react';
 import {StyleSheet, View} from 'react-native';
 import type {ImageStyle, StyleProp, ViewStyle} from 'react-native';
@@ -27,7 +28,6 @@ import OfflineWithFeedback from './OfflineWithFeedback';
 import PopoverMenu from './PopoverMenu';
 import PressableWithoutFeedback from './Pressable/PressableWithoutFeedback';
 import Tooltip from './Tooltip';
-import withNavigationFocus from './withNavigationFocus';
 
 type ErrorData = {
     validationError?: TranslationPaths | null | '';
@@ -107,9 +107,6 @@ type AvatarWithImagePickerProps = {
     /** File name of the avatar */
     originalFileName?: string;
 
-    /** Whether navigation is focused */
-    isFocused: boolean;
-
     /** Style applied to the avatar */
     avatarStyle: StyleProp<ViewStyle & ImageStyle>;
 
@@ -133,7 +130,6 @@ type AvatarWithImagePickerProps = {
 };
 
 function AvatarWithImagePicker({
-    isFocused,
     DefaultAvatar = () => null,
     style,
     disabledStyle,
@@ -164,6 +160,7 @@ function AvatarWithImagePicker({
 }: AvatarWithImagePickerProps) {
     const theme = useTheme();
     const styles = useThemeStyles();
+    const isFocused = useIsFocused();
     const {windowWidth} = useWindowDimensions();
     const [popoverPosition, setPopoverPosition] = useState({horizontal: 0, vertical: 0});
     const [isMenuVisible, setIsMenuVisible] = useState(false);
@@ -466,4 +463,4 @@ function AvatarWithImagePicker({
 
 AvatarWithImagePicker.displayName = 'AvatarWithImagePicker';
 
-export default withNavigationFocus(AvatarWithImagePicker);
+export default AvatarWithImagePicker;
diff --git a/src/components/SignInButtons/AppleSignIn/index.tsx b/src/components/SignInButtons/AppleSignIn/index.tsx
index dc7ae48f3b57..e152fbfb8018 100644
--- a/src/components/SignInButtons/AppleSignIn/index.tsx
+++ b/src/components/SignInButtons/AppleSignIn/index.tsx
@@ -1,9 +1,8 @@
+import {useIsFocused} from '@react-navigation/native';
 import React, {useEffect, useState} from 'react';
 import type {NativeConfig} from 'react-native-config';
 import Config from 'react-native-config';
 import getUserLanguage from '@components/SignInButtons/GetUserLanguage';
-import type {WithNavigationFocusProps} from '@components/withNavigationFocus';
-import withNavigationFocus from '@components/withNavigationFocus';
 import Log from '@libs/Log';
 import * as Session from '@userActions/Session';
 import CONFIG from '@src/CONFIG';
@@ -19,11 +18,9 @@ type AppleSignInDivProps = {
     onPointerDown?: () => void;
 };
 
-type SingletonAppleSignInButtonProps = AppleSignInDivProps & {
-    isFocused: boolean;
-};
+type SingletonAppleSignInButtonProps = AppleSignInDivProps;
 
-type AppleSignInProps = WithNavigationFocusProps & {
+type AppleSignInProps = {
     isDesktopFlow?: boolean;
     onPointerDown?: () => void;
     // eslint-disable-next-line react/no-unused-prop-types
@@ -110,7 +107,8 @@ function AppleSignInDiv({isDesktopFlow, onPointerDown}: AppleSignInDivProps) {
 // The Sign in with Apple script may fail to render button if there are multiple
 // of these divs present in the app, as it matches based on div id. So we'll
 // only mount the div when it should be visible.
-function SingletonAppleSignInButton({isFocused, isDesktopFlow, onPointerDown}: SingletonAppleSignInButtonProps) {
+function SingletonAppleSignInButton({isDesktopFlow, onPointerDown}: SingletonAppleSignInButtonProps) {
+    const isFocused = useIsFocused();
     if (!isFocused) {
         return null;
     }
@@ -122,9 +120,6 @@ function SingletonAppleSignInButton({isFocused, isDesktopFlow, onPointerDown}: S
     );
 }
 
-// withNavigationFocus is used to only render the button when it is visible.
-const SingletonAppleSignInButtonWithFocus = withNavigationFocus(SingletonAppleSignInButton);
-
 function AppleSignIn({isDesktopFlow = false, onPointerDown}: AppleSignInProps) {
     const [scriptLoaded, setScriptLoaded] = useState(false);
     useEffect(() => {
@@ -146,7 +141,7 @@ function AppleSignIn({isDesktopFlow = false, onPointerDown}: AppleSignInProps) {
     }
 
     return (
-        <SingletonAppleSignInButtonWithFocus
+        <SingletonAppleSignInButton
             isDesktopFlow={isDesktopFlow}
             onPointerDown={onPointerDown}
         />
@@ -154,5 +149,5 @@ function AppleSignIn({isDesktopFlow = false, onPointerDown}: AppleSignInProps) {
 }
 
 AppleSignIn.displayName = 'AppleSignIn';
-export default withNavigationFocus(AppleSignIn);
+export default AppleSignIn;
 export type {AppleSignInProps};
diff --git a/src/components/withNavigationFocus.tsx b/src/components/withNavigationFocus.tsx
deleted file mode 100644
index bd7a39620114..000000000000
--- a/src/components/withNavigationFocus.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import {useIsFocused} from '@react-navigation/native';
-import type {ComponentType, ForwardedRef, RefAttributes} from 'react';
-import React from 'react';
-import getComponentDisplayName from '@libs/getComponentDisplayName';
-
-type WithNavigationFocusProps = {
-    isFocused: boolean;
-};
-
-export default function withNavigationFocus<TProps extends WithNavigationFocusProps, TRef>(
-    WrappedComponent: ComponentType<TProps & RefAttributes<TRef>>,
-): (props: Omit<TProps, keyof WithNavigationFocusProps> & React.RefAttributes<TRef>) => React.ReactElement | null {
-    function WithNavigationFocus(props: Omit<TProps, keyof WithNavigationFocusProps>, ref: ForwardedRef<TRef>) {
-        const isFocused = useIsFocused();
-        return (
-            <WrappedComponent
-                // eslint-disable-next-line react/jsx-props-no-spreading
-                {...(props as TProps)}
-                ref={ref}
-                isFocused={isFocused}
-            />
-        );
-    }
-
-    WithNavigationFocus.displayName = `withNavigationFocus(${getComponentDisplayName(WrappedComponent)})`;
-    return React.forwardRef(WithNavigationFocus);
-}
-
-export type {WithNavigationFocusProps};
diff --git a/tests/perf-test/ReportActionCompose.perf-test.tsx b/tests/perf-test/ReportActionCompose.perf-test.tsx
index 1827e23ffe4b..64a79892f61e 100644
--- a/tests/perf-test/ReportActionCompose.perf-test.tsx
+++ b/tests/perf-test/ReportActionCompose.perf-test.tsx
@@ -1,10 +1,9 @@
 import {fireEvent, screen} from '@testing-library/react-native';
-import type {ComponentType, EffectCallback} from 'react';
+import type {EffectCallback} from 'react';
 import React from 'react';
 import Onyx from 'react-native-onyx';
 import type Animated from 'react-native-reanimated';
 import {measureRenders} from 'reassure';
-import type {WithNavigationFocusProps} from '@components/withNavigationFocus';
 import type {EmojiPickerRef} from '@libs/actions/EmojiPickerAction';
 import type Navigation from '@libs/Navigation/Navigation';
 import ComposeProviders from '@src/components/ComposeProviders';
@@ -59,22 +58,6 @@ jest.mock('@src/libs/actions/EmojiPickerAction', () => {
     };
 });
 
-jest.mock('@src/components/withNavigationFocus', <TProps extends WithNavigationFocusProps>() => (Component: ComponentType<TProps>) => {
-    function WithNavigationFocus(props: Omit<TProps, keyof WithNavigationFocusProps>) {
-        return (
-            <Component
-                // eslint-disable-next-line react/jsx-props-no-spreading
-                {...(props as TProps)}
-                isFocused={false}
-            />
-        );
-    }
-
-    WithNavigationFocus.displayName = 'WithNavigationFocus';
-
-    return WithNavigationFocus;
-});
-
 beforeAll(() =>
     Onyx.init({
         keys: ONYXKEYS,
diff --git a/tests/perf-test/SearchRouter.perf-test.tsx b/tests/perf-test/SearchRouter.perf-test.tsx
index 0784813127be..747e78b2a9ee 100644
--- a/tests/perf-test/SearchRouter.perf-test.tsx
+++ b/tests/perf-test/SearchRouter.perf-test.tsx
@@ -1,14 +1,12 @@
 import type * as NativeNavigation from '@react-navigation/native';
 import {fireEvent, screen} from '@testing-library/react-native';
 import React, {useMemo} from 'react';
-import type {ComponentType} from 'react';
 import Onyx from 'react-native-onyx';
 import {measureRenders} from 'reassure';
 import {LocaleContextProvider} from '@components/LocaleContextProvider';
 import {OptionsListContext} from '@components/OptionListContextProvider';
 import SearchRouter from '@components/Search/SearchRouter/SearchRouter';
 import SearchRouterInput from '@components/Search/SearchRouter/SearchRouterInput';
-import type {WithNavigationFocusProps} from '@components/withNavigationFocus';
 import {createOptionList} from '@libs/OptionsListUtils';
 import ComposeProviders from '@src/components/ComposeProviders';
 import OnyxProvider from '@src/components/OnyxProvider';
@@ -71,22 +69,6 @@ jest.mock('@react-navigation/native', () => {
     };
 });
 
-jest.mock('@src/components/withNavigationFocus', () => (Component: ComponentType<WithNavigationFocusProps>) => {
-    function WithNavigationFocus(props: WithNavigationFocusProps) {
-        return (
-            <Component
-                // eslint-disable-next-line react/jsx-props-no-spreading
-                {...props}
-                isFocused={false}
-            />
-        );
-    }
-
-    WithNavigationFocus.displayName = 'WithNavigationFocus';
-
-    return WithNavigationFocus;
-});
-
 const getMockedReports = (length = 100) =>
     createCollection<Report>(
         (item) => `${ONYXKEYS.COLLECTION.REPORT}${item.reportID}`,

From aaff5177592f9a89aa83b0eca38188d1768f3305 Mon Sep 17 00:00:00 2001
From: Bernhard Owen Josephus <bernhard.josephus@gmail.com>
Date: Thu, 23 Jan 2025 15:03:46 +0800
Subject: [PATCH 2/2] lint

---
 src/components/AvatarWithImagePicker.tsx           | 12 ++++++------
 src/components/SignInButtons/AppleSignIn/index.tsx |  4 ++--
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/components/AvatarWithImagePicker.tsx b/src/components/AvatarWithImagePicker.tsx
index c88e1be06d04..ee88c7c403dc 100644
--- a/src/components/AvatarWithImagePicker.tsx
+++ b/src/components/AvatarWithImagePicker.tsx
@@ -6,9 +6,9 @@ import useLocalize from '@hooks/useLocalize';
 import useTheme from '@hooks/useTheme';
 import useThemeStyles from '@hooks/useThemeStyles';
 import useWindowDimensions from '@hooks/useWindowDimensions';
-import * as Browser from '@libs/Browser';
+import {isSafari} from '@libs/Browser';
 import type {CustomRNImageManipulatorResult} from '@libs/cropOrRotateImage/types';
-import * as FileUtils from '@libs/fileDownload/FileUtils';
+import {splitExtensionFromFileName, validateImageForCorruption} from '@libs/fileDownload/FileUtils';
 import getImageResolution from '@libs/fileDownload/getImageResolution';
 import type {AvatarSource} from '@libs/UserUtils';
 import variables from '@styles/variables';
@@ -198,7 +198,7 @@ function AvatarWithImagePicker({
      * Check if the attachment extension is allowed.
      */
     const isValidExtension = useCallback((image: FileObject): boolean => {
-        const {fileExtension} = FileUtils.splitExtensionFromFileName(image?.name ?? '');
+        const {fileExtension} = splitExtensionFromFileName(image?.name ?? '');
         return CONST.AVATAR_ALLOWED_EXTENSIONS.some((extension) => extension === fileExtension.toLowerCase());
     }, []);
 
@@ -229,7 +229,7 @@ function AvatarWithImagePicker({
                 return;
             }
 
-            FileUtils.validateImageForCorruption(image)
+            validateImageForCorruption(image)
                 .then(() => isValidResolution(image))
                 .then((isValid) => {
                     if (!isValid) {
@@ -271,7 +271,7 @@ function AvatarWithImagePicker({
                 icon: Expensicons.Upload,
                 text: translate('avatarWithImagePicker.uploadPhoto'),
                 onSelected: () => {
-                    if (Browser.isSafari()) {
+                    if (isSafari()) {
                         return;
                     }
                     openPicker({
@@ -421,7 +421,7 @@ function AvatarWithImagePicker({
                                                 // In order for the file picker to open dynamically, the click
                                                 // function must be called from within an event handler that was initiated
                                                 // by the user on Safari.
-                                                if (index === 0 && Browser.isSafari()) {
+                                                if (index === 0 && isSafari()) {
                                                     openPicker({
                                                         onPicked: (data) => showAvatarCropModal(data.at(0) ?? {}),
                                                     });
diff --git a/src/components/SignInButtons/AppleSignIn/index.tsx b/src/components/SignInButtons/AppleSignIn/index.tsx
index e152fbfb8018..c9c5107c97fc 100644
--- a/src/components/SignInButtons/AppleSignIn/index.tsx
+++ b/src/components/SignInButtons/AppleSignIn/index.tsx
@@ -3,8 +3,8 @@ import React, {useEffect, useState} from 'react';
 import type {NativeConfig} from 'react-native-config';
 import Config from 'react-native-config';
 import getUserLanguage from '@components/SignInButtons/GetUserLanguage';
+import {beginAppleSignIn} from '@libs/actions/Session';
 import Log from '@libs/Log';
-import * as Session from '@userActions/Session';
 import CONFIG from '@src/CONFIG';
 import CONST from '@src/CONST';
 import type {AppleIDSignInOnFailureEvent, AppleIDSignInOnSuccessEvent} from '@src/types/modules/dom';
@@ -46,7 +46,7 @@ const config = {
 
 const successListener = (event: AppleIDSignInOnSuccessEvent) => {
     const token = event.detail.authorization.id_token;
-    Session.beginAppleSignIn(token);
+    beginAppleSignIn(token);
 };
 
 const failureListener = (event: AppleIDSignInOnFailureEvent) => {