From 32a46ee5af2b46ab6f0879c8b925149c91f79826 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 11 Aug 2023 05:22:36 +0700 Subject: [PATCH 01/49] feature: render markdown in thread header --- .../DisplayNames/displayNamesPropTypes.js | 2 ++ src/components/DisplayNames/index.js | 3 ++- src/libs/ReportUtils.js | 20 ++++++++++++++++++- src/pages/home/HeaderView.js | 1 + 4 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/components/DisplayNames/displayNamesPropTypes.js b/src/components/DisplayNames/displayNamesPropTypes.js index 5ad332f7a117..1dc75af1505a 100644 --- a/src/components/DisplayNames/displayNamesPropTypes.js +++ b/src/components/DisplayNames/displayNamesPropTypes.js @@ -4,6 +4,8 @@ const propTypes = { /** The full title of the DisplayNames component (not split up) */ fullTitle: PropTypes.string, + fullTitleHtml: PropTypes.string, + /** Array of objects that map display names to their corresponding tooltip */ displayNamesWithTooltips: PropTypes.arrayOf( PropTypes.shape({ diff --git a/src/components/DisplayNames/index.js b/src/components/DisplayNames/index.js index 6a7123f723f8..bf35446b77d4 100644 --- a/src/components/DisplayNames/index.js +++ b/src/components/DisplayNames/index.js @@ -6,6 +6,7 @@ import styles from '../../styles/styles'; import Tooltip from '../Tooltip'; import Text from '../Text'; import UserDetailsTooltip from '../UserDetailsTooltip'; +import RenderHTML from "../RenderHTML"; class DisplayNames extends PureComponent { constructor(props) { @@ -75,7 +76,7 @@ class DisplayNames extends PureComponent { ref={(el) => (this.containerRef = el)} > {this.props.shouldUseFullTitle - ? this.props.fullTitle + ? (this.props.fullTitleHtml ? : this.props.fullTitle) : _.map(this.props.displayNamesWithTooltips, ({displayName, accountID, avatar, login}, index) => ( ]*)>|\\r\\n|\\n|\\r)`; + const threadHeaderHtmlRegex = new RegExp(`^(?:<([^>]+)>)?((?:(?!${blockTagPattern}).)*)(${blockTagPattern}.*)`, 'gm'); + return parentReportActionMessage.replace(threadHeaderHtmlRegex, (match, g1, g2) => { + if (!g1 || g1 === 'h1') { + return g2; + } + + return `<${g1}>${g2}` + }); +} + /** * Get the title for a report. * @@ -1320,7 +1337,8 @@ function getReportName(report, policy = undefined) { } const isAttachment = _.has(parentReportAction, 'isAttachment') ? parentReportAction.isAttachment : isReportMessageAttachment(_.last(lodashGet(parentReportAction, 'message', [{}]))); - const parentReportActionMessage = lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); + const messageHtml = lodashGet(parentReportAction, ['message', 0, 'html']); + const parentReportActionMessage = /<\/?[a-z][\s\S]*>/i.test(messageHtml) ? getThreadReportNameHtml(lodashGet(parentReportAction, ['message', 0, 'html'], '')) : lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; } diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index fafd6caf4f1f..9f175f2957dc 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -183,6 +183,7 @@ function HeaderView(props) { /i.test(title)? title : ''} displayNamesWithTooltips={displayNamesWithTooltips} tooltipEnabled numberOfLines={1} From 9b3c217f2a2d72c4c7a718fa6351490df709f0af Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 15 Aug 2023 18:24:40 +0700 Subject: [PATCH 02/49] add comment --- .../DisplayNames/displayNamesPropTypes.js | 1 + src/components/DisplayNames/index.js | 6 ++- src/libs/ReportUtils.js | 43 ++++++++++++++----- src/pages/home/HeaderView.js | 4 +- 4 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/components/DisplayNames/displayNamesPropTypes.js b/src/components/DisplayNames/displayNamesPropTypes.js index 1dc75af1505a..b1cbf6a15e45 100644 --- a/src/components/DisplayNames/displayNamesPropTypes.js +++ b/src/components/DisplayNames/displayNamesPropTypes.js @@ -4,6 +4,7 @@ const propTypes = { /** The full title of the DisplayNames component (not split up) */ fullTitle: PropTypes.string, + /** The full title of the DisplayNames component in HTML */ fullTitleHtml: PropTypes.string, /** Array of objects that map display names to their corresponding tooltip */ diff --git a/src/components/DisplayNames/index.js b/src/components/DisplayNames/index.js index bf35446b77d4..92b782bd9970 100644 --- a/src/components/DisplayNames/index.js +++ b/src/components/DisplayNames/index.js @@ -6,7 +6,7 @@ import styles from '../../styles/styles'; import Tooltip from '../Tooltip'; import Text from '../Text'; import UserDetailsTooltip from '../UserDetailsTooltip'; -import RenderHTML from "../RenderHTML"; +import RenderHTML from '../RenderHTML'; class DisplayNames extends PureComponent { constructor(props) { @@ -68,6 +68,8 @@ class DisplayNames extends PureComponent { ); } + const fullTitle = this.props.fullTitleHtml ? : this.props.fullTitle; + return ( // Tokenization of string only support prop numberOfLines on Web (this.containerRef = el)} > {this.props.shouldUseFullTitle - ? (this.props.fullTitleHtml ? : this.props.fullTitle) + ? fullTitle : _.map(this.props.displayNamesWithTooltips, ({displayName, accountID, avatar, login}, index) => ( /i.test(text); +} + +/** + * Get the formatted title in HTML for a thread based on parent message. + * Only the first line of the message should display. + * + * @param {Object} parentReportAction + * @returns {String} + */ +function getThreadReportNameHtml(parentReportAction) { + const messageHtml = lodashGet(parentReportAction, ['message', 0, 'html']); + if (!messageHtml || !containsHtml(messageHtml)) { + return ''; } const blockTags = ['br', 'h1', 'pre', 'code', 'div', 'blockquote', 'p', 'li', 'comment', 'div']; - const blockTagPattern = `(?:<\\/?(?:${blockTags.join('|')})(?:[^>]*)>|\\r\\n|\\n|\\r)`; - const threadHeaderHtmlRegex = new RegExp(`^(?:<([^>]+)>)?((?:(?!${blockTagPattern}).)*)(${blockTagPattern}.*)`, 'gm'); - return parentReportActionMessage.replace(threadHeaderHtmlRegex, (match, g1, g2) => { + const blockTagRegExp = `(?:<\\/?(?:${blockTags.join('|')})(?:[^>]*)>|\\r\\n|\\n|\\r)`; + const threadHeaderHtmlRegExp = new RegExp(`^(?:<([^>]+)>)?((?:(?!${blockTagRegExp}).)*)(${blockTagRegExp}.*)`, 'gmi'); + return messageHtml.replace(threadHeaderHtmlRegExp, (match, g1, g2) => { + // If heading tag, display the text as is without any formatting if (!g1 || g1 === 'h1') { return g2; } - return `<${g1}>${g2}` + return `<${g1}>${g2}`; }); } @@ -1326,9 +1345,10 @@ function getThreadReportNameHtml(parentReportActionMessage) { * * @param {Object} report * @param {Object} [policy] + * @param {Boolean} isThreadTitle * @returns {String} */ -function getReportName(report, policy = undefined) { +function getReportName(report, policy = undefined, isThreadTitle = false) { let formattedName; if (isChatThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); @@ -1337,8 +1357,9 @@ function getReportName(report, policy = undefined) { } const isAttachment = _.has(parentReportAction, 'isAttachment') ? parentReportAction.isAttachment : isReportMessageAttachment(_.last(lodashGet(parentReportAction, 'message', [{}]))); - const messageHtml = lodashGet(parentReportAction, ['message', 0, 'html']); - const parentReportActionMessage = /<\/?[a-z][\s\S]*>/i.test(messageHtml) ? getThreadReportNameHtml(lodashGet(parentReportAction, ['message', 0, 'html'], '')) : lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); + const parentReportActionMessage = isThreadTitle + ? getThreadReportNameHtml(parentReportAction) + : lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; } @@ -2961,6 +2982,8 @@ export { getIcons, getRoomWelcomeMessage, getDisplayNamesWithTooltips, + containsHtml, + getThreadReportNameHtml, getReportName, getReport, getReportIDFromLink, diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 9f175f2957dc..6139dbf1032d 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -87,7 +87,7 @@ function HeaderView(props) { const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(props.report); const isTaskReport = ReportUtils.isTaskReport(props.report); const reportHeaderData = !isTaskReport && !isChatThread && props.report.parentReportID ? props.parentReport : props.report; - const title = ReportUtils.getReportName(reportHeaderData); + const title = ReportUtils.getReportName(reportHeaderData, undefined, true); const subtitle = ReportUtils.getChatRoomSubtitle(reportHeaderData); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(reportHeaderData); const isConcierge = ReportUtils.hasSingleParticipant(props.report) && _.contains(participants, CONST.ACCOUNT_ID.CONCIERGE); @@ -183,7 +183,7 @@ function HeaderView(props) { /i.test(title)? title : ''} + fullTitleHtml={ReportUtils.containsHtml(title) ? title : ''} displayNamesWithTooltips={displayNamesWithTooltips} tooltipEnabled numberOfLines={1} From 0c9829c2629fe83b9ef1135b47efa28f7d91f68c Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 15 Aug 2023 20:19:10 +0700 Subject: [PATCH 03/49] render code fence as inline code --- src/libs/ReportUtils.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index e4dee7685acc..806e264be61c 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1333,11 +1333,14 @@ function getThreadReportNameHtml(parentReportAction) { const blockTagRegExp = `(?:<\\/?(?:${blockTags.join('|')})(?:[^>]*)>|\\r\\n|\\n|\\r)`; const threadHeaderHtmlRegExp = new RegExp(`^(?:<([^>]+)>)?((?:(?!${blockTagRegExp}).)*)(${blockTagRegExp}.*)`, 'gmi'); return messageHtml.replace(threadHeaderHtmlRegExp, (match, g1, g2) => { - // If heading tag, display the text as is without any formatting if (!g1 || g1 === 'h1') { return g2; } + if (g1 === 'pre') { + return `${g2}` + } + return `<${g1}>${g2}`; }); } From 1cb14d0c454ad11d99ce76651e468cbba4e9f9e4 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 18 Aug 2023 15:00:15 +0700 Subject: [PATCH 04/49] render markdown on native --- src/components/DisplayNames/index.native.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/DisplayNames/index.native.js b/src/components/DisplayNames/index.native.js index f1b1d7d0b7b5..b746621d17ea 100644 --- a/src/components/DisplayNames/index.native.js +++ b/src/components/DisplayNames/index.native.js @@ -1,16 +1,19 @@ import React from 'react'; import {propTypes, defaultProps} from './displayNamesPropTypes'; import Text from '../Text'; +import RenderHTML from "../RenderHTML"; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. function DisplayNames(props) { + const fullTitle = props.fullTitleHtml ? : props.fullTitle; + return ( - {props.fullTitle} + {fullTitle} ); } From ad73440953a457e5e0f688ae85135b57b42a8099 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 18 Aug 2023 15:00:37 +0700 Subject: [PATCH 05/49] refactor html message --- src/libs/ReportUtils.js | 65 ++++++++++++++++++++--------------------- 1 file changed, 32 insertions(+), 33 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 806e264be61c..e7e08efd3eb6 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1316,35 +1316,6 @@ function containsHtml(text) { return /<\/?[a-z][\s\S]*>/i.test(text); } -/** - * Get the formatted title in HTML for a thread based on parent message. - * Only the first line of the message should display. - * - * @param {Object} parentReportAction - * @returns {String} - */ -function getThreadReportNameHtml(parentReportAction) { - const messageHtml = lodashGet(parentReportAction, ['message', 0, 'html']); - if (!messageHtml || !containsHtml(messageHtml)) { - return ''; - } - - const blockTags = ['br', 'h1', 'pre', 'code', 'div', 'blockquote', 'p', 'li', 'comment', 'div']; - const blockTagRegExp = `(?:<\\/?(?:${blockTags.join('|')})(?:[^>]*)>|\\r\\n|\\n|\\r)`; - const threadHeaderHtmlRegExp = new RegExp(`^(?:<([^>]+)>)?((?:(?!${blockTagRegExp}).)*)(${blockTagRegExp}.*)`, 'gmi'); - return messageHtml.replace(threadHeaderHtmlRegExp, (match, g1, g2) => { - if (!g1 || g1 === 'h1') { - return g2; - } - - if (g1 === 'pre') { - return `${g2}` - } - - return `<${g1}>${g2}`; - }); -} - /** * Get the report action message when expense has been modified. * @@ -1428,15 +1399,42 @@ function getModifiedExpenseOriginalMessage(oldTransaction, transactionChanges, i return originalMessage; } +/** + * Get the formatted title in HTML for a thread based on parent message. + * Only the first line of the message should display. + * + * @param {Object} parentReportActionMessageHtml + * @returns {String} + */ +function getThreadReportNameHtml(parentReportActionMessageHtml) { + const blockTags = ['br', 'h1', 'pre', 'code', 'div', 'blockquote', 'p', 'li', 'comment', 'div']; + const blockOpeningTagRegExp = `(?:<(?:${blockTags.join('|')})(?:[^>]*)>|\\r\\n|\\n|\\r)`; + const blockTagRegExp = `(?:<\\/?(?:${blockTags.join('|')})(?:[^>]*)>|\\r\\n|\\n|\\r)`; + const threadHeaderHtmlRegExp = new RegExp(`^(?:<([^>]+)>)?((?:(?!${blockOpeningTagRegExp}).)*)(${blockTagRegExp}.*)`, 'gmi'); + return parentReportActionMessageHtml.replace(threadHeaderHtmlRegExp, (match, g1, g2) => { + if (!g1 || g1 === 'h1') { + return g2; + } + if (g1 === 'pre') { + return `${g2}` + } + const parser = new ExpensiMark(); + if (parser.containsNonPairTag(g2)) { + return `<${g1}>${g2}` + } + return `<${g1}>${g2}`; + }); +} + /** * Get the title for a report. * * @param {Object} report * @param {Object} [policy] - * @param {Boolean} isThreadTitle + * @param {Boolean} isThreadHeader * @returns {String} */ -function getReportName(report, policy = undefined, isThreadTitle = false) { +function getReportName(report, policy = undefined, isThreadHeader = false) { let formattedName; if (isChatThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); @@ -1445,8 +1443,9 @@ function getReportName(report, policy = undefined, isThreadTitle = false) { } const isAttachment = _.has(parentReportAction, 'isAttachment') ? parentReportAction.isAttachment : isReportMessageAttachment(_.last(lodashGet(parentReportAction, 'message', [{}]))); - const parentReportActionMessage = isThreadTitle - ? getThreadReportNameHtml(parentReportAction) + const messageHtml = lodashGet(parentReportAction, ['message', 0, 'html']); + const parentReportActionMessage = isThreadHeader + ? getThreadReportNameHtml(messageHtml) : lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; From 8111823b4d7a4211083b58d71dd0d4363e6c14c1 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 18 Aug 2023 16:29:02 +0700 Subject: [PATCH 06/49] run prettier --- src/components/DisplayNames/DisplayNamesWithTooltip.js | 2 +- src/components/DisplayNames/DisplayNamesWithoutTooltip.js | 2 +- src/components/DisplayNames/index.native.js | 2 +- src/libs/ReportUtils.js | 8 +++----- 4 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.js b/src/components/DisplayNames/DisplayNamesWithTooltip.js index e2dc53fc8652..aefae750ee45 100644 --- a/src/components/DisplayNames/DisplayNamesWithTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithTooltip.js @@ -6,7 +6,7 @@ import Text from '../Text'; import Tooltip from '../Tooltip'; import DisplayNamesTooltipItem from './DisplayNamesTooltipItem'; import {defaultProps, propTypes} from './displayNamesPropTypes'; -import RenderHTML from "../RenderHTML"; +import RenderHTML from '../RenderHTML'; function DisplayNamesWithToolTip(props) { const containerRef = useRef(null); diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js index 115d135eec7a..892bf1964568 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types'; import React from 'react'; import styles from '../../styles/styles'; import Text from '../Text'; -import RenderHTML from "../RenderHTML"; +import RenderHTML from '../RenderHTML'; const propTypes = { /** The full title of the DisplayNames component (not split up) */ diff --git a/src/components/DisplayNames/index.native.js b/src/components/DisplayNames/index.native.js index b746621d17ea..f01805e163de 100644 --- a/src/components/DisplayNames/index.native.js +++ b/src/components/DisplayNames/index.native.js @@ -1,7 +1,7 @@ import React from 'react'; import {propTypes, defaultProps} from './displayNamesPropTypes'; import Text from '../Text'; -import RenderHTML from "../RenderHTML"; +import RenderHTML from '../RenderHTML'; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. function DisplayNames(props) { diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 13b96a27d5e1..2c046c9cfdab 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1427,11 +1427,11 @@ function getThreadReportNameHtml(parentReportActionMessageHtml) { return g2; } if (g1 === 'pre') { - return `${g2}` + return `${g2}`; } const parser = new ExpensiMark(); if (parser.containsNonPairTag(g2)) { - return `<${g1}>${g2}` + return `<${g1}>${g2}`; } return `<${g1}>${g2}`; }); @@ -1455,9 +1455,7 @@ function getReportName(report, policy = undefined, isThreadHeader = false) { const isAttachment = _.has(parentReportAction, 'isAttachment') ? parentReportAction.isAttachment : isReportMessageAttachment(_.last(lodashGet(parentReportAction, 'message', [{}]))); const messageHtml = lodashGet(parentReportAction, ['message', 0, 'html']); - const parentReportActionMessage = isThreadHeader - ? getThreadReportNameHtml(messageHtml) - : lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); + const parentReportActionMessage = isThreadHeader ? getThreadReportNameHtml(messageHtml) : lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; } From cc8071e809aa7c14f42db5a5290b526488cf63de Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 18 Aug 2023 16:32:07 +0700 Subject: [PATCH 07/49] rename shouldRenderHTML --- src/libs/ReportUtils.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 2c046c9cfdab..a08c99004638 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1442,10 +1442,10 @@ function getThreadReportNameHtml(parentReportActionMessageHtml) { * * @param {Object} report * @param {Object} [policy] - * @param {Boolean} isThreadHeader + * @param {Boolean} shouldRenderHTML * @returns {String} */ -function getReportName(report, policy = undefined, isThreadHeader = false) { +function getReportName(report, policy = undefined, shouldRenderHTML = false) { let formattedName; if (isChatThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); @@ -1455,7 +1455,7 @@ function getReportName(report, policy = undefined, isThreadHeader = false) { const isAttachment = _.has(parentReportAction, 'isAttachment') ? parentReportAction.isAttachment : isReportMessageAttachment(_.last(lodashGet(parentReportAction, 'message', [{}]))); const messageHtml = lodashGet(parentReportAction, ['message', 0, 'html']); - const parentReportActionMessage = isThreadHeader ? getThreadReportNameHtml(messageHtml) : lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); + const parentReportActionMessage = shouldRenderHTML ? getThreadReportNameHtml(messageHtml) : lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; } From 6a61a364594686348dd074c739897ceab1451a91 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 18 Aug 2023 17:53:15 +0700 Subject: [PATCH 08/49] update block element list --- src/libs/ReportUtils.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index a08c99004638..1831cc873561 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1418,10 +1418,9 @@ function getParentReport(report) { * @returns {String} */ function getThreadReportNameHtml(parentReportActionMessageHtml) { - const blockTags = ['br', 'h1', 'pre', 'code', 'div', 'blockquote', 'p', 'li', 'comment', 'div']; - const blockOpeningTagRegExp = `(?:<(?:${blockTags.join('|')})(?:[^>]*)>|\\r\\n|\\n|\\r)`; + const blockTags = ['br', 'h1', 'pre', 'div', 'blockquote', 'p', 'li', 'div']; const blockTagRegExp = `(?:<\\/?(?:${blockTags.join('|')})(?:[^>]*)>|\\r\\n|\\n|\\r)`; - const threadHeaderHtmlRegExp = new RegExp(`^(?:<([^>]+)>)?((?:(?!${blockOpeningTagRegExp}).)*)(${blockTagRegExp}.*)`, 'gmi'); + const threadHeaderHtmlRegExp = new RegExp(`^(?:<([^>]+)>)?((?:(?!${blockTagRegExp}).)*)(${blockTagRegExp}.*)`, 'gmi'); return parentReportActionMessageHtml.replace(threadHeaderHtmlRegExp, (match, g1, g2) => { if (!g1 || g1 === 'h1') { return g2; From 23ac6963b444c22f412dc11042ec7d7004787ffe Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 23 Aug 2023 00:49:43 +0700 Subject: [PATCH 09/49] custom renderer to pass numberOfLines --- .../HTMLEngineProvider/BaseHTMLEngineProvider.js | 14 +++++++++++++- src/components/RenderHTML.js | 1 + src/libs/ReportUtils.js | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js index e156c8bda3f4..6117330238f7 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js @@ -1,6 +1,11 @@ import _ from 'underscore'; import React, {useMemo} from 'react'; -import {TRenderEngineProvider, RenderHTMLConfigProvider, defaultHTMLElementModels} from 'react-native-render-html'; +import { + TRenderEngineProvider, + RenderHTMLConfigProvider, + defaultHTMLElementModels, + HTMLContentModel +} from 'react-native-render-html'; import PropTypes from 'prop-types'; import htmlRenderers from './HTMLRenderers'; import * as HTMLEngineUtils from './htmlEngineUtils'; @@ -40,6 +45,13 @@ const customHTMLElementModels = { tagName: 'email-comment', mixedUAStyles: {whiteSpace: 'normal'}, }), + 'thread-title': defaultHTMLElementModels.span.extend({ + tagName: 'thread-title', + contentModel: HTMLContentModel.textual, + reactNativeProps: { + text: {numberOfLines: 1}, + }, + }), strong: defaultHTMLElementModels.span.extend({ tagName: 'strong', mixedUAStyles: {whiteSpace: 'pre'}, diff --git a/src/components/RenderHTML.js b/src/components/RenderHTML.js index d2d4f0b58e71..13831610eb9b 100644 --- a/src/components/RenderHTML.js +++ b/src/components/RenderHTML.js @@ -13,6 +13,7 @@ const propTypes = { // context to RenderHTMLSource components. See https://git.io/JRcZb // The provider is available at src/components/HTMLEngineProvider/ function RenderHTML(props) { + console.log(props.html) const {windowWidth} = useWindowDimensions(); return ( ${getThreadReportNameHtml(messageHtml)}` : lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; } From f8fea9bdf71e77130597faa42600b5d8e57e7d4d Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 23 Aug 2023 01:03:05 +0700 Subject: [PATCH 10/49] fix lint --- .../HTMLEngineProvider/BaseHTMLEngineProvider.js | 7 +------ src/components/RenderHTML.js | 2 +- src/libs/ReportUtils.js | 4 +++- 3 files changed, 5 insertions(+), 8 deletions(-) diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js index 6117330238f7..b5da7826f890 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js @@ -1,11 +1,6 @@ import _ from 'underscore'; import React, {useMemo} from 'react'; -import { - TRenderEngineProvider, - RenderHTMLConfigProvider, - defaultHTMLElementModels, - HTMLContentModel -} from 'react-native-render-html'; +import {TRenderEngineProvider, RenderHTMLConfigProvider, defaultHTMLElementModels, HTMLContentModel} from 'react-native-render-html'; import PropTypes from 'prop-types'; import htmlRenderers from './HTMLRenderers'; import * as HTMLEngineUtils from './htmlEngineUtils'; diff --git a/src/components/RenderHTML.js b/src/components/RenderHTML.js index 13831610eb9b..8577cfd42aaf 100644 --- a/src/components/RenderHTML.js +++ b/src/components/RenderHTML.js @@ -13,7 +13,7 @@ const propTypes = { // context to RenderHTMLSource components. See https://git.io/JRcZb // The provider is available at src/components/HTMLEngineProvider/ function RenderHTML(props) { - console.log(props.html) + console.log(props.html); const {windowWidth} = useWindowDimensions(); return ( ${getThreadReportNameHtml(messageHtml)}` : lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); + const parentReportActionMessage = shouldRenderHTML + ? `${getThreadReportNameHtml(messageHtml)}` + : lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; } From e069de089931b56fdff969085ab3088a37c6fe5e Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 23 Aug 2023 01:19:32 +0700 Subject: [PATCH 11/49] fix lint --- src/components/RenderHTML.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/RenderHTML.js b/src/components/RenderHTML.js index 8577cfd42aaf..d2d4f0b58e71 100644 --- a/src/components/RenderHTML.js +++ b/src/components/RenderHTML.js @@ -13,7 +13,6 @@ const propTypes = { // context to RenderHTMLSource components. See https://git.io/JRcZb // The provider is available at src/components/HTMLEngineProvider/ function RenderHTML(props) { - console.log(props.html); const {windowWidth} = useWindowDimensions(); return ( Date: Wed, 23 Aug 2023 02:02:58 +0700 Subject: [PATCH 12/49] remove flex 1 --- src/styles/styles.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/styles.js b/src/styles/styles.js index 0da8cdd56680..564df0af11ed 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -152,7 +152,6 @@ const webViewStyles = { color: themeColors.text, fontSize: variables.fontSizeNormal, fontFamily: fontFamily.EXP_NEUE, - flex: 1, lineHeight: variables.fontSizeNormalHeight, }, }; From 555d4be60c3b27cafd55eef43cf786c29fe9d65c Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 24 Aug 2023 00:06:03 +0700 Subject: [PATCH 13/49] only show first line in thread lhn --- src/libs/ReportUtils.js | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 3f04ffbb91ad..46862f8bda97 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -21,6 +21,7 @@ import isReportMessageAttachment from './isReportMessageAttachment'; import * as defaultWorkspaceAvatars from '../components/Icon/WorkspaceDefaultAvatars'; import * as CurrencyUtils from './CurrencyUtils'; import * as UserUtils from './UserUtils'; +import str from "expensify-common/lib/str"; let currentUserEmail; let currentUserAccountID; @@ -1490,14 +1491,14 @@ function getParentReport(report) { * Get the formatted title in HTML for a thread based on parent message. * Only the first line of the message should display. * - * @param {Object} parentReportActionMessageHtml + * @param {String} parentReportActionMessage * @returns {String} */ -function getThreadReportNameHtml(parentReportActionMessageHtml) { +function getThreadReportNameHtml(parentReportActionMessage) { const blockTags = ['br', 'h1', 'pre', 'div', 'blockquote', 'p', 'li', 'div']; const blockTagRegExp = `(?:<\\/?(?:${blockTags.join('|')})(?:[^>]*)>|\\r\\n|\\n|\\r)`; const threadHeaderHtmlRegExp = new RegExp(`^(?:<([^>]+)>)?((?:(?!${blockTagRegExp}).)*)(${blockTagRegExp}.*)`, 'gmi'); - return parentReportActionMessageHtml.replace(threadHeaderHtmlRegExp, (match, g1, g2) => { + return parentReportActionMessage.replace(threadHeaderHtmlRegExp, (match, g1, g2) => { if (!g1 || g1 === 'h1') { return g2; } @@ -1512,6 +1513,19 @@ function getThreadReportNameHtml(parentReportActionMessageHtml) { }); } +/** + * Get the title for a thread based on parent message. + * Only the first line of the message should display. + * + * @param {String} parentReportActionMessage + * @param {Boolean} shouldRenderHTML + * @returns {String} + */ +function getThreadReportName(parentReportActionMessage, shouldRenderHTML) { + const threadReportNameHtml = getThreadReportNameHtml(parentReportActionMessage); + return shouldRenderHTML ? `${threadReportNameHtml}` : str.stripHTML(threadReportNameHtml).trim(); +} + /** * Get the title for a report. * @@ -1529,10 +1543,7 @@ function getReportName(report, policy = undefined, shouldRenderHTML = false) { } const isAttachment = _.has(parentReportAction, 'isAttachment') ? parentReportAction.isAttachment : isReportMessageAttachment(_.last(lodashGet(parentReportAction, 'message', [{}]))); - const messageHtml = lodashGet(parentReportAction, ['message', 0, 'html']); - const parentReportActionMessage = shouldRenderHTML - ? `${getThreadReportNameHtml(messageHtml)}` - : lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); + const parentReportActionMessage = getThreadReportName(lodashGet(parentReportAction, ['message', 0, 'html']), shouldRenderHTML); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; } From 6102ac9cbb1a2e772a4e3bdc4859e36398958946 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 24 Aug 2023 00:24:35 +0700 Subject: [PATCH 14/49] fix lint --- src/libs/ReportUtils.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 46862f8bda97..65b4e1226169 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -21,7 +21,6 @@ import isReportMessageAttachment from './isReportMessageAttachment'; import * as defaultWorkspaceAvatars from '../components/Icon/WorkspaceDefaultAvatars'; import * as CurrencyUtils from './CurrencyUtils'; import * as UserUtils from './UserUtils'; -import str from "expensify-common/lib/str"; let currentUserEmail; let currentUserAccountID; @@ -1523,7 +1522,7 @@ function getThreadReportNameHtml(parentReportActionMessage) { */ function getThreadReportName(parentReportActionMessage, shouldRenderHTML) { const threadReportNameHtml = getThreadReportNameHtml(parentReportActionMessage); - return shouldRenderHTML ? `${threadReportNameHtml}` : str.stripHTML(threadReportNameHtml).trim(); + return shouldRenderHTML ? `${threadReportNameHtml}` : Str.stripHTML(threadReportNameHtml).trim(); } /** From 171edbdd8c8d4a63e1cdedc5e9193177aed42845 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 8 Sep 2023 11:20:24 +0700 Subject: [PATCH 15/49] revert removing flex: 1 --- src/styles/styles.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/styles.js b/src/styles/styles.js index 6df127efb14c..040c9cb8d910 100644 --- a/src/styles/styles.js +++ b/src/styles/styles.js @@ -153,6 +153,7 @@ const webViewStyles = { color: themeColors.text, fontSize: variables.fontSizeNormal, fontFamily: fontFamily.EXP_NEUE, + flex: 1, lineHeight: variables.fontSizeNormalHeight, }, }; From 07448175b965da37d7fcf2b76b03fab4fd8d9840 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 19 Sep 2023 01:01:00 +0700 Subject: [PATCH 16/49] fix: ellipsis header --- .../DisplayNames/DisplayNamesWithTooltip.js | 6 +++++- .../DisplayNames/DisplayNamesWithoutTooltip.js | 18 +++++++++++++----- .../DisplayNames/displayNamesPropTypes.js | 6 ++++-- src/components/DisplayNames/index.js | 2 +- src/components/DisplayNames/index.native.js | 6 +++++- .../BaseHTMLEngineProvider.js | 1 - src/libs/ReportUtils.js | 10 ++++++---- src/pages/home/HeaderView.js | 2 +- 8 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.js b/src/components/DisplayNames/DisplayNamesWithTooltip.js index 27760b75de02..2ded525225ee 100644 --- a/src/components/DisplayNames/DisplayNamesWithTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithTooltip.js @@ -7,6 +7,7 @@ import Tooltip from '../Tooltip'; import DisplayNamesTooltipItem from './DisplayNamesTooltipItem'; import {defaultProps, propTypes} from './displayNamesPropTypes'; import RenderHTML from '../RenderHTML'; +import * as ReportUtils from '../../libs/ReportUtils'; function DisplayNamesWithToolTip(props) { const containerRef = useRef(null); @@ -49,7 +50,10 @@ function DisplayNamesWithToolTip(props) { return textNodeRight > containerRight ? -(tooltipX - newToolX) : 0; }, []); - const fullTitle = props.fullTitleHtml ? : props.fullTitle; + let fullTitleHtml = ReportUtils.getReportName(props.report, undefined, true, _.get(containerRef.current, 'offsetWidth')); + fullTitleHtml = ReportUtils.containsHtml(fullTitleHtml) ? fullTitleHtml : ''; + + const fullTitle = fullTitleHtml ? : props.fullTitle; return ( // Tokenization of string only support prop numberOfLines on Web diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js index 892bf1964568..59cefe9b5f6b 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js @@ -1,15 +1,18 @@ import PropTypes from 'prop-types'; -import React from 'react'; +import React, {useRef} from 'react'; +import {get} from 'lodash'; import styles from '../../styles/styles'; import Text from '../Text'; import RenderHTML from '../RenderHTML'; +import * as ReportUtils from '../../libs/ReportUtils'; +import reportPropTypes from '../../pages/reportPropTypes'; const propTypes = { /** The full title of the DisplayNames component (not split up) */ fullTitle: PropTypes.string, - /** The full title of the DisplayNames component in HTML */ - fullTitleHtml: PropTypes.string, + /** The report currently being looked at */ + report: reportPropTypes, /** Arbitrary styles of the displayName text */ // eslint-disable-next-line react/forbid-prop-types @@ -21,18 +24,23 @@ const propTypes = { const defaultProps = { fullTitle: '', - fullTitleHtml: '', textStyles: [], + report: {}, numberOfLines: 1, }; -function DisplayNamesWithoutTooltip({textStyles, numberOfLines, fullTitle, fullTitleHtml}) { +function DisplayNamesWithoutTooltip({textStyles, numberOfLines, fullTitle, report}) { + const ref = useRef(); + let fullTitleHtml = ReportUtils.getReportName(report, undefined, true, get(ref.current, 'offsetWidth')); + fullTitleHtml = ReportUtils.containsHtml(fullTitleHtml) ? fullTitleHtml : ''; + const title = fullTitleHtml ? : fullTitle; return ( {title} diff --git a/src/components/DisplayNames/displayNamesPropTypes.js b/src/components/DisplayNames/displayNamesPropTypes.js index b1cbf6a15e45..ca1c8aec34b9 100644 --- a/src/components/DisplayNames/displayNamesPropTypes.js +++ b/src/components/DisplayNames/displayNamesPropTypes.js @@ -1,11 +1,12 @@ import PropTypes from 'prop-types'; +import reportPropTypes from '../../pages/reportPropTypes'; const propTypes = { /** The full title of the DisplayNames component (not split up) */ fullTitle: PropTypes.string, - /** The full title of the DisplayNames component in HTML */ - fullTitleHtml: PropTypes.string, + /** The report currently being looked at */ + report: reportPropTypes, /** Array of objects that map display names to their corresponding tooltip */ displayNamesWithTooltips: PropTypes.arrayOf( @@ -38,6 +39,7 @@ const defaultProps = { numberOfLines: 1, tooltipEnabled: false, titleStyles: [], + report: {}, }; export {propTypes, defaultProps}; diff --git a/src/components/DisplayNames/index.js b/src/components/DisplayNames/index.js index 10591ddba614..1d5a164ea776 100644 --- a/src/components/DisplayNames/index.js +++ b/src/components/DisplayNames/index.js @@ -10,7 +10,7 @@ function DisplayNames(props) { textStyles={props.textStyles} numberOfLines={props.numberOfLines} fullTitle={props.fullTitle} - fullTitleHtml={props.fullTitleHtml} + report={props.report} /> ); } diff --git a/src/components/DisplayNames/index.native.js b/src/components/DisplayNames/index.native.js index f01805e163de..d9c3f02cfd52 100644 --- a/src/components/DisplayNames/index.native.js +++ b/src/components/DisplayNames/index.native.js @@ -2,10 +2,14 @@ import React from 'react'; import {propTypes, defaultProps} from './displayNamesPropTypes'; import Text from '../Text'; import RenderHTML from '../RenderHTML'; +import * as ReportUtils from '../../libs/ReportUtils'; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. function DisplayNames(props) { - const fullTitle = props.fullTitleHtml ? : props.fullTitle; + let fullTitleHtml = ReportUtils.getReportName(props.report, undefined, true); + fullTitleHtml = ReportUtils.containsHtml(fullTitleHtml) ? fullTitleHtml : ''; + + const fullTitle = fullTitleHtml ? : props.fullTitle; return ( diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index a97e86ea4e77..5ff99fa439d4 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1643,11 +1643,12 @@ function getThreadReportNameHtml(parentReportActionMessage) { * * @param {String} parentReportActionMessage * @param {Boolean} shouldRenderHTML + * @param {Number} width * @returns {String} */ -function getThreadReportName(parentReportActionMessage, shouldRenderHTML) { +function getThreadReportName(parentReportActionMessage, shouldRenderHTML, width = 0) { const threadReportNameHtml = getThreadReportNameHtml(parentReportActionMessage); - return shouldRenderHTML ? `${threadReportNameHtml}` : Str.stripHTML(threadReportNameHtml).trim(); + return shouldRenderHTML ? `${threadReportNameHtml}` : Str.stripHTML(threadReportNameHtml).trim(); } /** @@ -1656,9 +1657,10 @@ function getThreadReportName(parentReportActionMessage, shouldRenderHTML) { * @param {Object} report * @param {Object} [policy] * @param {Boolean} shouldRenderHTML + * @param {Number} width * @returns {String} */ -function getReportName(report, policy = undefined, shouldRenderHTML = false) { +function getReportName(report, policy = undefined, shouldRenderHTML = false, width = 0) { let formattedName; if (isChatThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); @@ -1667,7 +1669,7 @@ function getReportName(report, policy = undefined, shouldRenderHTML = false) { } const isAttachment = ReportActionsUtils.isReportActionAttachment(parentReportAction); - const parentReportActionMessage = getThreadReportName(lodashGet(parentReportAction, ['message', 0, 'html']), shouldRenderHTML); + const parentReportActionMessage = getThreadReportName(lodashGet(parentReportAction, ['message', 0, 'html']), shouldRenderHTML, width); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; } diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 4dd0527473b8..58f238fe1011 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -183,7 +183,7 @@ function HeaderView(props) { Date: Tue, 19 Sep 2023 06:03:38 +0700 Subject: [PATCH 17/49] Revert "fix: ellipsis header" This reverts commit 07448175b965da37d7fcf2b76b03fab4fd8d9840. --- .../DisplayNames/DisplayNamesWithTooltip.js | 6 +----- .../DisplayNames/DisplayNamesWithoutTooltip.js | 18 +++++------------- .../DisplayNames/displayNamesPropTypes.js | 6 ++---- src/components/DisplayNames/index.js | 2 +- src/components/DisplayNames/index.native.js | 6 +----- .../BaseHTMLEngineProvider.js | 1 + src/libs/ReportUtils.js | 10 ++++------ src/pages/home/HeaderView.js | 2 +- 8 files changed, 16 insertions(+), 35 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.js b/src/components/DisplayNames/DisplayNamesWithTooltip.js index 2ded525225ee..27760b75de02 100644 --- a/src/components/DisplayNames/DisplayNamesWithTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithTooltip.js @@ -7,7 +7,6 @@ import Tooltip from '../Tooltip'; import DisplayNamesTooltipItem from './DisplayNamesTooltipItem'; import {defaultProps, propTypes} from './displayNamesPropTypes'; import RenderHTML from '../RenderHTML'; -import * as ReportUtils from '../../libs/ReportUtils'; function DisplayNamesWithToolTip(props) { const containerRef = useRef(null); @@ -50,10 +49,7 @@ function DisplayNamesWithToolTip(props) { return textNodeRight > containerRight ? -(tooltipX - newToolX) : 0; }, []); - let fullTitleHtml = ReportUtils.getReportName(props.report, undefined, true, _.get(containerRef.current, 'offsetWidth')); - fullTitleHtml = ReportUtils.containsHtml(fullTitleHtml) ? fullTitleHtml : ''; - - const fullTitle = fullTitleHtml ? : props.fullTitle; + const fullTitle = props.fullTitleHtml ? : props.fullTitle; return ( // Tokenization of string only support prop numberOfLines on Web diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js index 59cefe9b5f6b..892bf1964568 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js @@ -1,18 +1,15 @@ import PropTypes from 'prop-types'; -import React, {useRef} from 'react'; -import {get} from 'lodash'; +import React from 'react'; import styles from '../../styles/styles'; import Text from '../Text'; import RenderHTML from '../RenderHTML'; -import * as ReportUtils from '../../libs/ReportUtils'; -import reportPropTypes from '../../pages/reportPropTypes'; const propTypes = { /** The full title of the DisplayNames component (not split up) */ fullTitle: PropTypes.string, - /** The report currently being looked at */ - report: reportPropTypes, + /** The full title of the DisplayNames component in HTML */ + fullTitleHtml: PropTypes.string, /** Arbitrary styles of the displayName text */ // eslint-disable-next-line react/forbid-prop-types @@ -24,23 +21,18 @@ const propTypes = { const defaultProps = { fullTitle: '', + fullTitleHtml: '', textStyles: [], - report: {}, numberOfLines: 1, }; -function DisplayNamesWithoutTooltip({textStyles, numberOfLines, fullTitle, report}) { - const ref = useRef(); - let fullTitleHtml = ReportUtils.getReportName(report, undefined, true, get(ref.current, 'offsetWidth')); - fullTitleHtml = ReportUtils.containsHtml(fullTitleHtml) ? fullTitleHtml : ''; - +function DisplayNamesWithoutTooltip({textStyles, numberOfLines, fullTitle, fullTitleHtml}) { const title = fullTitleHtml ? : fullTitle; return ( {title} diff --git a/src/components/DisplayNames/displayNamesPropTypes.js b/src/components/DisplayNames/displayNamesPropTypes.js index ca1c8aec34b9..b1cbf6a15e45 100644 --- a/src/components/DisplayNames/displayNamesPropTypes.js +++ b/src/components/DisplayNames/displayNamesPropTypes.js @@ -1,12 +1,11 @@ import PropTypes from 'prop-types'; -import reportPropTypes from '../../pages/reportPropTypes'; const propTypes = { /** The full title of the DisplayNames component (not split up) */ fullTitle: PropTypes.string, - /** The report currently being looked at */ - report: reportPropTypes, + /** The full title of the DisplayNames component in HTML */ + fullTitleHtml: PropTypes.string, /** Array of objects that map display names to their corresponding tooltip */ displayNamesWithTooltips: PropTypes.arrayOf( @@ -39,7 +38,6 @@ const defaultProps = { numberOfLines: 1, tooltipEnabled: false, titleStyles: [], - report: {}, }; export {propTypes, defaultProps}; diff --git a/src/components/DisplayNames/index.js b/src/components/DisplayNames/index.js index 1d5a164ea776..10591ddba614 100644 --- a/src/components/DisplayNames/index.js +++ b/src/components/DisplayNames/index.js @@ -10,7 +10,7 @@ function DisplayNames(props) { textStyles={props.textStyles} numberOfLines={props.numberOfLines} fullTitle={props.fullTitle} - report={props.report} + fullTitleHtml={props.fullTitleHtml} /> ); } diff --git a/src/components/DisplayNames/index.native.js b/src/components/DisplayNames/index.native.js index d9c3f02cfd52..f01805e163de 100644 --- a/src/components/DisplayNames/index.native.js +++ b/src/components/DisplayNames/index.native.js @@ -2,14 +2,10 @@ import React from 'react'; import {propTypes, defaultProps} from './displayNamesPropTypes'; import Text from '../Text'; import RenderHTML from '../RenderHTML'; -import * as ReportUtils from '../../libs/ReportUtils'; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. function DisplayNames(props) { - let fullTitleHtml = ReportUtils.getReportName(props.report, undefined, true); - fullTitleHtml = ReportUtils.containsHtml(fullTitleHtml) ? fullTitleHtml : ''; - - const fullTitle = fullTitleHtml ? : props.fullTitle; + const fullTitle = props.fullTitleHtml ? : props.fullTitle; return ( diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 5ff99fa439d4..a97e86ea4e77 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1643,12 +1643,11 @@ function getThreadReportNameHtml(parentReportActionMessage) { * * @param {String} parentReportActionMessage * @param {Boolean} shouldRenderHTML - * @param {Number} width * @returns {String} */ -function getThreadReportName(parentReportActionMessage, shouldRenderHTML, width = 0) { +function getThreadReportName(parentReportActionMessage, shouldRenderHTML) { const threadReportNameHtml = getThreadReportNameHtml(parentReportActionMessage); - return shouldRenderHTML ? `${threadReportNameHtml}` : Str.stripHTML(threadReportNameHtml).trim(); + return shouldRenderHTML ? `${threadReportNameHtml}` : Str.stripHTML(threadReportNameHtml).trim(); } /** @@ -1657,10 +1656,9 @@ function getThreadReportName(parentReportActionMessage, shouldRenderHTML, width * @param {Object} report * @param {Object} [policy] * @param {Boolean} shouldRenderHTML - * @param {Number} width * @returns {String} */ -function getReportName(report, policy = undefined, shouldRenderHTML = false, width = 0) { +function getReportName(report, policy = undefined, shouldRenderHTML = false) { let formattedName; if (isChatThread(report)) { const parentReportAction = ReportActionsUtils.getParentReportAction(report); @@ -1669,7 +1667,7 @@ function getReportName(report, policy = undefined, shouldRenderHTML = false, wid } const isAttachment = ReportActionsUtils.isReportActionAttachment(parentReportAction); - const parentReportActionMessage = getThreadReportName(lodashGet(parentReportAction, ['message', 0, 'html']), shouldRenderHTML, width); + const parentReportActionMessage = getThreadReportName(lodashGet(parentReportAction, ['message', 0, 'html']), shouldRenderHTML); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; } diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 58f238fe1011..4dd0527473b8 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -183,7 +183,7 @@ function HeaderView(props) { Date: Tue, 19 Sep 2023 06:04:08 +0700 Subject: [PATCH 18/49] fix ellipsis header --- src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js | 2 +- src/libs/ReportUtils.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js index 734b0aac6df7..17d156d233b4 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js @@ -55,7 +55,7 @@ const customHTMLElementModels = { 'mention-here': defaultHTMLElementModels.span.extend({tagName: 'mention-here'}), }; -const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText]}; +const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText, styles.dFlex, styles.flexShrink1]}; // We are using the explicit composite architecture for performance gains. // Configuration for RenderHTML is handled in a top-level component providing diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index a97e86ea4e77..5aff72afcc0d 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1647,7 +1647,7 @@ function getThreadReportNameHtml(parentReportActionMessage) { */ function getThreadReportName(parentReportActionMessage, shouldRenderHTML) { const threadReportNameHtml = getThreadReportNameHtml(parentReportActionMessage); - return shouldRenderHTML ? `${threadReportNameHtml}` : Str.stripHTML(threadReportNameHtml).trim(); + return shouldRenderHTML ? `${threadReportNameHtml}` : Str.stripHTML(threadReportNameHtml).trim(); } /** From 55b01cf53be43280ebb4e88dcbb520c6d6f6f68d Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 27 Sep 2023 08:24:55 +0700 Subject: [PATCH 19/49] render html in LHN and report details page --- .../CountryPicker/CountrySelectorModal.js | 2 +- .../DisplayNames/DisplayNamesWithTooltip.js | 3 ++- .../DisplayNamesWithoutTooltip.js | 9 +++---- .../DisplayNames/displayNamesPropTypes.js | 3 --- src/components/DisplayNames/index.js | 1 - src/components/DisplayNames/index.native.js | 3 ++- .../StatePicker/StateSelectorModal.js | 2 +- src/libs/ReportUtils.js | 26 +++++++------------ src/libs/SidebarUtils.js | 2 +- src/libs/StringUtils.ts | 14 +++++++++- src/libs/searchCountryOptions.ts | 2 +- src/pages/ReportDetailsPage.js | 3 ++- src/pages/home/HeaderView.js | 4 +-- 13 files changed, 38 insertions(+), 36 deletions(-) diff --git a/src/components/CountryPicker/CountrySelectorModal.js b/src/components/CountryPicker/CountrySelectorModal.js index 6c6cd19af0c7..eb0d5998e864 100644 --- a/src/components/CountryPicker/CountrySelectorModal.js +++ b/src/components/CountryPicker/CountrySelectorModal.js @@ -9,7 +9,7 @@ import Modal from '../Modal'; import ScreenWrapper from '../ScreenWrapper'; import styles from '../../styles/styles'; import searchCountryOptions from '../../libs/searchCountryOptions'; -import StringUtils from '../../libs/StringUtils'; +import * as StringUtils from '../../libs/StringUtils'; const propTypes = { /** Whether the modal is visible */ diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.js b/src/components/DisplayNames/DisplayNamesWithTooltip.js index 27760b75de02..b598c250ec23 100644 --- a/src/components/DisplayNames/DisplayNamesWithTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithTooltip.js @@ -7,6 +7,7 @@ import Tooltip from '../Tooltip'; import DisplayNamesTooltipItem from './DisplayNamesTooltipItem'; import {defaultProps, propTypes} from './displayNamesPropTypes'; import RenderHTML from '../RenderHTML'; +import * as StringUtils from "../../libs/StringUtils"; function DisplayNamesWithToolTip(props) { const containerRef = useRef(null); @@ -49,7 +50,7 @@ function DisplayNamesWithToolTip(props) { return textNodeRight > containerRight ? -(tooltipX - newToolX) : 0; }, []); - const fullTitle = props.fullTitleHtml ? : props.fullTitle; + const fullTitle = StringUtils.containsHtml(props.fullTitle) ? : props.fullTitle; return ( // Tokenization of string only support prop numberOfLines on Web diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js index 892bf1964568..d09eeafcf1ed 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js @@ -3,14 +3,12 @@ import React from 'react'; import styles from '../../styles/styles'; import Text from '../Text'; import RenderHTML from '../RenderHTML'; +import * as StringUtils from "../../libs/StringUtils"; const propTypes = { /** The full title of the DisplayNames component (not split up) */ fullTitle: PropTypes.string, - /** The full title of the DisplayNames component in HTML */ - fullTitleHtml: PropTypes.string, - /** Arbitrary styles of the displayName text */ // eslint-disable-next-line react/forbid-prop-types textStyles: PropTypes.arrayOf(PropTypes.object), @@ -21,13 +19,12 @@ const propTypes = { const defaultProps = { fullTitle: '', - fullTitleHtml: '', textStyles: [], numberOfLines: 1, }; -function DisplayNamesWithoutTooltip({textStyles, numberOfLines, fullTitle, fullTitleHtml}) { - const title = fullTitleHtml ? : fullTitle; +function DisplayNamesWithoutTooltip({textStyles, numberOfLines, fullTitle}) { + const title = StringUtils.containsHtml(fullTitle) ? : fullTitle; return ( ); } diff --git a/src/components/DisplayNames/index.native.js b/src/components/DisplayNames/index.native.js index f01805e163de..588f743ebc55 100644 --- a/src/components/DisplayNames/index.native.js +++ b/src/components/DisplayNames/index.native.js @@ -2,10 +2,11 @@ import React from 'react'; import {propTypes, defaultProps} from './displayNamesPropTypes'; import Text from '../Text'; import RenderHTML from '../RenderHTML'; +import * as StringUtils from '../../libs/StringUtils'; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. function DisplayNames(props) { - const fullTitle = props.fullTitleHtml ? : props.fullTitle; + const fullTitle = StringUtils.containsHtml(props.fullTitle) ? : props.fullTitle; return ( /i.test(text); -} - /** * Get the proper message schema for modified expense message. * @@ -1744,13 +1735,17 @@ function getThreadReportNameHtml(parentReportActionMessage) { * Get the title for a thread based on parent message. * Only the first line of the message should display. * - * @param {String} parentReportActionMessage + * @param {Object} parentReportAction * @param {Boolean} shouldRenderHTML * @returns {String} */ -function getThreadReportName(parentReportActionMessage, shouldRenderHTML) { - const threadReportNameHtml = getThreadReportNameHtml(parentReportActionMessage); - return shouldRenderHTML ? `${threadReportNameHtml}` : Str.stripHTML(threadReportNameHtml).trim(); +function getThreadReportName(parentReportAction, shouldRenderHTML) { + if (!shouldRenderHTML) { + return lodashGet(parentReportAction, ['message', 0, 'text']); + } + + const threadReportNameHtml = getThreadReportNameHtml(lodashGet(parentReportAction, ['message', 0, 'html'])); + return StringUtils.containsHtml(threadReportNameHtml) ? `${threadReportNameHtml}` : threadReportNameHtml; } /** @@ -1770,7 +1765,7 @@ function getReportName(report, policy = undefined, shouldRenderHTML = false) { } const isAttachment = ReportActionsUtils.isReportActionAttachment(parentReportAction); - const parentReportActionMessage = getThreadReportName(lodashGet(parentReportAction, ['message', 0, 'html']), shouldRenderHTML); + const parentReportActionMessage = getThreadReportName(parentReportAction, shouldRenderHTML); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; } @@ -3780,7 +3775,6 @@ export { getIcons, getRoomWelcomeMessage, getDisplayNamesWithTooltips, - containsHtml, getThreadReportNameHtml, getReportName, getReport, diff --git a/src/libs/SidebarUtils.js b/src/libs/SidebarUtils.js index df676f23ebc7..be15cb180a69 100644 --- a/src/libs/SidebarUtils.js +++ b/src/libs/SidebarUtils.js @@ -390,7 +390,7 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale, result.phoneNumber = personalDetail.phoneNumber; } - const reportName = ReportUtils.getReportName(report, policy); + const reportName = ReportUtils.getReportName(report, policy, true); result.text = reportName; result.subtitle = subtitle; diff --git a/src/libs/StringUtils.ts b/src/libs/StringUtils.ts index 8ef23bb0751b..1082530e0878 100644 --- a/src/libs/StringUtils.ts +++ b/src/libs/StringUtils.ts @@ -1,6 +1,15 @@ import _ from 'lodash'; import CONST from '../CONST'; +/** + * Check if the text contains HTML + * @param text + * @return whether the text contains HTML + */ +function containsHtml(text: string): boolean { + return /<\/?[a-z][\s\S]*>/i.test(text); +} + /** * Removes diacritical marks and non-alphabetic and non-latin characters from a string. * @param str - The input string to be sanitized. @@ -10,4 +19,7 @@ function sanitizeString(str: string): string { return _.deburr(str).toLowerCase().replaceAll(CONST.REGEX.NON_ALPHABETIC_AND_NON_LATIN_CHARS, ''); } -export default {sanitizeString}; +export { + containsHtml, + sanitizeString +}; diff --git a/src/libs/searchCountryOptions.ts b/src/libs/searchCountryOptions.ts index 8fb1cc9c37f3..50abefe9d6c7 100644 --- a/src/libs/searchCountryOptions.ts +++ b/src/libs/searchCountryOptions.ts @@ -1,4 +1,4 @@ -import StringUtils from './StringUtils'; +import * as StringUtils from './StringUtils'; type CountryData = { value: string; diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 98dae2e94780..171c750ed4f1 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -66,6 +66,7 @@ function ReportDetailsPage(props) { const isThread = useMemo(() => ReportUtils.isChatThread(props.report), [props.report]); const isUserCreatedPolicyRoom = useMemo(() => ReportUtils.isUserCreatedPolicyRoom(props.report), [props.report]); const isArchivedRoom = useMemo(() => ReportUtils.isArchivedRoom(props.report), [props.report]); + const title = ReportUtils.getReportName(props.report, undefined, true); // eslint-disable-next-line react-hooks/exhaustive-deps -- policy is a dependency because `getChatRoomSubtitle` calls `getPolicyName` which in turn retrieves the value from the `policy` value stored in Onyx const chatRoomSubtitle = useMemo(() => ReportUtils.getChatRoomSubtitle(props.report), [props.report, policy]); @@ -163,7 +164,7 @@ function ReportDetailsPage(props) { {!_.isEmpty(parentNavigationSubtitleData) && ( From 96da21f35a5afa0dfb9203afe8173eb7ffe1b7c3 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 27 Sep 2023 08:38:56 +0700 Subject: [PATCH 20/49] display one line text --- src/components/LHNOptionsList/OptionRowLHN.js | 2 +- src/pages/ReportDetailsPage.js | 3 ++- src/pages/home/HeaderView.js | 4 ++-- src/styles/StyleUtils.ts | 9 +++++++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/components/LHNOptionsList/OptionRowLHN.js b/src/components/LHNOptionsList/OptionRowLHN.js index 91b582221171..0954ff2033ff 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.js +++ b/src/components/LHNOptionsList/OptionRowLHN.js @@ -82,7 +82,7 @@ function OptionRowLHN(props) { const textStyle = props.isFocused ? styles.sidebarLinkActiveText : styles.sidebarLinkText; const textUnreadStyle = optionItem.isUnread ? [textStyle, styles.sidebarLinkTextBold] : [textStyle]; - const displayNameStyle = StyleUtils.combineStyles([styles.optionDisplayName, styles.optionDisplayNameCompact, styles.pre, ...textUnreadStyle], props.style); + const displayNameStyle = StyleUtils.combineStyles([styles.optionDisplayName, styles.optionDisplayNameCompact, styles.pre, StyleUtils.getHeightOfRenderHtmlTextOneLine(), ...textUnreadStyle], props.style); const alternateTextStyle = StyleUtils.combineStyles( props.viewMode === CONST.OPTION_MODE.COMPACT ? [textStyle, styles.optionAlternateText, styles.noWrap, styles.textLabelSupporting, styles.optionAlternateTextCompact, styles.ml2] diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 171c750ed4f1..e8c7b5a914cc 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -27,6 +27,7 @@ import reportPropTypes from './reportPropTypes'; import withReportOrNotFound from './home/report/withReportOrNotFound'; import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundView'; import PressableWithoutFeedback from '../components/Pressable/PressableWithoutFeedback'; +import * as StyleUtils from "../styles/StyleUtils"; const propTypes = { ...withLocalizePropTypes, @@ -168,7 +169,7 @@ function ReportDetailsPage(props) { displayNamesWithTooltips={displayNamesWithTooltips} tooltipEnabled numberOfLines={isChatRoom && !isThread ? 0 : 1} - textStyles={[styles.textHeadline, styles.textAlignCenter, isChatRoom && !isThread ? undefined : styles.pre]} + textStyles={[styles.textHeadline, styles.textAlignCenter, isChatRoom && !isThread ? undefined : StyleUtils.combineStyles(styles.pre, StyleUtils.getHeightOfRenderHtmlTextOneLine())]} shouldUseFullTitle={shouldUseFullTitle} /> diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index af39d48e706b..9e9efa690af3 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -30,6 +30,7 @@ import PressableWithoutFeedback from '../../components/Pressable/PressableWithou import PinButton from '../../components/PinButton'; import TaskHeaderActionButton from '../../components/TaskHeaderActionButton'; import * as ReportActionsUtils from '../../libs/ReportActionsUtils'; +import * as StyleUtils from '../../styles/StyleUtils'; import ParentNavigationSubtitle from '../../components/ParentNavigationSubtitle'; const propTypes = { @@ -137,7 +138,6 @@ function HeaderView(props) { const brickRoadIndicator = ReportUtils.hasReportNameError(props.report) ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : ''; const shouldShowBorderBottom = !isTaskReport || !props.isSmallScreenWidth; const shouldDisableDetailPage = ReportUtils.shouldDisableDetailPage(props.report); - const headerTextHeight = styles.webViewStyles.baseFontStyle.lineHeight; return ( {!_.isEmpty(parentNavigationSubtitleData) && ( diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 190f18f8d969..d511624f61a3 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -154,6 +154,14 @@ function getAvatarSize(size: AvatarSizeName): number { return avatarSizes[size]; } +/** + * Return the height of RenderHtml text container with numberOfLines=1 + */ +function getHeightOfRenderHtmlTextOneLine(): ViewStyle | CSSProperties { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return {height: styles.webViewStyles.baseFontStyle.lineHeight}; +} + /** * Return the height of magic code input container */ @@ -1282,6 +1290,7 @@ export { getMentionStyle, getMentionTextColor, getComposeTextAreaPadding, + getHeightOfRenderHtmlTextOneLine, getHeightOfMagicCodeInput, getOuterModalStyle, getWrappingStyle, From 99eff2e4ea3057dc9c9fde3c5b6d67c1d886c7df Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 27 Sep 2023 08:58:57 +0700 Subject: [PATCH 21/49] adjust height of render html container --- src/components/DisplayNames/DisplayNamesWithTooltip.js | 6 ++++-- src/components/DisplayNames/DisplayNamesWithoutTooltip.js | 6 ++++-- src/components/DisplayNames/index.native.js | 6 ++++-- src/components/LHNOptionsList/OptionRowLHN.js | 2 +- src/pages/ReportDetailsPage.js | 3 +-- src/pages/home/HeaderView.js | 2 +- src/styles/StyleUtils.ts | 7 +++---- 7 files changed, 18 insertions(+), 14 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.js b/src/components/DisplayNames/DisplayNamesWithTooltip.js index b598c250ec23..87d1a11d39b7 100644 --- a/src/components/DisplayNames/DisplayNamesWithTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithTooltip.js @@ -8,6 +8,7 @@ import DisplayNamesTooltipItem from './DisplayNamesTooltipItem'; import {defaultProps, propTypes} from './displayNamesPropTypes'; import RenderHTML from '../RenderHTML'; import * as StringUtils from "../../libs/StringUtils"; +import * as StyleUtils from "../../styles/StyleUtils"; function DisplayNamesWithToolTip(props) { const containerRef = useRef(null); @@ -50,12 +51,13 @@ function DisplayNamesWithToolTip(props) { return textNodeRight > containerRight ? -(tooltipX - newToolX) : 0; }, []); - const fullTitle = StringUtils.containsHtml(props.fullTitle) ? : props.fullTitle; + const containsHtml = StringUtils.containsHtml(props.fullTitle); + const fullTitle = containsHtml ? : props.fullTitle; return ( // Tokenization of string only support prop numberOfLines on Web (containerRef.current = el)} > diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js index d09eeafcf1ed..abfdf43424d0 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js @@ -4,6 +4,7 @@ import styles from '../../styles/styles'; import Text from '../Text'; import RenderHTML from '../RenderHTML'; import * as StringUtils from "../../libs/StringUtils"; +import * as StyleUtils from "../../styles/StyleUtils"; const propTypes = { /** The full title of the DisplayNames component (not split up) */ @@ -24,11 +25,12 @@ const defaultProps = { }; function DisplayNamesWithoutTooltip({textStyles, numberOfLines, fullTitle}) { - const title = StringUtils.containsHtml(fullTitle) ? : fullTitle; + const containsHtml = StringUtils.containsHtml(fullTitle); + const title = containsHtml ? : fullTitle; return ( {title} diff --git a/src/components/DisplayNames/index.native.js b/src/components/DisplayNames/index.native.js index 588f743ebc55..2e713bf1ef3a 100644 --- a/src/components/DisplayNames/index.native.js +++ b/src/components/DisplayNames/index.native.js @@ -3,15 +3,17 @@ import {propTypes, defaultProps} from './displayNamesPropTypes'; import Text from '../Text'; import RenderHTML from '../RenderHTML'; import * as StringUtils from '../../libs/StringUtils'; +import * as StyleUtils from "../../styles/StyleUtils"; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. function DisplayNames(props) { - const fullTitle = StringUtils.containsHtml(props.fullTitle) ? : props.fullTitle; + const containsHtml = StringUtils.containsHtml(props.fullTitle); + const fullTitle = containsHtml ? : props.fullTitle; return ( {fullTitle} diff --git a/src/components/LHNOptionsList/OptionRowLHN.js b/src/components/LHNOptionsList/OptionRowLHN.js index 0954ff2033ff..91b582221171 100644 --- a/src/components/LHNOptionsList/OptionRowLHN.js +++ b/src/components/LHNOptionsList/OptionRowLHN.js @@ -82,7 +82,7 @@ function OptionRowLHN(props) { const textStyle = props.isFocused ? styles.sidebarLinkActiveText : styles.sidebarLinkText; const textUnreadStyle = optionItem.isUnread ? [textStyle, styles.sidebarLinkTextBold] : [textStyle]; - const displayNameStyle = StyleUtils.combineStyles([styles.optionDisplayName, styles.optionDisplayNameCompact, styles.pre, StyleUtils.getHeightOfRenderHtmlTextOneLine(), ...textUnreadStyle], props.style); + const displayNameStyle = StyleUtils.combineStyles([styles.optionDisplayName, styles.optionDisplayNameCompact, styles.pre, ...textUnreadStyle], props.style); const alternateTextStyle = StyleUtils.combineStyles( props.viewMode === CONST.OPTION_MODE.COMPACT ? [textStyle, styles.optionAlternateText, styles.noWrap, styles.textLabelSupporting, styles.optionAlternateTextCompact, styles.ml2] diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index e8c7b5a914cc..171c750ed4f1 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -27,7 +27,6 @@ import reportPropTypes from './reportPropTypes'; import withReportOrNotFound from './home/report/withReportOrNotFound'; import FullPageNotFoundView from '../components/BlockingViews/FullPageNotFoundView'; import PressableWithoutFeedback from '../components/Pressable/PressableWithoutFeedback'; -import * as StyleUtils from "../styles/StyleUtils"; const propTypes = { ...withLocalizePropTypes, @@ -169,7 +168,7 @@ function ReportDetailsPage(props) { displayNamesWithTooltips={displayNamesWithTooltips} tooltipEnabled numberOfLines={isChatRoom && !isThread ? 0 : 1} - textStyles={[styles.textHeadline, styles.textAlignCenter, isChatRoom && !isThread ? undefined : StyleUtils.combineStyles(styles.pre, StyleUtils.getHeightOfRenderHtmlTextOneLine())]} + textStyles={[styles.textHeadline, styles.textAlignCenter, isChatRoom && !isThread ? undefined : styles.pre]} shouldUseFullTitle={shouldUseFullTitle} /> diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 9e9efa690af3..c162a8ede16b 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -190,7 +190,7 @@ function HeaderView(props) { displayNamesWithTooltips={displayNamesWithTooltips} tooltipEnabled numberOfLines={1} - textStyles={[styles.headerText, styles.pre, StyleUtils.getHeightOfRenderHtmlTextOneLine()]} + textStyles={[styles.headerText, styles.pre]} shouldUseFullTitle={isChatRoom || isPolicyExpenseChat || isChatThread || isTaskReport} /> {!_.isEmpty(parentNavigationSubtitleData) && ( diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index d511624f61a3..5ede212d87ff 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -157,9 +157,8 @@ function getAvatarSize(size: AvatarSizeName): number { /** * Return the height of RenderHtml text container with numberOfLines=1 */ -function getHeightOfRenderHtmlTextOneLine(): ViewStyle | CSSProperties { - // eslint-disable-next-line @typescript-eslint/no-unsafe-return - return {height: styles.webViewStyles.baseFontStyle.lineHeight}; +function getHeightOfRenderHtmlText(numberOfLines: number): ViewStyle | CSSProperties { + return numberOfLines === 1 ? {height: styles.webViewStyles.baseFontStyle.lineHeight} : {}; } /** @@ -1290,7 +1289,7 @@ export { getMentionStyle, getMentionTextColor, getComposeTextAreaPadding, - getHeightOfRenderHtmlTextOneLine, + getHeightOfRenderHtmlText, getHeightOfMagicCodeInput, getOuterModalStyle, getWrappingStyle, From 488e9d9f8f52de955a6839f14e3f848499972f76 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 27 Sep 2023 09:04:10 +0700 Subject: [PATCH 22/49] adjust height on native only --- src/components/DisplayNames/DisplayNamesWithTooltip.js | 6 ++---- src/components/DisplayNames/DisplayNamesWithoutTooltip.js | 5 ++--- src/components/DisplayNames/index.native.js | 3 ++- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.js b/src/components/DisplayNames/DisplayNamesWithTooltip.js index 87d1a11d39b7..b598c250ec23 100644 --- a/src/components/DisplayNames/DisplayNamesWithTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithTooltip.js @@ -8,7 +8,6 @@ import DisplayNamesTooltipItem from './DisplayNamesTooltipItem'; import {defaultProps, propTypes} from './displayNamesPropTypes'; import RenderHTML from '../RenderHTML'; import * as StringUtils from "../../libs/StringUtils"; -import * as StyleUtils from "../../styles/StyleUtils"; function DisplayNamesWithToolTip(props) { const containerRef = useRef(null); @@ -51,13 +50,12 @@ function DisplayNamesWithToolTip(props) { return textNodeRight > containerRight ? -(tooltipX - newToolX) : 0; }, []); - const containsHtml = StringUtils.containsHtml(props.fullTitle); - const fullTitle = containsHtml ? : props.fullTitle; + const fullTitle = StringUtils.containsHtml(props.fullTitle) ? : props.fullTitle; return ( // Tokenization of string only support prop numberOfLines on Web (containerRef.current = el)} > diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js index abfdf43424d0..3e3ad58182f4 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js @@ -25,12 +25,11 @@ const defaultProps = { }; function DisplayNamesWithoutTooltip({textStyles, numberOfLines, fullTitle}) { - const containsHtml = StringUtils.containsHtml(fullTitle); - const title = containsHtml ? : fullTitle; + const title = StringUtils.containsHtml(fullTitle) ? : fullTitle; return ( {title} diff --git a/src/components/DisplayNames/index.native.js b/src/components/DisplayNames/index.native.js index 2e713bf1ef3a..6923edbc7199 100644 --- a/src/components/DisplayNames/index.native.js +++ b/src/components/DisplayNames/index.native.js @@ -13,7 +13,8 @@ function DisplayNames(props) { return ( {fullTitle} From 72209df3de29258feaf48e038927e5991cf2e145 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 27 Sep 2023 09:06:35 +0700 Subject: [PATCH 23/49] truncate text on web --- src/libs/ReportUtils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 5d1879a43ae1..823fedf0919a 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1745,7 +1745,8 @@ function getThreadReportName(parentReportAction, shouldRenderHTML) { } const threadReportNameHtml = getThreadReportNameHtml(lodashGet(parentReportAction, ['message', 0, 'html'])); - return StringUtils.containsHtml(threadReportNameHtml) ? `${threadReportNameHtml}` : threadReportNameHtml; + // is to prevent the redundant body div which causes text overflown + return StringUtils.containsHtml(threadReportNameHtml) ? `${threadReportNameHtml}` : threadReportNameHtml; } /** From e27a398027877ddc01130e31ea1bacef119333d2 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 27 Sep 2023 09:09:38 +0700 Subject: [PATCH 24/49] do not render html in details page --- src/pages/ReportDetailsPage.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/ReportDetailsPage.js b/src/pages/ReportDetailsPage.js index 171c750ed4f1..98dae2e94780 100644 --- a/src/pages/ReportDetailsPage.js +++ b/src/pages/ReportDetailsPage.js @@ -66,7 +66,6 @@ function ReportDetailsPage(props) { const isThread = useMemo(() => ReportUtils.isChatThread(props.report), [props.report]); const isUserCreatedPolicyRoom = useMemo(() => ReportUtils.isUserCreatedPolicyRoom(props.report), [props.report]); const isArchivedRoom = useMemo(() => ReportUtils.isArchivedRoom(props.report), [props.report]); - const title = ReportUtils.getReportName(props.report, undefined, true); // eslint-disable-next-line react-hooks/exhaustive-deps -- policy is a dependency because `getChatRoomSubtitle` calls `getPolicyName` which in turn retrieves the value from the `policy` value stored in Onyx const chatRoomSubtitle = useMemo(() => ReportUtils.getChatRoomSubtitle(props.report), [props.report, policy]); @@ -164,7 +163,7 @@ function ReportDetailsPage(props) { Date: Wed, 27 Sep 2023 09:17:20 +0700 Subject: [PATCH 25/49] fix lint --- src/components/DisplayNames/DisplayNamesWithTooltip.js | 2 +- src/components/DisplayNames/DisplayNamesWithoutTooltip.js | 4 ++-- src/components/DisplayNames/index.native.js | 4 ++-- src/libs/StringUtils.ts | 5 +---- 4 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.js b/src/components/DisplayNames/DisplayNamesWithTooltip.js index b598c250ec23..aaff459decd2 100644 --- a/src/components/DisplayNames/DisplayNamesWithTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithTooltip.js @@ -7,7 +7,7 @@ import Tooltip from '../Tooltip'; import DisplayNamesTooltipItem from './DisplayNamesTooltipItem'; import {defaultProps, propTypes} from './displayNamesPropTypes'; import RenderHTML from '../RenderHTML'; -import * as StringUtils from "../../libs/StringUtils"; +import * as StringUtils from '../../libs/StringUtils'; function DisplayNamesWithToolTip(props) { const containerRef = useRef(null); diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js index 3e3ad58182f4..cb7b20c46e0f 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js @@ -3,8 +3,8 @@ import React from 'react'; import styles from '../../styles/styles'; import Text from '../Text'; import RenderHTML from '../RenderHTML'; -import * as StringUtils from "../../libs/StringUtils"; -import * as StyleUtils from "../../styles/StyleUtils"; +import * as StringUtils from '../../libs/StringUtils'; +import * as StyleUtils from '../../styles/StyleUtils'; const propTypes = { /** The full title of the DisplayNames component (not split up) */ diff --git a/src/components/DisplayNames/index.native.js b/src/components/DisplayNames/index.native.js index 6923edbc7199..7bfd71bb419f 100644 --- a/src/components/DisplayNames/index.native.js +++ b/src/components/DisplayNames/index.native.js @@ -3,7 +3,7 @@ import {propTypes, defaultProps} from './displayNamesPropTypes'; import Text from '../Text'; import RenderHTML from '../RenderHTML'; import * as StringUtils from '../../libs/StringUtils'; -import * as StyleUtils from "../../styles/StyleUtils"; +import * as StyleUtils from '../../styles/StyleUtils'; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. function DisplayNames(props) { @@ -14,7 +14,7 @@ function DisplayNames(props) { {fullTitle} diff --git a/src/libs/StringUtils.ts b/src/libs/StringUtils.ts index 1082530e0878..06c0d5b36f81 100644 --- a/src/libs/StringUtils.ts +++ b/src/libs/StringUtils.ts @@ -19,7 +19,4 @@ function sanitizeString(str: string): string { return _.deburr(str).toLowerCase().replaceAll(CONST.REGEX.NON_ALPHABETIC_AND_NON_LATIN_CHARS, ''); } -export { - containsHtml, - sanitizeString -}; +export {containsHtml, sanitizeString}; From 759ec0e43bfa1a098154031e4c6fce34eed157c4 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 27 Sep 2023 09:52:10 +0700 Subject: [PATCH 26/49] remove redundant imports --- .../DisplayNamesWithoutTooltip.js | 1 - src/libs/ReportUtils.js | 22 ------------------- src/pages/home/HeaderView.js | 1 - 3 files changed, 24 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js index cb7b20c46e0f..fe4f4c105dcd 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js @@ -4,7 +4,6 @@ import styles from '../../styles/styles'; import Text from '../Text'; import RenderHTML from '../RenderHTML'; import * as StringUtils from '../../libs/StringUtils'; -import * as StyleUtils from '../../styles/StyleUtils'; const propTypes = { /** The full title of the DisplayNames component (not split up) */ diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 913bf0d503b7..8d00a56f4ca4 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -3078,28 +3078,6 @@ function getChatByParticipants(newParticipantList) { }); } -/** - * Attempts to find a report in onyx with the provided email list of participants. Does not include threads - * This is temporary function while migrating from PersonalDetails to PersonalDetailsList - * - * @deprecated - use getChatByParticipants() - * - * @param {Array} participantsLoginList - * @returns {Array|undefined} - */ -function getChatByParticipantsByLoginList(participantsLoginList) { - participantsLoginList.sort(); - return _.find(allReports, (report) => { - // If the report has been deleted, or there are no participants (like an empty #admins room) then skip it - if (!report || _.isEmpty(report.participantAccountIDs) || isThread(report)) { - return false; - } - - // Only return the room if it has all the participants and is not a policy room - return !isUserCreatedPolicyRoom(report) && _.isEqual(participantsLoginList, _.sortBy(report.participants)); - }); -} - /** * Attempts to find a report in onyx with the provided list of participants in given policy * @param {Array} newParticipantList diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index c162a8ede16b..9721d7769f44 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -30,7 +30,6 @@ import PressableWithoutFeedback from '../../components/Pressable/PressableWithou import PinButton from '../../components/PinButton'; import TaskHeaderActionButton from '../../components/TaskHeaderActionButton'; import * as ReportActionsUtils from '../../libs/ReportActionsUtils'; -import * as StyleUtils from '../../styles/StyleUtils'; import ParentNavigationSubtitle from '../../components/ParentNavigationSubtitle'; const propTypes = { From 99fe8dd05521aa730f169d60d691da97d0de35e8 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 5 Oct 2023 01:48:35 +0700 Subject: [PATCH 27/49] resolve conflicts --- src/components/CountryPicker/CountrySelectorModal.js | 0 .../settings/Profile/PersonalDetails/CountrySelectionPage.js | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 src/components/CountryPicker/CountrySelectorModal.js diff --git a/src/components/CountryPicker/CountrySelectorModal.js b/src/components/CountryPicker/CountrySelectorModal.js deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js index 741974776df1..ef19ba6aad9d 100644 --- a/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js +++ b/src/pages/settings/Profile/PersonalDetails/CountrySelectionPage.js @@ -7,7 +7,7 @@ import ScreenWrapper from '../../../../components/ScreenWrapper'; import HeaderWithBackButton from '../../../../components/HeaderWithBackButton'; import SelectionList from '../../../../components/SelectionList'; import searchCountryOptions from '../../../../libs/searchCountryOptions'; -import StringUtils from '../../../../libs/StringUtils'; +import * as StringUtils from '../../../../libs/StringUtils'; import CONST from '../../../../CONST'; import useLocalize from '../../../../hooks/useLocalize'; From b630ff09cdd7cd50d8839164b9c4d39848ab5fe9 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 12 Oct 2023 23:42:00 +0700 Subject: [PATCH 28/49] render first line of text in LHN --- src/libs/ReportUtils.js | 21 ++++++++++++++------- src/libs/SidebarUtils.js | 2 +- src/pages/home/HeaderView.js | 2 +- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 6b8727725af7..8cf951b6bdaa 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1790,18 +1790,24 @@ function getThreadReportNameHtml(parentReportActionMessage) { /** * Get the title for a thread based on parent message. - * Only the first line of the message should display. + * If render in html, only the first line of the message should display. * * @param {Object} parentReportAction * @param {Boolean} shouldRenderHTML + * @param {Boolean} shouldRenderFirstLineOnly * @returns {String} */ -function getThreadReportName(parentReportAction, shouldRenderHTML) { - if (!shouldRenderHTML) { - return lodashGet(parentReportAction, ['message', 0, 'text']); +function getThreadReportName(parentReportAction, shouldRenderHTML, shouldRenderFirstLineOnly) { + if (!shouldRenderHTML && !shouldRenderFirstLineOnly) { + return lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); } const threadReportNameHtml = getThreadReportNameHtml(lodashGet(parentReportAction, ['message', 0, 'html'])); + + if (!shouldRenderHTML && shouldRenderFirstLineOnly) { + return Str.stripHTML(threadReportNameHtml); + } + // is to prevent the redundant body div which causes text overflown return StringUtils.containsHtml(threadReportNameHtml) ? `${threadReportNameHtml}` : threadReportNameHtml; } @@ -1811,10 +1817,11 @@ function getThreadReportName(parentReportAction, shouldRenderHTML) { * * @param {Object} report * @param {Object} [policy] - * @param {Boolean} shouldRenderHTML + * @param {Boolean} [shouldRenderHTML] + * @param {Boolean} [shouldRenderFirstLineOnly] * @returns {String} */ -function getReportName(report, policy = undefined, shouldRenderHTML = false) { +function getReportName(report, policy = undefined, shouldRenderHTML = false, shouldRenderFirstLineOnly = false) { let formattedName; const parentReportAction = ReportActionsUtils.getParentReportAction(report); if (isChatThread(report)) { @@ -1823,7 +1830,7 @@ function getReportName(report, policy = undefined, shouldRenderHTML = false) { } const isAttachment = ReportActionsUtils.isReportActionAttachment(parentReportAction); - const parentReportActionMessage = getThreadReportName(parentReportAction, shouldRenderHTML); + const parentReportActionMessage = getThreadReportName(parentReportAction, shouldRenderHTML, shouldRenderFirstLineOnly); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; } diff --git a/src/libs/SidebarUtils.js b/src/libs/SidebarUtils.js index 314a774ec299..b53f638407d7 100644 --- a/src/libs/SidebarUtils.js +++ b/src/libs/SidebarUtils.js @@ -392,7 +392,7 @@ function getOptionData(report, reportActions, personalDetails, preferredLocale, result.phoneNumber = personalDetail.phoneNumber; } - const reportName = ReportUtils.getReportName(report, policy, true); + const reportName = ReportUtils.getReportName(report, policy, false, true); result.text = reportName; result.subtitle = subtitle; diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index fc913fb201e0..1c33fb67ecaf 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -79,7 +79,7 @@ function HeaderView(props) { const isPolicyExpenseChat = ReportUtils.isPolicyExpenseChat(props.report); const isTaskReport = ReportUtils.isTaskReport(props.report); const reportHeaderData = !isTaskReport && !isChatThread && props.report.parentReportID ? props.parentReport : props.report; - const title = ReportUtils.getReportName(reportHeaderData); + const title = ReportUtils.getReportName(reportHeaderData, undefined, true); const subtitle = ReportUtils.getChatRoomSubtitle(reportHeaderData); const parentNavigationSubtitleData = ReportUtils.getParentNavigationSubtitle(reportHeaderData); const isConcierge = ReportUtils.hasSingleParticipant(props.report) && _.contains(participants, CONST.ACCOUNT_ID.CONCIERGE); From 65164ff32d2492260bd7a3704e527166ff983ed8 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 13 Oct 2023 15:23:49 +0700 Subject: [PATCH 29/49] fix text overflown on android --- src/components/DisplayNames/index.native.js | 15 +++++++++++---- .../HTMLEngineProvider/BaseHTMLEngineProvider.js | 1 + src/libs/ReportUtils.js | 14 +++++++------- 3 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/components/DisplayNames/index.native.js b/src/components/DisplayNames/index.native.js index 7bfd71bb419f..0b298021c4ad 100644 --- a/src/components/DisplayNames/index.native.js +++ b/src/components/DisplayNames/index.native.js @@ -1,4 +1,5 @@ import React from 'react'; +import {View} from 'react-native'; import {propTypes, defaultProps} from './displayNamesPropTypes'; import Text from '../Text'; import RenderHTML from '../RenderHTML'; @@ -8,16 +9,22 @@ import * as StyleUtils from '../../styles/StyleUtils'; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. function DisplayNames(props) { const containsHtml = StringUtils.containsHtml(props.fullTitle); - const fullTitle = containsHtml ? : props.fullTitle; + + if (containsHtml) { + return ( + + + + ); + } return ( - {fullTitle} + {props.fullTitle} ); } diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js index 0b6b5938109c..07ceabf9e154 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js @@ -47,6 +47,7 @@ const customHTMLElementModels = { reactNativeProps: { text: {numberOfLines: 1}, }, + mixedUAStyles: {width: '100%'}, }), strong: defaultHTMLElementModels.span.extend({ tagName: 'strong', diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 666c4120bc1e..a98cc745fe8c 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -1808,18 +1808,18 @@ function getThreadReportNameHtml(parentReportActionMessage) { * If render in html, only the first line of the message should display. * * @param {Object} parentReportAction - * @param {Boolean} shouldRenderHTML + * @param {Boolean} shouldRenderAsHTML * @param {Boolean} shouldRenderFirstLineOnly * @returns {String} */ -function getThreadReportName(parentReportAction, shouldRenderHTML, shouldRenderFirstLineOnly) { - if (!shouldRenderHTML && !shouldRenderFirstLineOnly) { +function getThreadReportName(parentReportAction, shouldRenderAsHTML, shouldRenderFirstLineOnly) { + if (!shouldRenderAsHTML && !shouldRenderFirstLineOnly) { return lodashGet(parentReportAction, ['message', 0, 'text'], '').replace(/(\r\n|\n|\r)/gm, ' '); } const threadReportNameHtml = getThreadReportNameHtml(lodashGet(parentReportAction, ['message', 0, 'html'])); - if (!shouldRenderHTML && shouldRenderFirstLineOnly) { + if (!shouldRenderAsHTML && shouldRenderFirstLineOnly) { return Str.stripHTML(threadReportNameHtml); } @@ -1832,11 +1832,11 @@ function getThreadReportName(parentReportAction, shouldRenderHTML, shouldRenderF * * @param {Object} report * @param {Object} [policy] - * @param {Boolean} [shouldRenderHTML] + * @param {Boolean} [shouldRenderAsHTML] * @param {Boolean} [shouldRenderFirstLineOnly] * @returns {String} */ -function getReportName(report, policy = undefined, shouldRenderHTML = false, shouldRenderFirstLineOnly = false) { +function getReportName(report, policy = undefined, shouldRenderAsHTML = false, shouldRenderFirstLineOnly = false) { let formattedName; const parentReportAction = ReportActionsUtils.getParentReportAction(report); if (isChatThread(report)) { @@ -1845,7 +1845,7 @@ function getReportName(report, policy = undefined, shouldRenderHTML = false, sho } const isAttachment = ReportActionsUtils.isReportActionAttachment(parentReportAction); - const parentReportActionMessage = getThreadReportName(parentReportAction, shouldRenderHTML, shouldRenderFirstLineOnly); + const parentReportActionMessage = getThreadReportName(parentReportAction, shouldRenderAsHTML, shouldRenderFirstLineOnly); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; } From 8f9512889015ec5b34c0390174cef4ee3aa98677 Mon Sep 17 00:00:00 2001 From: tienifr Date: Fri, 13 Oct 2023 16:06:33 +0700 Subject: [PATCH 30/49] fix text overflown on native --- src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js | 1 - src/pages/home/HeaderView.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js index 07ceabf9e154..0b6b5938109c 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js @@ -47,7 +47,6 @@ const customHTMLElementModels = { reactNativeProps: { text: {numberOfLines: 1}, }, - mixedUAStyles: {width: '100%'}, }), strong: defaultHTMLElementModels.span.extend({ tagName: 'strong', diff --git a/src/pages/home/HeaderView.js b/src/pages/home/HeaderView.js index 1c33fb67ecaf..125cf9570f20 100644 --- a/src/pages/home/HeaderView.js +++ b/src/pages/home/HeaderView.js @@ -225,7 +225,7 @@ function HeaderView(props) { displayNamesWithTooltips={displayNamesWithTooltips} tooltipEnabled numberOfLines={1} - textStyles={[styles.headerText, styles.pre]} + textStyles={[styles.headerText, styles.pre, styles.mw100]} shouldUseFullTitle={isChatRoom || isPolicyExpenseChat || isChatThread || isTaskReport} /> {!_.isEmpty(parentNavigationSubtitleData) && ( From 753b75c21e0277157896c2d3c2a786f9014b36ab Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 31 Oct 2023 05:22:01 +0700 Subject: [PATCH 31/49] fix lint --- src/components/DisplayNames/DisplayNamesWithTooltip.js | 4 ++-- src/components/DisplayNames/DisplayNamesWithoutTooltip.js | 4 ++-- src/components/DisplayNames/index.native.js | 2 +- src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.js b/src/components/DisplayNames/DisplayNamesWithTooltip.js index aef8d13ec950..5554939a29fb 100644 --- a/src/components/DisplayNames/DisplayNamesWithTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithTooltip.js @@ -2,11 +2,11 @@ import lodashGet from 'lodash/get'; import React, {Fragment, useCallback, useRef} from 'react'; import {View} from 'react-native'; import _ from 'underscore'; +import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; -import styles from '@styles/styles'; import * as StringUtils from '@libs/StringUtils'; -import RenderHTML from '@components/RenderHTML'; +import styles from '@styles/styles'; import {defaultProps, propTypes} from './displayNamesPropTypes'; import DisplayNamesTooltipItem from './DisplayNamesTooltipItem'; diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js index b640ab022d50..df0a52a45fd4 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.js +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.js @@ -1,9 +1,9 @@ import PropTypes from 'prop-types'; import React from 'react'; +import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; -import styles from '@styles/styles'; import * as StringUtils from '@libs/StringUtils'; -import RenderHTML from '@components/RenderHTML'; +import styles from '@styles/styles'; const propTypes = { /** The full title of the DisplayNames component (not split up) */ diff --git a/src/components/DisplayNames/index.native.js b/src/components/DisplayNames/index.native.js index c31d588a1403..9d358da20aa7 100644 --- a/src/components/DisplayNames/index.native.js +++ b/src/components/DisplayNames/index.native.js @@ -1,9 +1,9 @@ import React from 'react'; import {View} from 'react-native'; +import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; import * as StringUtils from '@libs/StringUtils'; import * as StyleUtils from '@styles/StyleUtils'; -import RenderHTML from '@components/RenderHTML'; import {defaultProps, propTypes} from './displayNamesPropTypes'; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js index 84a451d698c2..141ba28a55d9 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.js @@ -1,6 +1,6 @@ import PropTypes from 'prop-types'; import React, {useMemo} from 'react'; -import {defaultHTMLElementModels, RenderHTMLConfigProvider, TRenderEngineProvider} from 'react-native-render-html'; +import {defaultHTMLElementModels, HTMLContentModel, RenderHTMLConfigProvider, TRenderEngineProvider} from 'react-native-render-html'; import _ from 'underscore'; import convertToLTR from '@libs/convertToLTR'; import singleFontFamily from '@styles/fontFamily/singleFontFamily'; From ce9953be9f83f2ed35cc4e3541a52f9c8fa82139 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 1 Nov 2023 02:24:21 +0700 Subject: [PATCH 32/49] unescape html entities --- src/libs/ReportUtils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/ReportUtils.js b/src/libs/ReportUtils.js index 59630c6c4546..c241bc188892 100644 --- a/src/libs/ReportUtils.js +++ b/src/libs/ReportUtils.js @@ -2041,7 +2041,7 @@ function getThreadReportName(parentReportAction, shouldRenderAsHTML, shouldRende } // is to prevent the redundant body div which causes text overflown - return StringUtils.containsHtml(threadReportNameHtml) ? `${threadReportNameHtml}` : threadReportNameHtml; + return StringUtils.containsHtml(threadReportNameHtml) ? `${threadReportNameHtml}` : _.unescape(threadReportNameHtml); } /** From ddde5d65da05fd45e9db8ffef6a5dedd32d91693 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 5 Dec 2023 21:00:30 +0700 Subject: [PATCH 33/49] reapply changes --- .../DisplayNames/DisplayNamesWithTooltip.tsx | 6 ++- .../DisplayNamesWithoutTooltip.tsx | 6 ++- src/components/DisplayNames/index.native.tsx | 16 +++++++ .../BaseHTMLEngineProvider.js | 6 +-- .../StatePicker/StateSelectorModal.js | 2 +- src/libs/ReportUtils.ts | 47 ++++++++++++++++++- src/libs/searchCountryOptions.ts | 2 +- src/styles/StyleUtils.ts | 2 +- 8 files changed, 77 insertions(+), 10 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx index 8c8720c7c99f..ed0697723f2d 100644 --- a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx +++ b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx @@ -1,7 +1,9 @@ import React, {Fragment, useCallback, useRef} from 'react'; import {Text as RNText, View} from 'react-native'; +import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; +import StringUtils from '@libs/StringUtils'; import useThemeStyles from '@styles/useThemeStyles'; import DisplayNamesTooltipItem from './DisplayNamesTooltipItem'; import DisplayNamesProps from './types'; @@ -44,6 +46,8 @@ function DisplayNamesWithToolTip({shouldUseFullTitle, fullTitle, displayNamesWit return textNodeRight > containerRight ? -(tooltipX - newToolX) : 0; }, []); + const title = StringUtils.containsHtml(fullTitle) ? : fullTitle; + return ( // Tokenization of string only support prop numberOfLines on Web {shouldUseFullTitle - ? fullTitle + ? title : displayNamesWithTooltips.map(({displayName, accountID, avatar, login}, index) => ( // eslint-disable-next-line react/no-array-index-key diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx b/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx index 1854ebe2353d..68914107253e 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx @@ -1,6 +1,8 @@ import React from 'react'; import {StyleProp, TextStyle} from 'react-native'; +import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; +import StringUtils from '@libs/StringUtils'; import useThemeStyles from '@styles/useThemeStyles'; type DisplayNamesWithoutTooltipProps = { @@ -16,12 +18,14 @@ type DisplayNamesWithoutTooltipProps = { function DisplayNamesWithoutTooltip({textStyles = [], numberOfLines = 1, fullTitle = ''}: DisplayNamesWithoutTooltipProps) { const styles = useThemeStyles(); + const title = StringUtils.containsHtml(fullTitle) ? : fullTitle; + return ( - {fullTitle} + {title} ); } diff --git a/src/components/DisplayNames/index.native.tsx b/src/components/DisplayNames/index.native.tsx index 8f1fef37a6ba..56c309cd9b52 100644 --- a/src/components/DisplayNames/index.native.tsx +++ b/src/components/DisplayNames/index.native.tsx @@ -1,11 +1,27 @@ import React from 'react'; +import {View, ViewStyle} from 'react-native'; +import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; +import StringUtils from '@libs/StringUtils'; +import * as StyleUtils from '@styles/StyleUtils'; +import useThemeStyles from '@styles/useThemeStyles'; import DisplayNamesProps from './types'; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. function DisplayNames({accessibilityLabel, fullTitle, textStyles = [], numberOfLines = 1}: DisplayNamesProps) { + const styles = useThemeStyles(); const {translate} = useLocalize(); + + const containsHtml = StringUtils.containsHtml(fullTitle); + if (containsHtml) { + return ( + + + + ); + } + return ( ({selectable: props.textSelectable, allowFontScaling: false, textBreakStrategy: 'simple'}), [props.textSelectable]); - const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText]}; + const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText, styles.dFlex, styles.flexShrink1]}; return ( ): OnyxEntry | Emp return getRootParentReport(isNotEmptyObject(parentReport) ? parentReport : null); } +/** + * Get the formatted title in HTML for a thread based on parent message. + * Only the first line of the message should display. + */ +function getThreadReportNameHtml(parentReportActionMessage: string): string { + const blockTags = ['br', 'h1', 'pre', 'div', 'blockquote', 'p', 'li', 'div']; + const blockTagRegExp = `(?:<\\/?(?:${blockTags.join('|')})(?:[^>]*)>|\\r\\n|\\n|\\r)`; + const threadHeaderHtmlRegExp = new RegExp(`^(?:<([^>]+)>)?((?:(?!${blockTagRegExp}).)*)(${blockTagRegExp}.*)`, 'gmi'); + return parentReportActionMessage.replace(threadHeaderHtmlRegExp, (match, g1, g2) => { + if (!g1 || g1 === 'h1') { + return g2; + } + if (g1 === 'pre') { + return `${g2}`; + } + const parser = new ExpensiMark(); + if (parser.containsNonPairTag(g2)) { + return `<${g1}>${g2}`; + } + return `<${g1}>${g2}`; + }); +} + +/** + * Get the title for a thread based on parent message. + * If render in html, only the first line of the message should display. + */ +function getThreadReportName(parentReportAction: OnyxEntry | EmptyObject = {}, shouldRenderAsHTML: boolean, shouldRenderFirstLineOnly: boolean): string { + if (!shouldRenderAsHTML && !shouldRenderFirstLineOnly) { + return (parentReportAction?.message?.[0]?.text ?? '').replace(/(\r\n|\n|\r)/gm, ' '); + } + + const threadReportNameHtml = getThreadReportNameHtml(parentReportAction?.message?.[0]?.html ?? ''); + + if (!shouldRenderAsHTML && shouldRenderFirstLineOnly) { + return Str.stripHTML(threadReportNameHtml); + } + + // is to prevent the redundant body div which causes text overflown + return StringUtils.containsHtml(threadReportNameHtml) ? `${threadReportNameHtml}` : threadReportNameHtml; +} + /** * Get the title for a report. */ -function getReportName(report: OnyxEntry, policy: OnyxEntry = null): string { +function getReportName(report: OnyxEntry, policy: OnyxEntry = null, shouldRenderAsHTML = false, shouldRenderFirstLineOnly = false): string { let formattedName: string | undefined; const parentReportAction = ReportActionsUtils.getParentReportAction(report); if (isChatThread(report)) { @@ -2206,7 +2249,7 @@ function getReportName(report: OnyxEntry, policy: OnyxEntry = nu } const isAttachment = ReportActionsUtils.isReportActionAttachment(isNotEmptyObject(parentReportAction) ? parentReportAction : null); - const parentReportActionMessage = (parentReportAction?.message?.[0]?.text ?? '').replace(/(\r\n|\n|\r)/gm, ' '); + const parentReportActionMessage = getThreadReportName(parentReportAction, shouldRenderAsHTML, shouldRenderFirstLineOnly); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; } diff --git a/src/libs/searchCountryOptions.ts b/src/libs/searchCountryOptions.ts index 50abefe9d6c7..8fb1cc9c37f3 100644 --- a/src/libs/searchCountryOptions.ts +++ b/src/libs/searchCountryOptions.ts @@ -1,4 +1,4 @@ -import * as StringUtils from './StringUtils'; +import StringUtils from './StringUtils'; type CountryData = { value: string; diff --git a/src/styles/StyleUtils.ts b/src/styles/StyleUtils.ts index 056974274b15..cb6f99d22d0f 100644 --- a/src/styles/StyleUtils.ts +++ b/src/styles/StyleUtils.ts @@ -186,7 +186,7 @@ function getAvatarSize(size: AvatarSizeName): number { /** * Return the height of RenderHtml text container with numberOfLines=1 */ -function getHeightOfRenderHtmlText(numberOfLines: number): ViewStyle | CSSProperties { +function getHeightOfRenderHtmlText(styles: ThemeStyles, numberOfLines: number): ViewStyle { return numberOfLines === 1 ? {height: styles.webViewStyles.baseFontStyle.lineHeight} : {}; } From ee782057f32016158bf0cd717dfe45d863a76bac Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 6 Dec 2023 14:35:50 +0700 Subject: [PATCH 34/49] rename param --- src/libs/ReportUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 5b446e77fda0..bef621bc4dad 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2199,11 +2199,11 @@ function getRootParentReport(report: OnyxEntry): OnyxEntry | Emp * Get the formatted title in HTML for a thread based on parent message. * Only the first line of the message should display. */ -function getThreadReportNameHtml(parentReportActionMessage: string): string { +function getThreadReportNameHtml(reportActionMessageHtml: string): string { const blockTags = ['br', 'h1', 'pre', 'div', 'blockquote', 'p', 'li', 'div']; const blockTagRegExp = `(?:<\\/?(?:${blockTags.join('|')})(?:[^>]*)>|\\r\\n|\\n|\\r)`; const threadHeaderHtmlRegExp = new RegExp(`^(?:<([^>]+)>)?((?:(?!${blockTagRegExp}).)*)(${blockTagRegExp}.*)`, 'gmi'); - return parentReportActionMessage.replace(threadHeaderHtmlRegExp, (match, g1, g2) => { + return reportActionMessageHtml.replace(threadHeaderHtmlRegExp, (match, g1, g2) => { if (!g1 || g1 === 'h1') { return g2; } From 91903c1282f8e111286d063701b290a5435ce389 Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 23 Jan 2024 02:53:49 +0700 Subject: [PATCH 35/49] unescape html chars in thread title --- .../DisplayNames/DisplayNamesWithTooltip.tsx | 8 ++++---- .../DisplayNames/DisplayNamesWithoutTooltip.tsx | 2 +- src/components/DisplayNames/index.native.tsx | 10 +++++----- src/libs/ReportUtils.ts | 9 +++++---- src/styles/utils/index.ts | 5 +++++ 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx index d4e29617a64d..434f8177edcf 100644 --- a/src/components/DisplayNames/DisplayNamesWithTooltip.tsx +++ b/src/components/DisplayNames/DisplayNamesWithTooltip.tsx @@ -1,13 +1,13 @@ -import React, {Fragment, useCallback, useRef} from 'react'; +import React, {Fragment, useCallback, useMemo, useRef} from 'react'; // eslint-disable-next-line no-restricted-imports import type {Text as RNText} from 'react-native'; import {View} from 'react-native'; import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; import Tooltip from '@components/Tooltip'; -import StringUtils from '@libs/StringUtils'; import useThemeStyles from '@hooks/useThemeStyles'; import * as ReportUtils from '@libs/ReportUtils'; +import StringUtils from '@libs/StringUtils'; import DisplayNamesTooltipItem from './DisplayNamesTooltipItem'; import type DisplayNamesProps from './types'; @@ -49,7 +49,7 @@ function DisplayNamesWithToolTip({shouldUseFullTitle, fullTitle, displayNamesWit return textNodeRight > containerRight ? -(tooltipX - newToolX) : 0; }, []); - const title = StringUtils.containsHtml(fullTitle) ? : fullTitle; + const title = useMemo(() => (StringUtils.containsHtml(fullTitle) ? : ReportUtils.formatReportLastMessageText(fullTitle)), [fullTitle]); return ( // Tokenization of string only support prop numberOfLines on Web @@ -59,7 +59,7 @@ function DisplayNamesWithToolTip({shouldUseFullTitle, fullTitle, displayNamesWit ref={containerRef} > {shouldUseFullTitle - ? ReportUtils.formatReportLastMessageText(fullTitle) + ? title : displayNamesWithTooltips.map(({displayName, accountID, avatar, login}, index) => ( // eslint-disable-next-line react/no-array-index-key diff --git a/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx b/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx index edd8f9cf205a..bd0e7a12e484 100644 --- a/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx +++ b/src/components/DisplayNames/DisplayNamesWithoutTooltip.tsx @@ -2,8 +2,8 @@ import React from 'react'; import type {StyleProp, TextStyle} from 'react-native'; import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; -import StringUtils from '@libs/StringUtils'; import useThemeStyles from '@hooks/useThemeStyles'; +import StringUtils from '@libs/StringUtils'; type DisplayNamesWithoutTooltipProps = { /** The full title of the DisplayNames component (not split up) */ diff --git a/src/components/DisplayNames/index.native.tsx b/src/components/DisplayNames/index.native.tsx index 5a0ad7c4a7a5..a0a15e6f8e8f 100644 --- a/src/components/DisplayNames/index.native.tsx +++ b/src/components/DisplayNames/index.native.tsx @@ -1,22 +1,22 @@ import React from 'react'; -import {View, ViewStyle} from 'react-native'; +import type {ViewStyle} from 'react-native'; +import {View} from 'react-native'; import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; +import useStyleUtils from '@hooks/useStyleUtils'; import StringUtils from '@libs/StringUtils'; -import * as StyleUtils from '@styles/StyleUtils'; -import useThemeStyles from '@styles/useThemeStyles'; import type DisplayNamesProps from './types'; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. function DisplayNames({accessibilityLabel, fullTitle, textStyles = [], numberOfLines = 1}: DisplayNamesProps) { - const styles = useThemeStyles(); + const StyleUtils = useStyleUtils(); const {translate} = useLocalize(); const containsHtml = StringUtils.containsHtml(fullTitle); if (containsHtml) { return ( - + ); diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 41589d2e368f..548cd0815dd9 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -5,6 +5,7 @@ import lodashEscape from 'lodash/escape'; import lodashFindLastIndex from 'lodash/findLastIndex'; import lodashIntersection from 'lodash/intersection'; import lodashIsEqual from 'lodash/isEqual'; +import lodashUnescape from 'lodash/unescape'; import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; @@ -2325,7 +2326,7 @@ function getThreadReportNameHtml(reportActionMessageHtml: string): string { const blockTags = ['br', 'h1', 'pre', 'div', 'blockquote', 'p', 'li', 'div']; const blockTagRegExp = `(?:<\\/?(?:${blockTags.join('|')})(?:[^>]*)>|\\r\\n|\\n|\\r)`; const threadHeaderHtmlRegExp = new RegExp(`^(?:<([^>]+)>)?((?:(?!${blockTagRegExp}).)*)(${blockTagRegExp}.*)`, 'gmi'); - return reportActionMessageHtml.replace(threadHeaderHtmlRegExp, (match, g1, g2) => { + return reportActionMessageHtml.replace(threadHeaderHtmlRegExp, (match, g1: string, g2: string) => { if (!g1 || g1 === 'h1') { return g2; } @@ -2344,7 +2345,7 @@ function getThreadReportNameHtml(reportActionMessageHtml: string): string { * Get the title for a thread based on parent message. * If render in html, only the first line of the message should display. */ -function getThreadReportName(parentReportAction: OnyxEntry | EmptyObject = {}, shouldRenderAsHTML: boolean, shouldRenderFirstLineOnly: boolean): string { +function getThreadReportName(parentReportAction: OnyxEntry | EmptyObject = {}, shouldRenderAsHTML = false, shouldRenderFirstLineOnly = true): string { if (!shouldRenderAsHTML && !shouldRenderFirstLineOnly) { return (parentReportAction?.message?.[0]?.text ?? '').replace(/(\r\n|\n|\r)/gm, ' '); } @@ -2355,8 +2356,8 @@ function getThreadReportName(parentReportAction: OnyxEntry | Empty return Str.stripHTML(threadReportNameHtml); } - // is to prevent the redundant body div which causes text overflown - return StringUtils.containsHtml(threadReportNameHtml) ? `${threadReportNameHtml}` : threadReportNameHtml; + // is to prevent the redundant body which causes text overflown + return StringUtils.containsHtml(threadReportNameHtml) ? `${threadReportNameHtml}` : lodashUnescape(threadReportNameHtml); } /** diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 8b040dd8d72c..6853f55d99d1 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1297,6 +1297,11 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ }; }, + /** + * Return the height of RenderHtml text container with numberOfLines=1 + */ + getHeightOfRenderHtmlText: (numberOfLines: number): ViewStyle => (numberOfLines === 1 ? {height: styles.webViewStyles.baseFontStyle.lineHeight} : {}), + /** * Return the height of magic code input container */ From b9bbccfde2f5fcbd4d101b08d96cd6ee329bcd0b Mon Sep 17 00:00:00 2001 From: tienifr Date: Tue, 23 Jan 2024 03:11:06 +0700 Subject: [PATCH 36/49] fix: code blocks flicker --- src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx | 2 +- src/pages/home/HeaderView.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx index 811aa920c4f3..250d412b17ef 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx @@ -84,7 +84,7 @@ function BaseHTMLEngineProvider({textSelectable = false, children, enableExperim // We need to memoize this prop to make it referentially stable. const defaultTextProps: TextProps = useMemo(() => ({selectable: textSelectable, allowFontScaling: false, textBreakStrategy: 'simple'}), [textSelectable]); - const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText, styles.dFlex, styles.flexShrink1]}; + const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText]}; return ( {!_.isEmpty(parentNavigationSubtitleData) && ( From 776720526a01f179a2ffeb4e0eaf9a5d30e9a59d Mon Sep 17 00:00:00 2001 From: tienifr Date: Sat, 24 Feb 2024 02:04:28 +0700 Subject: [PATCH 37/49] unescape html entity in LHN --- src/libs/ReportUtils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index f7ddce1fd639..be9fb903c8ce 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2549,11 +2549,11 @@ function getThreadReportName(parentReportAction: OnyxEntry | Empty const threadReportNameHtml = getThreadReportNameHtml(parentReportAction?.message?.[0]?.html ?? ''); if (!shouldRenderAsHTML && shouldRenderFirstLineOnly) { - return Str.stripHTML(threadReportNameHtml); + return lodashUnescape(Str.stripHTML(threadReportNameHtml)); } // is to prevent the redundant body which causes text overflown - return StringUtils.containsHtml(threadReportNameHtml) ? `${threadReportNameHtml}` : lodashUnescape(threadReportNameHtml); + return StringUtils.containsHtml(threadReportNameHtml) ? `${threadReportNameHtml}` : lodashUnescape(threadReportNameHtml); } /** From e868c58b6a4cca9131f094eaea1c746b8caebe0e Mon Sep 17 00:00:00 2001 From: tienifr Date: Sat, 24 Feb 2024 02:04:49 +0700 Subject: [PATCH 38/49] show ellipsis for truncated text --- src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx index 02bcc5642086..00422fe570ac 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx @@ -84,7 +84,7 @@ function BaseHTMLEngineProvider({textSelectable = false, children, enableExperim // We need to memoize this prop to make it referentially stable. const defaultTextProps: TextProps = useMemo(() => ({selectable: textSelectable, allowFontScaling: false, textBreakStrategy: 'simple'}), [textSelectable]); - const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText]}; + const defaultViewProps = {style: [styles.alignItemsStart, styles.userSelectText, styles.mw100]}; return ( Date: Sat, 24 Feb 2024 02:15:45 +0700 Subject: [PATCH 39/49] fix: clipped blockquote --- src/components/DisplayNames/index.native.tsx | 2 +- src/styles/utils/index.ts | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/DisplayNames/index.native.tsx b/src/components/DisplayNames/index.native.tsx index 72945fc289dc..5a8bfc536f0b 100644 --- a/src/components/DisplayNames/index.native.tsx +++ b/src/components/DisplayNames/index.native.tsx @@ -16,7 +16,7 @@ function DisplayNames({accessibilityLabel, fullTitle, textStyles = [], numberOfL const containsHtml = StringUtils.containsHtml(fullTitle); if (containsHtml) { return ( - + ); diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 735f9980e56f..3e0ab80d95a5 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1320,7 +1320,17 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ /** * Return the height of RenderHtml text container with numberOfLines=1 */ - getHeightOfRenderHtmlText: (numberOfLines: number): ViewStyle => (numberOfLines === 1 ? {height: styles.webViewStyles.baseFontStyle.lineHeight} : {}), + getHeightOfRenderHtmlText: (text: string, numberOfLines: number): ViewStyle => { + if (numberOfLines !== 1) { + return {}; + } + let height = styles.webViewStyles.baseFontStyle.lineHeight; + if (text.includes('
')) { + const {marginTop, marginBottom} = styles.webViewStyles.tagStyles.blockquote; + height += marginTop + marginBottom; + } + return {height}; + }, /** * Return the height of magic code input container From 14b82ac62d1a163a07b0bae0356e4321ddb84d11 Mon Sep 17 00:00:00 2001 From: tienifr Date: Sat, 24 Feb 2024 05:03:11 +0700 Subject: [PATCH 40/49] fix: clipped code snippet --- src/styles/utils/codeStyles/index.android.ts | 4 +++- src/styles/utils/codeStyles/index.ios.ts | 4 +++- src/styles/utils/codeStyles/index.ts | 3 ++- src/styles/utils/index.ts | 3 +++ 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/styles/utils/codeStyles/index.android.ts b/src/styles/utils/codeStyles/index.android.ts index 0ac77729c83c..ff26e5f540a1 100644 --- a/src/styles/utils/codeStyles/index.android.ts +++ b/src/styles/utils/codeStyles/index.android.ts @@ -17,4 +17,6 @@ const codePlainTextStyle: CodeTextStyles = { lineHeight: 14.5, }; -export default {codeWordWrapper, codeWordStyle, codeTextStyle, codePlainTextStyle}; +const codeWrapperOffset = 0; + +export default {codeWordWrapper, codeWordStyle, codeTextStyle, codePlainTextStyle, codeWrapperOffset}; diff --git a/src/styles/utils/codeStyles/index.ios.ts b/src/styles/utils/codeStyles/index.ios.ts index 05e2b55ec205..03ff78ec8e38 100644 --- a/src/styles/utils/codeStyles/index.ios.ts +++ b/src/styles/utils/codeStyles/index.ios.ts @@ -18,4 +18,6 @@ const codePlainTextStyle: CodeTextStyles = { lineHeight: 15, }; -export default {codeWordWrapper, codeWordStyle, codeTextStyle, codePlainTextStyle}; +const codeWrapperOffset = 4; + +export default {codeWordWrapper, codeWordStyle, codeTextStyle, codePlainTextStyle, codeWrapperOffset}; diff --git a/src/styles/utils/codeStyles/index.ts b/src/styles/utils/codeStyles/index.ts index 80a66df95c7c..1ddd0e972155 100644 --- a/src/styles/utils/codeStyles/index.ts +++ b/src/styles/utils/codeStyles/index.ts @@ -5,4 +5,5 @@ const codeWordWrapper: CodeWordWrapperStyles = {}; const codeWordStyle: CodeWordStyles = {}; const codeTextStyle: CodeTextStyles = {}; const codePlainTextStyle: CodeTextStyles = {}; -export default {codeWordWrapper, codeWordStyle, codeTextStyle, codePlainTextStyle}; +const codeWrapperOffset = 0; +export default {codeWordWrapper, codeWordStyle, codeTextStyle, codePlainTextStyle, codeWrapperOffset}; diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 3e0ab80d95a5..b29c4c2ad748 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -16,6 +16,7 @@ import {defaultStyles} from '..'; import type {ThemeStyles} from '..'; import shouldPreventScrollOnAutoCompleteSuggestion from './autoCompleteSuggestion'; import getCardStyles from './cardStyles'; +import codeStyles from './codeStyles'; import containerComposeStyles from './containerComposeStyles'; import cursor from './cursor'; import FontUtils from './FontUtils'; @@ -1328,6 +1329,8 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ if (text.includes('
')) { const {marginTop, marginBottom} = styles.webViewStyles.tagStyles.blockquote; height += marginTop + marginBottom; + } else if (text.includes('')) { + height += codeStyles.codeWrapperOffset; } return {height}; }, From 8857f0488cf06b908e63fe31fb9dce284d2c5bda Mon Sep 17 00:00:00 2001 From: tienifr Date: Sat, 24 Feb 2024 05:38:25 +0700 Subject: [PATCH 41/49] fix: some markdowns are clipped --- src/components/DisplayNames/index.native.tsx | 6 +++--- src/styles/index.ts | 5 +++++ src/styles/utils/index.ts | 17 ----------------- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/components/DisplayNames/index.native.tsx b/src/components/DisplayNames/index.native.tsx index 5a8bfc536f0b..528a720e1687 100644 --- a/src/components/DisplayNames/index.native.tsx +++ b/src/components/DisplayNames/index.native.tsx @@ -4,19 +4,19 @@ import {View} from 'react-native'; import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; -import useStyleUtils from '@hooks/useStyleUtils'; +import useThemeStyles from '@hooks/useThemeStyles'; import StringUtils from '@libs/StringUtils'; import type DisplayNamesProps from './types'; // As we don't have to show tooltips of the Native platform so we simply render the full display names list. function DisplayNames({accessibilityLabel, fullTitle, textStyles = [], numberOfLines = 1, renderAdditionalText}: DisplayNamesProps) { - const StyleUtils = useStyleUtils(); + const styles = useThemeStyles(); const {translate} = useLocalize(); const containsHtml = StringUtils.containsHtml(fullTitle); if (containsHtml) { return ( - + ); diff --git a/src/styles/index.ts b/src/styles/index.ts index 62d50df5ef5e..5a064c103f90 100644 --- a/src/styles/index.ts +++ b/src/styles/index.ts @@ -1847,6 +1847,11 @@ const styles = (theme: ThemeColors) => ...wordBreak.breakWord, }, + renderHTMLThreadTitle: { + display: 'flex', + flexDirection: 'row', + }, + renderHTMLTitle: { color: theme.text, fontSize: variables.fontSizeNormal, diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index b29c4c2ad748..68789640d08c 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -1318,23 +1318,6 @@ const createStyleUtils = (theme: ThemeColors, styles: ThemeStyles) => ({ }; }, - /** - * Return the height of RenderHtml text container with numberOfLines=1 - */ - getHeightOfRenderHtmlText: (text: string, numberOfLines: number): ViewStyle => { - if (numberOfLines !== 1) { - return {}; - } - let height = styles.webViewStyles.baseFontStyle.lineHeight; - if (text.includes('
')) { - const {marginTop, marginBottom} = styles.webViewStyles.tagStyles.blockquote; - height += marginTop + marginBottom; - } else if (text.includes('')) { - height += codeStyles.codeWrapperOffset; - } - return {height}; - }, - /** * Return the height of magic code input container */ From 292f1131848d69e312103363fc558489fd41d728 Mon Sep 17 00:00:00 2001 From: tienifr Date: Sat, 24 Feb 2024 05:51:39 +0700 Subject: [PATCH 42/49] fix lint --- src/styles/utils/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts index 68789640d08c..3ac9a7f8e718 100644 --- a/src/styles/utils/index.ts +++ b/src/styles/utils/index.ts @@ -16,7 +16,6 @@ import {defaultStyles} from '..'; import type {ThemeStyles} from '..'; import shouldPreventScrollOnAutoCompleteSuggestion from './autoCompleteSuggestion'; import getCardStyles from './cardStyles'; -import codeStyles from './codeStyles'; import containerComposeStyles from './containerComposeStyles'; import cursor from './cursor'; import FontUtils from './FontUtils'; From 8eb159e63c6ffa2223036b80f9e228dc3aa2201d Mon Sep 17 00:00:00 2001 From: tienifr Date: Sun, 25 Feb 2024 01:15:50 +0700 Subject: [PATCH 43/49] fix: header text on native is not bold --- src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx index 00422fe570ac..e3d65dcb8f64 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx @@ -60,6 +60,7 @@ function BaseHTMLEngineProvider({textSelectable = false, children, enableExperim 'thread-title': HTMLElementModel.fromCustomModel({ tagName: 'thread-title', contentModel: HTMLContentModel.textual, + mixedUAStyles: {...styles.headerText}, reactNativeProps: { text: {numberOfLines: 1}, }, From c712a20ad42df0ca6e7e1372703bd6e3eadc7a89 Mon Sep 17 00:00:00 2001 From: tienifr Date: Sun, 25 Feb 2024 01:24:35 +0700 Subject: [PATCH 44/49] fix lint --- src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx index e3d65dcb8f64..25979046ae60 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx @@ -60,7 +60,7 @@ function BaseHTMLEngineProvider({textSelectable = false, children, enableExperim 'thread-title': HTMLElementModel.fromCustomModel({ tagName: 'thread-title', contentModel: HTMLContentModel.textual, - mixedUAStyles: {...styles.headerText}, + mixedUAStyles: {...styles.headerText, whiteSpace: 'pre'}, reactNativeProps: { text: {numberOfLines: 1}, }, @@ -79,7 +79,7 @@ function BaseHTMLEngineProvider({textSelectable = false, children, enableExperim contentModel: HTMLContentModel.block, }), }), - [styles.colorMuted, styles.formError, styles.mb0, styles.textLabelSupporting, styles.lh16], + [styles.colorMuted, styles.formError, styles.mb0, styles.textLabelSupporting, styles.lh16, styles.headerText], ); /* eslint-enable @typescript-eslint/naming-convention */ From c8edb0da97c9b227b547e6e88cfeb0450d520f21 Mon Sep 17 00:00:00 2001 From: tienifr Date: Wed, 28 Feb 2024 23:37:09 +0700 Subject: [PATCH 45/49] remove redundant change --- src/components/DisplayNames/index.native.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/DisplayNames/index.native.tsx b/src/components/DisplayNames/index.native.tsx index 528a720e1687..044b037ade01 100644 --- a/src/components/DisplayNames/index.native.tsx +++ b/src/components/DisplayNames/index.native.tsx @@ -1,5 +1,4 @@ import React from 'react'; -import type {ViewStyle} from 'react-native'; import {View} from 'react-native'; import RenderHTML from '@components/RenderHTML'; import Text from '@components/Text'; @@ -16,7 +15,7 @@ function DisplayNames({accessibilityLabel, fullTitle, textStyles = [], numberOfL const containsHtml = StringUtils.containsHtml(fullTitle); if (containsHtml) { return ( - + ); From 7068ddbea3544073349a773f9b032c01a83b1243 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 29 Feb 2024 03:30:11 +0700 Subject: [PATCH 46/49] remove oudated comment --- src/libs/ReportUtils.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index ccd9d6dacd87..0003352dac7c 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -2532,7 +2532,6 @@ function getThreadReportName(parentReportAction: OnyxEntry | Empty return lodashUnescape(Str.stripHTML(threadReportNameHtml)); } - // is to prevent the redundant body which causes text overflown return StringUtils.containsHtml(threadReportNameHtml) ? `${threadReportNameHtml}` : lodashUnescape(threadReportNameHtml); } From 4979581dc0d57c3bff08892fe0400d43ababa434 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 29 Feb 2024 03:36:51 +0700 Subject: [PATCH 47/49] remove redundant changes --- src/styles/utils/codeStyles/index.android.ts | 4 +--- src/styles/utils/codeStyles/index.ios.ts | 4 +--- src/styles/utils/codeStyles/index.ts | 3 +-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/styles/utils/codeStyles/index.android.ts b/src/styles/utils/codeStyles/index.android.ts index ff26e5f540a1..0ac77729c83c 100644 --- a/src/styles/utils/codeStyles/index.android.ts +++ b/src/styles/utils/codeStyles/index.android.ts @@ -17,6 +17,4 @@ const codePlainTextStyle: CodeTextStyles = { lineHeight: 14.5, }; -const codeWrapperOffset = 0; - -export default {codeWordWrapper, codeWordStyle, codeTextStyle, codePlainTextStyle, codeWrapperOffset}; +export default {codeWordWrapper, codeWordStyle, codeTextStyle, codePlainTextStyle}; diff --git a/src/styles/utils/codeStyles/index.ios.ts b/src/styles/utils/codeStyles/index.ios.ts index 03ff78ec8e38..05e2b55ec205 100644 --- a/src/styles/utils/codeStyles/index.ios.ts +++ b/src/styles/utils/codeStyles/index.ios.ts @@ -18,6 +18,4 @@ const codePlainTextStyle: CodeTextStyles = { lineHeight: 15, }; -const codeWrapperOffset = 4; - -export default {codeWordWrapper, codeWordStyle, codeTextStyle, codePlainTextStyle, codeWrapperOffset}; +export default {codeWordWrapper, codeWordStyle, codeTextStyle, codePlainTextStyle}; diff --git a/src/styles/utils/codeStyles/index.ts b/src/styles/utils/codeStyles/index.ts index 1ddd0e972155..80a66df95c7c 100644 --- a/src/styles/utils/codeStyles/index.ts +++ b/src/styles/utils/codeStyles/index.ts @@ -5,5 +5,4 @@ const codeWordWrapper: CodeWordWrapperStyles = {}; const codeWordStyle: CodeWordStyles = {}; const codeTextStyle: CodeTextStyles = {}; const codePlainTextStyle: CodeTextStyles = {}; -const codeWrapperOffset = 0; -export default {codeWordWrapper, codeWordStyle, codeTextStyle, codePlainTextStyle, codeWrapperOffset}; +export default {codeWordWrapper, codeWordStyle, codeTextStyle, codePlainTextStyle}; From 4f969fc38d292bd02c0615a2d41a973978263ee6 Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 21 Mar 2024 17:46:23 +0700 Subject: [PATCH 48/49] fix: blockquote and inline code truncation --- .../BaseHTMLEngineProvider.tsx | 11 +-- .../HTMLRenderers/ThreadTitleRenderer.tsx | 69 +++++++++++++++++++ .../HTMLEngineProvider/HTMLRenderers/index.ts | 2 + .../InlineCodeBlock/WrappedText.tsx | 12 +++- .../InlineCodeBlock/index.native.tsx | 2 + src/components/InlineCodeBlock/index.tsx | 8 ++- 6 files changed, 92 insertions(+), 12 deletions(-) create mode 100644 src/components/HTMLEngineProvider/HTMLRenderers/ThreadTitleRenderer.tsx diff --git a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx index 25979046ae60..4a32fa2c9d91 100755 --- a/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx +++ b/src/components/HTMLEngineProvider/BaseHTMLEngineProvider.tsx @@ -57,14 +57,7 @@ function BaseHTMLEngineProvider({textSelectable = false, children, enableExperim mixedUAStyles: {whiteSpace: 'pre'}, contentModel: HTMLContentModel.textual, }), - 'thread-title': HTMLElementModel.fromCustomModel({ - tagName: 'thread-title', - contentModel: HTMLContentModel.textual, - mixedUAStyles: {...styles.headerText, whiteSpace: 'pre'}, - reactNativeProps: { - text: {numberOfLines: 1}, - }, - }), + 'thread-title': HTMLElementModel.fromCustomModel({tagName: 'thread-title', contentModel: HTMLContentModel.block}), 'mention-user': HTMLElementModel.fromCustomModel({tagName: 'mention-user', contentModel: HTMLContentModel.textual}), 'mention-here': HTMLElementModel.fromCustomModel({tagName: 'mention-here', contentModel: HTMLContentModel.textual}), 'next-step': HTMLElementModel.fromCustomModel({ @@ -79,7 +72,7 @@ function BaseHTMLEngineProvider({textSelectable = false, children, enableExperim contentModel: HTMLContentModel.block, }), }), - [styles.colorMuted, styles.formError, styles.mb0, styles.textLabelSupporting, styles.lh16, styles.headerText], + [styles.colorMuted, styles.formError, styles.mb0, styles.textLabelSupporting, styles.lh16], ); /* eslint-enable @typescript-eslint/naming-convention */ diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ThreadTitleRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/ThreadTitleRenderer.tsx new file mode 100644 index 000000000000..77a2d9a05ebc --- /dev/null +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ThreadTitleRenderer.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import {View} from 'react-native'; +import {TChildrenRenderer} from 'react-native-render-html'; +import type {CustomRendererProps, TBlock, TNode} from 'react-native-render-html'; +import Text from '@components/Text'; + +type ThreadTitleRendererProps = CustomRendererProps; + +function ThreadTitleRenderer({tnode}: ThreadTitleRendererProps) { + const renderFn = (node: TNode) => { + const children = node.children; + + return children.map((child) => { + if (child.tagName === 'blockquote') { + return ( + + {renderFn(child)} + + ); + } + + // HTML node + if (child.tagName) { + return ( + + + + ); + } + + // TText node + if ('data' in child) { + return ( + + {child.data} + + ); + } + + return ( + + {renderFn(child)} + + ); + }); + }; + + return {renderFn(tnode)}; +} + +ThreadTitleRenderer.displayName = 'ThreadTitleRenderer'; + +export default ThreadTitleRenderer; diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/index.ts b/src/components/HTMLEngineProvider/HTMLRenderers/index.ts index 1914bcf4b5ff..b055c72a1262 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/index.ts +++ b/src/components/HTMLEngineProvider/HTMLRenderers/index.ts @@ -7,6 +7,7 @@ import MentionHereRenderer from './MentionHereRenderer'; import MentionUserRenderer from './MentionUserRenderer'; import NextStepEmailRenderer from './NextStepEmailRenderer'; import PreRenderer from './PreRenderer'; +import ThreadTitleRenderer from './ThreadTitleRenderer'; import VideoRenderer from './VideoRenderer'; /** @@ -26,6 +27,7 @@ const HTMLEngineProviderComponentList: CustomTagRendererRecord = { 'mention-user': MentionUserRenderer, 'mention-here': MentionHereRenderer, 'next-step-email': NextStepEmailRenderer, + 'thread-title': ThreadTitleRenderer, /* eslint-enable @typescript-eslint/naming-convention */ }; diff --git a/src/components/InlineCodeBlock/WrappedText.tsx b/src/components/InlineCodeBlock/WrappedText.tsx index 3c196f2a7492..1edee54eda06 100644 --- a/src/components/InlineCodeBlock/WrappedText.tsx +++ b/src/components/InlineCodeBlock/WrappedText.tsx @@ -14,6 +14,9 @@ type WrappedTextProps = ChildrenProps & { * Style for each individual word (token) in the text. Note that a token can also include whitespace characters between words. */ wordStyles?: StyleProp; + + /** Number of lines before wrapping */ + numberOfLines?: number; }; /** @@ -39,7 +42,7 @@ function containsEmoji(text: string): boolean { return CONST.REGEX.EMOJI.test(text); } -function WrappedText({children, wordStyles, textStyles}: WrappedTextProps) { +function WrappedText({children, wordStyles, textStyles, numberOfLines}: WrappedTextProps) { const styles = useThemeStyles(); if (typeof children !== 'string') { @@ -61,7 +64,12 @@ function WrappedText({children, wordStyles, textStyles}: WrappedTextProps) { style={styles.codeWordWrapper} > - {colText} + + {colText} + ))} diff --git a/src/components/InlineCodeBlock/index.native.tsx b/src/components/InlineCodeBlock/index.native.tsx index 85d02b7239ca..b056cd98a8ce 100644 --- a/src/components/InlineCodeBlock/index.native.tsx +++ b/src/components/InlineCodeBlock/index.native.tsx @@ -6,6 +6,7 @@ import WrappedText from './WrappedText'; function InlineCodeBlock({TDefaultRenderer, defaultRendererProps, textStyle, boxModelStyle}: InlineCodeBlockProps) { const styles = useThemeStyles(); + const numberOfLines = defaultRendererProps.propsFromParent?.numberOfLines; return ( ({TDefaultRenderer, {'data' in defaultRendererProps.tnode && defaultRendererProps.tnode.data} diff --git a/src/components/InlineCodeBlock/index.tsx b/src/components/InlineCodeBlock/index.tsx index 593a08aaad5e..4f0a0c5c3851 100644 --- a/src/components/InlineCodeBlock/index.tsx +++ b/src/components/InlineCodeBlock/index.tsx @@ -7,13 +7,19 @@ import type {TTextOrTPhrasing} from './types'; function InlineCodeBlock({TDefaultRenderer, textStyle, defaultRendererProps, boxModelStyle}: InlineCodeBlockProps) { const flattenTextStyle = StyleSheet.flatten(textStyle); const {textDecorationLine, ...textStyles} = flattenTextStyle; + const numberOfLines = defaultRendererProps.propsFromParent?.numberOfLines; return ( - {'data' in defaultRendererProps.tnode && defaultRendererProps.tnode.data} + + {'data' in defaultRendererProps.tnode && defaultRendererProps.tnode.data} + ); } From 808fd696b47887a5f17a5e3275ec4f5a78a1df0b Mon Sep 17 00:00:00 2001 From: tienifr Date: Thu, 9 May 2024 17:00:51 +0700 Subject: [PATCH 49/49] fix: trimmed spaces in thread markdown --- .../HTMLRenderers/ThreadTitleRenderer.tsx | 8 ++--- .../InlineCodeBlock/WrappedText.tsx | 2 -- src/libs/ReportUtils.ts | 30 +++++++------------ 3 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/ThreadTitleRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/ThreadTitleRenderer.tsx index 77a2d9a05ebc..112a065045e9 100644 --- a/src/components/HTMLEngineProvider/HTMLRenderers/ThreadTitleRenderer.tsx +++ b/src/components/HTMLEngineProvider/HTMLRenderers/ThreadTitleRenderer.tsx @@ -3,10 +3,13 @@ import {View} from 'react-native'; import {TChildrenRenderer} from 'react-native-render-html'; import type {CustomRendererProps, TBlock, TNode} from 'react-native-render-html'; import Text from '@components/Text'; +import useThemeStyles from '@hooks/useThemeStyles'; type ThreadTitleRendererProps = CustomRendererProps; function ThreadTitleRenderer({tnode}: ThreadTitleRendererProps) { + const styles = useThemeStyles(); + const renderFn = (node: TNode) => { const children = node.children; @@ -29,9 +32,6 @@ function ThreadTitleRenderer({tnode}: ThreadTitleRendererProps) { ); @@ -42,7 +42,7 @@ function ThreadTitleRenderer({tnode}: ThreadTitleRendererProps) { return ( {child.data} diff --git a/src/components/InlineCodeBlock/WrappedText.tsx b/src/components/InlineCodeBlock/WrappedText.tsx index 22b4823b3818..524bb6425f57 100644 --- a/src/components/InlineCodeBlock/WrappedText.tsx +++ b/src/components/InlineCodeBlock/WrappedText.tsx @@ -69,7 +69,6 @@ function WrappedText({children, wordStyles, textStyles, numberOfLines}: WrappedT numberOfLines={numberOfLines} style={[textStyles, !containsEmoji(colText) && styles.codePlainTextStyle]} > - {Array.from(colText).map((char, charIndex) => containsOnlyEmojis(char) ? ( diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts index 423a210728c9..4512cbedf671 100644 --- a/src/libs/ReportUtils.ts +++ b/src/libs/ReportUtils.ts @@ -3069,22 +3069,6 @@ function getInvoicePayerName(report: OnyxEntry): string { return getPolicyName(report, false, allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${invoiceReceiver?.policyID}`]); } -/** - * Get the report action message for a report action. - */ -function getReportActionMessage(reportAction: ReportAction | EmptyObject, parentReportID?: string) { - if (isEmptyObject(reportAction)) { - return ''; - } - if (ReportActionsUtils.isApprovedOrSubmittedReportAction(reportAction)) { - return ReportActionsUtils.getReportActionMessageText(reportAction); - } - if (ReportActionsUtils.isReimbursementQueuedAction(reportAction)) { - return getReimbursementQueuedActionMessage(reportAction, getReport(parentReportID), false); - } - return Str.removeSMSDomain(reportAction?.message?.[0]?.text ?? ''); -} - /** * Get the title for an invoice room. */ @@ -3135,12 +3119,18 @@ function getThreadReportNameHtml(reportActionMessageHtml: string): string { * Get the title for a thread based on parent message. * If render in html, only the first line of the message should display. */ -function getThreadReportName(parentReportAction: OnyxEntry | EmptyObject = {}, shouldRenderAsHTML = false, shouldRenderFirstLineOnly = true): string { +function getThreadReportName(parentReportAction: OnyxEntry | EmptyObject = {}, parentReportID?: string, shouldRenderAsHTML = false, shouldRenderFirstLineOnly = true): string { + if (isEmptyObject(parentReportAction)) { + return ''; + } if (ReportActionsUtils.isApprovedOrSubmittedReportAction(parentReportAction)) { - return ReportActionsUtils.getReportActionMessageText(parentReportAction).replace(/(\r\n|\n|\r)/gm, ' '); + return ReportActionsUtils.getReportActionMessageText(parentReportAction); + } + if (ReportActionsUtils.isReimbursementQueuedAction(parentReportAction)) { + return getReimbursementQueuedActionMessage(parentReportAction, getReport(parentReportID), false); } if (!shouldRenderAsHTML && !shouldRenderFirstLineOnly) { - return (parentReportAction?.message?.[0]?.text ?? '').replace(/(\r\n|\n|\r)/gm, ' '); + return Str.removeSMSDomain(parentReportAction?.message?.[0]?.text ?? ''); } const threadReportNameHtml = getThreadReportNameHtml(parentReportAction?.message?.[0]?.html ?? ''); @@ -3172,7 +3162,7 @@ function getReportName(report: OnyxEntry, policy: OnyxEntry = nu } const isAttachment = ReportActionsUtils.isReportActionAttachment(!isEmptyObject(parentReportAction) ? parentReportAction : null); - const parentReportActionMessage = getReportActionMessage(parentReportAction, report?.parentReportID).replace(/(\r\n|\n|\r)/gm, ' '); + const parentReportActionMessage = getThreadReportName(parentReportAction, report?.parentReportID, shouldRenderAsHTML, shouldRenderFirstLineOnly).replace(/(\r\n|\n|\r)/gm, ' '); if (isAttachment && parentReportActionMessage) { return `[${Localize.translateLocal('common.attachment')}]`; }