diff --git a/src/pages/workspace/WorkspaceConfirmationOwnerSelectorPage.tsx b/src/pages/workspace/WorkspaceConfirmationOwnerSelectorPage.tsx index c73de566adf9..c1117c9f1ae4 100644 --- a/src/pages/workspace/WorkspaceConfirmationOwnerSelectorPage.tsx +++ b/src/pages/workspace/WorkspaceConfirmationOwnerSelectorPage.tsx @@ -8,6 +8,7 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useDynamicBackPath from '@hooks/useDynamicBackPath'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import usePersonalDetailByLogin from '@hooks/usePersonalDetailByLogin'; import usePersonalDetailSearchSelector from '@hooks/usePersonalDetailSearchSelector'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -15,7 +16,6 @@ import {setDraftValues} from '@libs/actions/FormActions'; import {searchInServer} from '@libs/actions/Report'; import Navigation from '@libs/Navigation/Navigation'; import {getHeaderMessage, getUserToInviteOption} from '@libs/PersonalDetailOptionsListUtils'; -import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -39,7 +39,7 @@ function WorkspaceConfirmationOwnerSelectorPageContent({currentOwner}: Workspace const [isSearchingForReports] = useOnyx(ONYXKEYS.RAM_ONLY_IS_SEARCHING_FOR_REPORTS); const backPath = useDynamicBackPath(DYNAMIC_ROUTES.OWNER_SELECTOR.path); - const ownerPersonalDetails = getPersonalDetailByEmail(currentOwner); + const ownerPersonalDetails = usePersonalDetailByLogin(currentOwner); // When the current owner isn't in the personal details list (e.g. an external email), build an optimistic option to seed the selection const ownerExtraOption = diff --git a/src/pages/workspace/categories/CategorySettingsPage.tsx b/src/pages/workspace/categories/CategorySettingsPage.tsx index 00a4c1c54775..f9c4e7ff242f 100644 --- a/src/pages/workspace/categories/CategorySettingsPage.tsx +++ b/src/pages/workspace/categories/CategorySettingsPage.tsx @@ -20,6 +20,7 @@ import useNetwork from '@hooks/useNetwork'; import useOnboardingTaskInformation from '@hooks/useOnboardingTaskInformation'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; +import usePersonalDetailByLogin from '@hooks/usePersonalDetailByLogin'; import usePolicyData from '@hooks/usePolicyData'; import usePolicyFeatureWriteAccess from '@hooks/usePolicyFeatureWriteAccess'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -39,7 +40,6 @@ import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/crea import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {isDisablingOrDeletingLastEnabledCategory} from '@libs/OptionsListUtils'; -import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {arePolicyRulesEnabled, getWorkflowApprovalsUnavailable, hasTags, isAttendeeTrackingEnabled, isControlPolicy, tryNavigateToControlPolicyUpgrade} from '@libs/PolicyUtils'; import type {SettingsNavigatorParamList} from '@navigation/types'; @@ -147,11 +147,8 @@ function CategorySettingsPage({route: {params, name}, navigation}: CategorySetti )}`; }, [convertToDisplayString, policyCategory?.maxExpenseAmount, policyCategoryExpenseLimitType, policyCurrency, translate]); - const approverText = useMemo(() => { - const categoryApprover = getCategoryApproverRule(policy?.rules?.approvalRules ?? [], categoryName)?.approver ?? ''; - const approver = getPersonalDetailByEmail(categoryApprover); - return formatPhoneNumber(approver?.displayName ?? categoryApprover); - }, [categoryName, policy?.rules?.approvalRules, formatPhoneNumber]); + const categoryApprover = getCategoryApproverRule(policy?.rules?.approvalRules ?? [], categoryName)?.approver ?? ''; + const approverText = usePersonalDetailByLogin(categoryApprover, (personalDetails) => formatPhoneNumber(personalDetails?.displayName ?? categoryApprover)); const defaultTaxRateText = useMemo(() => { const taxID = getCategoryDefaultTaxRate(policy?.rules?.expenseRules ?? [], categoryName, policy?.taxRates?.defaultExternalID); diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index 5a07f93c7b44..c5be23ff1356 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -24,6 +24,7 @@ import useNetwork from '@hooks/useNetwork'; import useOnboardingTaskInformation from '@hooks/useOnboardingTaskInformation'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; +import {usePersonalDetailsByLogins} from '@hooks/usePersonalDetailByLogin'; import usePolicyData from '@hooks/usePolicyData'; import usePolicyFeatureWriteAccess from '@hooks/usePolicyFeatureWriteAccess'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -40,7 +41,6 @@ import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {WorkspaceSplitNavigatorParamList} from '@libs/Navigation/types'; import {isDisablingOrDeletingLastEnabledCategory} from '@libs/OptionsListUtils'; -import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import {arePolicyRulesEnabled, getConnectedIntegration, hasAccountingConnections, hasTags, isControlPolicy, shouldShowSyncError} from '@libs/PolicyUtils'; import AccessOrNotFoundWrapper from '@pages/workspace/AccessOrNotFoundWrapper'; @@ -76,6 +76,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { const isMobileSelectionModeEnabled = useMobileSelectionMode(); const policyData = usePolicyData(policyId); const {policy, categories: policyCategories} = policyData; + const employeePersonalDetails = usePersonalDetailsByLogins(Object.keys(policy.employeeList ?? {})); useWorkspaceDocumentTitle(policy?.name, 'workspace.common.categories'); const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`); const isSyncInProgress = isConnectionInProgress(connectionSyncProgress, policy); @@ -272,7 +273,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { } const approverEmail = shouldShowApproverColumn ? categoryApproverEmails[value.name] : undefined; - const approverPersonalDetail = getPersonalDetailByEmail(approverEmail); + const approverPersonalDetail = employeePersonalDetails[approverEmail ?? '']; const {avatar: approverAvatar, displayName = approverEmail, accountID: approverAccountID} = approverPersonalDetail ?? {}; const approverDisplayName = displayName ? formatPhoneNumber(displayName) : ''; @@ -306,6 +307,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { navigateToCategory, handleCategoryToggle, policyId, + employeePersonalDetails, formatPhoneNumber, ]); diff --git a/src/pages/workspace/members/WorkspaceInviteMessageComponent.tsx b/src/pages/workspace/members/WorkspaceInviteMessageComponent.tsx index 1266dcf68f20..6fbab9919e3f 100644 --- a/src/pages/workspace/members/WorkspaceInviteMessageComponent.tsx +++ b/src/pages/workspace/members/WorkspaceInviteMessageComponent.tsx @@ -14,6 +14,7 @@ import TextInput from '@components/TextInput'; import useAutoFocusInput from '@hooks/useAutoFocusInput'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import usePersonalDetailByLogin from '@hooks/usePersonalDetailByLogin'; import useThemeStyles from '@hooks/useThemeStyles'; import {clearDraftValues} from '@libs/actions/FormActions'; @@ -23,7 +24,7 @@ import {setWorkspaceInviteMessageDraft} from '@libs/actions/Policy/Policy'; import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import Navigation from '@libs/Navigation/Navigation'; import {getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils'; -import {getNewAccountIDsAndLogins, getPersonalDetailByEmail, getPersonalDetailsOnyxDataForOptimisticUsers, temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; +import {getNewAccountIDsAndLogins, getPersonalDetailsOnyxDataForOptimisticUsers, temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; import { canMemberAssignElevatedRole, canMemberAssignRole, @@ -114,7 +115,7 @@ function WorkspaceInviteMessageComponent({ const defaultApprover = getDefaultApprover(policy); const [approverDraft] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_APPROVER_DRAFT}${policyID}`); const workspaceInviteApproverDraft = approverDraft ?? defaultApprover; - const approverDetails = getPersonalDetailByEmail(workspaceInviteApproverDraft); + const approverDetails = usePersonalDetailByLogin(workspaceInviteApproverDraft); const isControl = isControlPolicy(policy); const shouldShowApproverRow = isControl && policy?.approvalMode === CONST.POLICY.APPROVAL_MODE.ADVANCED && policy?.areWorkflowsEnabled; @@ -247,7 +248,7 @@ function WorkspaceInviteMessageComponent({ }; const invitingMemberEmail = Object.keys(invitedEmailsToAccountIDsDraft ?? {}).at(0) ?? ''; - const invitingMemberDetails = getPersonalDetailByEmail(invitingMemberEmail); + const invitingMemberDetails = usePersonalDetailByLogin(invitingMemberEmail); const invitingMemberName = Str.removeSMSDomain(invitingMemberDetails?.displayName ?? ''); useEffect(() => { diff --git a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx index e2f750504502..7ad85ab5966e 100644 --- a/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx +++ b/src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx @@ -21,6 +21,7 @@ import useExpensifyCardFeeds from '@hooks/useExpensifyCardFeeds'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useOnyx from '@hooks/useOnyx'; +import usePersonalDetailByLogin from '@hooks/usePersonalDetailByLogin'; import usePrevious from '@hooks/usePrevious'; import useRuleBotGuardModal from '@hooks/useRuleBotGuardModal'; import useStyleUtils from '@hooks/useStyleUtils'; @@ -33,7 +34,7 @@ import {isRuleBotEnforcingRules} from '@libs/AgentRulesUtils'; import {getAllCardsForWorkspace, getCardFeedIcon, getCardFeedWithDomainID, getPlaidInstitutionIconUrl, lastFourNumbersFromCardName, maskCardNumber} from '@libs/CardUtils'; import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/createDynamicRoute'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; -import {getPersonalDetailByEmail, getPhoneNumber, temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; +import {getPhoneNumber, temporaryGetDisplayNameOrDefault} from '@libs/PersonalDetailsUtils'; import { canMemberAssignRole, canMemberManageMemberWithRole, @@ -125,7 +126,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM const routeAccountID = Number(route.params.accountID); const memberLogin = personalDetails?.[routeAccountID]?.login ?? getMemberLoginByOptimisticAccountID(policy, routeAccountID); - const memberPersonalDetails = personalDetails?.[routeAccountID] ?? getPersonalDetailByEmail(memberLogin); + const memberPersonalDetails = usePersonalDetailByLogin(memberLogin); const accountID = memberPersonalDetails?.accountID ?? routeAccountID; const member = policy?.employeeList?.[memberLogin]; const prevMember = usePrevious(member); diff --git a/src/pages/workspace/tags/DynamicTagSettingsPage.tsx b/src/pages/workspace/tags/DynamicTagSettingsPage.tsx index e21d4b8557d1..2a131e1007a2 100644 --- a/src/pages/workspace/tags/DynamicTagSettingsPage.tsx +++ b/src/pages/workspace/tags/DynamicTagSettingsPage.tsx @@ -14,6 +14,7 @@ import useEnvironment from '@hooks/useEnvironment'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; +import usePersonalDetailByLogin from '@hooks/usePersonalDetailByLogin'; import usePolicyData from '@hooks/usePolicyData'; import usePolicyFeatureWriteAccess from '@hooks/usePolicyFeatureWriteAccess'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -23,7 +24,6 @@ import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/crea import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import {isDisablingOrDeletingLastEnabledTag} from '@libs/OptionsListUtils'; -import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import { arePolicyRulesEnabled, getCleanedTagName, @@ -73,8 +73,7 @@ function DynamicTagSettingsPage({route, navigation}: DynamicTagSettingsPageProps const isQuickSettingsFlow = route.name === SCREENS.SETTINGS_TAGS.DYNAMIC_SETTINGS_TAG_SETTINGS; const backPath = useDynamicBackPath(DYNAMIC_ROUTES.SETTINGS_TAG_SETTINGS.path); const tagApprover = getTagApproverRule(policy, route.params?.tagName)?.approver ?? ''; - const approver = getPersonalDetailByEmail(tagApprover); - const approverText = formatPhoneNumber(approver?.displayName ?? tagApprover); + const approverText = usePersonalDetailByLogin(tagApprover, (personalDetails) => formatPhoneNumber(personalDetails?.displayName ?? tagApprover)); const hasDependentTags = hasDependentTagsPolicyUtils(policy, policyTags); const currentPolicyTag = hasDependentTags ? Object.values(policyTag.tags ?? {}).find((tag) => tag?.name === tagName && tag.rules?.parentTagsFilter === parentTagsFilter) diff --git a/src/pages/workspace/tags/WorkspaceTagsPage.tsx b/src/pages/workspace/tags/WorkspaceTagsPage.tsx index 9d73a07b1c0a..59279416a84b 100644 --- a/src/pages/workspace/tags/WorkspaceTagsPage.tsx +++ b/src/pages/workspace/tags/WorkspaceTagsPage.tsx @@ -22,6 +22,7 @@ import useMobileSelectionMode from '@hooks/useMobileSelectionMode'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import usePermissions from '@hooks/usePermissions'; +import {usePersonalDetailsByLogins} from '@hooks/usePersonalDetailByLogin'; import usePolicyData from '@hooks/usePolicyData'; import usePolicyFeatureWriteAccess from '@hooks/usePolicyFeatureWriteAccess'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; @@ -47,7 +48,6 @@ import Navigation from '@libs/Navigation/Navigation'; import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types'; import type {WorkspaceSplitNavigatorParamList} from '@libs/Navigation/types'; import {isDisablingOrDeletingLastEnabledTag, isMakingLastRequiredTagListOptional} from '@libs/OptionsListUtils'; -import {getPersonalDetailByEmail} from '@libs/PersonalDetailsUtils'; import { arePolicyRulesEnabled, getCleanedTagName, @@ -100,6 +100,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) { const {environmentURL} = useEnvironment(); const [connectionSyncProgress] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CONNECTION_SYNC_PROGRESS}${policy?.id}`); const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policy?.id}`); + const employeePersonalDetails = usePersonalDetailsByLogins(Object.keys(policy.employeeList ?? {})); const isSyncInProgress = isConnectionInProgress(connectionSyncProgress, policy); const syncingAccountingIntegration = CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES.find((connectionName) => connectionName === connectionSyncProgress?.connectionName); const hasSyncError = shouldShowSyncError(policy, isSyncInProgress, CONST.POLICY.CONNECTIONS.ACCOUNTING_CONNECTION_NAMES); @@ -388,7 +389,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) { } const approverEmail = shouldShowApproverColumn ? tagApproverEmails[tag.name] : undefined; - const approverPersonalDetail = getPersonalDetailByEmail(approverEmail); + const approverPersonalDetail = employeePersonalDetails[approverEmail ?? '']; const {avatar: approverAvatar, displayName = approverEmail, accountID: approverAccountID} = approverPersonalDetail ?? {}; const approverDisplayName = displayName ? formatPhoneNumber(displayName) : ''; const isLastEnabledTagAndEnabled = isLastEnabledTagLocked && tag.enabled; @@ -429,6 +430,7 @@ function WorkspaceTagsPage({route}: WorkspaceTagsPageProps) { policyTags, shouldShowApproverColumn, tagApproverEmails, + employeePersonalDetails, formatPhoneNumber, ]); diff --git a/tests/ui/WorkspaceMemberDetailsPageTest.tsx b/tests/ui/WorkspaceMemberDetailsPageTest.tsx index 68fa39d62ce3..dc15ca3a9ce4 100644 --- a/tests/ui/WorkspaceMemberDetailsPageTest.tsx +++ b/tests/ui/WorkspaceMemberDetailsPageTest.tsx @@ -5,6 +5,7 @@ import HTMLEngineProvider from '@components/HTMLEngineProvider'; import {LocaleContextProvider} from '@components/LocaleContextProvider'; import {ModalProvider} from '@components/Modal/Global/ModalContext'; import OnyxListItemProvider from '@components/OnyxListItemProvider'; +import PersonalDetailsByLoginProvider from '@components/PersonalDetailsByLoginProvider'; import {CurrentReportIDContextProvider} from '@hooks/useCurrentReportID'; import * as useResponsiveLayoutModule from '@hooks/useResponsiveLayout'; @@ -38,7 +39,7 @@ const Stack = createPlatformStackNavigator(); const renderPage = (initialParams: SettingsNavigatorParamList[typeof SCREENS.WORKSPACE.MEMBER_DETAILS]) => { return render( - +