From 54498cb55767f1b9e5f248b47f56126bd15d95ee Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 10 Nov 2023 15:37:05 +0700 Subject: [PATCH] fix: 29978 User mention in the system message after inviting member to the room cannot be copied --- src/components/Composer/index.js | 5 ++--- src/hooks/useCopySelectionHelper.ts | 7 +++---- src/libs/PersonalDetailsUtils.js | 6 +++++- src/libs/ReportUtils.js | 4 ++-- src/libs/actions/Report.js | 5 +++-- src/libs/parser.ts | 14 ++++++++++++++ src/pages/PrivateNotes/PrivateNotesEditPage.js | 7 +++---- src/pages/ReportWelcomeMessagePage.js | 5 ++--- .../home/report/ContextMenu/ContextMenuActions.js | 7 +++---- .../home/report/ReportActionItemMessageEdit.js | 5 ++--- 10 files changed, 39 insertions(+), 26 deletions(-) create mode 100644 src/libs/parser.ts diff --git a/src/components/Composer/index.js b/src/components/Composer/index.js index d5d905f7d639..d945f1bc9cc9 100755 --- a/src/components/Composer/index.js +++ b/src/components/Composer/index.js @@ -1,4 +1,3 @@ -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import PropTypes from 'prop-types'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {flushSync} from 'react-dom'; @@ -15,6 +14,7 @@ import * as ComposerUtils from '@libs/ComposerUtils'; import updateIsFullComposerAvailable from '@libs/ComposerUtils/updateIsFullComposerAvailable'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import isEnterWhileComposition from '@libs/KeyboardShortcut/isEnterWhileComposition'; +import {htmlToMarkdown} from '@libs/parser'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import styles from '@styles/styles'; import * as StyleUtils from '@styles/StyleUtils'; @@ -251,8 +251,7 @@ function Composer({ */ const handlePastedHTML = useCallback( (html) => { - const parser = new ExpensiMark(); - paste(parser.htmlToMarkdown(html)); + paste(htmlToMarkdown(html)); }, [paste], ); diff --git a/src/hooks/useCopySelectionHelper.ts b/src/hooks/useCopySelectionHelper.ts index be7830dc6170..4a2fc24f2710 100644 --- a/src/hooks/useCopySelectionHelper.ts +++ b/src/hooks/useCopySelectionHelper.ts @@ -1,7 +1,7 @@ -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import {useEffect} from 'react'; import Clipboard from '@libs/Clipboard'; import KeyboardShortcut from '@libs/KeyboardShortcut'; +import {htmlToMarkdown, htmlToText} from '@libs/parser'; import SelectionScraper from '@libs/SelectionScraper'; import CONST from '@src/CONST'; @@ -10,12 +10,11 @@ function copySelectionToClipboard() { if (!selection) { return; } - const parser = new ExpensiMark(); if (!Clipboard.canSetHtml()) { - Clipboard.setString(parser.htmlToMarkdown(selection)); + Clipboard.setString(htmlToMarkdown(selection)); return; } - Clipboard.setHtml(selection, parser.htmlToText(selection)); + Clipboard.setHtml(selection, htmlToText(selection)); } export default function useCopySelectionHelper() { diff --git a/src/libs/PersonalDetailsUtils.js b/src/libs/PersonalDetailsUtils.js index e346dcde4fc4..bacc3d8ebaed 100644 --- a/src/libs/PersonalDetailsUtils.js +++ b/src/libs/PersonalDetailsUtils.js @@ -177,4 +177,8 @@ function getFormattedAddress(privatePersonalDetails) { return formattedAddress.trim().replace(/,$/, ''); } -export {getDisplayNameOrDefault, getPersonalDetailsByIDs, getAccountIDsByLogins, getLoginsByAccountIDs, getNewPersonalDetailsOnyxData, getFormattedAddress}; +function getAllPersonalDetails() { + return allPersonalDetails; +} + +export {getDisplayNameOrDefault, getPersonalDetailsByIDs, getAccountIDsByLogins, getLoginsByAccountIDs, getNewPersonalDetailsOnyxData, getFormattedAddress, getAllPersonalDetails}; diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index ba95961e983b..aacdb04d3764 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -18,6 +18,7 @@ import * as Localize from './Localize'; import linkingConfig from './Navigation/linkingConfig'; import Navigation from './Navigation/Navigation'; import * as NumberUtils from './NumberUtils'; +import * as Parser from './parser'; import Permissions from './Permissions'; import * as ReportActionsUtils from './ReportActionsUtils'; import * as TransactionUtils from './TransactionUtils'; @@ -2255,14 +2256,13 @@ function getParsedComment(text) { * @returns {Object} */ function buildOptimisticAddCommentReportAction(text, file) { - const parser = new ExpensiMark(); const commentText = getParsedComment(text); const isAttachment = _.isEmpty(text) && file !== undefined; const attachmentInfo = isAttachment ? file : {}; const htmlForNewComment = isAttachment ? CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML : commentText; // Remove HTML from text when applying optimistic offline comment - const textForNewComment = isAttachment ? CONST.ATTACHMENT_MESSAGE_TEXT : parser.htmlToText(htmlForNewComment); + const textForNewComment = isAttachment ? CONST.ATTACHMENT_MESSAGE_TEXT : Parser.htmlToText(htmlForNewComment); return { commentText, diff --git a/src/libs/actions/Report.js b/src/libs/actions/Report.js index a10e7e01da03..85c08db89128 100644 --- a/src/libs/actions/Report.js +++ b/src/libs/actions/Report.js @@ -16,6 +16,7 @@ import * as ErrorUtils from '@libs/ErrorUtils'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; import LocalNotification from '@libs/Notification/LocalNotification'; +import {htmlToMarkdown, htmlToText} from '@libs/parser'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import * as Pusher from '@libs/Pusher/pusher'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; @@ -1236,7 +1237,7 @@ function editReportComment(reportID, originalReportAction, textForNewComment) { // https://github.com/Expensify/App/issues/9090 // https://github.com/Expensify/App/issues/13221 const originalCommentHTML = lodashGet(originalReportAction, 'message[0].html'); - const originalCommentMarkdown = parser.htmlToMarkdown(originalCommentHTML).trim(); + const originalCommentMarkdown = htmlToMarkdown(originalCommentHTML).trim(); // Skip the Edit if draft is not changed if (originalCommentMarkdown === textForNewComment) { @@ -1244,7 +1245,7 @@ function editReportComment(reportID, originalReportAction, textForNewComment) { } const htmlForNewComment = handleUserDeletedLinksInHtml(textForNewComment, originalCommentMarkdown); - const reportComment = parser.htmlToText(htmlForNewComment); + const reportComment = htmlToText(htmlForNewComment); // For comments shorter than or equal to 10k chars, convert the comment from MD into HTML because that's how it is stored in the database // For longer comments, skip parsing and display plaintext for performance reasons. It takes over 40s to parse a 100k long string!! diff --git a/src/libs/parser.ts b/src/libs/parser.ts new file mode 100644 index 000000000000..07d5dc0c1c22 --- /dev/null +++ b/src/libs/parser.ts @@ -0,0 +1,14 @@ +import ExpensiMark from 'expensify-common/lib/ExpensiMark'; +import * as PersonalDetailsUtils from './PersonalDetailsUtils'; + +function htmlToMarkdown(html: string) { + const parser = new ExpensiMark(); + return parser.htmlToMarkdown(html, PersonalDetailsUtils.getAllPersonalDetails()); +} + +function htmlToText(html: string) { + const parser = new ExpensiMark(); + return parser.htmlToText(html, PersonalDetailsUtils.getAllPersonalDetails()); +} + +export {htmlToMarkdown, htmlToText}; diff --git a/src/pages/PrivateNotes/PrivateNotesEditPage.js b/src/pages/PrivateNotes/PrivateNotesEditPage.js index f38dabee9183..1dba3d2a80fc 100644 --- a/src/pages/PrivateNotes/PrivateNotesEditPage.js +++ b/src/pages/PrivateNotes/PrivateNotesEditPage.js @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; @@ -18,6 +17,7 @@ import withLocalize from '@components/withLocalize'; import useLocalize from '@hooks/useLocalize'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; +import {htmlToMarkdown} from '@libs/parser'; import updateMultilineInputRange from '@libs/UpdateMultilineInputRange'; import withReportAndPrivateNotesOrNotFound from '@pages/home/report/withReportAndPrivateNotesOrNotFound'; import personalDetailsPropType from '@pages/personalDetailsPropType'; @@ -53,9 +53,8 @@ function PrivateNotesEditPage({route, personalDetailsList, report}) { const {translate} = useLocalize(); // We need to edit the note in markdown format, but display it in HTML format - const parser = new ExpensiMark(); const [privateNote, setPrivateNote] = useState( - () => Report.getDraftPrivateNote(report.reportID).trim() || parser.htmlToMarkdown(lodashGet(report, ['privateNotes', route.params.accountID, 'note'], '')).trim(), + () => Report.getDraftPrivateNote(report.reportID).trim() || htmlToMarkdown(lodashGet(report, ['privateNotes', route.params.accountID, 'note'], '')).trim(), ); /** @@ -95,7 +94,7 @@ function PrivateNotesEditPage({route, personalDetailsList, report}) { const originalNote = lodashGet(report, ['privateNotes', route.params.accountID, 'note'], ''); if (privateNote.trim() !== originalNote.trim()) { - const editedNote = Report.handleUserDeletedLinksInHtml(privateNote.trim(), parser.htmlToMarkdown(originalNote).trim()); + const editedNote = Report.handleUserDeletedLinksInHtml(privateNote.trim(), htmlToMarkdown(originalNote).trim()); Report.updatePrivateNotes(report.reportID, route.params.accountID, editedNote); } diff --git a/src/pages/ReportWelcomeMessagePage.js b/src/pages/ReportWelcomeMessagePage.js index c748e36c98e6..46692683f631 100644 --- a/src/pages/ReportWelcomeMessagePage.js +++ b/src/pages/ReportWelcomeMessagePage.js @@ -1,5 +1,4 @@ import {useFocusEffect} from '@react-navigation/native'; -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import PropTypes from 'prop-types'; import React, {useCallback, useRef, useState} from 'react'; import {View} from 'react-native'; @@ -13,6 +12,7 @@ import TextInput from '@components/TextInput'; import withLocalize, {withLocalizePropTypes} from '@components/withLocalize'; import compose from '@libs/compose'; import Navigation from '@libs/Navigation/Navigation'; +import {htmlToMarkdown} from '@libs/parser'; import * as PolicyUtils from '@libs/PolicyUtils'; import updateMultilineInputRange from '@libs/UpdateMultilineInputRange'; import styles from '@styles/styles'; @@ -45,8 +45,7 @@ const defaultProps = { }; function ReportWelcomeMessagePage(props) { - const parser = new ExpensiMark(); - const [welcomeMessage, setWelcomeMessage] = useState(() => parser.htmlToMarkdown(props.report.welcomeMessage)); + const [welcomeMessage, setWelcomeMessage] = useState(() => htmlToMarkdown(props.report.welcomeMessage)); const welcomeMessageInputRef = useRef(null); const focusTimeoutRef = useRef(null); diff --git a/src/pages/home/report/ContextMenu/ContextMenuActions.js b/src/pages/home/report/ContextMenu/ContextMenuActions.js index 5a1266d15a42..8e75ffa3a615 100644 --- a/src/pages/home/report/ContextMenu/ContextMenuActions.js +++ b/src/pages/home/report/ContextMenu/ContextMenuActions.js @@ -1,4 +1,3 @@ -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import lodashGet from 'lodash/get'; import React from 'react'; import _ from 'underscore'; @@ -11,6 +10,7 @@ import * as Environment from '@libs/Environment/Environment'; import fileDownload from '@libs/fileDownload'; import getAttachmentDetails from '@libs/fileDownload/getAttachmentDetails'; import Navigation from '@libs/Navigation/Navigation'; +import {htmlToMarkdown, htmlToText} from '@libs/parser'; import Permissions from '@libs/Permissions'; import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; @@ -282,11 +282,10 @@ export default [ const displayMessage = ReportUtils.getIOUReportActionDisplayMessage(reportAction); Clipboard.setString(displayMessage); } else if (content) { - const parser = new ExpensiMark(); if (!Clipboard.canSetHtml()) { - Clipboard.setString(parser.htmlToMarkdown(content)); + Clipboard.setString(htmlToMarkdown(content)); } else { - const plainText = parser.htmlToText(content); + const plainText = htmlToText(content); Clipboard.setHtml(content, plainText); } } diff --git a/src/pages/home/report/ReportActionItemMessageEdit.js b/src/pages/home/report/ReportActionItemMessageEdit.js index 60714dad9864..d8c7f3aaea39 100644 --- a/src/pages/home/report/ReportActionItemMessageEdit.js +++ b/src/pages/home/report/ReportActionItemMessageEdit.js @@ -1,4 +1,3 @@ -import ExpensiMark from 'expensify-common/lib/ExpensiMark'; import Str from 'expensify-common/lib/str'; import lodashGet from 'lodash/get'; import PropTypes from 'prop-types'; @@ -22,6 +21,7 @@ import * as ComposerUtils from '@libs/ComposerUtils'; import * as EmojiUtils from '@libs/EmojiUtils'; import focusComposerWithDelay from '@libs/focusComposerWithDelay'; import onyxSubscribe from '@libs/onyxSubscribe'; +import {htmlToMarkdown} from '@libs/parser'; import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager'; import * as ReportActionsUtils from '@libs/ReportActionsUtils'; import * as ReportUtils from '@libs/ReportUtils'; @@ -88,8 +88,7 @@ function ReportActionItemMessageEdit(props) { const getInitialDraft = () => { if (props.draftMessage === props.action.message[0].html) { // We only convert the report action message to markdown if the draft message is unchanged. - const parser = new ExpensiMark(); - return parser.htmlToMarkdown(props.draftMessage).trim(); + return htmlToMarkdown(props.draftMessage).trim(); } // We need to decode saved draft message because it's escaped before saving. return Str.htmlDecode(props.draftMessage);