diff --git a/src/CONST.ts b/src/CONST.ts
index 0ef02fcbd328..6983eb0bfcb3 100755
--- a/src/CONST.ts
+++ b/src/CONST.ts
@@ -373,7 +373,7 @@ const CONST = {
BILLABLE: 'billable',
NON_BILLABLE: 'nonBillable',
},
-
+ TASK_TITLE_DISABLED_RULES: ['image'],
// Note: Group and Self-DM excluded as these are not tied to a Workspace
WORKSPACE_ROOM_TYPES: [chatTypes.POLICY_ADMINS, chatTypes.POLICY_ANNOUNCE, chatTypes.DOMAIN_ALL, chatTypes.POLICY_ROOM, chatTypes.POLICY_EXPENSE_CHAT, chatTypes.INVOICE],
ANDROID_PACKAGE_NAME,
diff --git a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.tsx b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.tsx
index b4d163306cb3..4b53eb0b9913 100644
--- a/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.tsx
+++ b/src/components/HTMLEngineProvider/HTMLRenderers/AnchorRenderer.tsx
@@ -72,7 +72,15 @@ function AnchorRenderer({tnode, style, key}: AnchorRendererProps) {
// eslint-disable-next-line react/jsx-props-no-multi-spaces
target={htmlAttribs.target || '_blank'}
rel={htmlAttribs.rel || 'noopener noreferrer'}
- style={[style, parentStyle, textDecorationLineStyle, styles.textUnderlinePositionUnder, styles.textDecorationSkipInkNone, isChildOfTaskTitle && styles.taskTitleMenuItem]}
+ style={[
+ style,
+ parentStyle,
+ textDecorationLineStyle,
+ styles.textUnderlinePositionUnder,
+ styles.textDecorationSkipInkNone,
+ isChildOfTaskTitle && styles.taskTitleMenuItem,
+ styles.dInlineFlex,
+ ]}
key={key}
// Only pass the press handler for internal links. For public links or whitelisted internal links fallback to default link handling
onPress={internalNewExpensifyPath || internalExpensifyPath ? () => openLink(attrHref, environmentURL, isAttachment) : undefined}
@@ -88,7 +96,14 @@ function AnchorRenderer({tnode, style, key}: AnchorRendererProps) {
return (
{props.childTnode.data}
diff --git a/src/components/ReportActionItem/TaskPreview.tsx b/src/components/ReportActionItem/TaskPreview.tsx
index 4f22d65cbea0..9c7e2827a128 100644
--- a/src/components/ReportActionItem/TaskPreview.tsx
+++ b/src/components/ReportActionItem/TaskPreview.tsx
@@ -24,6 +24,7 @@ import ControlSelection from '@libs/ControlSelection';
import {canUseTouchScreen} from '@libs/DeviceCapabilities';
import getButtonState from '@libs/getButtonState';
import Navigation from '@libs/Navigation/Navigation';
+import Parser from '@libs/Parser';
import {isCanceledTaskReport, isOpenTaskReport, isReportManager} from '@libs/ReportUtils';
import type {ContextMenuAnchor} from '@pages/home/report/ContextMenu/ReportActionContextMenu';
import CONST from '@src/CONST';
@@ -64,6 +65,9 @@ function TaskPreview({taskReportID, action, contextMenuAnchor, chatReportID, che
const {translate} = useLocalize();
const theme = useTheme();
const [taskReport] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT}${taskReportID}`);
+ const taskTitle = action?.childReportName ?? taskReport?.reportName ?? '';
+
+ const taskTitleWithoutImage = Parser.replace(Parser.htmlToMarkdown(taskTitle), {disabledRules: [...CONST.TASK_TITLE_DISABLED_RULES]});
// The reportAction might not contain details regarding the taskReport
// Only the direct parent reportAction will contain details about the taskReport
@@ -127,7 +131,7 @@ function TaskPreview({taskReportID, action, contextMenuAnchor, chatReportID, che
)}
- ${taskReport?.reportName ?? action?.childReportName ?? ''}`} />
+ ${taskTitleWithoutImage}`} />
{shouldShowGreenDotIndicator && (
diff --git a/src/components/ReportActionItem/TaskView.tsx b/src/components/ReportActionItem/TaskView.tsx
index bc12663e5170..1ec73130e4af 100644
--- a/src/components/ReportActionItem/TaskView.tsx
+++ b/src/components/ReportActionItem/TaskView.tsx
@@ -22,6 +22,7 @@ import convertToLTR from '@libs/convertToLTR';
import getButtonState from '@libs/getButtonState';
import Navigation from '@libs/Navigation/Navigation';
import {getAvatarsForAccountIDs, getPersonalDetailsForAccountIDs} from '@libs/OptionsListUtils';
+import Parser from '@libs/Parser';
import {getDisplayNameForParticipant, getDisplayNamesWithTooltips, isCompletedTaskReport, isOpenTaskReport} from '@libs/ReportUtils';
import {isActiveTaskEditRoute} from '@libs/TaskUtils';
import {callFunctionIfActionIsAllowed} from '@userActions/Session';
@@ -44,7 +45,8 @@ function TaskView({report}: TaskViewProps) {
useEffect(() => {
setTaskReport(report);
}, [report]);
- const taskTitle = `${convertToLTR(report?.reportName ?? '')}`;
+ const titleWithoutImage = Parser.replace(Parser.htmlToMarkdown(report?.reportName ?? ''), {disabledRules: [...CONST.TASK_TITLE_DISABLED_RULES]});
+ const taskTitle = `${convertToLTR(titleWithoutImage)}`;
const assigneeTooltipDetails = getDisplayNamesWithTooltips(getPersonalDetailsForAccountIDs(report?.managerID ? [report?.managerID] : [], personalDetails), false);
const isOpen = isOpenTaskReport(report);
diff --git a/src/components/Search/SearchAutocompleteList.tsx b/src/components/Search/SearchAutocompleteList.tsx
index 9407c59718bd..5b730d70d8af 100644
--- a/src/components/Search/SearchAutocompleteList.tsx
+++ b/src/components/Search/SearchAutocompleteList.tsx
@@ -34,6 +34,7 @@ import {
parseForAutocomplete,
} from '@libs/SearchAutocompleteUtils';
import {buildSearchQueryJSON, buildUserReadableQueryString, sanitizeSearchValue} from '@libs/SearchQueryUtils';
+import StringUtils from '@libs/StringUtils';
import Timing from '@userActions/Timing';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
@@ -478,7 +479,12 @@ function SearchAutocompleteList(
sections.push({title: translate('search.recentSearches'), data: recentSearchesData});
}
- const styledRecentReports = recentReportsOptions.map((item) => ({...item, pressableStyle: styles.br2, wrapperStyle: [styles.pr3, styles.pl3]}));
+ const styledRecentReports = recentReportsOptions.map((item) => ({
+ ...item,
+ pressableStyle: styles.br2,
+ text: StringUtils.lineBreaksToSpaces(item.text),
+ wrapperStyle: [styles.pr3, styles.pl3],
+ }));
sections.push({title: autocompleteQueryValue.trim() === '' ? translate('search.recentChats') : undefined, data: styledRecentReports});
if (autocompleteSuggestions.length > 0) {
diff --git a/src/components/Search/SearchRouter/SearchRouter.tsx b/src/components/Search/SearchRouter/SearchRouter.tsx
index 5281032967cf..f3c8353c36c9 100644
--- a/src/components/Search/SearchRouter/SearchRouter.tsx
+++ b/src/components/Search/SearchRouter/SearchRouter.tsx
@@ -145,7 +145,7 @@ function SearchRouter({onRouterClose, shouldHideInputCaret}: SearchRouterProps,
{
data: [
{
- text: `${translate('search.searchIn')} ${reportForContextualSearch.text ?? reportForContextualSearch.alternateText}`,
+ text: StringUtils.lineBreaksToSpaces(`${translate('search.searchIn')} ${reportForContextualSearch.text ?? reportForContextualSearch.alternateText}`),
singleIcon: Expensicons.MagnifyingGlass,
searchQuery: reportQueryValue,
autocompleteID,
diff --git a/src/libs/ReportUtils.ts b/src/libs/ReportUtils.ts
index c847a92554a5..c5879d5d6fc4 100644
--- a/src/libs/ReportUtils.ts
+++ b/src/libs/ReportUtils.ts
@@ -4862,7 +4862,7 @@ function completeShortMention(text: string): string {
* For comments shorter than or equal to 10k chars, convert the comment from MD into HTML because that's how it is stored in the database
* For longer comments, skip parsing, but still escape the text, and display plaintext for performance reasons. It takes over 40s to parse a 100k long string!!
*/
-function getParsedComment(text: string, parsingDetails?: ParsingDetails, mediaAttributes?: Record): string {
+function getParsedComment(text: string, parsingDetails?: ParsingDetails, mediaAttributes?: Record, disabledRules?: string[]): string {
let isGroupPolicyReport = false;
if (parsingDetails?.reportID) {
const currentReport = getReportOrDraftReport(parsingDetails?.reportID);
@@ -4877,11 +4877,12 @@ function getParsedComment(text: string, parsingDetails?: ParsingDetails, mediaAt
}
const textWithMention = completeShortMention(text);
+ const rules = disabledRules ?? [];
return text.length <= CONST.MAX_MARKUP_LENGTH
? Parser.replace(textWithMention, {
shouldEscapeText: parsingDetails?.shouldEscapeText,
- disabledRules: isGroupPolicyReport ? [] : ['reportMentions'],
+ disabledRules: isGroupPolicyReport ? [...rules] : ['reportMentions', ...rules],
extras: {mediaAttributeCache: mediaAttributes},
})
: lodashEscape(text);
@@ -6506,7 +6507,7 @@ function buildOptimisticEditedTaskFieldReportAction({title, description}: Task):
{
type: CONST.REPORT.MESSAGE.TYPE.COMMENT,
text: changelog,
- html: getParsedComment(changelog),
+ html: getParsedComment(changelog, undefined, undefined, title !== undefined ? [...CONST.TASK_TITLE_DISABLED_RULES] : undefined),
},
],
person: [
@@ -6851,7 +6852,7 @@ function buildOptimisticTaskReport(
return {
reportID: generateReportID(),
- reportName: getParsedComment(title ?? ''),
+ reportName: getParsedComment(title ?? '', undefined, undefined, [...CONST.TASK_TITLE_DISABLED_RULES]),
description: getParsedComment(description ?? '', {}, mediaAttributes),
ownerAccountID,
participants,
diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts
index 9045b1123451..52ad31b64dea 100644
--- a/src/libs/actions/Task.ts
+++ b/src/libs/actions/Task.ts
@@ -550,7 +550,7 @@ function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task
// Ensure title is defined before parsing it with getParsedComment. If title is undefined, fall back to reportName from report.
// Trim the final parsed title for consistency.
- const reportName = title ? ReportUtils.getParsedComment(title) : report?.reportName ?? '';
+ const reportName = title ? ReportUtils.getParsedComment(title, undefined, undefined, [...CONST.TASK_TITLE_DISABLED_RULES]) : report?.reportName ?? '';
const parsedTitle = (reportName ?? '').trim();
// Description can be unset, so we default to an empty string if so
diff --git a/src/pages/tasks/NewTaskPage.tsx b/src/pages/tasks/NewTaskPage.tsx
index afe396891894..c3d0fd55e54c 100644
--- a/src/pages/tasks/NewTaskPage.tsx
+++ b/src/pages/tasks/NewTaskPage.tsx
@@ -139,6 +139,7 @@ function NewTaskPage({route}: NewTaskPageProps) {
shouldShowRightIcon
rightLabel={translate('common.required')}
shouldParseTitle
+ excludedMarkdownRules={[...CONST.TASK_TITLE_DISABLED_RULES]}
/>
): FormInputErrors => {
const errors: FormInputErrors = {};
- const parsedTitle = getParsedComment(title);
+ const parsedTitle = getParsedComment(title, undefined, undefined, [...CONST.TASK_TITLE_DISABLED_RULES]);
const parsedTitleLength = getCommentLength(parsedTitle);
if (!parsedTitle) {
@@ -110,7 +110,7 @@ function TaskTitlePage({report, currentUserPersonalDetails}: TaskTitlePageProps)
name={INPUT_IDS.TITLE}
label={translate('task.title')}
accessibilityLabel={translate('task.title')}
- defaultValue={Parser.htmlToMarkdown(report?.reportName ?? '')}
+ defaultValue={Parser.htmlToMarkdown(report?.reportName ?? '', {})}
ref={(element: AnimatedTextInputRef) => {
if (!element) {
return;
diff --git a/src/styles/index.ts b/src/styles/index.ts
index ad7f3a5bb808..68646107aae8 100644
--- a/src/styles/index.ts
+++ b/src/styles/index.ts
@@ -4278,18 +4278,20 @@ const styles = (theme: ThemeColors) =>
...writingDirection.ltr,
...headlineFont,
fontSize: variables.fontSizeXLarge,
- lineHeight: variables.lineHeightSizeh2,
+ lineHeight: variables.lineHeighTaskTitle,
maxWidth: '100%',
...wordBreak.breakWord,
+ textUnderlineOffset: -1,
},
taskTitleMenuItemItalic: {
...writingDirection.ltr,
...headlineItalicFont,
fontSize: variables.fontSizeXLarge,
- lineHeight: variables.lineHeightSizeh2,
+ lineHeight: variables.lineHeighTaskTitle,
maxWidth: '100%',
...wordBreak.breakWord,
+ textUnderlineOffset: -1,
},
taskDescriptionMenuItem: {
diff --git a/src/styles/utils/index.ts b/src/styles/utils/index.ts
index b6ee3085d981..c6df26fa2304 100644
--- a/src/styles/utils/index.ts
+++ b/src/styles/utils/index.ts
@@ -635,7 +635,7 @@ function getCodeFontSize(isInsideH1: boolean, isInsideTaskTitle?: boolean) {
return 15;
}
if (isInsideTaskTitle) {
- return 19;
+ return 18;
}
return 13;
}
diff --git a/src/styles/variables.ts b/src/styles/variables.ts
index 2b39f2320b91..15b3edb3736f 100644
--- a/src/styles/variables.ts
+++ b/src/styles/variables.ts
@@ -113,6 +113,7 @@ export default {
lineHeightXXXLarge: getValueUsingPixelRatio(32, 37),
lineHeightSizeh1: getValueUsingPixelRatio(28, 32),
lineHeightSizeh2: getValueUsingPixelRatio(24, 28),
+ lineHeighTaskTitle: getValueUsingPixelRatio(26, 30),
lineHeightSignInHeroXSmall: getValueUsingPixelRatio(32, 37),
inputHeight: getValueUsingPixelRatio(52, 72),
inputHeightSmall: 28,