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

[No QA] Console error cleanup: fix require cycle warnings (part 1) #52561

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions src/CONST.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as KeyCommand from 'react-native-key-command';
import type {ValueOf} from 'type-fest';
import type {Video} from './libs/actions/Report';
import type {MileageRate} from './libs/DistanceRequestUtils';
import BankAccount from './libs/models/BankAccount';
import BankAccountState from './libs/models/BankAccountState';
import * as Url from './libs/Url';
import SCREENS from './SCREENS';
import type PlaidBankAccount from './types/onyx/PlaidBankAccount';
Expand Down Expand Up @@ -5166,7 +5166,7 @@ const CONST = {
REIMBURSEMENT_ACCOUNT: {
DEFAULT_DATA: {
achData: {
state: BankAccount.STATE.SETUP,
state: BankAccountState.SETUP,
},
isLoading: false,
errorFields: {},
Expand Down
8 changes: 6 additions & 2 deletions src/libs/Log.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import pkg from '../../package.json';
import {addLog, flushAllLogsOnAppLaunch} from './actions/Console';
import {shouldAttachLog} from './Console';
import getPlatform from './getPlatform';
import * as Network from './Network';
import requireParameters from './requireParameters';

let timeout: NodeJS.Timeout;
let shouldCollectLogs = false;

// Dynamic Import to avoid circular dependency
const Network = () => import('./Network');

Onyx.connect({
key: ONYXKEYS.SHOULD_STORE_LOGS,
callback: (val) => {
Expand All @@ -40,7 +42,9 @@ function LogCommand(parameters: LogCommandParameters): Promise<{requestID: strin

// Note: We are forcing Log to run since it requires no authToken and should only be queued when we are offline.
// Non-cancellable request: during logout, when requests are cancelled, we don't want to cancel any remaining logs
return Network.post(commandName, {...parameters, forceNetworkRequest: true, canCancel: false}) as Promise<{requestID: string}>;
return Network().then((module) => {
return module.post(commandName, {...parameters, forceNetworkRequest: true, canCancel: false}) as Promise<{requestID: string}>;
});
}

// eslint-disable-next-line
Expand Down
25 changes: 15 additions & 10 deletions src/libs/Middleware/Reauthentication.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import * as Authentication from '@libs/Authentication';
import Log from '@libs/Log';
import * as MainQueue from '@libs/Network/MainQueue';
import * as NetworkStore from '@libs/Network/NetworkStore';
Expand All @@ -7,6 +6,9 @@ import * as Request from '@libs/Request';
import CONST from '@src/CONST';
import type Middleware from './types';

// Dynamic Import to avoid circular dependency
const Authentication = () => import('@libs/Authentication');

// We store a reference to the active authentication request so that we are only ever making one request to authenticate at a time.
let isAuthenticating: Promise<void> | null = null;

Expand All @@ -15,15 +17,18 @@ function reauthenticate(commandName?: string): Promise<void> {
return isAuthenticating;
}

isAuthenticating = Authentication.reauthenticate(commandName)
.then((response) => {
isAuthenticating = null;
return response;
})
.catch((error) => {
isAuthenticating = null;
throw error;
});
isAuthenticating = Authentication().then((module) =>
module
.reauthenticate(commandName)
.then((response) => {
isAuthenticating = null;
return response;
})
.catch((error) => {
isAuthenticating = null;
throw error;
}),
);

return isAuthenticating;
}
Expand Down
11 changes: 8 additions & 3 deletions src/libs/Navigation/Navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {CommonActions, getPathFromState, StackActions} from '@react-navigation/n
import type {OnyxEntry} from 'react-native-onyx';
import Log from '@libs/Log';
import {isCentralPaneName, removePolicyIDParamFromState} from '@libs/NavigationUtils';
import * as ReportConnection from '@libs/ReportConnection';
import * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import NAVIGATORS from '@src/NAVIGATORS';
Expand Down Expand Up @@ -39,6 +38,9 @@ let pendingRoute: Route | null = null;

let shouldPopAllStateOnUP = false;

// Dynamic Import to avoid circular dependency
const ReportConnection = () => import('@libs/ReportConnection');

/**
* Inform the navigation that next time user presses UP we should pop all the state back to LHN.
*/
Expand Down Expand Up @@ -66,8 +68,11 @@ const dismissModal = (reportID?: string, ref = navigationRef) => {
originalDismissModal(ref);
return;
}
const report = ReportConnection.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`];
originalDismissModalWithReport({reportID, ...report}, ref);

ReportConnection().then((module) => {
const report = module.getAllReports()?.[`${ONYXKEYS.COLLECTION.REPORT}${reportID}`];
originalDismissModalWithReport({reportID, ...report}, ref);
});
};
// Re-exporting the closeRHPFlow here to fill in default value for navigationRef. The closeRHPFlow isn't defined in this file to avoid cyclic dependencies.
const closeRHPFlow = (ref = navigationRef) => originalCloseRHPFlow(ref);
Expand Down
8 changes: 5 additions & 3 deletions src/libs/ReportConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import Onyx from 'react-native-onyx';
import ONYXKEYS from '@src/ONYXKEYS';
import type {Report} from '@src/types/onyx';
import * as PriorityModeActions from './actions/PriorityMode';
import * as ReportHelperActions from './actions/Report';

// Dynamic Import to avoid circular dependency
// Dynamic Imports to avoid circular dependencies
const UnreadIndicatorUpdaterHelper = () => import('./UnreadIndicatorUpdater');
const ReportHelperActions = () => import('./actions/Report');

const reportIDToNameMap: Record<string, string> = {};
let allReports: OnyxCollection<Report>;
Expand All @@ -29,7 +29,9 @@ Onyx.connect({
return;
}
reportIDToNameMap[report.reportID] = report.reportName ?? report.displayName ?? report.reportID;
ReportHelperActions.handleReportChanged(report);
ReportHelperActions().then((module) => {
module.handleReportChanged(report);
});
});
},
});
Expand Down
64 changes: 35 additions & 29 deletions src/libs/TransactionUtils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import lodashSet from 'lodash/set';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import {getPolicyCategoriesData} from '@libs/actions/Policy/Category';
import {getPolicyTagsData} from '@libs/actions/Policy/Tag';
import type {TransactionMergeParams} from '@libs/API/parameters';
import {getCurrencyDecimals} from '@libs/CurrencyUtils';
import DateUtils from '@libs/DateUtils';
Expand All @@ -32,6 +30,10 @@ import type DeepValueOf from '@src/types/utils/DeepValueOf';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import getDistanceInMeters from './getDistanceInMeters';

// Dynamic Imports to avoid circular dependencies
const CategoryActions = () => import('@libs/actions/Policy/Category');
const TagActions = () => import('@libs/actions/Policy/Tag');

let allTransactions: OnyxCollection<Transaction> = {};
Onyx.connect({
key: ONYXKEYS.COLLECTION.TRANSACTION,
Expand Down Expand Up @@ -1136,37 +1138,41 @@ function compareDuplicateTransactionFields(transactionID: string, reportID: stri
}
} else if (fieldName === 'category') {
const differentValues = getDifferentValues(transactions, keys);
const policyCategories = getPolicyCategoriesData(report?.policyID ?? '-1');
const availableCategories = Object.values(policyCategories)
.filter((category) => differentValues.includes(category.name) && category.enabled && category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)
.map((e) => e.name);

if (!areAllFieldsEqualForKey && policy?.areCategoriesEnabled && (availableCategories.length > 1 || (availableCategories.length === 1 && differentValues.includes('')))) {
change[fieldName] = [...availableCategories, ...(differentValues.includes('') ? [''] : [])];
} else if (areAllFieldsEqualForKey) {
keep[fieldName] = firstTransaction?.[keys[0]] ?? firstTransaction?.[keys[1]];
}
} else if (fieldName === 'tag') {
const policyTags = getPolicyTagsData(report?.policyID ?? '-1');
const isMultiLevelTags = PolicyUtils.isMultiLevelTags(policyTags);
if (isMultiLevelTags) {
if (areAllFieldsEqualForKey || !policy?.areTagsEnabled) {
keep[fieldName] = firstTransaction?.[keys[0]] ?? firstTransaction?.[keys[1]];
} else {
processChanges(fieldName, transactions, keys);
}
} else {
const differentValues = getDifferentValues(transactions, keys);
const policyTagsObj = Object.values(Object.values(policyTags).at(0)?.tags ?? {});
const availableTags = policyTagsObj
.filter((tag) => differentValues.includes(tag.name) && tag.enabled && tag.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)
CategoryActions().then((actions) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look right because you are mutating keep and change to return them at the end of the function. If this code is wrapped in a promise we will have racing condition problems

const policyCategories = actions.getPolicyCategoriesData(report?.policyID ?? '-1');
const availableCategories = Object.values(policyCategories)
.filter((category) => differentValues.includes(category.name) && category.enabled && category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)
.map((e) => e.name);
if (!areAllFieldsEqualForKey && policy?.areTagsEnabled && (availableTags.length > 1 || (availableTags.length === 1 && differentValues.includes('')))) {
change[fieldName] = [...availableTags, ...(differentValues.includes('') ? [''] : [])];

if (!areAllFieldsEqualForKey && policy?.areCategoriesEnabled && (availableCategories.length > 1 || (availableCategories.length === 1 && differentValues.includes('')))) {
change[fieldName] = [...availableCategories, ...(differentValues.includes('') ? [''] : [])];
} else if (areAllFieldsEqualForKey) {
keep[fieldName] = firstTransaction?.[keys[0]] ?? firstTransaction?.[keys[1]];
}
}
});
} else if (fieldName === 'tag') {
TagActions().then((module) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

const policyTags = module.getPolicyTagsData(report?.policyID ?? '-1');
const isMultiLevelTags = PolicyUtils.isMultiLevelTags(policyTags);
if (isMultiLevelTags) {
if (areAllFieldsEqualForKey || !policy?.areTagsEnabled) {
keep[fieldName] = firstTransaction?.[keys[0]] ?? firstTransaction?.[keys[1]];
} else {
processChanges(fieldName, transactions, keys);
}
} else {
const differentValues = getDifferentValues(transactions, keys);
const policyTagsObj = Object.values(Object.values(policyTags).at(0)?.tags ?? {});
const availableTags = policyTagsObj
.filter((tag) => differentValues.includes(tag.name) && tag.enabled && tag.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE)
.map((e) => e.name);
if (!areAllFieldsEqualForKey && policy?.areTagsEnabled && (availableTags.length > 1 || (availableTags.length === 1 && differentValues.includes('')))) {
change[fieldName] = [...availableTags, ...(differentValues.includes('') ? [''] : [])];
} else if (areAllFieldsEqualForKey) {
keep[fieldName] = firstTransaction?.[keys[0]] ?? firstTransaction?.[keys[1]];
}
}
});
} else if (areAllFieldsEqualForKey) {
keep[fieldName] = firstTransaction?.[keys[0]] ?? firstTransaction?.[keys[1]];
} else {
Expand Down
5 changes: 2 additions & 3 deletions src/libs/Url.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import 'react-native-url-polyfill/auto';
import CONST from '@src/CONST';
import type {Route} from '@src/ROUTES';

/**
Expand Down Expand Up @@ -71,8 +70,8 @@ function hasURL(text: string) {
return urlPattern.test(text);
}

function extractUrlDomain(url: string): string | undefined {
const match = String(url).match(CONST.REGEX.DOMAIN_BASE);
function extractUrlDomain(url: string, domainRegex: string): string | undefined {
const match = String(url).match(domainRegex);
return match?.[1];
}

Expand Down
8 changes: 6 additions & 2 deletions src/libs/actions/IOU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import * as CurrencyUtils from '@libs/CurrencyUtils';
import DateUtils from '@libs/DateUtils';
import DistanceRequestUtils from '@libs/DistanceRequestUtils';
import * as ErrorUtils from '@libs/ErrorUtils';
import * as FileUtils from '@libs/fileDownload/FileUtils';
import GoogleTagManager from '@libs/GoogleTagManager';
import * as IOUUtils from '@libs/IOUUtils';
import * as LocalePhoneNumber from '@libs/LocalePhoneNumber';
Expand Down Expand Up @@ -77,6 +76,9 @@ import * as Report from './Report';
import {getRecentWaypoints, sanitizeRecentWaypoints} from './Transaction';
import * as TransactionEdit from './TransactionEdit';

// Dynamic Import to avoid circular dependency
const FileUtils = () => import('@libs/fileDownload/FileUtils');

type IOURequestType = ValueOf<typeof CONST.IOU.REQUEST_TYPE>;

type OneOnOneIOUReport = OnyxTypes.Report | undefined | null;
Expand Down Expand Up @@ -7952,7 +7954,9 @@ function navigateToStartStepIfScanFileCannotBeRead(
}
IOUUtils.navigateToStartMoneyRequestStep(requestType, iouType, transactionID, reportID);
};
FileUtils.readFileAsync(receiptPath.toString(), receiptFilename, onSuccess, onFailure, receiptType);
FileUtils().then((module) => {
module.readFileAsync(receiptPath.toString(), receiptFilename, onSuccess, onFailure, receiptType);
});
}

/** Save the preferred payment method for a policy */
Expand Down
58 changes: 32 additions & 26 deletions src/libs/actions/PriorityMode.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import debounce from 'lodash/debounce';
import Onyx from 'react-native-onyx';
import Log from '@libs/Log';
import * as ReportConnection from '@libs/ReportConnection';
import * as ReportUtils from '@libs/ReportUtils';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';

const ReportConnection = () => import('@libs/ReportConnection');

/**
* This actions file is used to automatically switch a user into #focus mode when they exceed a certain number of reports. We do this primarily for performance reasons.
* Similar to the "Welcome action" we must wait for a number of things to happen when the user signs in or refreshes the page:
Expand Down Expand Up @@ -77,11 +78,13 @@ function resetHasReadRequiredDataFromStorage() {
}

function checkRequiredData() {
if (ReportConnection.getAllReports() === undefined || hasTriedFocusMode === undefined || isInFocusMode === undefined || isLoadingReportData) {
return;
}
ReportConnection().then((module) => {
if (module.getAllReports() === undefined || hasTriedFocusMode === undefined || isInFocusMode === undefined || isLoadingReportData) {
return;
}

resolveIsReadyPromise();
resolveIsReadyPromise();
});
}

function tryFocusModeUpdate() {
Expand All @@ -98,33 +101,36 @@ function tryFocusModeUpdate() {
}

const validReports = [];
const allReports = ReportConnection.getAllReports();
Object.keys(allReports ?? {}).forEach((key) => {
const report = allReports?.[key];
if (!report) {
return;
}

if (!ReportUtils.isValidReport(report) || !ReportUtils.isReportParticipant(currentUserAccountID ?? -1, report)) {
return;
}
ReportConnection().then((module) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same, validReports is used outside of this wrapped function, this might be a problem

const allReports = module.getAllReports();
Object.keys(allReports ?? {}).forEach((key) => {
const report = allReports?.[key];
if (!report) {
return;
}

validReports.push(report);
});
if (!ReportUtils.isValidReport(report) || !ReportUtils.isReportParticipant(currentUserAccountID ?? -1, report)) {
return;
}

const reportCount = validReports.length;
if (reportCount < CONST.REPORT.MAX_COUNT_BEFORE_FOCUS_UPDATE) {
Log.info('Not switching user to optimized focus mode as they do not have enough reports', false, {reportCount});
return;
}
validReports.push(report);
});

Log.info('Switching user to optimized focus mode', false, {reportCount, hasTriedFocusMode, isInFocusMode});
const reportCount = validReports.length;
if (reportCount < CONST.REPORT.MAX_COUNT_BEFORE_FOCUS_UPDATE) {
Log.info('Not switching user to optimized focus mode as they do not have enough reports', false, {reportCount});
return;
}

Log.info('Switching user to optimized focus mode', false, {reportCount, hasTriedFocusMode, isInFocusMode});

// Record that we automatically switched them so we don't ask again.
hasTriedFocusMode = true;
// Record that we automatically switched them so we don't ask again.
hasTriedFocusMode = true;

// Setting this triggers a modal to open and notify the user.
Onyx.set(ONYXKEYS.FOCUS_MODE_NOTIFICATION, true);
// Setting this triggers a modal to open and notify the user.
Onyx.set(ONYXKEYS.FOCUS_MODE_NOTIFICATION, true);
});
});
}

Expand Down
12 changes: 3 additions & 9 deletions src/libs/models/BankAccount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import type {ValueOf} from 'type-fest';
import CONST from '@src/CONST';
import type {BankAccountAdditionalData} from '@src/types/onyx/BankAccount';
import type BankAccountJSON from '@src/types/onyx/BankAccount';
import BankAccountState from './BankAccountState';

type State = ValueOf<typeof BankAccount.STATE>;
type State = ValueOf<typeof BankAccountState>;

type ACHData = {
routingNumber: string;
Expand All @@ -20,14 +21,7 @@ type ACHData = {
class BankAccount {
json: BankAccountJSON;

static STATE = {
PENDING: 'PENDING',
OPEN: 'OPEN',
DELETED: 'DELETED',
LOCKED: 'LOCKED',
SETUP: 'SETUP',
VERIFYING: 'VERIFYING',
};
static STATE = BankAccountState;

constructor(accountJSON: BankAccountJSON) {
this.json = accountJSON;
Expand Down
Loading
Loading