From bb2f05b79ec95f2f5b72df44b0b7f7a1abe3dbd4 Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Tue, 26 Nov 2024 10:05:06 +0700 Subject: [PATCH 1/3] fix resolve delay when dismissing attachment preview --- src/pages/ReportAvatar.tsx | 5 ++++- src/pages/TransactionReceiptPage.tsx | 7 ++++++- src/pages/home/report/ReportAttachments.tsx | 9 ++++++--- src/pages/settings/Profile/ProfileAvatar.tsx | 5 ++++- src/pages/workspace/WorkspaceAvatar.tsx | 7 ++++++- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/src/pages/ReportAvatar.tsx b/src/pages/ReportAvatar.tsx index 2e876dcfaa3e..2f064596fa9d 100644 --- a/src/pages/ReportAvatar.tsx +++ b/src/pages/ReportAvatar.tsx @@ -1,5 +1,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useMemo} from 'react'; +import {InteractionManager} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; import Navigation from '@libs/Navigation/Navigation'; @@ -43,7 +44,9 @@ function ReportAvatar({route}: ReportAvatarProps) { defaultOpen source={attachment.source} onModalClose={() => { - Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report?.reportID ?? '-1')); + InteractionManager.runAfterInteractions(() => { + Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report?.reportID ?? '-1')); + }); }} isWorkspaceAvatar={attachment.isWorkspaceAvatar} maybeIcon diff --git a/src/pages/TransactionReceiptPage.tsx b/src/pages/TransactionReceiptPage.tsx index d868b6535745..133abd658d70 100644 --- a/src/pages/TransactionReceiptPage.tsx +++ b/src/pages/TransactionReceiptPage.tsx @@ -1,5 +1,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useEffect} from 'react'; +import {InteractionManager} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; import Navigation from '@libs/Navigation/Navigation'; @@ -75,7 +76,11 @@ function TransactionReceipt({route}: TransactionReceiptProps) { isTrackExpenseAction={isTrackExpenseAction} originalFileName={receiptURIs?.filename} defaultOpen - onModalClose={onModalClose} + onModalClose={() => { + InteractionManager.runAfterInteractions(() => { + onModalClose(); + }); + }} isLoading={!transaction && reportMetadata?.isLoadingInitialReportActions} shouldShowNotFoundPage={shouldShowNotFoundPage} /> diff --git a/src/pages/home/report/ReportAttachments.tsx b/src/pages/home/report/ReportAttachments.tsx index 12a5032861ef..e25b68932fd6 100644 --- a/src/pages/home/report/ReportAttachments.tsx +++ b/src/pages/home/report/ReportAttachments.tsx @@ -1,5 +1,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback} from 'react'; +import {InteractionManager} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; import type {Attachment} from '@components/Attachments/types'; @@ -51,9 +52,11 @@ function ReportAttachments({route}: ReportAttachmentsProps) { report={report} source={source} onModalClose={() => { - Navigation.dismissModal(); - // This enables Composer refocus when the attachments modal is closed by the browser navigation - ComposerFocusManager.setReadyToFocus(); + InteractionManager.runAfterInteractions(() => { + Navigation.dismissModal(); + // This enables Composer refocus when the attachments modal is closed by the browser navigation + ComposerFocusManager.setReadyToFocus(); + }); }} onCarouselAttachmentChange={onCarouselAttachmentChange} shouldShowNotFoundPage={!isLoadingApp && type !== CONST.ATTACHMENT_TYPE.SEARCH && !report?.reportID} diff --git a/src/pages/settings/Profile/ProfileAvatar.tsx b/src/pages/settings/Profile/ProfileAvatar.tsx index 977719f63879..845ad553d051 100644 --- a/src/pages/settings/Profile/ProfileAvatar.tsx +++ b/src/pages/settings/Profile/ProfileAvatar.tsx @@ -1,5 +1,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useEffect} from 'react'; +import {InteractionManager} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; @@ -41,7 +42,9 @@ function ProfileAvatar({route, personalDetails, personalDetailsMetadata, isLoadi defaultOpen source={UserUtils.getFullSizeAvatar(avatarURL, accountID)} onModalClose={() => { - Navigation.goBack(); + InteractionManager.runAfterInteractions(() => { + Navigation.goBack(); + }); }} originalFileName={personalDetail?.originalFileName ?? ''} isLoading={!!isLoading} diff --git a/src/pages/workspace/WorkspaceAvatar.tsx b/src/pages/workspace/WorkspaceAvatar.tsx index dfe79feb7755..d8fe4983e932 100644 --- a/src/pages/workspace/WorkspaceAvatar.tsx +++ b/src/pages/workspace/WorkspaceAvatar.tsx @@ -1,5 +1,6 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; +import {InteractionManager} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; @@ -26,7 +27,11 @@ function WorkspaceAvatar({policy, isLoadingApp = true}: WorkspaceAvatarProps) { headerTitle={policy?.name ?? ''} defaultOpen source={UserUtils.getFullSizeAvatar(avatarURL, 0)} - onModalClose={Navigation.goBack} + onModalClose={() => { + InteractionManager.runAfterInteractions(() => { + Navigation.goBack(); + }); + }} isWorkspaceAvatar originalFileName={policy?.originalFileName ?? policy?.id} shouldShowNotFoundPage={!Object.keys(policy ?? {}).length && !isLoadingApp} From d8028c9f8f8bd7df403c324117ffad1f13b573de Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Thu, 28 Nov 2024 09:30:58 +0700 Subject: [PATCH 2/3] create attachment modal handler --- src/libs/AttachmentModalHandler/index.ios.ts | 12 ++++++++++++ src/libs/AttachmentModalHandler/index.ts | 9 +++++++++ src/libs/AttachmentModalHandler/types.ts | 5 +++++ src/pages/ReportAvatar.tsx | 12 ++++++------ src/pages/TransactionReceiptPage.tsx | 8 ++------ src/pages/home/report/ReportAttachments.tsx | 16 ++++++++-------- src/pages/settings/Profile/ProfileAvatar.tsx | 12 ++++++------ src/pages/workspace/WorkspaceAvatar.tsx | 11 +++++------ 8 files changed, 53 insertions(+), 32 deletions(-) create mode 100644 src/libs/AttachmentModalHandler/index.ios.ts create mode 100644 src/libs/AttachmentModalHandler/index.ts create mode 100644 src/libs/AttachmentModalHandler/types.ts diff --git a/src/libs/AttachmentModalHandler/index.ios.ts b/src/libs/AttachmentModalHandler/index.ios.ts new file mode 100644 index 000000000000..1e50f4990523 --- /dev/null +++ b/src/libs/AttachmentModalHandler/index.ios.ts @@ -0,0 +1,12 @@ +import {InteractionManager} from 'react-native'; +import type AttachmentModalHandler from './types'; + +const attachmentModalHandler: AttachmentModalHandler = { + handleModalClose: (onCloseCallback) => { + InteractionManager.runAfterInteractions(() => { + onCloseCallback?.(); + }); + }, +}; + +export default attachmentModalHandler; diff --git a/src/libs/AttachmentModalHandler/index.ts b/src/libs/AttachmentModalHandler/index.ts new file mode 100644 index 000000000000..fb00e8b6d577 --- /dev/null +++ b/src/libs/AttachmentModalHandler/index.ts @@ -0,0 +1,9 @@ +import type AttachmentModalHandler from './types'; + +const attachmentModalHandler: AttachmentModalHandler = { + handleModalClose: (onCloseCallback) => { + onCloseCallback?.(); + }, +}; + +export default attachmentModalHandler; diff --git a/src/libs/AttachmentModalHandler/types.ts b/src/libs/AttachmentModalHandler/types.ts new file mode 100644 index 000000000000..a9cf1527d9b2 --- /dev/null +++ b/src/libs/AttachmentModalHandler/types.ts @@ -0,0 +1,5 @@ +type AttachmentModalHandler = { + handleModalClose: (onCloseCallback?: () => void) => void; +}; + +export default AttachmentModalHandler; diff --git a/src/pages/ReportAvatar.tsx b/src/pages/ReportAvatar.tsx index 2f064596fa9d..a07ecace293e 100644 --- a/src/pages/ReportAvatar.tsx +++ b/src/pages/ReportAvatar.tsx @@ -1,8 +1,8 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useMemo} from 'react'; -import {InteractionManager} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; +import attachmentModalHandler from '@libs/AttachmentModalHandler'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList} from '@libs/Navigation/types'; import * as ReportUtils from '@libs/ReportUtils'; @@ -38,16 +38,16 @@ function ReportAvatar({route}: ReportAvatarProps) { }; }, [report, policy]); + const onModalClose = () => { + Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report?.reportID ?? '-1')); + }; + return ( { - InteractionManager.runAfterInteractions(() => { - Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report?.reportID ?? '-1')); - }); - }} + onModalClose={() => attachmentModalHandler.handleModalClose(onModalClose)} isWorkspaceAvatar={attachment.isWorkspaceAvatar} maybeIcon originalFileName={attachment.originalFileName} diff --git a/src/pages/TransactionReceiptPage.tsx b/src/pages/TransactionReceiptPage.tsx index 133abd658d70..af6309f3fa50 100644 --- a/src/pages/TransactionReceiptPage.tsx +++ b/src/pages/TransactionReceiptPage.tsx @@ -1,8 +1,8 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useEffect} from 'react'; -import {InteractionManager} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; +import attachmentModalHandler from '@libs/AttachmentModalHandler'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList, RootStackParamList, State} from '@libs/Navigation/types'; import * as ReceiptUtils from '@libs/ReceiptUtils'; @@ -76,11 +76,7 @@ function TransactionReceipt({route}: TransactionReceiptProps) { isTrackExpenseAction={isTrackExpenseAction} originalFileName={receiptURIs?.filename} defaultOpen - onModalClose={() => { - InteractionManager.runAfterInteractions(() => { - onModalClose(); - }); - }} + onModalClose={() => attachmentModalHandler.handleModalClose(onModalClose)} isLoading={!transaction && reportMetadata?.isLoadingInitialReportActions} shouldShowNotFoundPage={shouldShowNotFoundPage} /> diff --git a/src/pages/home/report/ReportAttachments.tsx b/src/pages/home/report/ReportAttachments.tsx index e25b68932fd6..6352aa6a9bf0 100644 --- a/src/pages/home/report/ReportAttachments.tsx +++ b/src/pages/home/report/ReportAttachments.tsx @@ -1,9 +1,9 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback} from 'react'; -import {InteractionManager} from 'react-native'; import {useOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; import type {Attachment} from '@components/Attachments/types'; +import attachmentModalHandler from '@libs/AttachmentModalHandler'; import ComposerFocusManager from '@libs/ComposerFocusManager'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList} from '@libs/Navigation/types'; @@ -43,6 +43,12 @@ function ReportAttachments({route}: ReportAttachmentsProps) { [reportID, type, accountID], ); + const onModalClose = () => { + Navigation.dismissModal(); + // This enables Composer refocus when the attachments modal is closed by the browser navigation + ComposerFocusManager.setReadyToFocus(); + }; + return ( { - InteractionManager.runAfterInteractions(() => { - Navigation.dismissModal(); - // This enables Composer refocus when the attachments modal is closed by the browser navigation - ComposerFocusManager.setReadyToFocus(); - }); - }} + onModalClose={() => attachmentModalHandler.handleModalClose(onModalClose)} onCarouselAttachmentChange={onCarouselAttachmentChange} shouldShowNotFoundPage={!isLoadingApp && type !== CONST.ATTACHMENT_TYPE.SEARCH && !report?.reportID} isAuthTokenRequired={!!isAuthTokenRequired} diff --git a/src/pages/settings/Profile/ProfileAvatar.tsx b/src/pages/settings/Profile/ProfileAvatar.tsx index 845ad553d051..f4842f3a5029 100644 --- a/src/pages/settings/Profile/ProfileAvatar.tsx +++ b/src/pages/settings/Profile/ProfileAvatar.tsx @@ -1,9 +1,9 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React, {useEffect} from 'react'; -import {InteractionManager} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; +import attachmentModalHandler from '@libs/AttachmentModalHandler'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList} from '@libs/Navigation/types'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; @@ -36,16 +36,16 @@ function ProfileAvatar({route, personalDetails, personalDetailsMetadata, isLoadi PersonalDetails.openPublicProfilePage(accountID); }, [accountID, avatarURL]); + const onModalClose = () => { + Navigation.goBack(); + }; + return ( { - InteractionManager.runAfterInteractions(() => { - Navigation.goBack(); - }); - }} + onModalClose={() => attachmentModalHandler.handleModalClose(onModalClose)} originalFileName={personalDetail?.originalFileName ?? ''} isLoading={!!isLoading} shouldShowNotFoundPage={!avatarURL} diff --git a/src/pages/workspace/WorkspaceAvatar.tsx b/src/pages/workspace/WorkspaceAvatar.tsx index d8fe4983e932..90704dd553b2 100644 --- a/src/pages/workspace/WorkspaceAvatar.tsx +++ b/src/pages/workspace/WorkspaceAvatar.tsx @@ -1,9 +1,9 @@ import type {StackScreenProps} from '@react-navigation/stack'; import React from 'react'; -import {InteractionManager} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; import {withOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; +import attachmentModalHandler from '@libs/AttachmentModalHandler'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList} from '@libs/Navigation/types'; import * as ReportUtils from '@libs/ReportUtils'; @@ -21,17 +21,16 @@ type WorkspaceAvatarProps = WorkspaceAvatarOnyxProps & StackScreenProps { + Navigation.goBack(); + }; return ( { - InteractionManager.runAfterInteractions(() => { - Navigation.goBack(); - }); - }} + onModalClose={() => attachmentModalHandler.handleModalClose(onModalClose)} isWorkspaceAvatar originalFileName={policy?.originalFileName ?? policy?.id} shouldShowNotFoundPage={!Object.keys(policy ?? {}).length && !isLoadingApp} From d57acf8029c575af4b710153fb01ad25775011ab Mon Sep 17 00:00:00 2001 From: Huu Le <20178761+huult@users.noreply.github.com> Date: Tue, 3 Dec 2024 08:50:10 +0700 Subject: [PATCH 3/3] replace withOnyx with useOnyx --- src/pages/settings/Profile/ProfileAvatar.tsx | 29 +++++--------------- src/pages/workspace/WorkspaceAvatar.tsx | 24 ++++------------ 2 files changed, 13 insertions(+), 40 deletions(-) diff --git a/src/pages/settings/Profile/ProfileAvatar.tsx b/src/pages/settings/Profile/ProfileAvatar.tsx index 1f2f67a99841..69b9c3c976db 100644 --- a/src/pages/settings/Profile/ProfileAvatar.tsx +++ b/src/pages/settings/Profile/ProfileAvatar.tsx @@ -1,6 +1,5 @@ import React, {useEffect} from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; import attachmentModalHandler from '@libs/AttachmentModalHandler'; import Navigation from '@libs/Navigation/Navigation'; @@ -12,17 +11,13 @@ import * as ValidationUtils from '@libs/ValidationUtils'; import * as PersonalDetails from '@userActions/PersonalDetails'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import type {PersonalDetailsList, PersonalDetailsMetadata} from '@src/types/onyx'; -type ProfileAvatarOnyxProps = { - personalDetails: OnyxEntry; - personalDetailsMetadata: OnyxEntry>; - isLoadingApp: OnyxEntry; -}; +type ProfileAvatarProps = PlatformStackScreenProps; -type ProfileAvatarProps = ProfileAvatarOnyxProps & PlatformStackScreenProps; - -function ProfileAvatar({route, personalDetails, personalDetailsMetadata, isLoadingApp = true}: ProfileAvatarProps) { +function ProfileAvatar({route}: ProfileAvatarProps) { + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + const [personalDetailsMetadata] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_METADATA); + const [isLoadingApp = true] = useOnyx(ONYXKEYS.IS_LOADING_APP); const personalDetail = personalDetails?.[route.params.accountID]; const avatarURL = personalDetail?.avatar ?? ''; const accountID = Number(route.params.accountID ?? '-1'); @@ -55,14 +50,4 @@ function ProfileAvatar({route, personalDetails, personalDetailsMetadata, isLoadi ProfileAvatar.displayName = 'ProfileAvatar'; -export default withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - personalDetailsMetadata: { - key: ONYXKEYS.PERSONAL_DETAILS_METADATA, - }, - isLoadingApp: { - key: ONYXKEYS.IS_LOADING_APP, - }, -})(ProfileAvatar); +export default ProfileAvatar; diff --git a/src/pages/workspace/WorkspaceAvatar.tsx b/src/pages/workspace/WorkspaceAvatar.tsx index fefcf2bb2735..3c04e69cfa51 100644 --- a/src/pages/workspace/WorkspaceAvatar.tsx +++ b/src/pages/workspace/WorkspaceAvatar.tsx @@ -1,6 +1,5 @@ import React from 'react'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import AttachmentModal from '@components/AttachmentModal'; import attachmentModalHandler from '@libs/AttachmentModalHandler'; import Navigation from '@libs/Navigation/Navigation'; @@ -10,16 +9,12 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as UserUtils from '@libs/UserUtils'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; -import type {Policy} from '@src/types/onyx'; -type WorkspaceAvatarOnyxProps = { - policy: OnyxEntry; - isLoadingApp: OnyxEntry; -}; +type WorkspaceAvatarProps = PlatformStackScreenProps; -type WorkspaceAvatarProps = WorkspaceAvatarOnyxProps & PlatformStackScreenProps; - -function WorkspaceAvatar({policy, isLoadingApp = true}: WorkspaceAvatarProps) { +function WorkspaceAvatar({route}: WorkspaceAvatarProps) { + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? '-1'}`); + const [isLoadingApp = true] = useOnyx(ONYXKEYS.IS_LOADING_APP); const avatarURL = policy?.avatarURL ?? '' ? policy?.avatarURL ?? '' : ReportUtils.getDefaultWorkspaceAvatar(policy?.name ?? ''); const onModalClose = () => { Navigation.goBack(); @@ -42,11 +37,4 @@ function WorkspaceAvatar({policy, isLoadingApp = true}: WorkspaceAvatarProps) { WorkspaceAvatar.displayName = 'WorkspaceAvatar'; -export default withOnyx({ - policy: { - key: ({route}) => `${ONYXKEYS.COLLECTION.POLICY}${route.params.policyID ?? '-1'}`, - }, - isLoadingApp: { - key: ONYXKEYS.IS_LOADING_APP, - }, -})(WorkspaceAvatar); +export default WorkspaceAvatar;