Skip to content

Commit

Permalink
Merge pull request #40565 from software-mansion-labs/mention-editing
Browse files Browse the repository at this point in the history
[Mentions v2] Support mentions in editing comments
  • Loading branch information
rlinoz authored Jun 14, 2024
2 parents 1fd7ecc + 8846545 commit f703ae0
Show file tree
Hide file tree
Showing 17 changed files with 87 additions and 55 deletions.
6 changes: 2 additions & 4 deletions src/components/LHNOptionsList/OptionRowLHN.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {useFocusEffect} from '@react-navigation/native';
import {ExpensiMark} from 'expensify-common';
import React, {useCallback, useRef, useState} from 'react';
import type {GestureResponderEvent, ViewStyle} from 'react-native';
import {StyleSheet, View} from 'react-native';
Expand All @@ -20,6 +19,7 @@ import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
import DateUtils from '@libs/DateUtils';
import DomUtils from '@libs/DomUtils';
import {parseHtmlToText} from '@libs/OnyxAwareParser';
import * as OptionsListUtils from '@libs/OptionsListUtils';
import Performance from '@libs/Performance';
import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
Expand All @@ -29,8 +29,6 @@ import CONST from '@src/CONST';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type {OptionRowLHNProps} from './types';

const parser = new ExpensiMark();

function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, optionItem, viewMode = 'default', style, onLayout = () => {}, hasDraftComment}: OptionRowLHNProps) {
const theme = useTheme();
const styles = useThemeStyles();
Expand Down Expand Up @@ -240,7 +238,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti
numberOfLines={1}
accessibilityLabel={translate('accessibilityHints.lastChatMessagePreview')}
>
{parser.htmlToText(optionItem.alternateText)}
{parseHtmlToText(optionItem.alternateText)}
</Text>
) : null}
</View>
Expand Down
7 changes: 3 additions & 4 deletions src/hooks/useCopySelectionHelper.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {ExpensiMark} from 'expensify-common';
import {useEffect} from 'react';
import Clipboard from '@libs/Clipboard';
import KeyboardShortcut from '@libs/KeyboardShortcut';
import {parseHtmlToMarkdown, parseHtmlToText} from '@libs/OnyxAwareParser';
import SelectionScraper from '@libs/SelectionScraper';
import CONST from '@src/CONST';

Expand All @@ -10,12 +10,11 @@ function copySelectionToClipboard() {
if (!selection) {
return;
}
const parser = new ExpensiMark();
if (!Clipboard.canSetHtml()) {
Clipboard.setString(parser.htmlToMarkdown(selection));
Clipboard.setString(parseHtmlToMarkdown(selection));
return;
}
Clipboard.setHtml(selection, parser.htmlToText(selection));
Clipboard.setHtml(selection, parseHtmlToText(selection));
}

export default function useCopySelectionHelper() {
Expand Down
5 changes: 2 additions & 3 deletions src/hooks/useHtmlPaste/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useNavigation} from '@react-navigation/native';
import {ExpensiMark} from 'expensify-common';
import {useCallback, useEffect} from 'react';
import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser';
import type UseHtmlPaste from './types';

const insertByCommand = (text: string) => {
Expand Down Expand Up @@ -62,8 +62,7 @@ const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeLi
*/
const handlePastedHTML = useCallback(
(html: string) => {
const parser = new ExpensiMark();
paste(parser.htmlToMarkdown(html));
paste(parseHtmlToMarkdown(html));
},
[paste],
);
Expand Down
42 changes: 42 additions & 0 deletions src/libs/OnyxAwareParser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {ExpensiMark} from 'expensify-common';
import Onyx from 'react-native-onyx';
import ONYXKEYS from '@src/ONYXKEYS';

const parser = new ExpensiMark();

const reportIDToNameMap: Record<string, string> = {};
const accountIDToNameMap: Record<string, string> = {};

Onyx.connect({
key: ONYXKEYS.COLLECTION.REPORT,
callback: (report) => {
if (!report) {
return;
}

reportIDToNameMap[report.reportID] = report.reportName ?? report.displayName ?? report.reportID;
},
});

Onyx.connect({
key: ONYXKEYS.PERSONAL_DETAILS_LIST,
callback: (personalDetailsList) => {
Object.values(personalDetailsList ?? {}).forEach((personalDetails) => {
if (!personalDetails) {
return;
}

accountIDToNameMap[personalDetails.accountID] = personalDetails.login ?? String(personalDetails.accountID);
});
},
});

function parseHtmlToMarkdown(html: string, reportIDToName?: Record<string, string>, accountIDToName?: Record<string, string>): string {
return parser.htmlToMarkdown(html, {reportIDToName: reportIDToName ?? reportIDToNameMap, accountIDToName: accountIDToName ?? accountIDToNameMap});
}

function parseHtmlToText(html: string, reportIDToName?: Record<string, string>, accountIDToName?: Record<string, string>): string {
return parser.htmlToText(html, {reportIDToName: reportIDToName ?? reportIDToNameMap, accountIDToName: accountIDToName ?? accountIDToNameMap});
}

export {parseHtmlToMarkdown, parseHtmlToText};
15 changes: 6 additions & 9 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import ModifiedExpenseMessage from './ModifiedExpenseMessage';
import linkingConfig from './Navigation/linkingConfig';
import Navigation from './Navigation/Navigation';
import * as NumberUtils from './NumberUtils';
import {parseHtmlToText} from './OnyxAwareParser';
import Permissions from './Permissions';
import * as PersonalDetailsUtils from './PersonalDetailsUtils';
import * as PhoneNumber from './PhoneNumber';
Expand Down Expand Up @@ -3238,8 +3239,7 @@ function parseReportActionHtmlToText(reportAction: OnyxEntry<ReportAction>, repo
const logins = PersonalDetailsUtils.getLoginsByAccountIDs(accountIDs);
accountIDs.forEach((id, index) => (accountIDToName[id] = logins[index]));

const parser = new ExpensiMark();
const textMessage = Str.removeSMSDomain(parser.htmlToText(html, {reportIDToName, accountIDToName}));
const textMessage = Str.removeSMSDomain(parseHtmlToText(html, reportIDToName, accountIDToName));
parsedReportActionMessageCache[key] = textMessage;

return textMessage;
Expand Down Expand Up @@ -3609,17 +3609,15 @@ function getReportDescriptionText(report: Report): string {
return '';
}

const parser = new ExpensiMark();
return parser.htmlToText(report.description);
return parseHtmlToText(report.description);
}

function getPolicyDescriptionText(policy: OnyxEntry<Policy>): string {
if (!policy?.description) {
return '';
}

const parser = new ExpensiMark();
return parser.htmlToText(policy.description);
return parseHtmlToText(policy.description);
}

function buildOptimisticAddCommentReportAction(
Expand All @@ -3630,7 +3628,6 @@ function buildOptimisticAddCommentReportAction(
shouldEscapeText?: boolean,
reportID?: string,
): OptimisticReportAction {
const parser = new ExpensiMark();
const commentText = getParsedComment(text ?? '', {shouldEscapeText, reportID});
const isAttachmentOnly = file && !text;
const isTextOnly = text && !file;
Expand All @@ -3642,10 +3639,10 @@ function buildOptimisticAddCommentReportAction(
textForNewComment = CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML;
} else if (isTextOnly) {
htmlForNewComment = commentText;
textForNewComment = parser.htmlToText(htmlForNewComment);
textForNewComment = parseHtmlToText(htmlForNewComment);
} else {
htmlForNewComment = `${commentText}<uploading-attachment>${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}</uploading-attachment>`;
textForNewComment = `${parser.htmlToText(commentText)}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`;
textForNewComment = `${parseHtmlToText(commentText)}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`;
}

const isAttachment = !text && file !== undefined;
Expand Down
5 changes: 3 additions & 2 deletions src/libs/actions/Report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ import Log from '@libs/Log';
import Navigation from '@libs/Navigation/Navigation';
import type {NetworkStatus} from '@libs/NetworkConnection';
import LocalNotification from '@libs/Notification/LocalNotification';
import {parseHtmlToMarkdown, parseHtmlToText} from '@libs/OnyxAwareParser';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as PhoneNumber from '@libs/PhoneNumber';
import getPolicyEmployeeAccountIDs from '@libs/PolicyEmployeeListUtils';
Expand Down Expand Up @@ -1473,15 +1474,15 @@ function editReportComment(reportID: string, originalReportAction: OnyxEntry<Rep
// https://github.com/Expensify/App/issues/9090
// https://github.com/Expensify/App/issues/13221
const originalCommentHTML = originalReportAction.message?.[0]?.html;
const originalCommentMarkdown = parser.htmlToMarkdown(originalCommentHTML ?? '').trim();
const originalCommentMarkdown = parseHtmlToMarkdown(originalCommentHTML ?? '').trim();

// Skip the Edit if draft is not changed
if (originalCommentMarkdown === textForNewComment) {
return;
}

const htmlForNewComment = handleUserDeletedLinksInHtml(textForNewComment, originalCommentMarkdown);
const reportComment = parser.htmlToText(htmlForNewComment);
const reportComment = parseHtmlToText(htmlForNewComment);

// For comments shorter than or equal to 10k chars, convert the comment from MD into HTML because that's how it is stored in the database
// For longer comments, skip parsing and display plaintext for performance reasons. It takes over 40s to parse a 100k long string!!
Expand Down
8 changes: 4 additions & 4 deletions src/pages/PrivateNotes/PrivateNotesEditPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useFocusEffect} from '@react-navigation/native';
import type {StackScreenProps} from '@react-navigation/stack';
import {ExpensiMark, Str} from 'expensify-common';
import {Str} from 'expensify-common';
import lodashDebounce from 'lodash/debounce';
import React, {useCallback, useMemo, useRef, useState} from 'react';
import {Keyboard} from 'react-native';
Expand All @@ -19,6 +19,7 @@ import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import type {PrivateNotesNavigatorParamList} from '@libs/Navigation/types';
import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser';
import * as ReportUtils from '@libs/ReportUtils';
import updateMultilineInputRange from '@libs/updateMultilineInputRange';
import type {WithReportAndPrivateNotesOrNotFoundProps} from '@pages/home/report/withReportAndPrivateNotesOrNotFound';
Expand Down Expand Up @@ -50,9 +51,8 @@ function PrivateNotesEditPage({route, personalDetailsList, report, session}: Pri
const {translate} = useLocalize();

// We need to edit the note in markdown format, but display it in HTML format
const parser = new ExpensiMark();
const [privateNote, setPrivateNote] = useState(
() => ReportActions.getDraftPrivateNote(report.reportID).trim() || parser.htmlToMarkdown(report?.privateNotes?.[Number(route.params.accountID)]?.note ?? '').trim(),
() => ReportActions.getDraftPrivateNote(report.reportID).trim() || parseHtmlToMarkdown(report?.privateNotes?.[Number(route.params.accountID)]?.note ?? '').trim(),
);

/**
Expand Down Expand Up @@ -93,7 +93,7 @@ function PrivateNotesEditPage({route, personalDetailsList, report, session}: Pri
const originalNote = report?.privateNotes?.[Number(route.params.accountID)]?.note ?? '';
let editedNote = '';
if (privateNote.trim() !== originalNote.trim()) {
editedNote = ReportActions.handleUserDeletedLinksInHtml(privateNote.trim(), parser.htmlToMarkdown(originalNote).trim());
editedNote = ReportActions.handleUserDeletedLinksInHtml(privateNote.trim(), parseHtmlToMarkdown(originalNote).trim());
ReportActions.updatePrivateNotes(report.reportID, Number(route.params.accountID), editedNote);
}

Expand Down
5 changes: 2 additions & 3 deletions src/pages/RoomDescriptionPage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {useFocusEffect} from '@react-navigation/native';
import {ExpensiMark} from 'expensify-common';
import React, {useCallback, useRef, useState} from 'react';
import {View} from 'react-native';
import type {OnyxCollection} from 'react-native-onyx';
Expand All @@ -13,6 +12,7 @@ import TextInput from '@components/TextInput';
import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types';
import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser';
import * as ReportUtils from '@libs/ReportUtils';
import updateMultilineInputRange from '@libs/updateMultilineInputRange';
import variables from '@styles/variables';
Expand All @@ -32,8 +32,7 @@ type RoomDescriptionPageProps = {

function RoomDescriptionPage({report, policies}: RoomDescriptionPageProps) {
const styles = useThemeStyles();
const parser = new ExpensiMark();
const [description, setDescription] = useState(() => parser.htmlToMarkdown(report?.description ?? ''));
const [description, setDescription] = useState(() => parseHtmlToMarkdown(report?.description ?? ''));
const reportDescriptionInputRef = useRef<BaseTextInputRef | null>(null);
const focusTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
const {translate} = useLocalize();
Expand Down
11 changes: 5 additions & 6 deletions src/pages/home/report/ContextMenu/ContextMenuActions.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {ExpensiMark, Str} from 'expensify-common';
import {Str} from 'expensify-common';
import type {MutableRefObject} from 'react';
import React from 'react';
import {InteractionManager} from 'react-native';
Expand All @@ -18,6 +18,7 @@ import getAttachmentDetails from '@libs/fileDownload/getAttachmentDetails';
import * as Localize from '@libs/Localize';
import ModifiedExpenseMessage from '@libs/ModifiedExpenseMessage';
import Navigation from '@libs/Navigation/Navigation';
import {parseHtmlToMarkdown, parseHtmlToText} from '@libs/OnyxAwareParser';
import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
Expand All @@ -40,13 +41,12 @@ function getActionHtml(reportAction: OnyxInputOrEntry<ReportAction>): string {

/** Sets the HTML string to Clipboard */
function setClipboardMessage(content: string) {
const parser = new ExpensiMark();
if (!Clipboard.canSetHtml()) {
Clipboard.setString(parser.htmlToMarkdown(content));
Clipboard.setString(parseHtmlToMarkdown(content));
} else {
const anchorRegex = CONST.REGEX_LINK_IN_ANCHOR;
const isAnchorTag = anchorRegex.test(content);
const plainText = isAnchorTag ? parser.htmlToMarkdown(content) : parser.htmlToText(content);
const plainText = isAnchorTag ? parseHtmlToMarkdown(content) : parseHtmlToText(content);
Clipboard.setHtml(content, plainText);
}
}
Expand Down Expand Up @@ -238,8 +238,7 @@ const ContextMenuActions: ContextMenuAction[] = [
}
const editAction = () => {
if (!draftMessage) {
const parser = new ExpensiMark();
Report.saveReportActionDraft(reportID, reportAction, parser.htmlToMarkdown(getActionHtml(reportAction)));
Report.saveReportActionDraft(reportID, reportAction, parseHtmlToMarkdown(getActionHtml(reportAction)));
} else {
Report.deleteReportActionDraft(reportID, reportAction);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import {useIsFocused, useNavigation} from '@react-navigation/native';
import {ExpensiMark} from 'expensify-common';
import lodashDebounce from 'lodash/debounce';
import type {ForwardedRef, MutableRefObject, RefAttributes, RefObject} from 'react';
import React, {forwardRef, memo, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react';
Expand Down Expand Up @@ -35,6 +34,7 @@ import * as EmojiUtils from '@libs/EmojiUtils';
import focusComposerWithDelay from '@libs/focusComposerWithDelay';
import getPlatform from '@libs/getPlatform';
import * as KeyDownListener from '@libs/KeyboardShortcut/KeyDownPressListener';
import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser';
import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
import * as ReportUtils from '@libs/ReportUtils';
Expand Down Expand Up @@ -542,8 +542,7 @@ function ComposerWithSuggestions(
) {
event.preventDefault();
if (lastReportAction) {
const parser = new ExpensiMark();
Report.saveReportActionDraft(reportID, lastReportAction, parser.htmlToMarkdown(lastReportAction.message?.at(-1)?.html ?? ''));
Report.saveReportActionDraft(reportID, lastReportAction, parseHtmlToMarkdown(lastReportAction.message?.at(-1)?.html ?? ''));
}
}
},
Expand Down
5 changes: 2 additions & 3 deletions src/pages/home/report/ReportActionItemMessageEdit.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {ExpensiMark} from 'expensify-common';
import lodashDebounce from 'lodash/debounce';
import type {ForwardedRef} from 'react';
import React, {forwardRef, useCallback, useEffect, useMemo, useRef, useState} from 'react';
Expand Down Expand Up @@ -27,6 +26,7 @@ import * as EmojiUtils from '@libs/EmojiUtils';
import focusComposerWithDelay from '@libs/focusComposerWithDelay';
import type {Selection} from '@libs/focusComposerWithDelay/types';
import focusEditAfterCancelDelete from '@libs/focusEditAfterCancelDelete';
import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser';
import onyxSubscribe from '@libs/onyxSubscribe';
import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
Expand Down Expand Up @@ -105,8 +105,7 @@ function ReportActionItemMessageEdit(
const isCommentPendingSaved = useRef(false);

useEffect(() => {
const parser = new ExpensiMark();
const originalMessage = parser.htmlToMarkdown(action.message?.[0]?.html ?? '');
const originalMessage = parseHtmlToMarkdown(action.message?.[0]?.html ?? '');
if (ReportActionsUtils.isDeletedAction(action) || !!(action.message && draftMessage === originalMessage) || !!(prevDraftMessage === draftMessage || isCommentPendingSaved.current)) {
return;
}
Expand Down
3 changes: 2 additions & 1 deletion src/pages/tasks/NewTaskDescriptionPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import * as ErrorUtils from '@libs/ErrorUtils';
import Navigation from '@libs/Navigation/Navigation';
import type {NewTaskNavigatorParamList} from '@libs/Navigation/types';
import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser';
import updateMultilineInputRange from '@libs/updateMultilineInputRange';
import variables from '@styles/variables';
import * as TaskActions from '@userActions/Task';
Expand Down Expand Up @@ -78,7 +79,7 @@ function NewTaskDescriptionPage({task}: NewTaskDescriptionPageProps) {
<View style={styles.mb5}>
<InputWrapperWithRef
InputComponent={TextInput}
defaultValue={parser.htmlToMarkdown(parser.replace(task?.description ?? ''))}
defaultValue={parseHtmlToMarkdown(parser.replace(task?.description ?? ''))}
inputID={INPUT_IDS.TASK_DESCRIPTION}
label={translate('newTaskPage.descriptionOptional')}
accessibilityLabel={translate('newTaskPage.descriptionOptional')}
Expand Down
Loading

0 comments on commit f703ae0

Please sign in to comment.