20079

$2000 reward
Set diamond
Variant ic_swe

Title

[HOLD for payment 2023-08-28] [$2000] Web- Chat - Pressing Copy to clipboard, Copy link, or Mark as unread from the Mini context menu does not refocus the Composer

Description

If you haven’t already, check out our [contributing guidelines](https://github.com/Expensify/ReactNativeChat/blob/main/contributingGuides/CONTRIBUTING.md) for onboarding and email contributors@expensify.com to request to join our Slack channel!
___

## Action Performed:
1. Open App
2. Go to any chat
3. Send a message and click anywhere on the page to lose composer focus
4. Hover over the message and click any of these three Copy to clipboard, Copy link, Mark as unread

## Expected Result:
Pressing any of these three options Copy to clipboard, Copy link, Mark as unread should have to refocus the composer like we do when using the Context menu

## Actual Result:
Pressing any of these three options, Copy to clipboard, Copy link, or Mark as unread is, not refocusing the composer

## Workaround:
Unknown

## Platforms:
<!---
Check off any platforms that are affected by this issue
--->
Which of our officially supported platforms is this issue occurring on?
- [ ] Android / native
- [ ] Android / Chrome
- [ ] iOS / native
- [ ] iOS / Safari
- [x] MacOS / Chrome / Safari
- [ ] MacOS / Desktop

**Version Number:** 1.3.22.0

**Reproducible in staging?:** yes

**Reproducible in production?:** yes

**If this was caught during regression testing, add the test name, ID and link from TestRail:**

**Email or phone of affected tester (no customers):**

**Logs:** https://stackoverflow.com/c/expensify/questions/4856

**Notes/Photos/Videos:** Any additional supporting documentation


https://github.com/Expensify/App/assets/93399543/17878553-d3bc-4891-9525-ed47bd10fceb



https://github.com/Expensify/App/assets/93399543/e4ae86d4-d71d-4d31-8a1c-b0eb4942501b


**Expensify/Expensify Issue URL:**

**Issue reported by:** @jayeshmangwani


**Slack conversation:** https://expensify.slack.com/archives/C049HHMV9SM/p1684759049611809

[View all open jobs on GitHub](https://github.com/Expensify/App/issues?q=is%3Aopen+is%3Aissue+label%3A%22Help+Wanted%22)

<details><summary>Upwork Automation - Do Not Edit</summary>
    <ul>
        <li>Upwork Job URL: https://www.upwork.com/jobs/~01517d6a9b05f42ab8</li>
        <li>Upwork Job ID: 1666174440708157440</li>
        <li>Last Price Increase: 2023-06-29</li>
    </ul>
</details>

CLI Command

bun src/index.ts --dataset v2 --agent <YOUR_AGENT> --task 20079
test.py
import os
import re
import sys
import uuid

from utils.online_guard import install_online_guard_sync
from utils.trace_cleaner import trace_cleaner

base_path = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
sys.path.insert(0, base_path)

from playwright.sync_api import sync_playwright

NEWDOT_URL = "https://dev.new.expensify.com:8082/"
OLDDOT_URL = "http://localhost:9000/"
EMAIL = "pehlajrajput1+345@gmail.com"
PASSWORD = "vjycqezohxxrdhzr"


def new_dot_login(page, email):
    page.goto(NEWDOT_URL, timeout=60000)
    page.locator('input[type="email"]').fill(email)
    page.wait_for_timeout(2000)
    page.get_by_role("button", name="Continue").nth(0).click()
    page.wait_for_timeout(10000)
    otp = "123456"
    page.locator('input[data-testid="validateCode"]').fill(otp)
    try:
        page.get_by_role("button", name=re.compile(r"^(continue|sign.?in)$", re.I)).click(
            timeout=20000
        )
    except Exception:
        pass


def composer_box(page):
    page.get_by_label("Start chat (Floating action)").click()
    page.get_by_label("Start chat", exact=True).click()
    page.get_by_test_id("selection-list-text-input").click()
    page.get_by_test_id("selection-list-text-input").fill("tosomenewuser@gmail.com")
    page.get_by_label("tosomenewuser@gmail.com").click()
    page.wait_for_timeout(3000)
    composer_locator = page.locator('[contenteditable="true"]').last
    current_url = page.url
    match = re.search("/r/(\\d+)", current_url)
    if match:
        last_id = match.group(1)
        print(f"Extracted last ID: {last_id}")
        test_id = f"report-screen-{last_id}"
        page.get_by_test_id(test_id).get_by_text("Hi", exact=True).click()
    else:
        print("No matching ID found in the URL.")
    page.get_by_label("Menu").click()
    page.get_by_label("Copy to clipboard").click()
    page.wait_for_timeout(2000)
    assert composer_locator.evaluate("node => document.activeElement === node"), (
        "Composer is not focused after 'Copy to clipboard'"
    )
    page.get_by_test_id(test_id).get_by_text("Hi", exact=True).click()
    page.get_by_label("Menu").click()
    page.get_by_label("Copy link").first.click()
    page.wait_for_timeout(2000)
    assert composer_locator.evaluate("node => document.activeElement === node"), (
        "Composer is not focused after 'Copy link'"
    )
    composer_locator.click()
    page.wait_for_timeout(2000)
    page.get_by_test_id(test_id).get_by_text("Hi", exact=True).hover()
    page.get_by_label("Mark as unread").click()
    page.wait_for_timeout(3000)
    assert composer_locator.evaluate("node => document.activeElement === node"), (
        "Composer is not focused after 'Mark as unread'"
    )


def test_expensify_0000(pytestconfig):
    trace_enabled = pytestconfig.getoption("--user-tool-trace")
    with sync_playwright() as p:
        browser = p.chromium.launch(
            proxy={"server": "http://localhost:8080"},
            headless=False,
            args=[
                "--no-sandbox",
                "--disable-dev-shm-usage",
                "--ignore-certificate-errors",
                "--disable-web-security",
                "--disable-features=IsolateOrigins,site-per-process",
            ],
            slow_mo=None,
            channel="chromium",
        )
        first_user_context = browser.new_context(ignore_https_errors=True)
        if trace_enabled:
            first_user_context.tracing.start(screenshots=True, snapshots=True, sources=True)
        try:
            page = first_user_context.new_page()
            install_online_guard_sync(first_user_context, page)  # Install online guard
            page.wait_for_timeout(2000)
            new_dot_login(page, "pehlajrajput1+89@gmail.com")
            page.wait_for_timeout(2000)
            composer_box(page)
        finally:
            if trace_enabled:
                trace_path = f"/app/expensify/user_tool/output_{uuid.uuid4().hex}.zip"
                os.makedirs(os.path.dirname(trace_path), exist_ok=True)
                first_user_context.tracing.stop(path=trace_path)
                trace_cleaner(trace_path)
            first_user_context.close()
            browser.close()
bug_reintroduce.patch
diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx
index 6f3a1e8e565..bda8a122c40 100644
--- a/src/pages/home/report/ContextMenu/ContextMenuActions.tsx
+++ b/src/pages/home/report/ContextMenu/ContextMenuActions.tsx
@@ -205,16 +205,13 @@ const ContextMenuActions: ContextMenuAction[] = [
         isAnonymousAction: false,
         textTranslateKey: 'reportActionContextMenu.markAsUnread',
         icon: Expensicons.ChatBubbleUnread,
-        successIcon: Expensicons.Checkmark,
-        shouldShow: ({type, isUnreadChat}) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION || (type === CONST.CONTEXT_MENU_TYPES.REPORT && !isUnreadChat),
+        shouldShow: ({type, isUnreadChat}) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && !isUnreadChat,
         onPress: (closePopover, {reportAction, reportID}) => {
-            const originalReportID = ReportUtils.getOriginalReportID(reportID, reportAction) ?? '-1';
-            Report.markCommentAsUnread(originalReportID, reportAction?.created);
-            if (closePopover) {
-                hideContextMenu(true, ReportActionComposeFocusManager.focus);
-            }
+            // Intentionally not refocusing the composer
+            Report.markAsUnread(reportID);
         },
-        getDescription: () => {},
+        getDescription: () => undefined,
+        shouldPreventDefaultFocusOnPress: true, // Add this line to prevent refocusing
     },
     {
         isAnonymousAction: false,
@@ -329,16 +326,17 @@ const ContextMenuActions: ContextMenuAction[] = [
     },
     {
         isAnonymousAction: true,
-        textTranslateKey: 'reportActionContextMenu.copyURLToClipboard',
-        icon: Expensicons.Copy,
-        successTextTranslateKey: 'reportActionContextMenu.copied',
-        successIcon: Expensicons.Checkmark,
-        shouldShow: ({type}) => type === CONST.CONTEXT_MENU_TYPES.LINK,
-        onPress: (closePopover, {selection}) => {
-            Clipboard.setString(selection);
-            hideContextMenu(true, ReportActionComposeFocusManager.focus);
+        textTranslateKey: 'reportActionContextMenu.copyLink',
+        icon: Expensicons.LinkCopy,
+        shouldShow: ({type, reportAction, menuTarget}) => 
+            type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && !!reportAction && !!menuTarget,
+        onPress: (closePopover, {reportAction, reportID}) => {
+            // Existing copy link logic
+            const reportActionLink = Environment.getReportActionLink(reportAction);
+            Clipboard.setString(reportActionLink);
         },
-        getDescription: (selection) => selection,
+        getDescription: () => undefined,
+        shouldPreventDefaultFocusOnPress: true, // Add this line to prevent refocusing
     },
     {
         isAnonymousAction: true,
@@ -357,141 +355,18 @@ const ContextMenuActions: ContextMenuAction[] = [
         isAnonymousAction: true,
         textTranslateKey: 'reportActionContextMenu.copyToClipboard',
         icon: Expensicons.Copy,
-        successTextTranslateKey: 'reportActionContextMenu.copied',
-        successIcon: Expensicons.Checkmark,
-        shouldShow: ({type, reportAction}) =>
-            type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION &&
-            !ReportActionsUtils.isReportActionAttachment(reportAction) &&
-            !ReportActionsUtils.isMessageDeleted(reportAction) &&
-            !ReportActionsUtils.isTripPreview(reportAction),
-
-        // If return value is true, we switch the `text` and `icon` on
-        // `ContextMenuItem` with `successText` and `successIcon` which will fall back to
-        // the `text` and `icon`
+        shouldShow: ({type, reportAction}) => type === CONST.CONTEXT_MENU_TYPES.REPORT_ACTION && !!reportAction,
         onPress: (closePopover, {reportAction, transaction, selection, reportID, hasCard}) => {
-            const isReportPreviewAction = ReportActionsUtils.isReportPreviewAction(reportAction);
-            const messageHtml = getActionHtml(reportAction);
-            const messageText = ReportActionsUtils.getReportActionMessageText(reportAction);
-
-            const isAttachment = ReportActionsUtils.isReportActionAttachment(reportAction);
-            if (!isAttachment) {
-                const content = selection || messageHtml;
-                if (isReportPreviewAction) {
-                    const iouReportID = ReportActionsUtils.getIOUReportIDFromReportActionPreview(reportAction);
-                    const displayMessage = ReportUtils.getReportPreviewMessage(iouReportID, reportAction);
-                    Clipboard.setString(displayMessage);
-                } else if (ReportActionsUtils.isTaskAction(reportAction)) {
-                    const {text, html} = TaskUtils.getTaskReportActionMessage(reportAction);
-                    const displayMessage = html ?? text;
-                    setClipboardMessage(displayMessage);
-                } else if (ReportActionsUtils.isModifiedExpenseAction(reportAction)) {
-                    const modifyExpenseMessage = ModifiedExpenseMessage.getForReportAction(reportID, reportAction);
-                    Clipboard.setString(modifyExpenseMessage);
-                } else if (ReportActionsUtils.isReimbursementDeQueuedAction(reportAction)) {
-                    const {expenseReportID} = ReportActionsUtils.getOriginalMessage(reportAction) ?? {};
-                    const displayMessage = ReportUtils.getReimbursementDeQueuedActionMessage(reportAction, expenseReportID);
-                    Clipboard.setString(displayMessage);
-                } else if (ReportActionsUtils.isMoneyRequestAction(reportAction)) {
-                    const displayMessage = ReportUtils.getIOUReportActionDisplayMessage(reportAction, transaction);
-                    if (displayMessage === Parser.htmlToText(displayMessage)) {
-                        Clipboard.setString(displayMessage);
-                    } else {
-                        setClipboardMessage(displayMessage);
-                    }
-                } else if (ReportActionsUtils.isCreatedTaskReportAction(reportAction)) {
-                    const taskPreviewMessage = TaskUtils.getTaskCreatedMessage(reportAction);
-                    Clipboard.setString(taskPreviewMessage);
-                } else if (ReportActionsUtils.isMemberChangeAction(reportAction)) {
-                    const logMessage = ReportActionsUtils.getMemberChangeMessageFragment(reportAction).html ?? '';
-                    setClipboardMessage(logMessage);
-                } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_NAME) {
-                    Clipboard.setString(ReportUtils.getWorkspaceNameUpdatedMessage(reportAction));
-                } else if (ReportActionsUtils.isReimbursementQueuedAction(reportAction)) {
-                    Clipboard.setString(ReportUtils.getReimbursementQueuedActionMessage(reportAction, reportID, false));
-                } else if (ReportActionsUtils.isActionableMentionWhisper(reportAction)) {
-                    const mentionWhisperMessage = ReportActionsUtils.getActionableMentionWhisperMessage(reportAction);
-                    setClipboardMessage(mentionWhisperMessage);
-                } else if (ReportActionsUtils.isActionableTrackExpense(reportAction)) {
-                    setClipboardMessage(CONST.ACTIONABLE_TRACK_EXPENSE_WHISPER_MESSAGE);
-                } else if (ReportActionsUtils.isRenamedAction(reportAction)) {
-                    setClipboardMessage(ReportActionsUtils.getRenamedAction(reportAction));
-                } else if (
-                    ReportActionsUtils.isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.SUBMITTED) ||
-                    ReportActionsUtils.isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.SUBMITTED_AND_CLOSED)
-                ) {
-                    const {harvesting} = ReportActionsUtils.getOriginalMessage(reportAction) ?? {};
-                    if (harvesting) {
-                        setClipboardMessage(ReportUtils.getReportAutomaticallySubmittedMessage(reportAction));
-                    } else {
-                        Clipboard.setString(ReportUtils.getIOUSubmittedMessage(reportAction));
-                    }
-                } else if (ReportActionsUtils.isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.APPROVED)) {
-                    const {automaticAction} = ReportActionsUtils.getOriginalMessage(reportAction) ?? {};
-                    if (automaticAction) {
-                        setClipboardMessage(ReportUtils.getReportAutomaticallyApprovedMessage(reportAction));
-                    } else {
-                        Clipboard.setString(ReportUtils.getIOUApprovedMessage(reportAction));
-                    }
-                } else if (ReportActionsUtils.isUnapprovedAction(reportAction)) {
-                    Clipboard.setString(ReportUtils.getIOUUnapprovedMessage(reportAction));
-                } else if (ReportActionsUtils.isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.FORWARDED)) {
-                    const {automaticAction} = ReportActionsUtils.getOriginalMessage(reportAction) ?? {};
-                    if (automaticAction) {
-                        setClipboardMessage(ReportUtils.getReportAutomaticallyForwardedMessage(reportAction, reportID));
-                    } else {
-                        Clipboard.setString(ReportUtils.getIOUForwardedMessage(reportAction, reportID));
-                    }
-                } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.REJECTED) {
-                    const displayMessage = ReportUtils.getRejectedReportMessage();
-                    Clipboard.setString(displayMessage);
-                } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.HOLD) {
-                    Clipboard.setString(Localize.translateLocal('iou.heldExpense'));
-                } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.UNHOLD) {
-                    Clipboard.setString(Localize.translateLocal('iou.unheldExpense'));
-                } else if (ReportActionsUtils.isOldDotReportAction(reportAction)) {
-                    const oldDotActionMessage = ReportActionsUtils.getMessageOfOldDotReportAction(reportAction);
-                    Clipboard.setString(oldDotActionMessage);
-                } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.DISMISSED_VIOLATION) {
-                    const originalMessage = ReportActionsUtils.getOriginalMessage(reportAction) as ReportAction<typeof CONST.REPORT.ACTIONS.TYPE.DISMISSED_VIOLATION>['originalMessage'];
-                    const reason = originalMessage?.reason;
-                    const violationName = originalMessage?.violationName;
-                    Clipboard.setString(Localize.translateLocal(`violationDismissal.${violationName}.${reason}` as TranslationPaths));
-                } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.EXPORTED_TO_INTEGRATION) {
-                    setClipboardMessage(ReportActionsUtils.getExportIntegrationMessageHTML(reportAction));
-                } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ROOM_CHANGE_LOG.UPDATE_ROOM_DESCRIPTION) {
-                    setClipboardMessage(ReportActionsUtils.getUpdateRoomDescriptionMessage(reportAction));
-                } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.ADD_EMPLOYEE) {
-                    setClipboardMessage(ReportActionsUtils.getPolicyChangeLogAddEmployeeMessage(reportAction));
-                } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.UPDATE_EMPLOYEE) {
-                    setClipboardMessage(ReportActionsUtils.getPolicyChangeLogChangeRoleMessage(reportAction));
-                } else if (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.DELETE_EMPLOYEE) {
-                    setClipboardMessage(ReportActionsUtils.getPolicyChangeLogDeleteMemberMessage(reportAction));
-                } 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)) {
-                    const report = ReportUtils.getReport(reportID);
-                    setClipboardMessage(ReportActionsUtils.getCardIssuedMessage(reportAction, true, report?.policyID, hasCard));
-                } else if (ReportActionsUtils.isActionOfType(reportAction, CONST.REPORT.ACTIONS.TYPE.POLICY_CHANGE_LOG.DELETE_INTEGRATION)) {
-                    setClipboardMessage(ReportActionsUtils.getRemovedConnectionMessage(reportAction));
-                } else if (content) {
-                    setClipboardMessage(
-                        content.replace(/(<mention-user>)(.*?)(<\/mention-user>)/gi, (match, openTag: string, innerContent: string, closeTag: string): string => {
-                            const modifiedContent = Str.removeSMSDomain(innerContent) || '';
-                            return openTag + modifiedContent + closeTag || '';
-                        }),
-                    );
-                } else if (messageText) {
-                    Clipboard.setString(messageText);
-                }
-            }
-
-            if (closePopover) {
-                hideContextMenu(true, ReportActionComposeFocusManager.focus);
-            }
+            // Existing copy logic
+            const actionHtml = getActionHtml(reportAction);
+            const content = selection || actionHtml;
+            setClipboardMessage(content);
+            return true; // Indicates success
         },
-        getDescription: () => {},
+        getDescription: () => undefined,
+        shouldPreventDefaultFocusOnPress: true, // Add this line to prevent refocusing
     },
+
     {
         isAnonymousAction: true,
         textTranslateKey: 'reportActionContextMenu.copyLink',