diff --git a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js index 3939e847707d..291c32511bfd 100755 --- a/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js +++ b/src/components/MoneyTemporaryForRefactorRequestConfirmationList.js @@ -763,7 +763,7 @@ function MoneyTemporaryForRefactorRequestConfirmationList({ diff --git a/src/components/ReportActionItem/MoneyRequestView.tsx b/src/components/ReportActionItem/MoneyRequestView.tsx index d3c86698f910..84a734846ce7 100644 --- a/src/components/ReportActionItem/MoneyRequestView.tsx +++ b/src/components/ReportActionItem/MoneyRequestView.tsx @@ -379,7 +379,7 @@ function MoneyRequestView({ > = {}; Onyx.connect({ @@ -189,8 +190,8 @@ function getForReportAction(reportID: string | undefined, reportAction: OnyxEntr const policyTags = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {}; const transactionTag = reportActionOriginalMessage?.tag ?? ''; const oldTransactionTag = reportActionOriginalMessage?.oldTag ?? ''; - const splittedTag = transactionTag.split(CONST.COLON); - const splittedOldTag = oldTransactionTag.split(CONST.COLON); + const splittedTag = TransactionUtils.getTagArrayFromName(transactionTag); + const splittedOldTag = TransactionUtils.getTagArrayFromName(oldTransactionTag); const localizedTagListName = Localize.translateLocal('common.tag'); Object.keys(policyTags).forEach((policyTagKey, index) => { diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts index d3eafc6554db..3ff2c39859c2 100644 --- a/src/libs/TransactionUtils.ts +++ b/src/libs/TransactionUtils.ts @@ -364,18 +364,50 @@ function getBillable(transaction: OnyxEntry): boolean { return transaction?.billable ?? false; } +/** + * Return a colon-delimited tag string as an array, considering escaped colons and double backslashes. + */ +function getTagArrayFromName(tagName: string): string[] { + // WAIT!!!!!!!!!!!!!!!!!! + // You need to keep this in sync with TransactionUtils.php + + // We need to be able to preserve double backslashes in the original string + // and not have it interfere with splitting on a colon (:). + // So, let's replace it with something absurd to begin with, do our split, and + // then replace the double backslashes in the end. + const tagWithoutDoubleSlashes = tagName.replace(/\\\\/g, '☠'); + const tagWithoutEscapedColons = tagWithoutDoubleSlashes.replace(/\\:/g, '☢'); + + // Do our split + const matches = tagWithoutEscapedColons.split(':'); + const newMatches: string[] = []; + + for (const item of matches) { + const tagWithEscapedColons = item.replace(/☢/g, '\\:'); + const tagWithDoubleSlashes = tagWithEscapedColons.replace(/☠/g, '\\\\'); + newMatches.push(tagWithDoubleSlashes); + } + + return newMatches; +} + /** * Return the tag from the transaction. When the tagIndex is passed, return the tag based on the index. * This "tag" field has no "modified" complement. */ function getTag(transaction: OnyxEntry, tagIndex?: number): string { if (tagIndex !== undefined) { - return transaction?.tag?.split(CONST.COLON)[tagIndex] ?? ''; + const tagsArray = getTagArrayFromName(transaction?.tag ?? ''); + return tagsArray[tagIndex] ?? ''; } return transaction?.tag ?? ''; } +function getTagForDisplay(transaction: OnyxEntry, tagIndex?: number): string { + return getTag(transaction, tagIndex).replace(/[\\\\]:/g, ':'); +} + /** * Return the created field from the transaction, return the modifiedCreated if present. */ @@ -605,6 +637,8 @@ export { getCategory, getBillable, getTag, + getTagArrayFromName, + getTagForDisplay, getTransactionViolations, getLinkedTransaction, getAllReportTransactions, diff --git a/src/libs/Violations/ViolationsUtils.ts b/src/libs/Violations/ViolationsUtils.ts index a1cd001badee..16b428549156 100644 --- a/src/libs/Violations/ViolationsUtils.ts +++ b/src/libs/Violations/ViolationsUtils.ts @@ -2,6 +2,7 @@ import reject from 'lodash/reject'; import Onyx from 'react-native-onyx'; import type {OnyxUpdate} from 'react-native-onyx'; import type {Phrase, PhraseParameters} from '@libs/Localize'; +import * as TransactionUtils from '@libs/TransactionUtils'; import CONST from '@src/CONST'; import type {TranslationPaths} from '@src/languages/types'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -50,7 +51,7 @@ const ViolationsUtils = { } if (policyRequiresTags) { - const selectedTags = updatedTransaction.tag?.split(CONST.COLON) ?? []; + const selectedTags = TransactionUtils.getTagArrayFromName(updatedTransaction.tag ?? '') ?? []; const policyTagKeys = Object.keys(policyTagList); if (policyTagKeys.length === 0) { diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts index 843a0744b617..5243faa2888a 100644 --- a/src/libs/actions/Policy.ts +++ b/src/libs/actions/Policy.ts @@ -1633,8 +1633,8 @@ function buildOptimisticPolicyRecentlyUsedCategories(policyID?: string, category return lodashUnion([category], policyRecentlyUsedCategories); } -function buildOptimisticPolicyRecentlyUsedTags(policyID?: string, reportTags?: string): RecentlyUsedTags { - if (!policyID || !reportTags) { +function buildOptimisticPolicyRecentlyUsedTags(policyID?: string, transactionTags?: string): RecentlyUsedTags { + if (!policyID || !transactionTags) { return {}; } @@ -1643,7 +1643,7 @@ function buildOptimisticPolicyRecentlyUsedTags(policyID?: string, reportTags?: s const policyRecentlyUsedTags = allRecentlyUsedTags?.[`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_TAGS}${policyID}`] ?? {}; const newOptimisticPolicyRecentlyUsedTags: RecentlyUsedTags = {}; - reportTags.split(CONST.COLON).forEach((tag, index) => { + TransactionUtils.getTagArrayFromName(transactionTags).forEach((tag, index) => { if (!tag) { return; } diff --git a/src/pages/EditRequestPage.js b/src/pages/EditRequestPage.js index 29917154a527..d52436fbbf4f 100644 --- a/src/pages/EditRequestPage.js +++ b/src/pages/EditRequestPage.js @@ -135,7 +135,7 @@ function EditRequestPage({report, route, policy, policyCategories, policyTags, p IOU.updateMoneyRequestTag( transaction.transactionID, report.reportID, - IOUUtils.insertTagIntoReportTagsString(transactionTag, updatedTag, tagIndex), + IOUUtils.insertTagIntoTransactionTagsString(transactionTag, updatedTag, tagIndex), policy, policyTags, policyCategories, diff --git a/src/pages/iou/request/step/IOURequestStepTag.js b/src/pages/iou/request/step/IOURequestStepTag.js index 20ab92d3d446..572663160090 100644 --- a/src/pages/iou/request/step/IOURequestStepTag.js +++ b/src/pages/iou/request/step/IOURequestStepTag.js @@ -82,7 +82,7 @@ function IOURequestStepTag({ */ const updateTag = (selectedTag) => { const isSelectedTag = selectedTag.searchText === tag; - const updatedTag = IOUUtils.insertTagIntoReportTagsString(transactionTag, isSelectedTag ? '' : selectedTag.searchText, tagIndex); + const updatedTag = IOUUtils.insertTagIntoTransactionTagsString(transactionTag, isSelectedTag ? '' : selectedTag.searchText, tagIndex); if (isSplitBill && isEditing) { IOU.setDraftSplitTransaction(transactionID, {tag: updatedTag}); navigateBack();