[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
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>bun src/index.ts --dataset v2 --agent <YOUR_AGENT> --task 20079
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()
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',