Skip to content

Commit

Permalink
Merge pull request Expensify#30603 from Expensify/cristi_modified-exp…
Browse files Browse the repository at this point in the history
…ense-message

ReportAction text for transactions edited in OldDot
  • Loading branch information
stitesExpensify authored Dec 19, 2023
2 parents 4b95010 + 13f7b9c commit df98114
Show file tree
Hide file tree
Showing 12 changed files with 617 additions and 194 deletions.
10 changes: 6 additions & 4 deletions src/languages/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -594,11 +594,10 @@ export default {
noReimbursableExpenses: 'This report has an invalid amount',
pendingConversionMessage: "Total will update when you're back online",
changedTheRequest: 'changed the request',
setTheRequest: ({valueName, newValueToDisplay}: SetTheRequestParams) => `set the ${valueName} to ${newValueToDisplay}`,
setTheRequest: ({valueName, newValueToDisplay}: SetTheRequestParams) => `the ${valueName} to ${newValueToDisplay}`,
setTheDistance: ({newDistanceToDisplay, newAmountToDisplay}: SetTheDistanceParams) => `set the distance to ${newDistanceToDisplay}, which set the amount to ${newAmountToDisplay}`,
removedTheRequest: ({valueName, oldValueToDisplay}: RemovedTheRequestParams) => `removed the ${valueName} (previously ${oldValueToDisplay})`,
updatedTheRequest: ({valueName, newValueToDisplay, oldValueToDisplay}: UpdatedTheRequestParams) =>
`changed the ${valueName} to ${newValueToDisplay} (previously ${oldValueToDisplay})`,
removedTheRequest: ({valueName, oldValueToDisplay}: RemovedTheRequestParams) => `the ${valueName} (previously ${oldValueToDisplay})`,
updatedTheRequest: ({valueName, newValueToDisplay, oldValueToDisplay}: UpdatedTheRequestParams) => `the ${valueName} to ${newValueToDisplay} (previously ${oldValueToDisplay})`,
updatedTheDistance: ({newDistanceToDisplay, oldDistanceToDisplay, newAmountToDisplay, oldAmountToDisplay}: UpdatedTheDistanceParams) =>
`changed the distance to ${newDistanceToDisplay} (previously ${oldDistanceToDisplay}), which updated the amount to ${newAmountToDisplay} (previously ${oldAmountToDisplay})`,
threadRequestReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `${formattedAmount} request${comment ? ` for ${comment}` : ''}`,
Expand All @@ -622,6 +621,9 @@ export default {
},
waitingOnEnabledWallet: ({submitterDisplayName}: WaitingOnBankAccountParams) => `Started settling up, payment is held until ${submitterDisplayName} enables their Wallet`,
enableWallet: 'Enable Wallet',
set: 'set',
changed: 'changed',
removed: 'removed',
},
notificationPreferencesPage: {
header: 'Notification preferences',
Expand Down
10 changes: 6 additions & 4 deletions src/languages/es.ts
Original file line number Diff line number Diff line change
Expand Up @@ -587,13 +587,12 @@ export default {
noReimbursableExpenses: 'El importe de este informe no es válido',
pendingConversionMessage: 'El total se actualizará cuando estés online',
changedTheRequest: 'cambió la solicitud',
setTheRequest: ({valueName, newValueToDisplay}: SetTheRequestParams) => `estableció ${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} a ${newValueToDisplay}`,
setTheRequest: ({valueName, newValueToDisplay}: SetTheRequestParams) => `${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} a ${newValueToDisplay}`,
setTheDistance: ({newDistanceToDisplay, newAmountToDisplay}: SetTheDistanceParams) =>
`estableció la distancia a ${newDistanceToDisplay}, lo que estableció el importe a ${newAmountToDisplay}`,
removedTheRequest: ({valueName, oldValueToDisplay}: RemovedTheRequestParams) =>
`eliminó ${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} (previamente ${oldValueToDisplay})`,
removedTheRequest: ({valueName, oldValueToDisplay}: RemovedTheRequestParams) => `${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} (previamente ${oldValueToDisplay})`,
updatedTheRequest: ({valueName, newValueToDisplay, oldValueToDisplay}: UpdatedTheRequestParams) =>
`cambió ${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} a ${newValueToDisplay} (previamente ${oldValueToDisplay})`,
`${valueName === 'comerciante' ? 'el' : 'la'} ${valueName} a ${newValueToDisplay} (previamente ${oldValueToDisplay})`,
updatedTheDistance: ({newDistanceToDisplay, oldDistanceToDisplay, newAmountToDisplay, oldAmountToDisplay}: UpdatedTheDistanceParams) =>
`cambió la distancia a ${newDistanceToDisplay} (previamente ${oldDistanceToDisplay}), lo que cambió el importe a ${newAmountToDisplay} (previamente ${oldAmountToDisplay})`,
threadRequestReportName: ({formattedAmount, comment}: ThreadRequestReportNameParams) => `Solicitud de ${formattedAmount}${comment ? ` para ${comment}` : ''}`,
Expand All @@ -617,6 +616,9 @@ export default {
},
waitingOnEnabledWallet: ({submitterDisplayName}: WaitingOnBankAccountParams) => `Inició el pago, pero no se procesará hasta que ${submitterDisplayName} active su Billetera`,
enableWallet: 'Habilitar Billetera',
set: 'estableció',
changed: 'cambió',
removed: 'eliminó',
},
notificationPreferencesPage: {
header: 'Preferencias de avisos',
Expand Down
226 changes: 226 additions & 0 deletions src/libs/ModifiedExpenseMessage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
import {format} from 'date-fns';
import Onyx from 'react-native-onyx';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {PolicyTags, ReportAction} from '@src/types/onyx';
import * as CurrencyUtils from './CurrencyUtils';
import * as Localize from './Localize';
import * as PolicyUtils from './PolicyUtils';
import * as ReportUtils from './ReportUtils';
import {ExpenseOriginalMessage} from './ReportUtils';

let allPolicyTags: Record<string, PolicyTags | null> = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.POLICY_TAGS,
waitForCollectionCallback: true,
callback: (value) => {
if (!value) {
allPolicyTags = {};
return;
}
allPolicyTags = value;
},
});

/**
* Builds the partial message fragment for a modified field on the expense.
*/
function buildMessageFragmentForValue(
newValue: string,
oldValue: string,
valueName: string,
valueInQuotes: boolean,
setFragments: string[],
removalFragments: string[],
changeFragments: string[],
shouldConvertToLowercase = true,
) {
const newValueToDisplay = valueInQuotes ? `"${newValue}"` : newValue;
const oldValueToDisplay = valueInQuotes ? `"${oldValue}"` : oldValue;
const displayValueName = shouldConvertToLowercase ? valueName.toLowerCase() : valueName;

if (!oldValue) {
const fragment = Localize.translateLocal('iou.setTheRequest', {valueName: displayValueName, newValueToDisplay});
setFragments.push(fragment);
} else if (!newValue) {
const fragment = Localize.translateLocal('iou.removedTheRequest', {valueName: displayValueName, oldValueToDisplay});
removalFragments.push(fragment);
} else {
const fragment = Localize.translateLocal('iou.updatedTheRequest', {valueName: displayValueName, newValueToDisplay, oldValueToDisplay});
changeFragments.push(fragment);
}
}

/**
* Get the message line for a modified expense.
*/
function getMessageLine(prefix: string, messageFragments: string[]): string {
if (messageFragments.length === 0) {
return '';
}
return messageFragments.reduce((acc, value, index) => {
if (index === messageFragments.length - 1) {
if (messageFragments.length === 1) {
return `${acc} ${value}.`;
}
if (messageFragments.length === 2) {
return `${acc} ${Localize.translateLocal('common.and')} ${value}.`;
}
return `${acc}, ${Localize.translateLocal('common.and')} ${value}.`;
}
if (index === 0) {
return `${acc} ${value}`;
}
return `${acc}, ${value}`;
}, prefix);
}

function getForDistanceRequest(newDistance: string, oldDistance: string, newAmount: string, oldAmount: string): string {
if (!oldDistance) {
return Localize.translateLocal('iou.setTheDistance', {newDistanceToDisplay: newDistance, newAmountToDisplay: newAmount});
}
return Localize.translateLocal('iou.updatedTheDistance', {
newDistanceToDisplay: newDistance,
oldDistanceToDisplay: oldDistance,
newAmountToDisplay: newAmount,
oldAmountToDisplay: oldAmount,
});
}

/**
* Get the report action message when expense has been modified.
*
* ModifiedExpense::getNewDotComment in Web-Expensify should match this.
* If we change this function be sure to update the backend as well.
*/
function getForReportAction(reportAction: ReportAction): string {
if (reportAction.actionName !== CONST.REPORT.ACTIONS.TYPE.MODIFIEDEXPENSE) {
return '';
}
const reportActionOriginalMessage = reportAction.originalMessage as ExpenseOriginalMessage | undefined;
const policyID = ReportUtils.getReportPolicyID(reportAction.reportID) ?? '';
const policyTags = allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${policyID}`] ?? {};
const policyTagListName = PolicyUtils.getTagListName(policyTags) || Localize.translateLocal('common.tag');

const removalFragments: string[] = [];
const setFragments: string[] = [];
const changeFragments: string[] = [];

const hasModifiedAmount =
reportActionOriginalMessage &&
'oldAmount' in reportActionOriginalMessage &&
'oldCurrency' in reportActionOriginalMessage &&
'amount' in reportActionOriginalMessage &&
'currency' in reportActionOriginalMessage;

const hasModifiedMerchant = reportActionOriginalMessage && 'oldMerchant' in reportActionOriginalMessage && 'merchant' in reportActionOriginalMessage;
if (hasModifiedAmount) {
const oldCurrency = reportActionOriginalMessage?.oldCurrency ?? '';
const oldAmount = CurrencyUtils.convertToDisplayString(reportActionOriginalMessage?.oldAmount ?? 0, oldCurrency);

const currency = reportActionOriginalMessage?.currency ?? '';
const amount = CurrencyUtils.convertToDisplayString(reportActionOriginalMessage?.amount ?? 0, currency);

// Only Distance edits should modify amount and merchant (which stores distance) in a single transaction.
// We check the merchant is in distance format (includes @) as a sanity check
if (hasModifiedMerchant && (reportActionOriginalMessage?.merchant ?? '').includes('@')) {
return getForDistanceRequest(reportActionOriginalMessage?.merchant ?? '', reportActionOriginalMessage?.oldMerchant ?? '', amount, oldAmount);
}

buildMessageFragmentForValue(amount, oldAmount, Localize.translateLocal('iou.amount'), false, setFragments, removalFragments, changeFragments);
}

const hasModifiedComment = reportActionOriginalMessage && 'oldComment' in reportActionOriginalMessage && 'newComment' in reportActionOriginalMessage;
if (hasModifiedComment) {
buildMessageFragmentForValue(
reportActionOriginalMessage?.newComment ?? '',
reportActionOriginalMessage?.oldComment ?? '',
Localize.translateLocal('common.description'),
true,
setFragments,
removalFragments,
changeFragments,
);
}

const hasModifiedCreated = reportActionOriginalMessage && 'oldCreated' in reportActionOriginalMessage && 'created' in reportActionOriginalMessage;
if (hasModifiedCreated) {
// Take only the YYYY-MM-DD value as the original date includes timestamp
let formattedOldCreated: Date | string = new Date(reportActionOriginalMessage?.oldCreated ? reportActionOriginalMessage.oldCreated : 0);
formattedOldCreated = format(formattedOldCreated, CONST.DATE.FNS_FORMAT_STRING);
buildMessageFragmentForValue(
reportActionOriginalMessage?.created ?? '',
formattedOldCreated,
Localize.translateLocal('common.date'),
false,
setFragments,
removalFragments,
changeFragments,
);
}

if (hasModifiedMerchant) {
buildMessageFragmentForValue(
reportActionOriginalMessage?.merchant ?? '',
reportActionOriginalMessage?.oldMerchant ?? '',
Localize.translateLocal('common.merchant'),
true,
setFragments,
removalFragments,
changeFragments,
);
}

const hasModifiedCategory = reportActionOriginalMessage && 'oldCategory' in reportActionOriginalMessage && 'category' in reportActionOriginalMessage;
if (hasModifiedCategory) {
buildMessageFragmentForValue(
reportActionOriginalMessage?.category ?? '',
reportActionOriginalMessage?.oldCategory ?? '',
Localize.translateLocal('common.category'),
true,
setFragments,
removalFragments,
changeFragments,
);
}

const hasModifiedTag = reportActionOriginalMessage && 'oldTag' in reportActionOriginalMessage && 'tag' in reportActionOriginalMessage;
if (hasModifiedTag) {
buildMessageFragmentForValue(
reportActionOriginalMessage?.tag ?? '',
reportActionOriginalMessage?.oldTag ?? '',
policyTagListName,
true,
setFragments,
removalFragments,
changeFragments,
policyTagListName === Localize.translateLocal('common.tag'),
);
}

const hasModifiedBillable = reportActionOriginalMessage && 'oldBillable' in reportActionOriginalMessage && 'billable' in reportActionOriginalMessage;
if (hasModifiedBillable) {
buildMessageFragmentForValue(
reportActionOriginalMessage?.billable ?? '',
reportActionOriginalMessage?.oldBillable ?? '',
Localize.translateLocal('iou.request'),
true,
setFragments,
removalFragments,
changeFragments,
);
}

const message =
getMessageLine(`\n${Localize.translateLocal('iou.changed')}`, changeFragments) +
getMessageLine(`\n${Localize.translateLocal('iou.set')}`, setFragments) +
getMessageLine(`\n${Localize.translateLocal('iou.removed')}`, removalFragments);
if (message === '') {
return Localize.translateLocal('iou.changedTheRequest');
}
return `${message.substring(1, message.length)}`;
}

export default {
getForReportAction,
};
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import Str from 'expensify-common/lib/str';
import {ImageSourcePropType} from 'react-native';
import EXPENSIFY_ICON_URL from '@assets/images/expensify-logo-round-clearspace.png';
import ModifiedExpenseMessage from '@libs/ModifiedExpenseMessage';
import * as ReportUtils from '@libs/ReportUtils';
import * as AppUpdate from '@userActions/AppUpdate';
import {Report, ReportAction} from '@src/types/onyx';
Expand Down Expand Up @@ -108,7 +109,7 @@ export default {

pushModifiedExpenseNotification(report: Report, reportAction: ReportAction, onClick: LocalNotificationClickHandler, usesIcon = false) {
const title = reportAction.person?.map((f) => f.text).join(', ') ?? '';
const body = ReportUtils.getModifiedExpenseMessage(reportAction);
const body = ModifiedExpenseMessage.getForReportAction(reportAction);
const icon = usesIcon ? EXPENSIFY_ICON_URL : '';
const data = {
reportID: report.reportID,
Expand Down
3 changes: 2 additions & 1 deletion src/libs/OptionsListUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import * as ErrorUtils from './ErrorUtils';
import * as LocalePhoneNumber from './LocalePhoneNumber';
import * as Localize from './Localize';
import * as LoginUtils from './LoginUtils';
import ModifiedExpenseMessage from './ModifiedExpenseMessage';
import Navigation from './Navigation/Navigation';
import Permissions from './Permissions';
import * as PersonalDetailsUtils from './PersonalDetailsUtils';
Expand Down Expand Up @@ -407,7 +408,7 @@ function getLastMessageTextForReport(report) {
} else if (ReportUtils.isReportMessageAttachment({text: report.lastMessageText, html: report.lastMessageHtml, translationKey: report.lastMessageTranslationKey})) {
lastMessageTextFromReport = `[${Localize.translateLocal(report.lastMessageTranslationKey || 'common.attachment')}]`;
} else if (ReportActionUtils.isModifiedExpenseAction(lastReportAction)) {
const properSchemaForModifiedExpenseMessage = ReportUtils.getModifiedExpenseMessage(lastReportAction);
const properSchemaForModifiedExpenseMessage = ModifiedExpenseMessage.getForReportAction(lastReportAction);
lastMessageTextFromReport = ReportUtils.formatReportLastMessageText(properSchemaForModifiedExpenseMessage, true);
} else if (
lastActionName === CONST.REPORT.ACTIONS.TYPE.TASKCOMPLETED ||
Expand Down
Loading

0 comments on commit df98114

Please sign in to comment.