Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix lhn and copying message content #49177

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 5 additions & 26 deletions src/components/ReportActionItem/IssueCardMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import type {OnyxEntry} from 'react-native-onyx';
import {useOnyx} from 'react-native-onyx';
import Button from '@components/Button';
import RenderHTML from '@components/RenderHTML';
import useEnvironment from '@hooks/useEnvironment';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type {ReportAction} from '@src/types/onyx';
import type OriginalMessage from '@src/types/onyx/OriginalMessage';
import type {IssueNewCardOriginalMessage} from '@src/types/onyx/OriginalMessage';
import {isEmptyObject} from '@src/types/utils/EmptyObject';

type IssueCardMessageProps = {
Expand All @@ -20,22 +20,14 @@ type IssueCardMessageProps = {
policyID: string | undefined;
};

type IssueNewCardOriginalMessage = OriginalMessage<
typeof CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS | typeof CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED | typeof CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED_VIRTUAL
>;

function IssueCardMessage({action, policyID}: IssueCardMessageProps) {
const {translate} = useLocalize();
const styles = useThemeStyles();
const {environmentURL} = useEnvironment();
const [privatePersonalDetails] = useOnyx(ONYXKEYS.PRIVATE_PERSONAL_DETAILS);
const [session] = useOnyx(ONYXKEYS.SESSION);

const assigneeAccountID = (action?.originalMessage as IssueNewCardOriginalMessage)?.assigneeAccountID;

const assignee = `<mention-user accountID=${assigneeAccountID}></mention-user>`;
const link = `<a href='${environmentURL}/${ROUTES.SETTINGS_WALLET}'>${translate('cardPage.expensifyCard')}</a>`;

const missingDetails =
!privatePersonalDetails?.legalFirstName ||
!privatePersonalDetails?.legalLastName ||
Expand All @@ -46,25 +38,12 @@ function IssueCardMessage({action, policyID}: IssueCardMessageProps) {

const isAssigneeCurrentUser = !isEmptyObject(session) && session.accountID === assigneeAccountID;

const shouldShowDetailsButton = action?.actionName === CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS && missingDetails && isAssigneeCurrentUser;

const getTranslation = () => {
switch (action?.actionName) {
case CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED:
return translate('workspace.expensifyCard.issuedCard', assignee);
case CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED_VIRTUAL:
return translate('workspace.expensifyCard.issuedCardVirtual', {assignee, link});
case CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS:
return translate(`workspace.expensifyCard.${!isAssigneeCurrentUser || shouldShowDetailsButton ? 'issuedCardNoShippingDetails' : 'addedShippingDetails'}`, assignee);
default:
return '';
}
};
const shouldShowAddMissingDetailsButton = action?.actionName === CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS && missingDetails && isAssigneeCurrentUser;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition we used here is wrong, we should had checked !isAssigneeCurrentUser instead, this caused #50233.

nitpick @allgandalf 😅 , should had looked more carefully


return (
<>
<RenderHTML html={`<muted-text>${getTranslation()}</muted-text>`} />
{shouldShowDetailsButton && (
<RenderHTML html={`<muted-text>${ReportActionsUtils.getCardIssuedMessage(action, true)}</muted-text>`} />
{shouldShowAddMissingDetailsButton && (
<Button
onPress={() => {
if (!policyID) {
Expand Down
58 changes: 55 additions & 3 deletions src/libs/ReportActionsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,9 @@ import type {ValueOf} from 'type-fest';
import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import type {OnyxInputOrEntry} from '@src/types/onyx';
import type {JoinWorkspaceResolution, OriginalMessageChangeLog, OriginalMessageExportIntegration} from '@src/types/onyx/OriginalMessage';
import ROUTES from '@src/ROUTES';
import type {OnyxInputOrEntry, PrivatePersonalDetails} from '@src/types/onyx';
import type {IssueNewCardOriginalMessage, JoinWorkspaceResolution, OriginalMessageChangeLog, OriginalMessageExportIntegration} from '@src/types/onyx/OriginalMessage';
import type Report from '@src/types/onyx/Report';
import type ReportAction from '@src/types/onyx/ReportAction';
import type {Message, OldDotReportAction, OriginalMessage, ReportActions} from '@src/types/onyx/ReportAction';
Expand Down Expand Up @@ -82,6 +83,14 @@ Onyx.connect({
},
});

let privatePersonalDetails: PrivatePersonalDetails | undefined;
Onyx.connect({
key: ONYXKEYS.PRIVATE_PERSONAL_DETAILS,
callback: (personalDetails) => {
privatePersonalDetails = personalDetails;
},
});

let environmentURL: string;
Environment.getEnvironmentURL().then((url: string) => (environmentURL = url));

Expand Down Expand Up @@ -152,6 +161,7 @@ function isReportPreviewAction(reportAction: OnyxInputOrEntry<ReportAction>): re
function isSubmittedAction(reportAction: OnyxInputOrEntry<ReportAction>): reportAction is ReportAction<typeof CONST.REPORT.ACTIONS.TYPE.SUBMITTED> {
return isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.SUBMITTED);
}

function isApprovedAction(reportAction: OnyxInputOrEntry<ReportAction>): reportAction is ReportAction<typeof CONST.REPORT.ACTIONS.TYPE.APPROVED> {
return isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.APPROVED);
}
Expand Down Expand Up @@ -237,7 +247,10 @@ function getWhisperedTo(reportAction: OnyxInputOrEntry<ReportAction>): number[]
}

if (typeof originalMessage !== 'object') {
Log.info('Original message is not an object for reportAction: ', true, {reportActionID: reportAction?.reportActionID, actionName: reportAction?.actionName});
Log.info('Original message is not an object for reportAction: ', true, {
reportActionID: reportAction?.reportActionID,
actionName: reportAction?.actionName,
});
}

return [];
Expand Down Expand Up @@ -965,6 +978,7 @@ const iouRequestTypes = new Set<ValueOf<typeof CONST.IOU.REPORT_ACTION_TYPE>>([
CONST.IOU.REPORT_ACTION_TYPE.PAY,
CONST.IOU.REPORT_ACTION_TYPE.TRACK,
]);

/**
* Gets the reportID for the transaction thread associated with a report by iterating over the reportActions and identifying the IOU report actions.
* Returns a reportID if there is exactly one transaction thread for the report, and null otherwise.
Expand Down Expand Up @@ -1664,6 +1678,42 @@ function getRemovedFromApprovalChainMessage(reportAction: OnyxEntry<ReportAction
return Localize.translateLocal('workspaceActions.removedFromApprovalWorkflow', {submittersNames});
}

function isCardIssuedAction(reportAction: OnyxEntry<ReportAction>) {
return isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED, CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED_VIRTUAL, CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS);
}

function getCardIssuedMessage(reportAction: OnyxEntry<ReportAction>, shouldRenderHTML = false) {
const assigneeAccountID = (reportAction?.originalMessage as IssueNewCardOriginalMessage)?.assigneeAccountID;
const assigneeDetails = PersonalDetailsUtils.getPersonalDetailsByIDs([assigneeAccountID], currentUserAccountID ?? -1)[0];

const assignee = shouldRenderHTML ? `<mention-user accountID="${assigneeAccountID}"/>` : assigneeDetails?.firstName ?? assigneeDetails.login ?? '';
const link = shouldRenderHTML
? `<a href='${environmentURL}/${ROUTES.SETTINGS_WALLET}'>${Localize.translateLocal('cardPage.expensifyCard')}</a>`
: Localize.translateLocal('cardPage.expensifyCard');

const missingDetails =
!privatePersonalDetails?.legalFirstName ||
!privatePersonalDetails?.legalLastName ||
!privatePersonalDetails?.dob ||
!privatePersonalDetails?.phoneNumber ||
isEmptyObject(privatePersonalDetails?.addresses) ||
privatePersonalDetails.addresses.length === 0;

const isAssigneeCurrentUser = currentUserAccountID === assigneeAccountID;

const shouldShowAddMissingDetailsButton = reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS && missingDetails && isAssigneeCurrentUser;
switch (reportAction?.actionName) {
case CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED:
return Localize.translateLocal('workspace.expensifyCard.issuedCard', assignee);
case CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED_VIRTUAL:
return Localize.translateLocal('workspace.expensifyCard.issuedCardVirtual', {assignee, link});
case CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS:
return Localize.translateLocal(`workspace.expensifyCard.${shouldShowAddMissingDetailsButton ? 'issuedCardNoShippingDetails' : 'addedShippingDetails'}`, assignee);
default:
return '';
}
}

export {
doesReportHaveVisibleActions,
extractLinksFromMessageHtml,
Expand Down Expand Up @@ -1766,6 +1816,8 @@ export {
getPolicyChangeLogChangeRoleMessage,
getPolicyChangeLogDeleteMemberMessage,
getRenamedAction,
isCardIssuedAction,
getCardIssuedMessage,
};

export type {LastVisibleMessage};
3 changes: 3 additions & 0 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3778,6 +3778,9 @@ function getReportName(
return report?.reportName ?? '';
}

if (ReportActionsUtils.isCardIssuedAction(parentReportAction)) {
return ReportActionsUtils.getCardIssuedMessage(parentReportAction);
}
return reportActionMessage;
}

Expand Down
2 changes: 2 additions & 0 deletions src/libs/SidebarUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,8 @@ function getOptionData({
result.alternateText = ReportUtils.getWorkspaceNameUpdatedMessage(lastAction);
} else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.LEAVE_POLICY) {
result.alternateText = Localize.translateLocal('workspace.invite.leftWorkspace');
} else if (ReportActionsUtils.isCardIssuedAction(lastAction)) {
result.alternateText = ReportActionsUtils.getCardIssuedMessage(lastAction);
} else if (lastAction?.actionName !== CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && lastActorDisplayName && lastMessageTextFromReport) {
result.alternateText = ReportUtils.formatReportLastMessageText(Parser.htmlToText(`${lastActorDisplayName}: ${lastMessageText}`));
} else if (lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.ADD_TAG) {
Expand Down
2 changes: 2 additions & 0 deletions src/pages/home/report/ContextMenu/ContextMenuActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -476,6 +476,8 @@ const ContextMenuActions: ContextMenuAction[] = [
} else if (ReportActionsUtils.isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.INTEGRATION_SYNC_FAILED)) {
const {label, errorMessage} = ReportActionsUtils.getOriginalMessage(reportAction) ?? {label: '', errorMessage: ''};
setClipboardMessage(Localize.translateLocal('report.actions.type.integrationSyncFailed', label, errorMessage));
} else if (ReportActionsUtils.isCardIssuedAction(reportAction)) {
setClipboardMessage(ReportActionsUtils.getCardIssuedMessage(reportAction, true));
} else if (content) {
setClipboardMessage(
content.replace(/(<mention-user>)(.*?)(<\/mention-user>)/gi, (match, openTag: string, innerContent: string, closeTag: string): string => {
Expand Down
10 changes: 9 additions & 1 deletion src/types/onyx/OriginalMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -523,13 +523,20 @@ type OriginalMessageIntegrationSyncFailed = {
};

/**
* Original message for CARD_ISSUED, CARD_MISSING_ADDRESS, and CARD_ISSUED_VIRTUAL actions
* Model of CARD_ISSUED, CARD_MISSING_ADDRESS, and CARD_ISSUED_VIRTUAL actions
*/
type OriginalMessageExpensifyCard = {
/** The id of the user the card was assigned to */
assigneeAccountID: number;
};

/**
* Original message for CARD_ISSUED, CARD_MISSING_ADDRESS, and CARD_ISSUED_VIRTUAL actions
*/
type IssueNewCardOriginalMessage = OriginalMessage<
typeof CONST.REPORT.ACTIONS.TYPE.CARD_MISSING_ADDRESS | typeof CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED | typeof CONST.REPORT.ACTIONS.TYPE.CARD_ISSUED_VIRTUAL
>;

/** The map type of original message */
/* eslint-disable jsdoc/require-jsdoc */
type OriginalMessageMap = {
Expand Down Expand Up @@ -620,4 +627,5 @@ export type {
JoinWorkspaceResolution,
OriginalMessageModifiedExpense,
OriginalMessageExportIntegration,
IssueNewCardOriginalMessage,
};
Loading