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

feat: Build optimistic Edit Task actions #36768

Merged
merged 17 commits into from
Mar 7, 2024
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
10 changes: 7 additions & 3 deletions src/components/ReportActionItem/TaskAction.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
import React from 'react';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import RenderHTML from '@components/RenderHTML';
import Text from '@components/Text';
import useThemeStyles from '@hooks/useThemeStyles';
import * as TaskUtils from '@libs/TaskUtils';
import type {ReportAction} from '@src/types/onyx';

type TaskActionProps = {
/** Name of the reportAction action */
actionName: string;
action: OnyxEntry<ReportAction>;
};

function TaskAction({actionName}: TaskActionProps) {
function TaskAction({action}: TaskActionProps) {
const styles = useThemeStyles();
const message = TaskUtils.getTaskReportActionMessage(action);

return (
<View style={[styles.flex1, styles.flexRow, styles.alignItemsCenter]}>
<Text style={[styles.chatItemMessage, styles.colorMuted]}>{TaskUtils.getTaskReportActionMessage(actionName)}</Text>
{message.html ? <RenderHTML html={`<muted-text>${message.html}</muted-text>`} /> : <Text style={[styles.chatItemMessage, styles.colorMuted]}>{message.text}</Text>}
</View>
);
}
Expand Down
9 changes: 2 additions & 7 deletions src/libs/OptionsListUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,6 @@ function getLastMessageTextForReport(report: OnyxEntry<Report>, lastActorDetails
// some types of actions are filtered out for lastReportAction, in some cases we need to check the actual last action
const lastOriginalReportAction = lastReportActions[report?.reportID ?? ''] ?? null;
let lastMessageTextFromReport = '';
const lastActionName = lastReportAction?.actionName ?? '';

if (ReportUtils.isArchivedRoom(report)) {
const archiveReason =
Expand Down Expand Up @@ -584,12 +583,8 @@ function getLastMessageTextForReport(report: OnyxEntry<Report>, lastActorDetails
} else if (ReportActionUtils.isModifiedExpenseAction(lastReportAction)) {
const properSchemaForModifiedExpenseMessage = ModifiedExpenseMessage.getForReportAction(report?.reportID, lastReportAction);
lastMessageTextFromReport = ReportUtils.formatReportLastMessageText(properSchemaForModifiedExpenseMessage, true);
} else if (
lastActionName === CONST.REPORT.ACTIONS.TYPE.TASKCOMPLETED ||
lastActionName === CONST.REPORT.ACTIONS.TYPE.TASKREOPENED ||
lastActionName === CONST.REPORT.ACTIONS.TYPE.TASKCANCELLED
) {
lastMessageTextFromReport = lastReportAction?.message?.[0].text ?? '';
} else if (ReportActionUtils.isTaskAction(lastReportAction)) {
lastMessageTextFromReport = TaskUtils.getTaskReportActionMessage(lastReportAction).text;
Copy link
Contributor

Choose a reason for hiding this comment

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

This line will introduce the regression #37976, causing the last message text to become multiple lines in the LHN. So I think in the future whenever we develop a feature that relate to text, we should also test the text overflow case.

} else if (ReportActionUtils.isCreatedTaskReportAction(lastReportAction)) {
lastMessageTextFromReport = TaskUtils.getTaskCreatedMessage(lastReportAction);
} else if (ReportActionUtils.isApprovedOrSubmittedReportAction(lastReportAction)) {
Expand Down
7 changes: 2 additions & 5 deletions src/libs/ReportActionsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -382,10 +382,6 @@ function shouldReportActionBeVisible(reportAction: OnyxEntry<ReportAction>, key:
return false;
}

if (reportAction.actionName === CONST.REPORT.ACTIONS.TYPE.TASKEDITED) {
return false;
}

// Filter out any unsupported reportAction types
if (!supportedActionTypes.includes(reportAction.actionName)) {
return false;
Expand Down Expand Up @@ -675,7 +671,8 @@ function isTaskAction(reportAction: OnyxEntry<ReportAction>): boolean {
return (
reportActionName === CONST.REPORT.ACTIONS.TYPE.TASKCOMPLETED ||
reportActionName === CONST.REPORT.ACTIONS.TYPE.TASKCANCELLED ||
reportActionName === CONST.REPORT.ACTIONS.TYPE.TASKREOPENED
reportActionName === CONST.REPORT.ACTIONS.TYPE.TASKREOPENED ||
reportActionName === CONST.REPORT.ACTIONS.TYPE.TASKEDITED
);
}

Expand Down
107 changes: 75 additions & 32 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import type {
ReportAction,
ReportMetadata,
Session,
Task,
Transaction,
TransactionViolation,
} from '@src/types/onyx';
Expand Down Expand Up @@ -515,6 +516,14 @@ Onyx.connect({
},
});

function getCurrentUserAvatarOrDefault(): UserUtils.AvatarSource {
return currentUserPersonalDetails?.avatar ?? UserUtils.getDefaultAvatarURL(currentUserAccountID);
}

function getCurrentUserDisplayNameOrEmail(): string | undefined {
return currentUserPersonalDetails?.displayName ?? currentUserEmail;
}

function getChatType(report: OnyxEntry<Report> | Participant | EmptyObject): ValueOf<typeof CONST.REPORT.CHAT_TYPE> | undefined {
return report?.chatType;
}
Expand Down Expand Up @@ -1834,7 +1843,7 @@ function buildOptimisticCancelPaymentReportAction(expenseReportID: string, amoun
person: [
{
style: 'strong',
text: currentUserPersonalDetails?.displayName ?? currentUserEmail,
text: getCurrentUserDisplayNameOrEmail(),
type: 'TEXT',
},
],
Expand Down Expand Up @@ -3171,14 +3180,14 @@ function buildOptimisticIOUReportAction(
actionName: CONST.REPORT.ACTIONS.TYPE.IOU,
actorAccountID: currentUserAccountID,
automatic: false,
avatar: currentUserPersonalDetails?.avatar ?? UserUtils.getDefaultAvatarURL(currentUserAccountID),
avatar: getCurrentUserAvatarOrDefault(),
isAttachment: false,
originalMessage,
message: getIOUReportActionMessage(iouReportID, type, amount, comment, currency, paymentType, isSettlingUp),
person: [
{
style: 'strong',
text: currentUserPersonalDetails?.displayName ?? currentUserEmail,
text: getCurrentUserDisplayNameOrEmail(),
type: 'TEXT',
},
],
Expand All @@ -3204,14 +3213,14 @@ function buildOptimisticApprovedReportAction(amount: number, currency: string, e
actionName: CONST.REPORT.ACTIONS.TYPE.APPROVED,
actorAccountID: currentUserAccountID,
automatic: false,
avatar: currentUserPersonalDetails?.avatar ?? UserUtils.getDefaultAvatarURL(currentUserAccountID),
avatar: getCurrentUserAvatarOrDefault(),
isAttachment: false,
originalMessage,
message: getIOUReportActionMessage(expenseReportID, CONST.REPORT.ACTIONS.TYPE.APPROVED, Math.abs(amount), '', currency),
person: [
{
style: 'strong',
text: currentUserPersonalDetails?.displayName ?? currentUserEmail,
text: getCurrentUserDisplayNameOrEmail(),
type: 'TEXT',
},
],
Expand Down Expand Up @@ -3246,14 +3255,14 @@ function buildOptimisticMovedReportAction(fromPolicyID: string, toPolicyID: stri
actionName: CONST.REPORT.ACTIONS.TYPE.MOVED,
actorAccountID: currentUserAccountID,
automatic: false,
avatar: currentUserPersonalDetails?.avatar ?? UserUtils.getDefaultAvatarURL(currentUserAccountID),
avatar: getCurrentUserAvatarOrDefault(),
isAttachment: false,
originalMessage,
message: movedActionMessage,
person: [
{
style: 'strong',
text: currentUserPersonalDetails?.displayName ?? currentUserEmail,
text: getCurrentUserDisplayNameOrEmail(),
type: 'TEXT',
},
],
Expand All @@ -3279,14 +3288,14 @@ function buildOptimisticSubmittedReportAction(amount: number, currency: string,
actionName: CONST.REPORT.ACTIONS.TYPE.SUBMITTED,
actorAccountID: currentUserAccountID,
automatic: false,
avatar: currentUserPersonalDetails?.avatar ?? UserUtils.getDefaultAvatar(currentUserAccountID),
avatar: getCurrentUserAvatarOrDefault(),
isAttachment: false,
originalMessage,
message: getIOUReportActionMessage(expenseReportID, CONST.REPORT.ACTIONS.TYPE.SUBMITTED, Math.abs(amount), '', currency),
person: [
{
style: 'strong',
text: currentUserPersonalDetails?.displayName ?? currentUserEmail,
text: getCurrentUserDisplayNameOrEmail(),
type: 'TEXT',
},
],
Expand Down Expand Up @@ -3352,7 +3361,7 @@ function buildOptimisticModifiedExpenseReportAction(
actionName: CONST.REPORT.ACTIONS.TYPE.MODIFIEDEXPENSE,
actorAccountID: currentUserAccountID,
automatic: false,
avatar: currentUserPersonalDetails?.avatar ?? UserUtils.getDefaultAvatarURL(currentUserAccountID),
avatar: getCurrentUserAvatarOrDefault(),
created: DateUtils.getDBTime(),
isAttachment: false,
message: [
Expand Down Expand Up @@ -3435,7 +3444,7 @@ function buildOptimisticTaskReportAction(taskReportID: string, actionName: Origi
actionName,
actorAccountID: currentUserAccountID,
automatic: false,
avatar: currentUserPersonalDetails?.avatar ?? UserUtils.getDefaultAvatarURL(currentUserAccountID),
avatar: getCurrentUserAvatarOrDefault(),
isAttachment: false,
originalMessage,
message: [
Expand Down Expand Up @@ -3511,10 +3520,6 @@ function buildOptimisticChatReport(
};
}

function getCurrentUserAvatarOrDefault(): UserUtils.AvatarSource {
return allPersonalDetails?.[currentUserAccountID ?? '']?.avatar ?? UserUtils.getDefaultAvatarURL(currentUserAccountID);
}

/**
* Returns the necessary reportAction onyx data to indicate that the chat has been created optimistically
* @param [created] - Action created time
Expand All @@ -3541,7 +3546,7 @@ function buildOptimisticCreatedReportAction(emailCreatingAction: string, created
{
type: CONST.REPORT.MESSAGE.TYPE.TEXT,
style: 'strong',
text: allPersonalDetails?.[currentUserAccountID ?? '']?.displayName ?? currentUserEmail,
text: getCurrentUserDisplayNameOrEmail(),
},
],
automatic: false,
Expand Down Expand Up @@ -3577,7 +3582,7 @@ function buildOptimisticRenamedRoomReportAction(newName: string, oldName: string
{
type: CONST.REPORT.MESSAGE.TYPE.TEXT,
style: 'strong',
text: allPersonalDetails?.[currentUserAccountID ?? '']?.displayName ?? currentUserEmail,
text: getCurrentUserDisplayNameOrEmail(),
},
],
originalMessage: {
Expand Down Expand Up @@ -3618,11 +3623,11 @@ function buildOptimisticHoldReportAction(comment: string, created = DateUtils.ge
{
type: CONST.REPORT.MESSAGE.TYPE.TEXT,
style: 'strong',
text: allPersonalDetails?.[currentUserAccountID ?? '']?.displayName ?? currentUserEmail,
text: getCurrentUserDisplayNameOrEmail(),
},
],
automatic: false,
avatar: allPersonalDetails?.[currentUserAccountID ?? '']?.avatar ?? UserUtils.getDefaultAvatarURL(currentUserAccountID),
avatar: getCurrentUserAvatarOrDefault(),
created,
shouldShow: true,
};
Expand All @@ -3649,42 +3654,79 @@ function buildOptimisticUnHoldReportAction(created = DateUtils.getDBTime()): Opt
{
type: CONST.REPORT.MESSAGE.TYPE.TEXT,
style: 'normal',
text: allPersonalDetails?.[currentUserAccountID ?? '']?.displayName ?? currentUserEmail,
text: getCurrentUserDisplayNameOrEmail(),
},
],
automatic: false,
avatar: allPersonalDetails?.[currentUserAccountID ?? '']?.avatar ?? UserUtils.getDefaultAvatarURL(currentUserAccountID),
avatar: getCurrentUserAvatarOrDefault(),
created,
shouldShow: true,
};
}

/**
* Returns the necessary reportAction onyx data to indicate that a task report has been edited
*/
function buildOptimisticEditedTaskReportAction(emailEditingTask: string): OptimisticEditedTaskReportAction {
function buildOptimisticEditedTaskFieldReportAction({title, description}: Task): OptimisticEditedTaskReportAction {
// We do not modify title & description in one request, so we need to create a different optimistic action for each field modification
let field = '';
let value = '';
if (title !== undefined) {
field = 'task title';
value = title;
} else if (description !== undefined) {
field = 'description';
value = description;
}

let changelog = 'edited this task';
if (field && value) {
changelog = `updated the ${field} to ${value}`;
} else if (field) {
changelog = `removed the ${field}`;
}

return {
reportActionID: NumberUtils.rand64(),
actionName: CONST.REPORT.ACTIONS.TYPE.TASKEDITED,
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
actorAccountID: currentUserAccountID,
message: [
{
type: CONST.REPORT.MESSAGE.TYPE.COMMENT,
text: changelog,
html: changelog,
},
],
person: [
{
type: CONST.REPORT.MESSAGE.TYPE.TEXT,
style: 'strong',
text: emailEditingTask,
text: getCurrentUserDisplayNameOrEmail(),
},
],
automatic: false,
avatar: getCurrentUserAvatarOrDefault(),
created: DateUtils.getDBTime(),
shouldShow: false,
};
}

function buildOptimisticChangedTaskAssigneeReportAction(assigneeAccountID: number): OptimisticEditedTaskReportAction {
return {
reportActionID: NumberUtils.rand64(),
actionName: CONST.REPORT.ACTIONS.TYPE.TASKEDITED,
pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
actorAccountID: currentUserAccountID,
message: [
{
type: CONST.REPORT.MESSAGE.TYPE.TEXT,
style: 'normal',
text: ' edited this task',
type: CONST.REPORT.MESSAGE.TYPE.COMMENT,
text: `assigned to ${getDisplayNameForParticipant(assigneeAccountID)}`,
html: `assigned to <mention-user accountID=${assigneeAccountID}></mention-user>`,
Copy link
Contributor

Choose a reason for hiding this comment

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

This line created a bug where LHN won't show the user. See more details on #45167

},
],
person: [
{
type: CONST.REPORT.MESSAGE.TYPE.TEXT,
style: 'strong',
text: allPersonalDetails?.[currentUserAccountID ?? '']?.displayName ?? currentUserEmail,
text: getCurrentUserDisplayNameOrEmail(),
},
],
automatic: false,
Expand Down Expand Up @@ -3727,7 +3769,7 @@ function buildOptimisticClosedReportAction(emailClosingReport: string, policyNam
{
type: CONST.REPORT.MESSAGE.TYPE.TEXT,
style: 'strong',
text: allPersonalDetails?.[currentUserAccountID ?? '']?.displayName ?? currentUserEmail,
text: getCurrentUserDisplayNameOrEmail(),
},
],
reportActionID: NumberUtils.rand64(),
Expand Down Expand Up @@ -5183,7 +5225,8 @@ export {
buildOptimisticClosedReportAction,
buildOptimisticCreatedReportAction,
buildOptimisticRenamedRoomReportAction,
buildOptimisticEditedTaskReportAction,
buildOptimisticEditedTaskFieldReportAction,
buildOptimisticChangedTaskAssigneeReportAction,
buildOptimisticIOUReport,
buildOptimisticApprovedReportAction,
buildOptimisticMovedReportAction,
Expand Down
2 changes: 1 addition & 1 deletion src/libs/SidebarUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ function getOptionData({
const newName = lastAction?.originalMessage?.newName ?? '';
result.alternateText = Localize.translate(preferredLocale, 'newRoomPage.roomRenamedTo', {newName});
} else if (ReportActionsUtils.isTaskAction(lastAction)) {
result.alternateText = TaskUtils.getTaskReportActionMessage(lastAction.actionName);
result.alternateText = TaskUtils.getTaskReportActionMessage(lastAction).text;
} else if (
lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.INVITE_TO_ROOM ||
lastAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOMCHANGELOG.REMOVE_FROM_ROOM ||
Expand Down
18 changes: 12 additions & 6 deletions src/libs/TaskUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import Onyx from 'react-native-onyx';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Report} from '@src/types/onyx';
import type {Message} from '@src/types/onyx/ReportAction';
import type ReportAction from '@src/types/onyx/ReportAction';
import * as CollectionUtils from './CollectionUtils';
import * as Localize from './Localize';
Expand All @@ -22,16 +23,21 @@ Onyx.connect({
/**
* Given the Task reportAction name, return the appropriate message to be displayed and copied to clipboard.
*/
function getTaskReportActionMessage(actionName: string): string {
switch (actionName) {
function getTaskReportActionMessage(action: OnyxEntry<ReportAction>): Pick<Message, 'text' | 'html'> {
switch (action?.actionName) {
case CONST.REPORT.ACTIONS.TYPE.TASKCOMPLETED:
return Localize.translateLocal('task.messages.completed');
return {text: Localize.translateLocal('task.messages.completed')};
case CONST.REPORT.ACTIONS.TYPE.TASKCANCELLED:
return Localize.translateLocal('task.messages.canceled');
return {text: Localize.translateLocal('task.messages.canceled')};
case CONST.REPORT.ACTIONS.TYPE.TASKREOPENED:
return Localize.translateLocal('task.messages.reopened');
return {text: Localize.translateLocal('task.messages.reopened')};
case CONST.REPORT.ACTIONS.TYPE.TASKEDITED:
return {
text: action?.message?.[0].text ?? '',
html: action?.message?.[0].html,
};
default:
return Localize.translateLocal('task.task');
return {text: Localize.translateLocal('task.task')};
}
}

Expand Down
Loading
Loading