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] Implement room mention actionable whisper #41406

Merged
merged 11 commits into from
May 6, 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
5 changes: 5 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -643,6 +643,7 @@ const CONST = {
TYPE: {
ACTIONABLE_JOIN_REQUEST: 'ACTIONABLEJOINREQUEST',
ACTIONABLE_MENTION_WHISPER: 'ACTIONABLEMENTIONWHISPER',
ACTIONABLE_REPORT_MENTION_WHISPER: 'ACTIONABLEREPORTMENTIONWHISPER',
ACTIONABLE_TRACK_EXPENSE_WHISPER: 'ACTIONABLETRACKEXPENSEWHISPER',
ADD_COMMENT: 'ADDCOMMENT',
APPROVED: 'APPROVED',
Expand Down Expand Up @@ -781,6 +782,10 @@ const CONST = {
ACTIONABLE_TRACK_EXPENSE_WHISPER_RESOLUTION: {
NOTHING: 'nothing',
},
ACTIONABLE_REPORT_MENTION_WHISPER_RESOLUTION: {
CREATE: 'created',
NOTHING: 'nothing',
},
ACTIONABLE_MENTION_JOIN_WORKSPACE_RESOLUTION: {
ACCEPT: 'accept',
DECLINE: 'decline',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type {ValueOf} from 'type-fest';
import type CONST from '@src/CONST';

type ResolveActionableReportMentionWhisperParams = {
reportActionID: string;
resolution: ValueOf<typeof CONST.REPORT.ACTIONABLE_REPORT_MENTION_WHISPER_RESOLUTION>;
};

export default ResolveActionableReportMentionWhisperParams;
1 change: 1 addition & 0 deletions src/libs/API/parameters/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export type {default as RequestReplacementExpensifyCardParams} from './RequestRe
export type {default as RequestUnlinkValidationLinkParams} from './RequestUnlinkValidationLinkParams';
export type {default as RequestAccountValidationLinkParams} from './RequestAccountValidationLinkParams';
export type {default as ResolveActionableMentionWhisperParams} from './ResolveActionableMentionWhisperParams';
export type {default as ResolveActionableReportMentionWhisperParams} from './ResolveActionableReportMentionWhisperParams';
export type {default as RevealExpensifyCardDetailsParams} from './RevealExpensifyCardDetailsParams';
export type {default as SearchForReportsParams} from './SearchForReportsParams';
export type {default as SearchForRoomsToMentionParams} from './SearchForRoomsToMentionParams';
Expand Down
2 changes: 2 additions & 0 deletions src/libs/API/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ const WRITE_COMMANDS = {
FLAG_COMMENT: 'FlagComment',
UPDATE_REPORT_PRIVATE_NOTE: 'UpdateReportPrivateNote',
RESOLVE_ACTIONABLE_MENTION_WHISPER: 'ResolveActionableMentionWhisper',
RESOLVE_ACTIONABLE_REPORT_MENTION_WHISPER: 'ResolveActionableReportMentionWhisper',
DELETE_WORKSPACE: 'DeleteWorkspace',
DELETE_MEMBERS_FROM_WORKSPACE: 'DeleteMembersFromWorkspace',
ADD_MEMBERS_TO_WORKSPACE: 'AddMembersToWorkspace',
Expand Down Expand Up @@ -316,6 +317,7 @@ type WriteCommandParameters = {
[WRITE_COMMANDS.FLAG_COMMENT]: Parameters.FlagCommentParams;
[WRITE_COMMANDS.UPDATE_REPORT_PRIVATE_NOTE]: Parameters.UpdateReportPrivateNoteParams;
[WRITE_COMMANDS.RESOLVE_ACTIONABLE_MENTION_WHISPER]: Parameters.ResolveActionableMentionWhisperParams;
[WRITE_COMMANDS.RESOLVE_ACTIONABLE_REPORT_MENTION_WHISPER]: Parameters.ResolveActionableReportMentionWhisperParams;
[WRITE_COMMANDS.CHRONOS_REMOVE_OOO_EVENT]: Parameters.ChronosRemoveOOOEventParams;
[WRITE_COMMANDS.TRANSFER_WALLET_BALANCE]: Parameters.TransferWalletBalanceParams;
[WRITE_COMMANDS.DELETE_WORKSPACE]: Parameters.DeleteWorkspaceParams;
Expand Down
19 changes: 14 additions & 5 deletions src/libs/ReportActionsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import type {
ChangeLog,
IOUMessage,
OriginalMessageActionableMentionWhisper,
OriginalMessageActionableReportMentionWhisper,
OriginalMessageActionableTrackedExpenseWhisper,
OriginalMessageDismissedViolation,
OriginalMessageIOU,
OriginalMessageJoinPolicyChangeLog,
Expand Down Expand Up @@ -1027,6 +1029,14 @@ function isActionableMentionWhisper(reportAction: OnyxEntry<ReportAction>): repo
return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_MENTION_WHISPER;
}

/**
* Checks if a given report action corresponds to an actionable report mention whisper.
* @param reportAction
*/
function isActionableReportMentionWhisper(reportAction: OnyxEntry<ReportAction>): reportAction is ReportActionBase & OriginalMessageActionableReportMentionWhisper {
return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_REPORT_MENTION_WHISPER;
}

/**
* Constructs a message for an actionable mention whisper report action.
* @param reportAction
Expand Down Expand Up @@ -1079,11 +1089,11 @@ function isCurrentActionUnread(report: Report | EmptyObject, reportAction: Repor
* Checks if a given report action corresponds to a join request action.
* @param reportAction
*/
function isActionableJoinRequest(reportAction: OnyxEntry<ReportAction>): boolean {
function isActionableJoinRequest(reportAction: OnyxEntry<ReportAction>): reportAction is ReportActionBase & OriginalMessageJoinPolicyChangeLog {
return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_JOIN_REQUEST;
}

function isActionableTrackExpense(reportAction: OnyxEntry<ReportAction>): boolean {
function isActionableTrackExpense(reportAction: OnyxEntry<ReportAction>): reportAction is ReportActionBase & OriginalMessageActionableTrackedExpenseWhisper {
return reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_TRACK_EXPENSE_WHISPER;
}

Expand All @@ -1093,9 +1103,7 @@ function isActionableTrackExpense(reportAction: OnyxEntry<ReportAction>): boolea
*/
function isActionableJoinRequestPending(reportID: string): boolean {
const sortedReportActions = getSortedReportActions(Object.values(getAllReportActions(reportID)));
const findPendingRequest = sortedReportActions.find(
(reportActionItem) => isActionableJoinRequest(reportActionItem) && (reportActionItem as OriginalMessageJoinPolicyChangeLog)?.originalMessage?.choice === '',
);
const findPendingRequest = sortedReportActions.find((reportActionItem) => isActionableJoinRequest(reportActionItem) && reportActionItem.originalMessage.choice === '');
return !!findPendingRequest;
}

Expand Down Expand Up @@ -1180,6 +1188,7 @@ export {
getMemberChangeMessagePlainText,
isReimbursementDeQueuedAction,
isActionableMentionWhisper,
isActionableReportMentionWhisper,
getActionableMentionWhisperMessage,
isCurrentActionUnread,
isActionableJoinRequest,
Expand Down
47 changes: 47 additions & 0 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import type {
RemoveFromGroupChatParams,
RemoveFromRoomParams,
ResolveActionableMentionWhisperParams,
ResolveActionableReportMentionWhisperParams,
SearchForReportsParams,
SearchForRoomsToMentionParams,
SetNameValuePairParams,
Expand Down Expand Up @@ -3577,6 +3578,51 @@ function resolveActionableMentionWhisper(reportId: string, reportAction: OnyxEnt
API.write(WRITE_COMMANDS.RESOLVE_ACTIONABLE_MENTION_WHISPER, parameters, {optimisticData, failureData});
}

function resolveActionableReportMentionWhisper(
reportId: string,
reportAction: OnyxEntry<ReportAction>,
resolution: ValueOf<typeof CONST.REPORT.ACTIONABLE_REPORT_MENTION_WHISPER_RESOLUTION>,
) {
if (!reportAction) {
return;
}

const optimisticData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportId}`,
value: {
[reportAction.reportActionID]: {
originalMessage: {
resolution,
},
},
},
},
];

const failureData: OnyxUpdate[] = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${reportId}`,
value: {
[reportAction.reportActionID]: {
originalMessage: {
resolution: null,
},
},
},
},
];

const parameters: ResolveActionableReportMentionWhisperParams = {
reportActionID: reportAction.reportActionID,
resolution,
};

API.write(WRITE_COMMANDS.RESOLVE_ACTIONABLE_REPORT_MENTION_WHISPER, parameters, {optimisticData, failureData});
}

function dismissTrackExpenseActionableWhisper(reportID: string, reportAction: OnyxEntry<ReportAction>): void {
const message = reportAction?.message?.[0];
if (!message) {
Expand Down Expand Up @@ -3698,6 +3744,7 @@ export {
deleteReportField,
clearReportFieldErrors,
resolveActionableMentionWhisper,
resolveActionableReportMentionWhisper,
updateRoomVisibility,
dismissTrackExpenseActionableWhisper,
setGroupDraft,
Expand Down
50 changes: 27 additions & 23 deletions src/pages/home/report/ReportActionItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@ import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
import type {OriginalMessageActionableMentionWhisper, OriginalMessageActionableTrackedExpenseWhisper, OriginalMessageJoinPolicyChangeLog} from '@src/types/onyx/OriginalMessage';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import AnimatedEmptyStateBackground from './AnimatedEmptyStateBackground';
import {RestrictedReadOnlyContextMenuActions} from './ContextMenu/ContextMenuActions';
Expand Down Expand Up @@ -210,14 +209,16 @@ function ReportActionItem({
const isReportActionLinked = linkedReportActionID && action.reportActionID && linkedReportActionID === action.reportActionID;
const transactionCurrency = TransactionUtils.getCurrency(transaction);
const reportScrollManager = useReportScrollManager();
const isActionableWhisper =
ReportActionsUtils.isActionableMentionWhisper(action) || ReportActionsUtils.isActionableTrackExpense(action) || ReportActionsUtils.isActionableReportMentionWhisper(action);

const highlightedBackgroundColorIfNeeded = useMemo(
() => (isReportActionLinked ? StyleUtils.getBackgroundColorStyle(theme.messageHighlightBG) : {}),
[StyleUtils, isReportActionLinked, theme.messageHighlightBG],
);

const isDeletedParentAction = ReportActionsUtils.isDeletedParentAction(action);
const prevActionResolution = usePrevious(ReportActionsUtils.isActionableMentionWhisper(action) ? action.originalMessage.resolution : null);
const prevActionResolution = usePrevious(isActionableWhisper ? action.originalMessage.resolution : null);

// IOUDetails only exists when we are sending money
const isSendingMoney = isIOUReport(action) && action.originalMessage.type === CONST.IOU.REPORT_ACTION_TYPE.PAY && action.originalMessage.IOUDetails;
Expand Down Expand Up @@ -360,15 +361,14 @@ function ReportActionItem({
// Handles manual scrolling to the bottom of the chat when the last message is an actionable whisper and it's resolved.
// This fixes an issue where InvertedFlatList fails to auto scroll down and results in an empty space at the bottom of the chat in IOS.
useEffect(() => {
const isActionableWhisper = ReportActionsUtils.isActionableMentionWhisper(action) || ReportActionsUtils.isActionableTrackExpense(action);
if (index !== 0 || !isActionableWhisper) {
return;
}

if (ReportActionsUtils.isActionableMentionWhisper(action) && prevActionResolution !== (action.originalMessage.resolution ?? null)) {
if (prevActionResolution !== (action.originalMessage.resolution ?? null)) {
reportScrollManager.scrollToIndex(index);
}
}, [index, action, prevActionResolution, reportScrollManager]);
}, [index, action, prevActionResolution, reportScrollManager, isActionableWhisper]);

const toggleReaction = useCallback(
(emoji: Emoji) => {
Expand All @@ -389,20 +389,12 @@ function ReportActionItem({
);

const actionableItemButtons: ActionableItem[] = useMemo(() => {
const isWhisperResolution = (action?.originalMessage as OriginalMessageActionableMentionWhisper['originalMessage'])?.resolution !== null;
const isJoinChoice = (action?.originalMessage as OriginalMessageJoinPolicyChangeLog['originalMessage'])?.choice === '';

if (
!(
((ReportActionsUtils.isActionableMentionWhisper(action) || ReportActionsUtils.isActionableTrackExpense(action)) && isWhisperResolution) ||
(ReportActionsUtils.isActionableJoinRequest(action) && isJoinChoice)
)
) {
if (!isActionableWhisper && (!ReportActionsUtils.isActionableJoinRequest(action) || action.originalMessage.choice !== '')) {
return [];
}

if (ReportActionsUtils.isActionableTrackExpense(action)) {
const transactionID = (action?.originalMessage as OriginalMessageActionableTrackedExpenseWhisper['originalMessage'])?.transactionID;
const transactionID = action?.originalMessage?.transactionID;
return [
{
text: 'actionableMentionTrackExpense.submit',
Expand Down Expand Up @@ -454,6 +446,23 @@ function ReportActionItem({
},
];
}

if (ReportActionsUtils.isActionableReportMentionWhisper(action)) {
return [
{
text: 'common.yes',
key: `${action.reportActionID}-actionableReportMentionWhisper-${CONST.REPORT.ACTIONABLE_REPORT_MENTION_WHISPER_RESOLUTION.CREATE}`,
onPress: () => Report.resolveActionableReportMentionWhisper(report.reportID, action, CONST.REPORT.ACTIONABLE_REPORT_MENTION_WHISPER_RESOLUTION.CREATE),
isPrimary: true,
},
{
text: 'common.no',
key: `${action.reportActionID}-actionableReportMentionWhisper-${CONST.REPORT.ACTIONABLE_REPORT_MENTION_WHISPER_RESOLUTION.NOTHING}`,
onPress: () => Report.resolveActionableReportMentionWhisper(report.reportID, action, CONST.REPORT.ACTIONABLE_REPORT_MENTION_WHISPER_RESOLUTION.NOTHING),
},
];
}

return [
{
text: 'actionableMentionWhisperOptions.invite',
Expand All @@ -467,7 +476,7 @@ function ReportActionItem({
onPress: () => Report.resolveActionableMentionWhisper(report.reportID, action, CONST.REPORT.ACTIONABLE_MENTION_WHISPER_RESOLUTION.NOTHING),
},
];
}, [action, report.reportID]);
}, [action, isActionableWhisper, report.reportID]);

const renderThreadDivider = useMemo(
() =>
Expand Down Expand Up @@ -894,13 +903,8 @@ function ReportActionItem({
return null;
}

// if action is actionable mention whisper and resolved by user, then we don't want to render anything
if (ReportActionsUtils.isActionableMentionWhisper(action) && (action.originalMessage.resolution ?? null)) {
return null;
}

// if action is actionable track expense whisper and resolved by user, then we don't want to render anything
if (ReportActionsUtils.isActionableTrackExpense(action) && (action.originalMessage as OriginalMessageActionableTrackedExpenseWhisper['originalMessage']).resolution) {
// If action is actionable whisper and resolved by user, then we don't want to render anything
if (isActionableWhisper && (action.originalMessage.resolution ?? null)) {
return null;
}

Expand Down
16 changes: 16 additions & 0 deletions src/types/onyx/OriginalMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type OriginalMessageActionName =
| 'TASKREOPENED'
| 'ACTIONABLEJOINREQUEST'
| 'ACTIONABLEMENTIONWHISPER'
| 'ACTIONABLEREPORTMENTIONWHISPER'
| 'ACTIONABLETRACKEXPENSEWHISPER'
| ValueOf<typeof CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG>;
type OriginalMessageApproved = {
Expand Down Expand Up @@ -152,6 +153,19 @@ type OriginalMessageActionableMentionWhisper = {
};
};

type OriginalMessageActionableReportMentionWhisper = {
actionName: typeof CONST.REPORT.ACTIONS.TYPE.ACTIONABLE_REPORT_MENTION_WHISPER;
originalMessage: {
reportNames: string[];
mentionedAccountIDs: number[];
reportActionID: number;
reportID: number;
lastModified: string;
resolution?: ValueOf<typeof CONST.REPORT.ACTIONABLE_REPORT_MENTION_WHISPER_RESOLUTION> | null;
whisperedTo?: number[];
};
};

type OriginalMessageSubmitted = {
actionName: typeof CONST.REPORT.ACTIONS.TYPE.SUBMITTED;
originalMessage: unknown;
Expand Down Expand Up @@ -324,6 +338,7 @@ type OriginalMessage =
| OriginalMessageIOU
| OriginalMessageAddComment
| OriginalMessageActionableMentionWhisper
| OriginalMessageActionableReportMentionWhisper
| OriginalMessageSubmitted
| OriginalMessageClosed
| OriginalMessageCreated
Expand Down Expand Up @@ -362,6 +377,7 @@ export type {
OriginalMessageAddComment,
OriginalMessageJoinPolicyChangeLog,
OriginalMessageActionableMentionWhisper,
OriginalMessageActionableReportMentionWhisper,
OriginalMessageChronosOOOList,
OriginalMessageRoomChangeLog,
OriginalMessageSource,
Expand Down
2 changes: 1 addition & 1 deletion src/types/onyx/ReportAction.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ type Message = {
currency?: string;

/** resolution for actionable mention whisper */
resolution?: ValueOf<typeof CONST.REPORT.ACTIONABLE_MENTION_WHISPER_RESOLUTION> | null;
resolution?: ValueOf<typeof CONST.REPORT.ACTIONABLE_MENTION_WHISPER_RESOLUTION> | ValueOf<typeof CONST.REPORT.ACTIONABLE_REPORT_MENTION_WHISPER_RESOLUTION> | null;

/** The time this report action was deleted */
deleted?: string;
Expand Down
Loading