Skip to content

Commit

Permalink
Merge pull request #44732 from software-mansion-labs/refactor/central…
Browse files Browse the repository at this point in the history
…ize-expensimark

Centralize ExpensiMark usage with a dedicated Parser module
  • Loading branch information
neil-marcellini authored Jul 8, 2024
2 parents 29e8df5 + 5570f03 commit b419c06
Show file tree
Hide file tree
Showing 30 changed files with 133 additions and 150 deletions.
10 changes: 7 additions & 3 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const restrictedImportPaths = [
'',
"For 'useWindowDimensions', please use '@src/hooks/useWindowDimensions' instead.",
"For 'TouchableOpacity', 'TouchableWithoutFeedback', 'TouchableNativeFeedback', 'TouchableHighlight', 'Pressable', please use 'PressableWithFeedback' and/or 'PressableWithoutFeedback' from '@components/Pressable' instead.",
"For 'StatusBar', please use '@src/libs/StatusBar' instead.",
"For 'StatusBar', please use '@libs/StatusBar' instead.",
"For 'Text', please use '@components/Text' instead.",
"For 'ScrollView', please use '@components/ScrollView' instead.",
].join('\n'),
Expand Down Expand Up @@ -59,8 +59,12 @@ const restrictedImportPaths = [
},
{
name: 'expensify-common',
importNames: ['Device'],
message: "Do not import Device directly, it's known to make VSCode's IntelliSense crash. Please import the desired module from `expensify-common/dist/Device` instead.",
importNames: ['Device', 'ExpensiMark'],
message: [
'',
"For 'Device', do not import it directly, it's known to make VSCode's IntelliSense crash. Please import the desired module from `expensify-common/dist/Device` instead.",
"For 'ExpensiMark', please use '@libs/Parser' instead.",
].join('\n'),
},
];

Expand Down
4 changes: 2 additions & 2 deletions src/components/LHNOptionsList/OptionRowLHN.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ 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 Parser from '@libs/Parser';
import Performance from '@libs/Performance';
import ReportActionComposeFocusManager from '@libs/ReportActionComposeFocusManager';
import * as ReportUtils from '@libs/ReportUtils';
Expand Down Expand Up @@ -251,7 +251,7 @@ function OptionRowLHN({reportID, isFocused = false, onSelectRow = () => {}, opti
numberOfLines={1}
accessibilityLabel={translate('accessibilityHints.lastChatMessagePreview')}
>
{parseHtmlToText(optionItem.alternateText)}
{Parser.htmlToText(optionItem.alternateText)}
</Text>
) : null}
</View>
Expand Down
8 changes: 3 additions & 5 deletions src/components/MenuItem.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {ExpensiMark} from 'expensify-common';
import type {ImageContentFit} from 'expo-image';
import type {ReactElement, ReactNode} from 'react';
import React, {forwardRef, useContext, useMemo} from 'react';
Expand All @@ -14,6 +13,7 @@ import ControlSelection from '@libs/ControlSelection';
import convertToLTR from '@libs/convertToLTR';
import * as DeviceCapabilities from '@libs/DeviceCapabilities';
import getButtonState from '@libs/getButtonState';
import Parser from '@libs/Parser';
import type {AvatarSource} from '@libs/UserUtils';
import variables from '@styles/variables';
import * as Session from '@userActions/Session';
Expand Down Expand Up @@ -433,16 +433,14 @@ function MenuItem(
if (!title || !shouldParseTitle) {
return '';
}
const parser = new ExpensiMark();
return parser.replace(title, {shouldEscapeText});
return Parser.replace(title, {shouldEscapeText});
}, [title, shouldParseTitle, shouldEscapeText]);

const helperHtml = useMemo(() => {
if (!helperText || !shouldParseHelperText) {
return '';
}
const parser = new ExpensiMark();
return parser.replace(helperText, {shouldEscapeText});
return Parser.replace(helperText, {shouldEscapeText});
}, [helperText, shouldParseHelperText, shouldEscapeText]);

const processedTitle = useMemo(() => {
Expand Down
6 changes: 3 additions & 3 deletions src/hooks/useCopySelectionHelper.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {useEffect} from 'react';
import Clipboard from '@libs/Clipboard';
import KeyboardShortcut from '@libs/KeyboardShortcut';
import {parseHtmlToMarkdown, parseHtmlToText} from '@libs/OnyxAwareParser';
import Parser from '@libs/Parser';
import SelectionScraper from '@libs/SelectionScraper';
import CONST from '@src/CONST';

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

export default function useCopySelectionHelper() {
Expand Down
4 changes: 2 additions & 2 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 {useCallback, useEffect} from 'react';
import {parseHtmlToMarkdown} from '@libs/OnyxAwareParser';
import Parser from '@libs/Parser';
import type UseHtmlPaste from './types';

const insertByCommand = (text: string) => {
Expand Down Expand Up @@ -71,7 +71,7 @@ const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeLi
*/
const handlePastedHTML = useCallback(
(html: string) => {
paste(parseHtmlToMarkdown(html));
paste(Parser.htmlToMarkdown(html));
},
[paste],
);
Expand Down
3 changes: 1 addition & 2 deletions src/libs/Log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// action would likely cause confusion about which one to use. But most other API methods should happen inside an action file.

/* eslint-disable rulesdir/no-api-in-views */
import {ExpensiMark, Logger} from 'expensify-common';
import {Logger} from 'expensify-common';
import Onyx from 'react-native-onyx';
import type {Merge} from 'type-fest';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -80,6 +80,5 @@ const Log = new Logger({
isDebug: true,
});
timeout = setTimeout(() => Log.info('Flushing logs older than 10 minutes', true, {}, true), 10 * 60 * 1000);
ExpensiMark.setLogger(Log);

export default Log;
45 changes: 0 additions & 45 deletions src/libs/OnyxAwareParser.ts

This file was deleted.

51 changes: 51 additions & 0 deletions src/libs/Parser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// eslint-disable-next-line no-restricted-imports
import {ExpensiMark} from 'expensify-common';
import Onyx from 'react-native-onyx';
import ONYXKEYS from '@src/ONYXKEYS';
import Log from './Log';
import * as ReportConnection from './ReportConnection';

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

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);
});
},
});

type Extras = {
reportIDToName?: Record<string, string>;
accountIDToName?: Record<string, string>;
cacheVideoAttributes?: (vidSource: string, attrs: string) => void;
videoAttributeCache?: Record<string, string>;
};

class ExpensiMarkWithContext extends ExpensiMark {
htmlToMarkdown(htmlString: string, extras?: Extras): string {
return super.htmlToMarkdown(htmlString, {
reportIDToName: extras?.reportIDToName ?? ReportConnection.getAllReportsNameMap(),
accountIDToName: extras?.accountIDToName ?? accountIDToNameMap,
cacheVideoAttributes: extras?.cacheVideoAttributes,
});
}

htmlToText(htmlString: string, extras?: Extras): string {
return super.htmlToText(htmlString, {
reportIDToName: extras?.reportIDToName ?? ReportConnection.getAllReportsNameMap(),
accountIDToName: extras?.accountIDToName ?? accountIDToNameMap,
cacheVideoAttributes: extras?.cacheVideoAttributes,
});
}
}

ExpensiMarkWithContext.setLogger(Log);
const Parser = new ExpensiMarkWithContext();

export default Parser;
6 changes: 3 additions & 3 deletions src/libs/ReportActionsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import isReportMessageAttachment from './isReportMessageAttachment';
import * as Localize from './Localize';
import Log from './Log';
import type {MessageElementBase, MessageTextElement} from './MessageElement';
import {parseHtmlToText} from './OnyxAwareParser';
import Parser from './Parser';
import * as PersonalDetailsUtils from './PersonalDetailsUtils';
import * as ReportConnection from './ReportConnection';
import type {OptimisticIOUReportAction, PartialReportAction} from './ReportUtils';
Expand Down Expand Up @@ -1080,11 +1080,11 @@ function getReportActionText(reportAction: PartialReportAction): string {
// Sometime html can be an empty string
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
const text = (message?.html || message?.text) ?? '';
return text ? parseHtmlToText(text) : '';
return text ? Parser.htmlToText(text) : '';
}

function getTextFromHtml(html?: string): string {
return html ? parseHtmlToText(html) : '';
return html ? Parser.htmlToText(html) : '';
}

function getMemberChangeMessageFragment(reportAction: OnyxEntry<ReportAction>): Message {
Expand Down
17 changes: 8 additions & 9 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {format} from 'date-fns';
import {ExpensiMark, Str} from 'expensify-common';
import {Str} from 'expensify-common';
import {isEmpty} from 'lodash';
import lodashEscape from 'lodash/escape';
import lodashFindLastIndex from 'lodash/findLastIndex';
Expand Down Expand Up @@ -63,7 +63,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 Parser from './Parser';
import Permissions from './Permissions';
import * as PersonalDetailsUtils from './PersonalDetailsUtils';
import * as PhoneNumber from './PhoneNumber';
Expand Down Expand Up @@ -3302,7 +3302,7 @@ function parseReportActionHtmlToText(reportAction: OnyxEntry<ReportAction>, repo
const logins = PersonalDetailsUtils.getLoginsByAccountIDs(accountIDs);
accountIDs.forEach((id, index) => (accountIDToName[id] = logins[index]));

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

return textMessage;
Expand Down Expand Up @@ -3657,7 +3657,6 @@ function getParsedComment(text: string, parsingDetails?: ParsingDetails): string
isGroupPolicyReport = isReportInGroupPolicy(currentReport);
}

const parser = new ExpensiMark();
const textWithMention = text.replace(CONST.REGEX.SHORT_MENTION, (match) => {
if (!Str.isValidMention(match)) {
return match;
Expand All @@ -3668,7 +3667,7 @@ function getParsedComment(text: string, parsingDetails?: ParsingDetails): string
});

return text.length <= CONST.MAX_MARKUP_LENGTH
? parser.replace(textWithMention, {shouldEscapeText: parsingDetails?.shouldEscapeText, disabledRules: isGroupPolicyReport ? [] : ['reportMentions']})
? Parser.replace(textWithMention, {shouldEscapeText: parsingDetails?.shouldEscapeText, disabledRules: isGroupPolicyReport ? [] : ['reportMentions']})
: lodashEscape(text);
}

Expand All @@ -3677,15 +3676,15 @@ function getReportDescriptionText(report: Report): string {
return '';
}

return parseHtmlToText(report.description);
return Parser.htmlToText(report.description);
}

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

return parseHtmlToText(policy.description);
return Parser.htmlToText(policy.description);
}

function buildOptimisticAddCommentReportAction(
Expand All @@ -3707,10 +3706,10 @@ function buildOptimisticAddCommentReportAction(
textForNewComment = CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML;
} else if (isTextOnly) {
htmlForNewComment = commentText;
textForNewComment = parseHtmlToText(htmlForNewComment);
textForNewComment = Parser.htmlToText(htmlForNewComment);
} else {
htmlForNewComment = `${commentText}<uploading-attachment>${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}</uploading-attachment>`;
textForNewComment = `${parseHtmlToText(commentText)}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`;
textForNewComment = `${Parser.htmlToText(commentText)}\n${CONST.ATTACHMENT_UPLOADING_MESSAGE_HTML}`;
}

const isAttachment = !text && file !== undefined;
Expand Down
4 changes: 2 additions & 2 deletions src/libs/actions/Policy/Member.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import {ExpensiMark} from 'expensify-common';
import type {NullishDeep, OnyxCollection, OnyxCollectionInputValue, OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import * as API from '@libs/API';
Expand All @@ -12,6 +11,7 @@ import type {
import {READ_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
import * as ErrorUtils from '@libs/ErrorUtils';
import Log from '@libs/Log';
import Parser from '@libs/Parser';
import * as PersonalDetailsUtils from '@libs/PersonalDetailsUtils';
import * as PhoneNumber from '@libs/PhoneNumber';
import * as ReportActionsUtils from '@libs/ReportActionsUtils';
Expand Down Expand Up @@ -627,7 +627,7 @@ function addMembersToWorkspace(invitedEmailsToAccountIDs: InvitedEmailsToAccount

const params: AddMembersToWorkspaceParams = {
employees: JSON.stringify(logins.map((login) => ({email: login}))),
welcomeNote: new ExpensiMark().replace(welcomeNote),
welcomeNote: Parser.replace(welcomeNote),
policyID,
};
if (!isEmptyObject(membersChats.reportCreationData)) {
Expand Down
Loading

0 comments on commit b419c06

Please sign in to comment.