diff --git a/src/components/MoneyReportHeaderActions/MoneyReportHeaderSecondaryActions.tsx b/src/components/MoneyReportHeaderActions/MoneyReportHeaderSecondaryActions.tsx index 84eb944cc7b0..0367e0e1cbff 100644 --- a/src/components/MoneyReportHeaderActions/MoneyReportHeaderSecondaryActions.tsx +++ b/src/components/MoneyReportHeaderActions/MoneyReportHeaderSecondaryActions.tsx @@ -20,6 +20,7 @@ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' import useExpenseActions from '@hooks/useExpenseActions'; import useExportActions from '@hooks/useExportActions'; import useHoldRejectActions from '@hooks/useHoldRejectActions'; +import useLastWorkspaceNumber from '@hooks/useLastWorkspaceNumber'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLifecycleActions from '@hooks/useLifecycleActions'; import useLocalize from '@hooks/useLocalize'; @@ -36,6 +37,7 @@ import useSearchShouldCalculateTotals from '@hooks/useSearchShouldCalculateTotal import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport'; +import {generateDefaultWorkspaceName} from '@libs/actions/Policy/Policy'; import {search} from '@libs/actions/Search'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import getPlatform from '@libs/getPlatform'; @@ -106,6 +108,7 @@ function MoneyReportHeaderSecondaryActionsInner({reportID, primaryAction, isRepo const {isOffline} = useNetwork(); const activePolicy = usePolicy(activePolicyID); + const lastWorkspaceNumber = useLastWorkspaceNumber(); const {isBetaEnabled} = usePermissions(); const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT); @@ -167,6 +170,7 @@ function MoneyReportHeaderSecondaryActionsInner({reportID, primaryAction, isRepo activePolicy, betas, isSelfTourViewed, + defaultWorkspaceName: generateDefaultWorkspaceName(email ?? '', lastWorkspaceNumber, translate), }); } else { startAnimation(); diff --git a/src/components/MoneyReportHeaderActions/MoneyReportHeaderSelectionDropdown.tsx b/src/components/MoneyReportHeaderActions/MoneyReportHeaderSelectionDropdown.tsx index 10994cf45f6c..d410bc6bb848 100644 --- a/src/components/MoneyReportHeaderActions/MoneyReportHeaderSelectionDropdown.tsx +++ b/src/components/MoneyReportHeaderActions/MoneyReportHeaderSelectionDropdown.tsx @@ -22,6 +22,7 @@ import useActiveAdminPolicies from '@hooks/useActiveAdminPolicies'; import useConfirmModal from '@hooks/useConfirmModal'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; import useExportActions from '@hooks/useExportActions'; +import useLastWorkspaceNumber from '@hooks/useLastWorkspaceNumber'; import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset'; import useLifecycleActions from '@hooks/useLifecycleActions'; import useLocalize from '@hooks/useLocalize'; @@ -36,6 +37,7 @@ import useSearchShouldCalculateTotals from '@hooks/useSearchShouldCalculateTotal import useSelectedTransactionsActions from '@hooks/useSelectedTransactionsActions'; import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport'; import useTransactionThreadReport from '@hooks/useTransactionThreadReport'; +import {generateDefaultWorkspaceName} from '@libs/actions/Policy/Policy'; import {search} from '@libs/actions/Search'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {getTotalAmountForIOUReportPreviewButton} from '@libs/MoneyRequestReportUtils'; @@ -77,6 +79,7 @@ function MoneyReportHeaderSelectionDropdown({reportID, primaryAction, isReportIn const isASAPSubmitBetaEnabled = isBetaEnabled(CONST.BETAS.ASAP_SUBMIT); const isBulkSubmitApprovePayBetaEnabled = isBetaEnabled(CONST.BETAS.BULK_SUBMIT_APPROVE_PAY); const activeAdminPolicies = useActiveAdminPolicies(); + const lastWorkspaceNumber = useLastWorkspaceNumber(); const {selectedTransactionIDs, currentSearchQueryJSON, currentSearchKey, currentSearchResults} = useSearchStateContext(); const {clearSelectedTransactions} = useSearchActionsContext(); @@ -251,6 +254,7 @@ function MoneyReportHeaderSelectionDropdown({reportID, primaryAction, isReportIn activePolicy, betas, isSelfTourViewed, + defaultWorkspaceName: generateDefaultWorkspaceName(email ?? '', lastWorkspaceNumber, translate), }); } else { payMoneyRequest({ diff --git a/src/components/MoneyReportHeaderPrimaryAction/PayPrimaryAction.tsx b/src/components/MoneyReportHeaderPrimaryAction/PayPrimaryAction.tsx index aaa9ac4ae37c..2dd47cf02318 100644 --- a/src/components/MoneyReportHeaderPrimaryAction/PayPrimaryAction.tsx +++ b/src/components/MoneyReportHeaderPrimaryAction/PayPrimaryAction.tsx @@ -7,12 +7,15 @@ import {useSearchStateContext} from '@components/Search/SearchContext'; import AnimatedSettlementButton from '@components/SettlementButton/AnimatedSettlementButton'; import type {PaymentActionParams} from '@components/SettlementButton/types'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useLastWorkspaceNumber from '@hooks/useLastWorkspaceNumber'; +import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useParticipantsInvoiceReport from '@hooks/useParticipantsInvoiceReport'; import usePolicy from '@hooks/usePolicy'; import useSearchShouldCalculateTotals from '@hooks/useSearchShouldCalculateTotals'; import useTransactionsAndViolationsForReport from '@hooks/useTransactionsAndViolationsForReport'; +import {generateDefaultWorkspaceName} from '@libs/actions/Policy/Policy'; import {search} from '@libs/actions/Search'; import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID'; import {getTotalAmountForIOUReportPreviewButton} from '@libs/MoneyRequestReportUtils'; @@ -35,9 +38,11 @@ type PayPrimaryActionProps = { function PayPrimaryAction({reportID, chatReportID}: PayPrimaryActionProps) { const {isPaidAnimationRunning, isApprovedAnimationRunning, stopAnimation, startAnimation, startApprovedAnimation} = usePaymentAnimationsContext(); const {isOffline} = useNetwork(); + const {translate} = useLocalize(); const {accountID, email} = useCurrentUserPersonalDetails(); const {isDelegateAccessRestricted} = useDelegateNoAccessState(); const {showDelegateNoAccessModal} = useDelegateNoAccessActions(); + const lastWorkspaceNumber = useLastWorkspaceNumber(); const {moneyRequestReport, chatReport, transaction} = useTransactionThreadData(reportID, chatReportID); @@ -112,6 +117,7 @@ function PayPrimaryAction({reportID, chatReportID}: PayPrimaryActionProps) { activePolicy, betas, isSelfTourViewed, + defaultWorkspaceName: generateDefaultWorkspaceName(email ?? '', lastWorkspaceNumber, translate), }); } else { startAnimation(); diff --git a/src/components/ReportActionItem/MoneyRequestReportPreview/PayActionButton.tsx b/src/components/ReportActionItem/MoneyRequestReportPreview/PayActionButton.tsx index 27fa0b2a630d..64f208cf0348 100644 --- a/src/components/ReportActionItem/MoneyRequestReportPreview/PayActionButton.tsx +++ b/src/components/ReportActionItem/MoneyRequestReportPreview/PayActionButton.tsx @@ -6,12 +6,15 @@ import {useDelegateNoAccessActions, useDelegateNoAccessState} from '@components/ import AnimatedSettlementButton from '@components/SettlementButton/AnimatedSettlementButton'; import type {PaymentActionParams} from '@components/SettlementButton/types'; import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails'; +import useLastWorkspaceNumber from '@hooks/useLastWorkspaceNumber'; +import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useOnyx from '@hooks/useOnyx'; import useParticipantsInvoiceReport from '@hooks/useParticipantsInvoiceReport'; import usePermissions from '@hooks/usePermissions'; import usePolicy from '@hooks/usePolicy'; import useReportTransactionsCollection from '@hooks/useReportTransactionsCollection'; +import {generateDefaultWorkspaceName} from '@libs/actions/Policy/Policy'; import {getTotalAmountForIOUReportPreviewButton} from '@libs/MoneyRequestReportUtils'; import {hasHeldExpenses as hasHeldExpensesReportUtils, hasUpdatedTotal, hasViolations as hasViolationsReportUtils, isInvoiceReport as isInvoiceReportUtils} from '@libs/ReportUtils'; import {payInvoice, payMoneyRequest} from '@userActions/IOU/PayMoneyRequest'; @@ -52,12 +55,14 @@ function PayActionButton({ reportPreviewAction, }: PayActionButtonProps) { const {isOffline} = useNetwork(); + const {translate} = useLocalize(); const currentUserDetails = useCurrentUserPersonalDetails(); const currentUserAccountID = currentUserDetails.accountID; const currentUserEmail = currentUserDetails.email ?? ''; const {isBetaEnabled} = usePermissions(); const {isDelegateAccessRestricted} = useDelegateNoAccessState(); const {showDelegateNoAccessModal} = useDelegateNoAccessActions(); + const lastWorkspaceNumber = useLastWorkspaceNumber(); const [activePolicyID] = useOnyx(ONYXKEYS.NVP_ACTIVE_POLICY_ID); const activePolicy = usePolicy(activePolicyID); @@ -149,6 +154,7 @@ function PayActionButton({ activePolicy, betas, isSelfTourViewed, + defaultWorkspaceName: generateDefaultWorkspaceName(currentUserEmail, lastWorkspaceNumber, translate), }); } else { payMoneyRequest({ diff --git a/src/hooks/useSelectionModeReportActions.ts b/src/hooks/useSelectionModeReportActions.ts index ae0b48f1f792..d200cd6aaf0e 100644 --- a/src/hooks/useSelectionModeReportActions.ts +++ b/src/hooks/useSelectionModeReportActions.ts @@ -15,6 +15,7 @@ import type {PaymentActionParams} from '@components/SettlementButton/types'; import {payInvoice, payMoneyRequest} from '@libs/actions/IOU/PayMoneyRequest'; import {approveMoneyRequest, canApproveIOU, canIOUBePaid as canIOUBePaidAction, submitReport} from '@libs/actions/IOU/ReportWorkflow'; import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; +import {generateDefaultWorkspaceName} from '@libs/actions/Policy/Policy'; import {search} from '@libs/actions/Search'; import getPlatform from '@libs/getPlatform'; import {getTotalAmountForIOUReportPreviewButton} from '@libs/MoneyRequestReportUtils'; @@ -46,6 +47,7 @@ import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage'; import useActiveAdminPolicies from './useActiveAdminPolicies'; import useConfirmPendingRTERAndProceed from './useConfirmPendingRTERAndProceed'; import useCurrentUserPersonalDetails from './useCurrentUserPersonalDetails'; +import useLastWorkspaceNumber from './useLastWorkspaceNumber'; import {useMemoizedLazyExpensifyIcons} from './useLazyAsset'; import useLocalize from './useLocalize'; import useNetwork from './useNetwork'; @@ -116,6 +118,7 @@ function useSelectionModeReportActions({ ); const existingB2BInvoiceReport = useParticipantsInvoiceReport(activePolicyID, CONST.REPORT.INVOICE_RECEIVER_TYPE.BUSINESS, chatReport?.policyID); const activeAdminPolicies = useActiveAdminPolicies(); + const lastWorkspaceNumber = useLastWorkspaceNumber(); const isChatReportArchived = useReportIsArchived(chatReport?.reportID); @@ -352,6 +355,7 @@ function useSelectionModeReportActions({ setIsHoldMenuVisible(true); } } else if (isInvoiceReport) { + const email = currentUserEmail ?? ''; payInvoice({ paymentMethodType: type, chatReport, @@ -359,7 +363,7 @@ function useSelectionModeReportActions({ invoiceReportCurrentNextStepDeprecated: nextStep, introSelected, currentUserAccountIDParam: currentUserAccountID, - currentUserEmailParam: currentUserEmail ?? '', + currentUserEmailParam: email, payAsBusiness, existingB2BInvoiceReport, methodID, @@ -367,6 +371,7 @@ function useSelectionModeReportActions({ activePolicy, betas, isSelfTourViewed, + defaultWorkspaceName: generateDefaultWorkspaceName(email, lastWorkspaceNumber, translate), }); clearSelectedTransactions(true); turnOffMobileSelectionMode(); diff --git a/src/libs/actions/IOU/PayMoneyRequest.ts b/src/libs/actions/IOU/PayMoneyRequest.ts index f5ecb52fecac..ec70fb31b6e9 100644 --- a/src/libs/actions/IOU/PayMoneyRequest.ts +++ b/src/libs/actions/IOU/PayMoneyRequest.ts @@ -57,6 +57,7 @@ type PayInvoiceArgs = { activePolicy?: OnyxTypes.Policy; betas: OnyxEntry; isSelfTourViewed: boolean | undefined; + defaultWorkspaceName: string; }; type PayMoneyRequestData = { @@ -112,6 +113,7 @@ function getPayMoneyRequestParams({ iouReportCurrentNextStepDeprecated, betas, isSelfTourViewed, + defaultWorkspaceName, }: { initialChatReport: OnyxTypes.Report; iouReport: OnyxEntry; @@ -131,6 +133,7 @@ function getPayMoneyRequestParams({ iouReportCurrentNextStepDeprecated: OnyxEntry; betas: OnyxEntry; isSelfTourViewed: boolean | undefined; + defaultWorkspaceName?: string; }): PayMoneyRequestData { const deprecatedCurrentUserEmail = getCurrentUserEmail(); const allTransactionViolations = getAllTransactionViolations(); @@ -153,7 +156,7 @@ function getPayMoneyRequestParams({ successData: [], failureData: [], }; - const shouldCreatePolicy = !activePolicy || !isPolicyAdmin(activePolicy) || !isPaidGroupPolicy(activePolicy); + const shouldCreatePolicy = (!activePolicy || !isPolicyAdmin(activePolicy) || !isPaidGroupPolicy(activePolicy)) && defaultWorkspaceName; if (isIndividualInvoiceRoom(chatReport) && payAsBusiness && shouldCreatePolicy) { payerPolicyID = generatePolicyID(); @@ -164,6 +167,7 @@ function getPayMoneyRequestParams({ params, } = buildPolicyData({ policyOwnerEmail: deprecatedCurrentUserEmail, + policyName: defaultWorkspaceName, makeMeAdmin: true, policyID: payerPolicyID, currentUserAccountIDParam: currentUserAccountIDParam ?? CONST.DEFAULT_NUMBER_ID, @@ -793,6 +797,7 @@ function payInvoice({ invoiceReportCurrentNextStepDeprecated, betas, isSelfTourViewed, + defaultWorkspaceName, }: PayInvoiceArgs) { const recipient = {accountID: invoiceReport?.ownerAccountID ?? CONST.DEFAULT_NUMBER_ID}; const { @@ -825,6 +830,7 @@ function payInvoice({ introSelected, betas, isSelfTourViewed, + defaultWorkspaceName, }); const paymentSelected = paymentMethodType === CONST.IOU.PAYMENT_TYPE.VBBA ? CONST.IOU.PAYMENT_SELECTED.BBA : CONST.IOU.PAYMENT_SELECTED.PBA; diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts index b50a23b3a579..75ccef628481 100644 --- a/src/libs/actions/Policy/Policy.ts +++ b/src/libs/actions/Policy/Policy.ts @@ -1,6 +1,5 @@ /* eslint-disable max-lines */ import {PUBLIC_DOMAINS_SET, Str} from 'expensify-common'; -import escapeRegExp from 'lodash/escapeRegExp'; import type {OnyxCollection, OnyxCollectionInputValue, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {TupleToUnion, ValueOf} from 'type-fest'; @@ -84,7 +83,7 @@ import {createFile} from '@libs/fileDownload/FileUtils'; import getIsNarrowLayout from '@libs/getIsNarrowLayout'; import GoogleTagManager from '@libs/GoogleTagManager'; // eslint-disable-next-line @typescript-eslint/no-deprecated -import {translate, translateLocal} from '@libs/Localize'; +import {translateLocal} from '@libs/Localize'; import Log from '@libs/Log'; import {buildNextStepNew} from '@libs/NextStepUtils'; import * as NumberUtils from '@libs/NumberUtils'; @@ -186,7 +185,7 @@ type PolicyCashExpenseMode = ValueOf { - deprecatedSessionEmail = val?.email ?? ''; deprecatedSessionAccountID = val?.accountID ?? CONST.DEFAULT_NUMBER_ID; }, }); @@ -2202,64 +2199,6 @@ function generateDefaultWorkspaceName(email: string, lastWorkspaceNumber: number return localeTranslate('workspace.new.workspaceName', displayNameForWorkspace, lastWorkspaceNumber !== undefined ? lastWorkspaceNumber + 1 : undefined); } -/** - * Generate a policy name based on an email and policy list. - * @param [email] the email to base the workspace name on. If not passed, will use the logged-in user's email instead - */ -function oldGenerateDefaultWorkspaceName(email = '', displayNameOverride?: string): string { - const emailParts = email ? email.split('@') : deprecatedSessionEmail.split('@'); - if (emailParts?.length !== 2) { - return ''; - } - const username = emailParts.at(0) ?? ''; - const domain = emailParts.at(1) ?? ''; - const userDetails = PersonalDetailsUtils.getPersonalDetailByEmail(email || deprecatedSessionEmail); - const displayName = displayNameOverride?.trim() ?? userDetails?.displayName?.trim(); - let displayNameForWorkspace = ''; - - if (!PUBLIC_DOMAINS_SET.has(domain.toLowerCase())) { - displayNameForWorkspace = Str.UCFirst(domain.split('.').at(0) ?? ''); - } else if (displayName) { - displayNameForWorkspace = Str.UCFirst(displayName); - } else if (PUBLIC_DOMAINS_SET.has(domain.toLowerCase())) { - displayNameForWorkspace = Str.UCFirst(username); - } else { - displayNameForWorkspace = userDetails?.phoneNumber ?? ''; - } - - const isSMSDomain = `@${domain}` === CONST.SMS.DOMAIN; - if (isSMSDomain) { - // eslint-disable-next-line @typescript-eslint/no-deprecated - displayNameForWorkspace = translateLocal('workspace.new.myGroupWorkspace', {}); - } - - if (isEmptyObject(deprecatedAllPolicies)) { - // eslint-disable-next-line @typescript-eslint/no-deprecated - return isSMSDomain ? translateLocal('workspace.new.myGroupWorkspace', {}) : translateLocal('workspace.new.workspaceName', displayNameForWorkspace); - } - - // find default named workspaces and increment the last number - const escapedName = escapeRegExp(displayNameForWorkspace); - const workspaceTranslations = Object.values(CONST.LOCALES) - .map((lang) => translate(lang, 'workspace.common.workspace')) - .join('|'); - - const workspaceRegex = isSMSDomain ? new RegExp(`^${escapedName}\\s*(\\d+)?$`, 'i') : new RegExp(`^(?=.*${escapedName})(?:.*(?:${workspaceTranslations})\\s*(\\d+)?)`, 'i'); - - const workspaceNumbers = Object.values(deprecatedAllPolicies) - .map((policy) => workspaceRegex.exec(policy?.name ?? '')) - .filter(Boolean) // Remove null matches - .map((match) => Number(match?.[1] ?? '0')); - const lastWorkspaceNumber = workspaceNumbers.length > 0 ? Math.max(...workspaceNumbers) : undefined; - - if (isSMSDomain) { - // eslint-disable-next-line @typescript-eslint/no-deprecated - return translateLocal('workspace.new.myGroupWorkspace', {workspaceNumber: lastWorkspaceNumber !== undefined ? lastWorkspaceNumber + 1 : undefined}); - } - // eslint-disable-next-line @typescript-eslint/no-deprecated - return translateLocal('workspace.new.workspaceName', displayNameForWorkspace, lastWorkspaceNumber !== undefined ? lastWorkspaceNumber + 1 : undefined); -} - /** * Returns a client generated 16 character hexadecimal value for the policyID */ @@ -2412,7 +2351,7 @@ function buildPolicyData(options: BuildPolicyDataOptions): OnyxData