From 8d9b81221a587b4892242a2553bd1a8d3a0c7b7b Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Mon, 13 Oct 2025 12:10:46 +0100 Subject: [PATCH 1/4] feat: migrate WorkspaceAvatar screen to AttachmentModalScreen --- .../Navigation/AppNavigator/AuthScreens.tsx | 16 +++---- .../AttachmentModalBaseContent/index.tsx | 6 +-- .../routes/WorkspaceAvatarModalContent.tsx | 22 ++++++---- src/pages/workspace/WorkspaceAvatar.tsx | 42 ------------------- 4 files changed, 22 insertions(+), 64 deletions(-) delete mode 100644 src/pages/workspace/WorkspaceAvatar.tsx diff --git a/src/libs/Navigation/AppNavigator/AuthScreens.tsx b/src/libs/Navigation/AppNavigator/AuthScreens.tsx index 64b60bbe948a..7cdc93d6cf60 100644 --- a/src/libs/Navigation/AppNavigator/AuthScreens.tsx +++ b/src/libs/Navigation/AppNavigator/AuthScreens.tsx @@ -85,7 +85,6 @@ const loadConciergePage = () => require('../../../pages/Co const loadTrackExpensePage = () => require('../../../pages/TrackExpensePage').default; const loadSubmitExpensePage = () => require('../../../pages/SubmitExpensePage').default; const loadProfileAvatar = () => require('../../../pages/settings/Profile/Avatar/ProfileAvatar').default; -const loadWorkspaceAvatar = () => require('../../../pages/workspace/WorkspaceAvatar').default; const loadReportAvatar = () => require('../../../pages/ReportAvatar').default; const loadReceiptView = () => require('../../../pages/TransactionReceiptPage').default; const loadWorkspaceJoinUser = () => require('@pages/workspace/WorkspaceJoinUserPage').default; @@ -582,22 +581,19 @@ function AuthScreens() { listeners={modalScreenListeners} /> (originalFileName ? {name: originalFileName} : undefined), [originalFileName]); - const [files, setFilesInternal] = useState(); + const [files, setFilesInternal] = useState(() => filesProp ?? fallbackFile); const [isMultipleFiles, setIsMultipleFiles] = useState(() => Array.isArray(files)); const fileToDisplay = useMemo(() => { if (isMultipleFiles) { @@ -106,10 +106,6 @@ function AttachmentModalBaseContent({ }, []); useEffect(() => { - if (!filesProp) { - return; - } - setFile(filesProp ?? fallbackFile); }, [filesProp, fallbackFile, setFile]); diff --git a/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx index 0faa06da22f5..2c971ec8a59d 100644 --- a/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx +++ b/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx @@ -1,5 +1,6 @@ import React, {useMemo} from 'react'; import useOnyx from '@hooks/useOnyx'; +import usePolicy from '@hooks/usePolicy'; import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; import {getFullSizeAvatar} from '@libs/UserUtils'; import type {AttachmentModalBaseContentProps} from '@pages/media/AttachmentModalScreen/AttachmentModalBaseContent/types'; @@ -7,26 +8,33 @@ import AttachmentModalContainer from '@pages/media/AttachmentModalScreen/Attachm import type {AttachmentModalScreenProps} from '@pages/media/AttachmentModalScreen/types'; import ONYXKEYS from '@src/ONYXKEYS'; import type SCREENS from '@src/SCREENS'; +import useDownloadAttachment from './hooks/useDownloadAttachment'; function WorkspaceAvatarModalContent({navigation, route}: AttachmentModalScreenProps) { - const {policyID} = route.params; + const {policyID, letter: fallbackLetter} = route.params; - const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`, {canBeMissing: false}); - const [isLoadingApp = true] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true}); + const policy = usePolicy(policyID); + const [isLoadingApp = false] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true, initWithStoredValues: false}); - const avatarURL = policy?.avatarURL ?? getDefaultWorkspaceAvatar(policy?.name ?? ''); + const avatarURL = policy?.avatarURL ?? getDefaultWorkspaceAvatar(policy?.name ?? fallbackLetter); + + // eslint-disable-next-line rulesdir/no-negated-variables + const shouldShowNotFoundPage = !Object.keys(policy ?? {}).length && !isLoadingApp && (!policyID || !fallbackLetter); + + const onDownloadAttachment = useDownloadAttachment(); const contentProps = useMemo( () => ({ source: getFullSizeAvatar(avatarURL, 0), headerTitle: policy?.name, - isWorkspaceAvatar: true, originalFileName: policy?.originalFileName ?? policy?.id, - shouldShowNotFoundPage: !Object.keys(policy ?? {}).length && !isLoadingApp, + shouldShowNotFoundPage, + isWorkspaceAvatar: true, isLoading: !Object.keys(policy ?? {}).length && !!isLoadingApp, maybeIcon: true, + onDownloadAttachment, }), - [avatarURL, isLoadingApp, policy], + [avatarURL, isLoadingApp, onDownloadAttachment, policy, shouldShowNotFoundPage], ); return ( diff --git a/src/pages/workspace/WorkspaceAvatar.tsx b/src/pages/workspace/WorkspaceAvatar.tsx deleted file mode 100644 index 0ee3a3a872bf..000000000000 --- a/src/pages/workspace/WorkspaceAvatar.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React from 'react'; -import AttachmentModal from '@components/AttachmentModal'; -import useOnyx from '@hooks/useOnyx'; -import usePolicy from '@hooks/usePolicy'; -import Navigation from '@libs/Navigation/Navigation'; -import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; -import type {AuthScreensParamList} from '@libs/Navigation/types'; -import {getDefaultWorkspaceAvatar} from '@libs/ReportUtils'; -import {getFullSizeAvatar} from '@libs/UserUtils'; -import ONYXKEYS from '@src/ONYXKEYS'; -import type SCREENS from '@src/SCREENS'; - -type WorkspaceAvatarProps = PlatformStackScreenProps; - -function WorkspaceAvatar({route}: WorkspaceAvatarProps) { - const {policyID, letter: fallbackLetter} = route?.params ?? {}; - const policy = usePolicy(policyID); - const [isLoadingApp = false] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true, initWithStoredValues: false}); - const policyAvatarURL = policy?.avatarURL; - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - const avatarURL = policyAvatarURL || getDefaultWorkspaceAvatar(policy?.name ?? fallbackLetter); - // eslint-disable-next-line rulesdir/no-negated-variables - const shouldShowNotFoundPage = !Object.keys(policy ?? {}).length && !isLoadingApp && (!policyID || !fallbackLetter); - - return ( - - ); -} - -WorkspaceAvatar.displayName = 'WorkspaceAvatar'; - -export default WorkspaceAvatar; From 8a6fe733ddaaa2aa3865cc897be16026eb0ff5e4 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Mon, 13 Oct 2025 12:56:42 +0100 Subject: [PATCH 2/4] fix: minor changes from `WorkspaceAvatar.tsx` --- .../routes/WorkspaceAvatarModalContent.tsx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx index 2c971ec8a59d..df642d1ea227 100644 --- a/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx +++ b/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx @@ -18,23 +18,25 @@ function WorkspaceAvatarModalContent({navigation, route}: AttachmentModalScreenP const avatarURL = policy?.avatarURL ?? getDefaultWorkspaceAvatar(policy?.name ?? fallbackLetter); + const policyKeysLength = Object.keys(policy ?? {}).length; + // eslint-disable-next-line rulesdir/no-negated-variables - const shouldShowNotFoundPage = !Object.keys(policy ?? {}).length && !isLoadingApp && (!policyID || !fallbackLetter); + const shouldShowNotFoundPage = policyKeysLength === 0 && !isLoadingApp && (!policyID || !fallbackLetter); const onDownloadAttachment = useDownloadAttachment(); const contentProps = useMemo( () => ({ source: getFullSizeAvatar(avatarURL, 0), - headerTitle: policy?.name, - originalFileName: policy?.originalFileName ?? policy?.id, + headerTitle: policy?.name ?? '', + originalFileName: policy?.originalFileName ?? policy?.id ?? policyID, shouldShowNotFoundPage, isWorkspaceAvatar: true, - isLoading: !Object.keys(policy ?? {}).length && !!isLoadingApp, + isLoading: policyKeysLength === 0 && !!isLoadingApp, maybeIcon: true, onDownloadAttachment, }), - [avatarURL, isLoadingApp, onDownloadAttachment, policy, shouldShowNotFoundPage], + [avatarURL, isLoadingApp, onDownloadAttachment, policy?.id, policy?.name, policy?.originalFileName, policyID, policyKeysLength, shouldShowNotFoundPage], ); return ( From 01205b3f26d63845c668a93621b3647bcd606a73 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 14 Oct 2025 09:44:43 +0100 Subject: [PATCH 3/4] fix: add modal fade out animation on web --- .../AttachmentModalScreen/AttachmentModalContainer/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/index.tsx b/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/index.tsx index 30a0991123f6..3ec85b9211bc 100644 --- a/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/index.tsx +++ b/src/pages/media/AttachmentModalScreen/AttachmentModalContainer/index.tsx @@ -27,7 +27,8 @@ function AttachmentModalContainer({ const closeModal = useCallback(() => { Navigation.dismissModal(); - }, []); + resetAttachmentModalAndClose(); + }, [resetAttachmentModalAndClose]); useEffect(() => { onShow?.(); From 6921ddbbd7ad22b0c72567a572f17c02208927d9 Mon Sep 17 00:00:00 2001 From: Christoph Pader Date: Tue, 14 Oct 2025 15:46:41 +0100 Subject: [PATCH 4/4] refactor: extract variables --- .../routes/WorkspaceAvatarModalContent.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx b/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx index df642d1ea227..6804754d312c 100644 --- a/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx +++ b/src/pages/media/AttachmentModalScreen/routes/WorkspaceAvatarModalContent.tsx @@ -17,26 +17,29 @@ function WorkspaceAvatarModalContent({navigation, route}: AttachmentModalScreenP const [isLoadingApp = false] = useOnyx(ONYXKEYS.IS_LOADING_APP, {canBeMissing: true, initWithStoredValues: false}); const avatarURL = policy?.avatarURL ?? getDefaultWorkspaceAvatar(policy?.name ?? fallbackLetter); - + const source = getFullSizeAvatar(avatarURL, 0); const policyKeysLength = Object.keys(policy ?? {}).length; // eslint-disable-next-line rulesdir/no-negated-variables const shouldShowNotFoundPage = policyKeysLength === 0 && !isLoadingApp && (!policyID || !fallbackLetter); + const isLoading = policyKeysLength === 0 && !!isLoadingApp; + const originalFileName = policy?.originalFileName ?? policy?.id ?? policyID; + const headerTitle = policy?.name ?? ''; const onDownloadAttachment = useDownloadAttachment(); const contentProps = useMemo( () => ({ - source: getFullSizeAvatar(avatarURL, 0), - headerTitle: policy?.name ?? '', - originalFileName: policy?.originalFileName ?? policy?.id ?? policyID, + source, + headerTitle, + originalFileName, shouldShowNotFoundPage, + isLoading, isWorkspaceAvatar: true, - isLoading: policyKeysLength === 0 && !!isLoadingApp, maybeIcon: true, onDownloadAttachment, }), - [avatarURL, isLoadingApp, onDownloadAttachment, policy?.id, policy?.name, policy?.originalFileName, policyID, policyKeysLength, shouldShowNotFoundPage], + [headerTitle, isLoading, onDownloadAttachment, originalFileName, shouldShowNotFoundPage, source], ); return (