From 5cf2e0e01cc23efd39e100c316a584146833f4a4 Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Mon, 27 Jul 2026 12:28:00 +0200 Subject: [PATCH 1/2] Onyx GET PayActionCell --- src/components/KYCWall/BaseKYCWall.tsx | 93 +++++++------------ .../ListItem/ActionCell/PayActionCell.tsx | 17 ++-- src/components/SettlementButton/index.tsx | 84 ++++++++--------- src/hooks/useDelegateAccountID.ts | 20 ++++ 4 files changed, 104 insertions(+), 110 deletions(-) diff --git a/src/components/KYCWall/BaseKYCWall.tsx b/src/components/KYCWall/BaseKYCWall.tsx index 64749883ac7d..5b1d4e9dbe3c 100644 --- a/src/components/KYCWall/BaseKYCWall.tsx +++ b/src/components/KYCWall/BaseKYCWall.tsx @@ -2,9 +2,6 @@ import AddPaymentMethodMenu from '@components/AddPaymentMethodMenu'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useLocalize from '@hooks/useLocalize'; -import useOnyx from '@hooks/useOnyx'; -import useParentReportAction from '@hooks/useParentReportAction'; -import useReportTransactions from '@hooks/useReportTransactions'; import {openPersonalBankAccountSetupView, setPersonalBankAccountContinueKYCOnSuccess} from '@libs/actions/BankAccounts'; import {completePaymentOnboarding, savePreferredPaymentMethod} from '@libs/actions/IOU/PayMoneyRequest'; @@ -12,6 +9,7 @@ import {navigateToBankAccountRoute} from '@libs/actions/ReimbursementAccount'; import {moveIOUReportToPolicy, moveIOUReportToPolicyAndInviteSubmitter} from '@libs/actions/Report'; import {doesPolicyHavePartiallySetupBankAccount} from '@libs/BankAccountUtils'; import getClickedTargetLocation from '@libs/getClickedTargetLocation'; +import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import Log from '@libs/Log'; import setNavigationActionToMicrotaskQueue from '@libs/Navigation/helpers/setNavigationActionToMicrotaskQueue'; import Navigation from '@libs/Navigation/Navigation'; @@ -28,22 +26,32 @@ import ROUTES from '@src/ROUTES'; import type {Route} from '@src/ROUTES'; import {doesPersonalDetailExistSelector, personalDetailsLoginSelector} from '@src/selectors/PersonalDetails'; import {lastWorkspaceNumberSelector} from '@src/selectors/Policy'; -import type {BankAccountList, PersonalDetailsList, Policy} from '@src/types/onyx'; +import type {BankAccountList, Policy, Transaction} from '@src/types/onyx'; import {getEmptyObject} from '@src/types/utils/EmptyObject'; import viewRef from '@src/types/utils/viewRef'; import type {EmitterSubscription, View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; import {hasSeenTourSelector} from '@selectors/Onboarding'; +import {getParentReportActionSelector} from '@selectors/ReportAction'; import React, {useCallback, useEffect, useImperativeHandle, useRef, useState} from 'react'; import {Dimensions} from 'react-native'; +import OnyxUtils from 'react-native-onyx/dist/OnyxUtils'; import type {AnchorPosition, ContinueActionParams, DomRect, KYCWallProps, PaymentMethod} from './types'; // This sets the Horizontal anchor position offset for POPOVER MENU. const POPOVER_MENU_ANCHOR_POSITION_HORIZONTAL_OFFSET = 20; +// Sync mirror of useReportTransactions, for reads at handler-invocation time. +function getReportTransactionsForReport(reportID: string | undefined): Transaction[] { + const transactions = OnyxUtils.getCachedCollection(ONYXKEYS.COLLECTION.TRANSACTION); + if (!transactions || !reportID) { + return []; + } + return Object.values(transactions).filter((transaction): transaction is Transaction => !!transaction && transaction.reportID === reportID); +} + // This component allows us to block various actions by forcing the user to first add a default payment method and successfully make it through our Know Your Customer flow // before continuing to take whatever action they originally intended to take. It requires a button as a child and a native event so we can get the coordinates and use it // to render the AddPaymentMethodMenu in the correct location. @@ -67,33 +75,11 @@ function KYCWall({ shouldShowPersonalBankAccountOption = false, ref, }: KYCWallProps) { - const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET); - const [walletTerms] = useOnyx(ONYXKEYS.WALLET_TERMS); - const [fundList] = useOnyx(ONYXKEYS.FUND_LIST); - const [bankAccountList = getEmptyObject()] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); - const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}`); - const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY); - const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); - const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); - const [betas] = useOnyx(ONYXKEYS.BETAS); - const ownerAccountID = iouReport?.ownerAccountID; - const employeeLoginSelector = useCallback((personalDetailsList: OnyxEntry) => personalDetailsLoginSelector(ownerAccountID)(personalDetailsList), [ownerAccountID]); - const [employeeLogin] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: employeeLoginSelector}); - const doesSubmitterPersonalDetailExistSelector = useCallback( - (personalDetailsList: OnyxEntry) => doesPersonalDetailExistSelector(ownerAccountID)(personalDetailsList), - [ownerAccountID], - ); - const [doesSubmitterPersonalDetailExist] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: doesSubmitterPersonalDetailExistSelector}); - const {translate} = useLocalize(); const currentUserDetails = useCurrentUserPersonalDetails(); const currentUserAccountID = currentUserDetails.accountID; const currentUserEmail = currentUserDetails.email ?? ''; const localCurrency = currentUserDetails.localCurrencyCode ?? CONST.CURRENCY.USD; - const reportPreviewAction = useParentReportAction(iouReport); - const reportTransactions = useReportTransactions(iouReport?.reportID); - const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - const [allReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS); const anchorRef = useRef(null); const transferBalanceButtonRef = useRef(null); @@ -107,8 +93,6 @@ function KYCWall({ anchorPositionHorizontal: 0, }); - const [lastPaymentMethod] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD); - const getAnchorPosition = useCallback( (domRect: DomRect): AnchorPosition => { if (anchorAlignment.vertical === CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP) { @@ -148,6 +132,23 @@ function KYCWall({ const selectPaymentMethod = useCallback( (paymentMethod?: PaymentMethod, policy?: Policy, personalBankAccountOnSuccessFallbackRoute?: Route) => { + const bankAccountList = OnyxUtils.get(ONYXKEYS.BANK_ACCOUNT_LIST) ?? getEmptyObject(); + const policies = OnyxUtils.getCachedCollection(ONYXKEYS.COLLECTION.POLICY); + const allReports = OnyxUtils.getCachedCollection(ONYXKEYS.COLLECTION.REPORT); + const allReportActions = OnyxUtils.getCachedCollection(ONYXKEYS.COLLECTION.REPORT_ACTIONS); + const chatReport = OnyxUtils.get(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID}` as const); + const lastPaymentMethod = OnyxUtils.get(ONYXKEYS.NVP_LAST_PAYMENT_METHOD); + const introSelected = OnyxUtils.get(ONYXKEYS.NVP_INTRO_SELECTED); + const isSelfTourViewed = hasSeenTourSelector(OnyxUtils.get(ONYXKEYS.NVP_ONBOARDING)); + const betas = OnyxUtils.get(ONYXKEYS.BETAS); + const personalDetailsList = OnyxUtils.get(ONYXKEYS.PERSONAL_DETAILS_LIST); + const employeeLogin = personalDetailsLoginSelector(iouReport?.ownerAccountID)(personalDetailsList); + const doesSubmitterPersonalDetailExist = doesPersonalDetailExistSelector(iouReport?.ownerAccountID)(personalDetailsList); + const reportPreviewAction = getParentReportActionSelector( + OnyxUtils.get(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(iouReport?.parentReportID)}` as const), + iouReport?.parentReportActionID, + ); + const reportTransactions = getReportTransactionsForReport(iouReport?.reportID); const canLinkExistingBusinessBankAccount = getEligibleExistingBusinessBankAccounts(bankAccountList, policy?.outputCurrency, true).length > 0; if (paymentMethod) { @@ -252,27 +253,15 @@ function KYCWall({ } }, [ - bankAccountList, getPersonalBankAccountOnSuccessFallbackRoute, onSelectPaymentMethod, iouReport, addDebitCardRoute, addBankAccountRoute, - chatReport, - policies, - reportPreviewAction, + chatReportID, currentUserAccountID, currentUserEmail, - employeeLogin, - doesSubmitterPersonalDetailExist, - introSelected, translate, - reportTransactions, - allReports, - allReportActions, - lastPaymentMethod, - isSelfTourViewed, - betas, localCurrency, ], ); @@ -286,6 +275,10 @@ function KYCWall({ const continueAction = useCallback( (params?: ContinueActionParams) => { const {event, iouPaymentType, paymentMethod, policy, goBackRoute, personalBankAccountOnSuccessFallbackRoute} = params ?? {}; + const walletTerms = OnyxUtils.get(ONYXKEYS.WALLET_TERMS); + const fundList = OnyxUtils.get(ONYXKEYS.FUND_LIST); + const bankAccountList = OnyxUtils.get(ONYXKEYS.BANK_ACCOUNT_LIST) ?? getEmptyObject(); + const userWallet = OnyxUtils.get(ONYXKEYS.USER_WALLET); const currentSource = walletTerms?.source ?? source; /** @@ -375,21 +368,7 @@ function KYCWall({ onSuccessfulKYC(iouPaymentType, currentSource); }, - [ - bankAccountList, - chatReportID, - enablePaymentsRoute, - fundList, - getAnchorPosition, - iouReport, - onSuccessfulKYC, - selectPaymentMethod, - shouldIncludeDebitCard, - shouldShowAddPaymentMenu, - source, - userWallet?.tierName, - walletTerms?.source, - ], + [chatReportID, enablePaymentsRoute, getAnchorPosition, iouReport, onSuccessfulKYC, selectPaymentMethod, shouldIncludeDebitCard, shouldShowAddPaymentMenu, source], ); useEffect(() => { diff --git a/src/components/Search/SearchList/ListItem/ActionCell/PayActionCell.tsx b/src/components/Search/SearchList/ListItem/ActionCell/PayActionCell.tsx index 8f3fc18b3275..de259ffef2ce 100644 --- a/src/components/Search/SearchList/ListItem/ActionCell/PayActionCell.tsx +++ b/src/components/Search/SearchList/ListItem/ActionCell/PayActionCell.tsx @@ -27,6 +27,7 @@ import type {OnyxEntry} from 'react-native-onyx'; import {isTrackIntentUserSelector} from '@selectors/Onboarding'; import React from 'react'; +import OnyxUtils from 'react-native-onyx/dist/OnyxUtils'; type PayActionCellProps = { isLoading: boolean; @@ -47,10 +48,6 @@ function PayActionCell({isLoading, policyID, reportID, hash, amount, shouldDisab const [iouReport, transactions] = useReportWithTransactionsAndViolations(reportID); const policy = usePolicy(policyID); const [bankAccountList] = useOnyx(ONYXKEYS.BANK_ACCOUNT_LIST); - const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT); - const [allReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS); - const [reportNameValuePairs] = useOnyx(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS); - const [isTrackIntentUser] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED, {selector: isTrackIntentUserSelector}); const invoiceReceiverPolicyID = chatReport?.invoiceReceiver && 'policyID' in chatReport.invoiceReceiver ? chatReport.invoiceReceiver.policyID : undefined; const invoiceReceiverPolicy = usePolicy(invoiceReceiverPolicyID); @@ -94,11 +91,12 @@ function PayActionCell({isLoading, policyID, reportID, hash, amount, shouldDisab } const additionalOnyxData = getSearchPayOnyxData(hash, reportID); + const isTrackIntentUser = isTrackIntentUserSelector(OnyxUtils.get(ONYXKEYS.NVP_INTRO_SELECTED)); if (isInvoiceReport(iouReport)) { const existingB2BInvoiceReport = getParticipantsInvoiceReport( - allReports, - reportNameValuePairs, + OnyxUtils.getCachedCollection(ONYXKEYS.COLLECTION.REPORT), + OnyxUtils.getCachedCollection(ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS), activePolicyID, CONST.REPORT.INVOICE_RECEIVER_TYPE.BUSINESS, invoiceReceiverPolicyID ?? chatReport?.policyID, @@ -108,8 +106,9 @@ function PayActionCell({isLoading, policyID, reportID, hash, amount, shouldDisab // when paying an individual invoice room as a business. `payAsBusiness` is only known at click time, so pick // the right report's actions here in the function scope. const shouldUseB2BInvoiceReport = !!payAsBusiness && !!existingB2BInvoiceReport && isIndividualInvoiceRoom(chatReport); - const chatReportActions = - allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(shouldUseB2BInvoiceReport ? existingB2BInvoiceReport?.reportID : chatReport?.reportID)}`]; + const chatReportActions = OnyxUtils.get( + `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(shouldUseB2BInvoiceReport ? existingB2BInvoiceReport?.reportID : chatReport?.reportID)}` as const, + ); payInvoice({ paymentMethodType: type, @@ -155,7 +154,7 @@ function PayActionCell({isLoading, policyID, reportID, hash, amount, shouldDisab ownerBillingGracePeriodEnd, methodID: type === CONST.IOU.PAYMENT_TYPE.VBBA ? methodID : undefined, additionalOnyxData, - chatReportActions: allReportActions?.[`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(chatReport?.reportID)}`], + chatReportActions: OnyxUtils.get(`${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${getNonEmptyStringOnyxID(chatReport?.reportID)}` as const), delegateAccountID, isTrackIntentUser, }); diff --git a/src/components/SettlementButton/index.tsx b/src/components/SettlementButton/index.tsx index 2c5d205c68d6..88d2a7b7fe69 100644 --- a/src/components/SettlementButton/index.tsx +++ b/src/components/SettlementButton/index.tsx @@ -11,8 +11,7 @@ import RenderHTML from '@components/RenderHTML'; import useActiveAdminPolicies from '@hooks/useActiveAdminPolicies'; import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; -import useDelegateAccountID from '@hooks/useDelegateAccountID'; -import useLastWorkspaceNumber from '@hooks/useLastWorkspaceNumber'; +import {getDelegateAccountIDSync} from '@hooks/useDelegateAccountID'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; @@ -29,7 +28,7 @@ import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/crea import Navigation from '@libs/Navigation/Navigation'; import {isTrackOnboardingChoice} from '@libs/OnboardingUtils'; import {formatPaymentMethods, getActivePaymentType, getBusinessBankAccountOptions, matchesCurrency} from '@libs/PaymentUtils'; -import {isPaidGroupPolicy, isPolicyAdmin, sortPoliciesByName} from '@libs/PolicyUtils'; +import {getPolicyIDOrDefault, isPaidGroupPolicy, isPolicyAdmin, sortPoliciesByName} from '@libs/PolicyUtils'; import {hasRequestFromCurrentAccount} from '@libs/ReportActionsUtils'; import { doesReportBelongToWorkspace, @@ -61,9 +60,12 @@ import type {TupleToUnion} from 'type-fest'; import {delegateEmailSelector, isUserValidatedSelector} from '@selectors/Account'; import {hasSeenTourSelector} from '@selectors/Onboarding'; import {personalDetailsLoginSelector} from '@selectors/PersonalDetails'; +import {lastWorkspaceNumberSelector} from '@selectors/Policy'; +import {emailSelector} from '@selectors/Session'; import truncate from 'lodash/truncate'; import React, {useCallback, useContext} from 'react'; import {View} from 'react-native'; +import OnyxUtils from 'react-native-onyx/dist/OnyxUtils'; import type SettlementButtonProps from './types'; @@ -117,19 +119,12 @@ function SettlementButton({ const {translate, localeCompare} = useLocalize(); const {isOffline} = useNetwork(); const policy = usePolicy(policyID); - const expenseReportPolicy = usePolicy(iouReport?.policyID); const {accountID, email = ''} = useCurrentUserPersonalDetails(); - const lastWorkspaceNumber = useLastWorkspaceNumber(); // The app would crash due to subscribing to the entire report collection if chatReportID is an empty string. So we should have a fallback ID here. // eslint-disable-next-line rulesdir/no-default-id-values const [chatReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${chatReportID || CONST.DEFAULT_NUMBER_ID}`); const [conciergeReportID] = useOnyx(ONYXKEYS.CONCIERGE_REPORT_ID); - const [conciergeChat] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${conciergeReportID}`); - const [iouReportNextStep] = useOnyx(`${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport?.reportID}`); - const [ownerLogin] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST, {selector: personalDetailsLoginSelector(iouReport?.ownerAccountID)}); - const [isUserValidated] = useOnyx(ONYXKEYS.ACCOUNT, {selector: isUserValidatedSelector}); - const [amountOwed] = useOnyx(ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED); const reportBelongsToWorkspace = policyID ? doesReportBelongToWorkspace(chatReport, policyID, conciergeReportID) : false; const policyIDKey = reportBelongsToWorkspace ? policyID : (iouReport?.policyID ?? CONST.POLICY.ID_FAKE); const [userWallet] = useOnyx(ONYXKEYS.USER_WALLET); @@ -137,9 +132,6 @@ function SettlementButton({ const paymentMethods = useSettlementButtonPaymentMethods(hasActivatedWallet, translate); const [lastPaymentMethods] = useOnyx(ONYXKEYS.NVP_LAST_PAYMENT_METHOD); const [personalPolicyID] = useOnyx(ONYXKEYS.PERSONAL_POLICY_ID); - const [betas] = useOnyx(ONYXKEYS.BETAS); - const [userBillingGracePeriodEnds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END); - const [ownerBillingGracePeriodEnd] = useOnyx(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END); const lastPaymentMethod = iouReport?.type ? getLastPolicyPaymentMethod(policyIDKey, personalPolicyID, lastPaymentMethods, iouReport?.type as keyof LastPaymentMethodType, isIOUReport(iouReport)) @@ -172,17 +164,10 @@ function SettlementButton({ policy?.achAccount?.state === CONST.BANK_ACCOUNT.STATE.LOCKED) && !lastPaymentMethod; const {isBetaEnabled} = usePermissions(); - const [introSelected] = useOnyx(ONYXKEYS.NVP_INTRO_SELECTED); - const [isSelfTourViewed] = useOnyx(ONYXKEYS.NVP_ONBOARDING, {selector: hasSeenTourSelector}); const currentUserPersonalDetails = useCurrentUserPersonalDetails(); - const [transactionViolations] = useOnyx(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS); - const [delegateEmail] = useOnyx(ONYXKEYS.ACCOUNT, {selector: delegateEmailSelector}); - const isTrackIntentUser = isTrackOnboardingChoice(introSelected?.choice); - const delegateAccountID = useDelegateAccountID(); const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT); const isPayInvoiceViaExpensifyBetaEnabled = isBetaEnabled(CONST.BETAS.PAY_INVOICE_VIA_EXPENSIFY); - const hasViolations = hasViolationsReportUtils(iouReport?.reportID, transactionViolations, accountID, email); const isInvoiceReport = (!isEmptyObject(iouReport) && isInvoiceReportUtil(iouReport)) || false; @@ -212,6 +197,7 @@ function SettlementButton({ return true; } + const isUserValidated = isUserValidatedSelector(OnyxUtils.get(ONYXKEYS.ACCOUNT)); if (!isUserValidated && paymentMethodType !== CONST.IOU.PAYMENT_TYPE.ELSEWHERE) { Navigation.navigate(createDynamicRoute(DYNAMIC_ROUTES.VERIFY_ACCOUNT.path)); return true; @@ -234,13 +220,28 @@ function SettlementButton({ if (policy?.achAccount?.bankAccountID === undefined) { return; } - pressLockedBankAccount(policy?.achAccount?.bankAccountID, translate, conciergeReportID, delegateAccountID); - navigateToConciergeChat(conciergeReportID, introSelected, currentUserAccountID, isSelfTourViewed, betas); + pressLockedBankAccount(policy?.achAccount?.bankAccountID, translate, conciergeReportID, getDelegateAccountIDSync()); + navigateToConciergeChat( + conciergeReportID, + OnyxUtils.get(ONYXKEYS.NVP_INTRO_SELECTED), + currentUserAccountID, + hasSeenTourSelector(OnyxUtils.get(ONYXKEYS.NVP_ONBOARDING)), + OnyxUtils.get(ONYXKEYS.BETAS), + ); }); return true; } - if (policy && shouldRestrictUserBillableActions(policy, ownerBillingGracePeriodEnd, userBillingGracePeriodEnds, amountOwed, currentUserAccountID)) { + if ( + policy && + shouldRestrictUserBillableActions( + policy, + OnyxUtils.get(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END), + OnyxUtils.getCachedCollection(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END), + OnyxUtils.get(ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED), + currentUserAccountID, + ) + ) { Navigation.navigate(ROUTES.RESTRICTED_ACTION.getRoute(policy.id)); return true; } @@ -250,11 +251,8 @@ function SettlementButton({ [ isDelegateAccessRestricted, isAccountLocked, - isUserValidated, isBankAccountLocked, policy, - userBillingGracePeriodEnds, - ownerBillingGracePeriodEnd, showDelegateNoAccessModal, showLockedAccountModal, showConfirmModal, @@ -262,12 +260,7 @@ function SettlementButton({ styles.renderHTML, styles.flexRow, conciergeReportID, - introSelected, currentUserAccountID, - isSelfTourViewed, - betas, - amountOwed, - delegateAccountID, ], ); @@ -430,15 +423,17 @@ function SettlementButton({ return activePolicy.id; } + const conciergeReportIDForWorkspace = OnyxUtils.get(ONYXKEYS.CONCIERGE_REPORT_ID); + const lastWorkspaceNumber = lastWorkspaceNumberSelector(OnyxUtils.getCachedCollection(ONYXKEYS.COLLECTION.POLICY), emailSelector(OnyxUtils.get(ONYXKEYS.SESSION)) ?? ''); return createWorkspace({ - introSelected, + introSelected: OnyxUtils.get(ONYXKEYS.NVP_INTRO_SELECTED), activePolicy, - conciergeChat, + conciergeChat: OnyxUtils.get(`${ONYXKEYS.COLLECTION.REPORT}${conciergeReportIDForWorkspace}` as const), currentUserAccountIDParam: currentUserPersonalDetails.accountID, currentUserEmailParam: email, currency: currentUserPersonalDetails.localCurrencyCode ?? CONST.CURRENCY.USD, - betas, - isSelfTourViewed, + betas: OnyxUtils.get(ONYXKEYS.BETAS), + isSelfTourViewed: hasSeenTourSelector(OnyxUtils.get(ONYXKEYS.NVP_ONBOARDING)), hasActiveAdminPolicies: !!activeAdminPolicies.length, policyName: generateDefaultWorkspaceName(email, lastWorkspaceNumber, translate), }).policyID; @@ -516,22 +511,23 @@ function SettlementButton({ if (confirmApproval) { confirmApproval(); } else { + const hasViolations = hasViolationsReportUtils(iouReport?.reportID, OnyxUtils.getCachedCollection(ONYXKEYS.COLLECTION.TRANSACTION_VIOLATIONS), accountID, email); approveMoneyRequest({ expenseReport: iouReport, - expenseReportPolicy, + expenseReportPolicy: OnyxUtils.get(`${ONYXKEYS.COLLECTION.POLICY}${getPolicyIDOrDefault(iouReport?.policyID)}` as const), currentUserAccountIDParam: accountID, currentUserEmailParam: email ?? '', hasViolations, isASAPSubmitBetaEnabled, - expenseReportCurrentNextStepDeprecated: iouReportNextStep, - betas, - userBillingGracePeriodEnds, - amountOwed, - ownerBillingGracePeriodEnd, - ownerLogin, + expenseReportCurrentNextStepDeprecated: OnyxUtils.get(`${ONYXKEYS.COLLECTION.NEXT_STEP}${iouReport?.reportID}` as const), + betas: OnyxUtils.get(ONYXKEYS.BETAS), + userBillingGracePeriodEnds: OnyxUtils.getCachedCollection(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_USER_BILLING_GRACE_PERIOD_END), + amountOwed: OnyxUtils.get(ONYXKEYS.NVP_PRIVATE_AMOUNT_OWED), + ownerBillingGracePeriodEnd: OnyxUtils.get(ONYXKEYS.NVP_PRIVATE_OWNER_BILLING_GRACE_PERIOD_END), + ownerLogin: personalDetailsLoginSelector(iouReport?.ownerAccountID)(OnyxUtils.get(ONYXKEYS.PERSONAL_DETAILS_LIST)), full: false, - delegateEmail, - isTrackIntentUser, + delegateEmail: delegateEmailSelector(OnyxUtils.get(ONYXKEYS.ACCOUNT)), + isTrackIntentUser: isTrackOnboardingChoice(OnyxUtils.get(ONYXKEYS.NVP_INTRO_SELECTED)?.choice), }); } return; diff --git a/src/hooks/useDelegateAccountID.ts b/src/hooks/useDelegateAccountID.ts index 6e229d6f3c8d..c3e133a11036 100644 --- a/src/hooks/useDelegateAccountID.ts +++ b/src/hooks/useDelegateAccountID.ts @@ -4,9 +4,28 @@ import type {PersonalDetailsList} from '@src/types/onyx'; import type {OnyxEntry} from 'react-native-onyx'; import {delegateEmailSelector} from '@selectors/Account'; +import OnyxUtils from 'react-native-onyx/dist/OnyxUtils'; import useOnyx from './useOnyx'; +/** + * Synchronous, event-time equivalent of `useDelegateAccountID`. Reads from the Onyx cache so it can be + * called inside event handlers without any render-bound subscriptions. + */ +function getDelegateAccountIDSync(): number | undefined { + const lowerEmail = delegateEmailSelector(OnyxUtils.get(ONYXKEYS.ACCOUNT)).toLowerCase(); + if (!lowerEmail) { + return undefined; + } + const personalDetails = OnyxUtils.get(ONYXKEYS.PERSONAL_DETAILS_LIST); + for (const detail of Object.values(personalDetails ?? {})) { + if (detail?.login?.toLowerCase() === lowerEmail) { + return detail.accountID; + } + } + return undefined; +} + function useDelegateAccountID(): number | undefined { const [delegateEmail] = useOnyx(ONYXKEYS.ACCOUNT, {selector: delegateEmailSelector}); const lowerEmail = delegateEmail?.toLowerCase(); @@ -29,3 +48,4 @@ function useDelegateAccountID(): number | undefined { } export default useDelegateAccountID; +export {getDelegateAccountIDSync}; From df1b2287f5b54e6c55f5a45c730dde03a2fb0b4d Mon Sep 17 00:00:00 2001 From: Lukasz Modzelewski Date: Mon, 27 Jul 2026 15:12:37 +0200 Subject: [PATCH 2/2] Onyx GET ParentNavigationSubtitle --- src/components/AvatarWithDisplayName.tsx | 5 +++-- src/components/ParentNavigationSubtitle.tsx | 23 +++++++++------------ src/pages/DynamicReportDetailsPage.tsx | 9 +++++--- src/pages/inbox/HeaderView.tsx | 6 ++++-- 4 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/components/AvatarWithDisplayName.tsx b/src/components/AvatarWithDisplayName.tsx index bb22c76f2fc4..9c8a8cebe990 100644 --- a/src/components/AvatarWithDisplayName.tsx +++ b/src/components/AvatarWithDisplayName.tsx @@ -292,6 +292,7 @@ function AvatarWithDisplayName({ isAnonymous, isMoneyRequestOrReport, ); + const pressableStyles = [styles.alignSelfStart, styles.mw100]; const multipleAvatars = ( 0 && ( { // Find the tab navigator, which wraps all full-screen navigators const tabNavigatorRoute = state?.routes?.findLast((route) => route.name === NAVIGATORS.TAB_NAVIGATOR); @@ -154,8 +148,11 @@ function ParentNavigationSubtitle({ } const onPress = () => { + const report = OnyxUtils.get(`${ONYXKEYS.COLLECTION.REPORT}${parentReportID}` as const); + const isParentReportArchived = !!isArchivedReport(OnyxUtils.get(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${parentReportID}` as const)); + const canUserPerformWriteAction = canUserPerformWriteActionReportUtils(report, isParentReportArchived); const parentAction = getReportAction(parentReportID, parentReportActionID); - const isVisibleAction = isReportActionVisible(parentAction, parentReportID, canUserPerformWriteAction, visibleReportActionsData); + const isVisibleAction = isReportActionVisible(parentAction, parentReportID, canUserPerformWriteAction, OnyxUtils.get(ONYXKEYS.DERIVED.VISIBLE_REPORT_ACTIONS)); const focusedNavigatorState = currentFocusedNavigator?.state; const currentReportIndex = focusedNavigatorState?.index; diff --git a/src/pages/DynamicReportDetailsPage.tsx b/src/pages/DynamicReportDetailsPage.tsx index 99a66f71706e..a49367d98d05 100644 --- a/src/pages/DynamicReportDetailsPage.tsx +++ b/src/pages/DynamicReportDetailsPage.tsx @@ -878,6 +878,9 @@ function DynamicReportDetailsPage({policy, report, route, reportMetadata, report const shouldShowEditableTitleField = caseID !== CASES.MONEY_REQUEST && canEditReportTitle(report, policy, currentUserPersonalDetails?.accountID); + const pressableStyles = [styles.mt1, styles.mw100]; + const textStyles = [styles.popoverMenuText, styles.flexShrink1, styles.preWrap, styles.mw100]; + const nameSectionFurtherDetailsContent = (