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) => {