Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Audit][Implementation] - Reduce Redundant Operations #37200

Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
60ea1a1
perf: remove redundant translations
hurali97 Feb 26, 2024
22f0ffa
perf: only parse the number if it matches the pattern
hurali97 Feb 26, 2024
643c7e1
perf: return early if displayName exists
hurali97 Feb 27, 2024
c683db7
test: fix failing test for formatPhoneNumber
hurali97 Feb 27, 2024
454a9b2
refactor: move translations to utils
hurali97 Feb 27, 2024
eb8f26c
fix: linting
hurali97 Feb 27, 2024
34cd2e8
refactor: use regex and dedicated const for sms domain
hurali97 Feb 28, 2024
707f56c
refactor: remove allocating a variable and return the evaluated expre…
hurali97 Feb 28, 2024
f36550f
refactor: use single-line comments
hurali97 Feb 29, 2024
e02b55a
refactor: use default policy room chat types from CONST
hurali97 Mar 4, 2024
7cbca08
revert: bring back localize.translateLocal
hurali97 Mar 4, 2024
4df1d3d
feat: add cache for translated values in Localize
hurali97 Mar 4, 2024
a7bd7d1
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Mar 4, 2024
55eb5d2
refactor: delete common translation utils
hurali97 Mar 4, 2024
e643960
refactor: avoid multiple cache lookups
hurali97 Mar 4, 2024
f3d6f1f
fix: lint
hurali97 Mar 4, 2024
1755560
refactor: use dedicated map for each locale
hurali97 Mar 6, 2024
99dd34c
refactor: remove setImmediate
hurali97 Mar 6, 2024
3ff2f9c
refactor: remove not needed variable
hurali97 Mar 11, 2024
70bd88f
refactor: use Map of Maps to hold the locales with translated phrases
hurali97 Mar 11, 2024
9fc04db
refactor: move cache mechanism to translate function
hurali97 Mar 12, 2024
ce18f61
Merge branch 'main' of https://github.com/callstack-internal/Expensif…
hurali97 Mar 14, 2024
e22c22b
refactor: use dedicated function to get translated phrase to avoid du…
hurali97 Mar 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ const KEYBOARD_SHORTCUT_NAVIGATION_TYPE = 'NAVIGATION_SHORTCUT';
const cardActiveStates: number[] = [2, 3, 4, 7];

const CONST = {
MERGED_ACCOUNT_PREFIX: 'MERGED_',
hurali97 marked this conversation as resolved.
Show resolved Hide resolved
SMS_DOMAIN_PATTERN: 'expensify.sms',
hurali97 marked this conversation as resolved.
Show resolved Hide resolved
ANDROID_PACKAGE_NAME,
ANIMATED_TRANSITION: 300,
ANIMATED_TRANSITION_FROM_VALUE: 100,
Expand Down
49 changes: 49 additions & 0 deletions src/libs/CommonTranslationUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import Onyx from 'react-native-onyx';
import ONYXKEYS from '@src/ONYXKEYS';
import * as Localize from './Localize';

/**
* This file contains common translations that are used in multiple places in the app.
hurali97 marked this conversation as resolved.
Show resolved Hide resolved
* This is done to avoid duplicate translations and to keep the translations consistent.
* This also allows us to not repeatedly translate the same string which may happen due
* to translations being done for eg, in a loop.
*
* This was identified as part of a performance audit.
* details: https://github.com/Expensify/App/issues/35234#issuecomment-1926911643
*/

let deletedTaskText = '';
let deletedMessageText = '';
let attachmentText = '';
let archivedText = '';
let hiddenText = '';
let unavailableWorkspaceText = '';

function isTranslationAvailable() {
return deletedTaskText && deletedMessageText && attachmentText && archivedText && hiddenText && unavailableWorkspaceText;
}

Onyx.connect({
key: ONYXKEYS.NVP_PREFERRED_LOCALE,
callback: (val) => {
if (!val && isTranslationAvailable()) {
return;
}

deletedTaskText = Localize.translateLocal('parentReportAction.deletedTask');
deletedMessageText = Localize.translateLocal('parentReportAction.deletedMessage');
attachmentText = Localize.translateLocal('common.attachment');
archivedText = Localize.translateLocal('common.archived');
hiddenText = Localize.translateLocal('common.hidden');
unavailableWorkspaceText = Localize.translateLocal('workspace.common.unavailable');
},
});

export default {
deletedTaskText: () => deletedTaskText,
deletedMessageText: () => deletedMessageText,
attachmentText: () => attachmentText,
archivedText: () => archivedText,
hiddenText: () => hiddenText,
unavailableWorkspaceText: () => unavailableWorkspaceText,
};
27 changes: 27 additions & 0 deletions src/libs/LocalePhoneNumber.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import Str from 'expensify-common/lib/str';
import Onyx from 'react-native-onyx';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {parsePhoneNumber} from './PhoneNumber';

Expand All @@ -9,6 +10,28 @@ Onyx.connect({
callback: (val) => (countryCodeByIP = val ?? 1),
});

/**
* Checks whether the given string contains any numbers.
* It uses indexOf instead of regex and includes for performance reasons.
*
* @param text
* @returns boolean
*/
function containsNumbers(text: string) {
return (
hurali97 marked this conversation as resolved.
Show resolved Hide resolved
text.indexOf('0') !== -1 ||
text.indexOf('1') !== -1 ||
text.indexOf('2') !== -1 ||
text.indexOf('3') !== -1 ||
text.indexOf('4') !== -1 ||
text.indexOf('5') !== -1 ||
text.indexOf('6') !== -1 ||
text.indexOf('7') !== -1 ||
text.indexOf('8') !== -1 ||
text.indexOf('9') !== -1
);
}

/**
* Returns a locally converted phone number for numbers from the same region
* and an internationally converted phone number with the country code for numbers from other regions
Expand All @@ -18,6 +41,10 @@ function formatPhoneNumber(number: string): string {
return '';
}

// do not parse the string, if it's not a phone number
if (number.indexOf(CONST.SMS_DOMAIN_PATTERN) === -1 && !containsNumbers(number)) {
hurali97 marked this conversation as resolved.
Show resolved Hide resolved
return number;
}
const numberWithoutSMSDomain = Str.removeSMSDomain(number);
const parsedPhoneNumber = parsePhoneNumber(numberWithoutSMSDomain);

Expand Down
26 changes: 23 additions & 3 deletions src/libs/PersonalDetailsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type {PersonalDetails, PersonalDetailsList, PrivatePersonalDetails} from '@src/types/onyx';
import type {OnyxData} from '@src/types/onyx/Request';
import CommonTranslationUtils from './CommonTranslationUtils';
import * as LocalePhoneNumber from './LocalePhoneNumber';
import * as Localize from './Localize';
import * as UserUtils from './UserUtils';
Expand All @@ -24,10 +25,29 @@ Onyx.connect({
},
});

/**
hurali97 marked this conversation as resolved.
Show resolved Hide resolved
* Index for the substring method to remove the merged account prefix.
*/
const substringStartIndex = CONST.MERGED_ACCOUNT_PREFIX.length;
hurali97 marked this conversation as resolved.
Show resolved Hide resolved

function getDisplayNameOrDefault(passedPersonalDetails?: Partial<PersonalDetails> | null, defaultValue = '', shouldFallbackToHidden = true): string {
const displayName = passedPersonalDetails?.displayName ? passedPersonalDetails.displayName.replace(CONST.REGEX.MERGED_ACCOUNT_PREFIX, '') : '';
const fallbackValue = shouldFallbackToHidden ? Localize.translateLocal('common.hidden') : '';
return displayName || defaultValue || fallbackValue;
let displayName = passedPersonalDetails?.displayName ?? '';
/**
* If the displayName starts with the merged account prefix, remove it.
*/
if (displayName.startsWith(CONST.MERGED_ACCOUNT_PREFIX)) {
hurali97 marked this conversation as resolved.
Show resolved Hide resolved
displayName = displayName.substring(substringStartIndex);
hurali97 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* If displayName exists, return it early so we don't have to allocate
* memory for the fallback string.
*/
if (displayName) {
return displayName;
}
const fallbackValue = shouldFallbackToHidden ? CommonTranslationUtils.hiddenText() : '';
return defaultValue || fallbackValue;
hurali97 marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
18 changes: 10 additions & 8 deletions src/libs/ReportUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import type {EmptyObject} from '@src/types/utils/EmptyObject';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type IconAsset from '@src/types/utils/IconAsset';
import * as CollectionUtils from './CollectionUtils';
import CommonTranslationUtils from './CommonTranslationUtils';
import * as CurrencyUtils from './CurrencyUtils';
import DateUtils from './DateUtils';
import isReportMessageAttachment from './isReportMessageAttachment';
Expand Down Expand Up @@ -579,13 +580,13 @@ function getPolicyType(report: OnyxEntry<Report>, policies: OnyxCollection<Polic
* Get the policy name from a given report
*/
function getPolicyName(report: OnyxEntry<Report> | undefined | EmptyObject, returnEmptyIfNotFound = false, policy: OnyxEntry<Policy> | undefined = undefined): string {
const noPolicyFound = returnEmptyIfNotFound ? '' : Localize.translateLocal('workspace.common.unavailable');
const noPolicyFound = returnEmptyIfNotFound ? '' : CommonTranslationUtils.unavailableWorkspaceText();
if (isEmptyObject(report)) {
return noPolicyFound;
}

if ((!allPolicies || Object.keys(allPolicies).length === 0) && !report?.policyName) {
return Localize.translateLocal('workspace.common.unavailable');
return CommonTranslationUtils.unavailableWorkspaceText();
}
const finalPolicy = policy ?? allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${report?.policyID}`];

Expand Down Expand Up @@ -773,11 +774,12 @@ function isAnnounceRoom(report: OnyxEntry<Report>): boolean {
return getChatType(report) === CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE;
}

const chatTypes = [CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, CONST.REPORT.CHAT_TYPE.DOMAIN_ALL];
hurali97 marked this conversation as resolved.
Show resolved Hide resolved
/**
* Whether the provided report is a default room
*/
function isDefaultRoom(report: OnyxEntry<Report>): boolean {
return [CONST.REPORT.CHAT_TYPE.POLICY_ADMINS, CONST.REPORT.CHAT_TYPE.POLICY_ANNOUNCE, CONST.REPORT.CHAT_TYPE.DOMAIN_ALL].some((type) => type === getChatType(report));
return chatTypes.some((type) => type === getChatType(report));
}

/**
Expand Down Expand Up @@ -1646,7 +1648,7 @@ function getDisplayNameForParticipant(accountID?: number, shouldUseShortForm = f
const longName = PersonalDetailsUtils.getDisplayNameOrDefault(personalDetails, formattedLogin, shouldFallbackToHidden);

// If the user's personal details (first name) should be hidden, make sure we return "hidden" instead of the short name
if (shouldFallbackToHidden && longName === Localize.translateLocal('common.hidden')) {
if (shouldFallbackToHidden && longName === CommonTranslationUtils.hiddenText()) {
return longName;
}

Expand Down Expand Up @@ -2507,13 +2509,13 @@ function getReportName(report: OnyxEntry<Report>, policy: OnyxEntry<Policy> = nu
}

if (parentReportAction?.message?.[0]?.isDeletedParentAction) {
return Localize.translateLocal('parentReportAction.deletedMessage');
return CommonTranslationUtils.deletedMessageText();
}

const isAttachment = ReportActionsUtils.isReportActionAttachment(!isEmptyObject(parentReportAction) ? parentReportAction : null);
const parentReportActionMessage = (parentReportAction?.message?.[0]?.text ?? '').replace(/(\r\n|\n|\r)/gm, ' ');
if (isAttachment && parentReportActionMessage) {
return `[${Localize.translateLocal('common.attachment')}]`;
return `[${CommonTranslationUtils.attachmentText()}]`;
}
if (
parentReportAction?.message?.[0]?.moderationDecision?.decision === CONST.MODERATION.MODERATOR_DECISION_PENDING_HIDE ||
Expand All @@ -2529,7 +2531,7 @@ function getReportName(report: OnyxEntry<Report>, policy: OnyxEntry<Policy> = nu
}

if (isTaskReport(report) && isCanceledTaskReport(report, parentReportAction)) {
return Localize.translateLocal('parentReportAction.deletedTask');
return CommonTranslationUtils.deletedTaskText();
}

if (isChatRoom(report) || isTaskReport(report)) {
Expand All @@ -2545,7 +2547,7 @@ function getReportName(report: OnyxEntry<Report>, policy: OnyxEntry<Policy> = nu
}

if (isArchivedRoom(report)) {
formattedName += ` (${Localize.translateLocal('common.archived')})`;
formattedName += ` (${CommonTranslationUtils.archivedText()})`;
}

if (formattedName) {
Expand Down
Loading