, policies: OnyxCollection): Outsta
const policy = getPolicy(iouReport.policyID);
const shouldBeManuallySubmitted = PolicyUtils.isPaidGroupPolicy(policy) && !policy?.harvesting?.enabled;
- const isOwnFreePolicy = PolicyUtils.isFreeGroupPolicy(policy) && PolicyUtils.isPolicyAdmin(policy);
- if (shouldBeManuallySubmitted || isOwnFreePolicy) {
+ if (shouldBeManuallySubmitted) {
return {
hasOutstandingChildRequest: true,
};
diff --git a/src/libs/SearchUtils.ts b/src/libs/SearchUtils.ts
index 9760ff80ca19..e178c7dcb77b 100644
--- a/src/libs/SearchUtils.ts
+++ b/src/libs/SearchUtils.ts
@@ -10,7 +10,6 @@ import CONST from '@src/CONST';
import type {TranslationPaths} from '@src/languages/types';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
-import SCREENS from '@src/SCREENS';
import type {SearchAdvancedFiltersForm} from '@src/types/form';
import FILTER_KEYS from '@src/types/form/SearchAdvancedFiltersForm';
import type * as OnyxTypes from '@src/types/onyx';
@@ -20,8 +19,6 @@ import * as CurrencyUtils from './CurrencyUtils';
import DateUtils from './DateUtils';
import {translateLocal} from './Localize';
import Navigation from './Navigation/Navigation';
-import navigationRef from './Navigation/navigationRef';
-import type {AuthScreensParamList, RootStackParamList, State} from './Navigation/types';
import * as PersonalDetailsUtils from './PersonalDetailsUtils';
import * as ReportActionsUtils from './ReportActionsUtils';
import * as ReportUtils from './ReportUtils';
@@ -64,6 +61,7 @@ const emptyPersonalDetails = {
displayName: undefined,
login: undefined,
};
+/* Search list and results related */
/**
* @private
@@ -391,14 +389,6 @@ function getSortedReportActionData(data: ReportActionListItemType[]) {
});
}
-function getCurrentSearchParams() {
- const rootState = navigationRef.getRootState() as State;
-
- const lastSearchRoute = rootState.routes.filter((route) => route.name === SCREENS.SEARCH.CENTRAL_PANE).at(-1);
-
- return lastSearchRoute ? (lastSearchRoute.params as AuthScreensParamList[typeof SCREENS.SEARCH.CENTRAL_PANE]) : undefined;
-}
-
function isSearchResultsEmpty(searchResults: SearchResults) {
return !Object.keys(searchResults?.data).some((key) => key.startsWith(ONYXKEYS.COLLECTION.TRANSACTION));
}
@@ -407,6 +397,20 @@ function getQueryHashFromString(query: SearchQueryString): number {
return UserUtils.hashText(query, 2 ** 32);
}
+function getExpenseTypeTranslationKey(expenseType: ValueOf): TranslationPaths {
+ // eslint-disable-next-line default-case
+ switch (expenseType) {
+ case CONST.SEARCH.TRANSACTION_TYPE.DISTANCE:
+ return 'common.distance';
+ case CONST.SEARCH.TRANSACTION_TYPE.CARD:
+ return 'common.card';
+ case CONST.SEARCH.TRANSACTION_TYPE.CASH:
+ return 'iou.cash';
+ }
+}
+
+/* Search query related */
+
/**
* Update string query with all the default params that are set by parser
*/
@@ -467,16 +471,58 @@ function sanitizeString(str: string) {
return str;
}
-function getExpenseTypeTranslationKey(expenseType: ValueOf): TranslationPaths {
- // eslint-disable-next-line default-case
- switch (expenseType) {
- case CONST.SEARCH.TRANSACTION_TYPE.DISTANCE:
- return 'common.distance';
- case CONST.SEARCH.TRANSACTION_TYPE.CARD:
- return 'common.card';
- case CONST.SEARCH.TRANSACTION_TYPE.CASH:
- return 'iou.cash';
+/**
+ * @private
+ * traverses the AST and returns filters as a QueryFilters object
+ */
+function getFilters(queryJSON: SearchQueryJSON) {
+ const filters = {} as QueryFilters;
+ const filterKeys = Object.values(CONST.SEARCH.SYNTAX_FILTER_KEYS);
+
+ function traverse(node: ASTNode) {
+ if (!node.operator) {
+ return;
+ }
+
+ if (typeof node?.left === 'object' && node.left) {
+ traverse(node.left);
+ }
+
+ if (typeof node?.right === 'object' && node.right && !Array.isArray(node.right)) {
+ traverse(node.right);
+ }
+
+ const nodeKey = node.left as ValueOf;
+ if (!filterKeys.includes(nodeKey)) {
+ return;
+ }
+
+ if (!filters[nodeKey]) {
+ filters[nodeKey] = [];
+ }
+
+ // the "?? []" is added only for typescript because otherwise TS throws an error, in newer TS versions this should be fixed
+ const filterArray = filters[nodeKey] ?? [];
+ if (!Array.isArray(node.right)) {
+ filterArray.push({
+ operator: node.operator,
+ value: node.right as string | number,
+ });
+ } else {
+ node.right.forEach((element) => {
+ filterArray.push({
+ operator: node.operator,
+ value: element as string | number,
+ });
+ });
+ }
}
+
+ if (queryJSON.filters) {
+ traverse(queryJSON.filters);
+ }
+
+ return filters;
}
function buildSearchQueryJSON(query: SearchQueryString) {
@@ -528,7 +574,7 @@ function buildSearchQueryString(queryJSON?: SearchQueryJSON) {
/**
* Given object with chosen search filters builds correct query string from them
*/
-function buildQueryStringFromFilterValues(filterValues: Partial) {
+function buildQueryStringFromFilterFormValues(filterValues: Partial) {
// We separate type and status filters from other filters to maintain hashes consistency for saved searches
const {type, status, ...otherFilters} = filterValues;
const filtersString: string[] = [];
@@ -592,65 +638,10 @@ function buildQueryStringFromFilterValues(filterValues: Partial;
- if (!filterKeys.includes(nodeKey)) {
- return;
- }
-
- if (!filters[nodeKey]) {
- filters[nodeKey] = [];
- }
-
- // the "?? []" is added only for typescript because otherwise TS throws an error, in newer TS versions this should be fixed
- const filterArray = filters[nodeKey] ?? [];
- if (!Array.isArray(node.right)) {
- filterArray.push({
- operator: node.operator,
- value: node.right as string | number,
- });
- } else {
- node.right.forEach((element) => {
- filterArray.push({
- operator: node.operator,
- value: element as string | number,
- });
- });
- }
- }
-
- if (queryJSON.filters) {
- traverse(queryJSON.filters);
- }
-
- return filters;
-}
-
/**
* returns the values of the filters in a format that can be used in the SearchAdvancedFiltersForm as initial form values
*/
-function getFiltersFormValues(queryJSON: SearchQueryJSON) {
+function buildFilterFormValuesFromQuery(queryJSON: SearchQueryJSON) {
const filters = getFilters(queryJSON);
const filterKeys = Object.keys(filters);
const filtersForm = {} as Partial;
@@ -827,14 +818,12 @@ function isCorrectSearchUserName(displayName?: string) {
}
export {
- buildQueryStringFromFilterValues,
+ buildQueryStringFromFilterFormValues,
buildSearchQueryJSON,
buildSearchQueryString,
- getCurrentSearchParams,
- getFiltersFormValues,
+ buildFilterFormValuesFromQuery,
getPolicyIDFromSearchQuery,
getListItem,
- getSearchHeaderTitle,
getSections,
getShouldShowMerchant,
getSortedSections,
@@ -842,6 +831,7 @@ export {
isSearchResultsEmpty,
isTransactionListItemType,
isReportActionListItemType,
+ getSearchHeaderTitle,
normalizeQuery,
shouldShowYear,
buildCannedSearchQuery,
diff --git a/src/libs/TransactionUtils/index.ts b/src/libs/TransactionUtils/index.ts
index 63c2f9aa9862..f14703fabdc8 100644
--- a/src/libs/TransactionUtils/index.ts
+++ b/src/libs/TransactionUtils/index.ts
@@ -982,7 +982,7 @@ function compareDuplicateTransactionFields(transactionID: string): {keep: Partia
// Helper function to check if all comments are equal
function areAllCommentsEqual(items: Array>, firstTransaction: OnyxEntry) {
- return items.every((item) => lodashIsEqual(item?.comment, firstTransaction?.comment));
+ return items.every((item) => lodashIsEqual(getDescription(item), getDescription(firstTransaction)));
}
// Helper function to check if all fields are equal for a given key
@@ -1007,8 +1007,7 @@ function compareDuplicateTransactionFields(transactionID: string): {keep: Partia
if (fieldName === 'description') {
const allCommentsAreEqual = areAllCommentsEqual(transactions, firstTransaction);
- const allCommentsAreEmpty = isFirstTransactionCommentEmptyObject && transactions.every((item) => item?.comment === undefined);
-
+ const allCommentsAreEmpty = isFirstTransactionCommentEmptyObject && transactions.every((item) => getDescription(item) === '');
if (allCommentsAreEqual || allCommentsAreEmpty) {
keep[fieldName] = firstTransaction?.comment?.comment ?? firstTransaction?.comment;
} else {
diff --git a/src/libs/actions/Card.ts b/src/libs/actions/Card.ts
index e423af5bb7e7..c0de9510c6ba 100644
--- a/src/libs/actions/Card.ts
+++ b/src/libs/actions/Card.ts
@@ -198,8 +198,32 @@ function revealVirtualCardDetails(cardID: number, validateCode: string): Promise
return new Promise((resolve, reject) => {
const parameters: RevealExpensifyCardDetailsParams = {cardID, validateCode};
+ const optimisticData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: ONYXKEYS.ACCOUNT,
+ value: {isLoading: true},
+ },
+ ];
+
+ const successData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: ONYXKEYS.ACCOUNT,
+ value: {isLoading: false},
+ },
+ ];
+
+ const failureData: OnyxUpdate[] = [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: ONYXKEYS.ACCOUNT,
+ value: {isLoading: false},
+ },
+ ];
+
// eslint-disable-next-line rulesdir/no-api-side-effects-method
- API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.REVEAL_EXPENSIFY_CARD_DETAILS, parameters)
+ API.makeRequestWithSideEffects(SIDE_EFFECT_REQUEST_COMMANDS.REVEAL_EXPENSIFY_CARD_DETAILS, parameters, {optimisticData, successData, failureData})
.then((response) => {
if (response?.jsonCode !== CONST.JSON_CODE.SUCCESS) {
if (response?.jsonCode === CONST.JSON_CODE.INCORRECT_MAGIC_CODE) {
diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts
index edee4a7fca6f..daaa766145ed 100644
--- a/src/libs/actions/IOU.ts
+++ b/src/libs/actions/IOU.ts
@@ -6450,7 +6450,7 @@ function getReportFromHoldRequestsOnyxData(
chatReport.reportID,
chatReport.policyID ?? iouReport.policyID ?? '',
recipient.accountID ?? 1,
- holdTransactions.reduce((acc, transaction) => acc + transaction.amount, 0) * -1,
+ holdTransactions.reduce((acc, transaction) => acc + transaction.amount, 0) * (ReportUtils.isIOUReport(iouReport) ? 1 : -1),
getCurrency(firstHoldTransaction),
false,
newParentReportActionID,
@@ -7400,14 +7400,9 @@ function cancelPayment(expenseReport: OnyxEntry, chatReport: O
const optimisticReportAction = ReportUtils.buildOptimisticCancelPaymentReportAction(expenseReport.reportID, -(expenseReport.total ?? 0), expenseReport.currency ?? '');
const policy = PolicyUtils.getPolicy(chatReport.policyID);
- const isFree = policy && policy.type === CONST.POLICY.TYPE.FREE;
const approvalMode = policy?.approvalMode ?? CONST.POLICY.APPROVAL_MODE.BASIC;
- let stateNum: ValueOf = CONST.REPORT.STATE_NUM.SUBMITTED;
- let statusNum: ValueOf = CONST.REPORT.STATUS_NUM.SUBMITTED;
- if (!isFree) {
- stateNum = approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL ? CONST.REPORT.STATE_NUM.SUBMITTED : CONST.REPORT.STATE_NUM.APPROVED;
- statusNum = approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL ? CONST.REPORT.STATUS_NUM.CLOSED : CONST.REPORT.STATUS_NUM.APPROVED;
- }
+ const stateNum: ValueOf = approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL ? CONST.REPORT.STATE_NUM.SUBMITTED : CONST.REPORT.STATE_NUM.APPROVED;
+ const statusNum: ValueOf = approvalMode === CONST.POLICY.APPROVAL_MODE.OPTIONAL ? CONST.REPORT.STATUS_NUM.CLOSED : CONST.REPORT.STATUS_NUM.APPROVED;
const optimisticNextStep = NextStepUtils.buildNextStep(expenseReport, statusNum);
const optimisticData: OnyxUpdate[] = [
{
@@ -7433,13 +7428,11 @@ function cancelPayment(expenseReport: OnyxEntry, chatReport: O
},
];
- if (!isFree) {
- optimisticData.push({
- onyxMethod: Onyx.METHOD.MERGE,
- key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`,
- value: optimisticNextStep,
- });
- }
+ optimisticData.push({
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`,
+ value: optimisticNextStep,
+ });
const successData: OnyxUpdate[] = [
{
@@ -7482,13 +7475,11 @@ function cancelPayment(expenseReport: OnyxEntry, chatReport: O
},
});
}
- if (!isFree) {
- failureData.push({
- onyxMethod: Onyx.METHOD.MERGE,
- key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`,
- value: NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.REIMBURSED),
- });
- }
+ failureData.push({
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.NEXT_STEP}${expenseReport.reportID}`,
+ value: NextStepUtils.buildNextStep(expenseReport, CONST.REPORT.STATUS_NUM.REIMBURSED),
+ });
API.write(
WRITE_COMMANDS.CANCEL_PAYMENT,
diff --git a/src/libs/actions/Policy/Policy.ts b/src/libs/actions/Policy/Policy.ts
index b8c9014ecdcc..2123f2a47764 100644
--- a/src/libs/actions/Policy/Policy.ts
+++ b/src/libs/actions/Policy/Policy.ts
@@ -1,5 +1,4 @@
import {PUBLIC_DOMAINS, Str} from 'expensify-common';
-import lodashClone from 'lodash/clone';
import escapeRegExp from 'lodash/escapeRegExp';
import lodashUnion from 'lodash/union';
import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx';
@@ -35,7 +34,6 @@ import type {
OpenPolicyWorkflowsPageParams,
OpenWorkspaceInvitePageParams,
OpenWorkspaceParams,
- OpenWorkspaceReimburseViewParams,
RequestExpensifyCardLimitIncreaseParams,
SetCompanyCardExportAccountParams,
SetPolicyAutomaticApprovalLimitParams,
@@ -54,7 +52,6 @@ import type {
UpdateCompanyCardNameParams,
UpdatePolicyAddressParams,
UpdateWorkspaceAvatarParams,
- UpdateWorkspaceCustomUnitAndRateParams,
UpdateWorkspaceDescriptionParams,
UpdateWorkspaceGeneralSettingsParams,
UpgradeToCorporateParams,
@@ -92,7 +89,7 @@ import type {
Transaction,
} from '@src/types/onyx';
import type {Errors} from '@src/types/onyx/OnyxCommon';
-import type {Attributes, CompanyAddress, CustomUnit, NetSuiteCustomList, NetSuiteCustomSegment, Rate, TaxRate, Unit} from '@src/types/onyx/Policy';
+import type {Attributes, CompanyAddress, CustomUnit, NetSuiteCustomList, NetSuiteCustomSegment, Rate, TaxRate} from '@src/types/onyx/Policy';
import type {OnyxData} from '@src/types/onyx/Request';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import {buildOptimisticPolicyCategories} from './Category';
@@ -1363,87 +1360,6 @@ function updateAddress(policyID: string, newAddress: CompanyAddress) {
});
}
-function updateWorkspaceCustomUnitAndRate(policyID: string, currentCustomUnit: CustomUnit, newCustomUnit: NewCustomUnit, lastModified?: string) {
- if (!currentCustomUnit.customUnitID || !newCustomUnit?.customUnitID || !newCustomUnit.rates?.customUnitRateID) {
- return;
- }
-
- const optimisticData: OnyxUpdate[] = [
- {
- onyxMethod: Onyx.METHOD.MERGE,
- key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
- value: {
- customUnits: {
- [newCustomUnit.customUnitID]: {
- ...newCustomUnit,
- rates: {
- [newCustomUnit.rates.customUnitRateID]: {
- ...newCustomUnit.rates,
- errors: null,
- pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
- },
- },
- pendingAction: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
- },
- },
- },
- },
- ];
-
- const successData: OnyxUpdate[] = [
- {
- onyxMethod: Onyx.METHOD.MERGE,
- key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
- value: {
- customUnits: {
- [newCustomUnit.customUnitID]: {
- pendingAction: null,
- errors: null,
- rates: {
- [newCustomUnit.rates.customUnitRateID]: {
- pendingAction: null,
- },
- },
- },
- },
- },
- },
- ];
-
- const failureData: OnyxUpdate[] = [
- {
- onyxMethod: Onyx.METHOD.MERGE,
- key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
- value: {
- customUnits: {
- [currentCustomUnit.customUnitID]: {
- customUnitID: currentCustomUnit.customUnitID,
- rates: {
- [newCustomUnit.rates.customUnitRateID]: {
- ...currentCustomUnit.rates,
- errors: ErrorUtils.getMicroSecondOnyxErrorWithTranslationKey('workspace.reimburse.updateCustomUnitError'),
- },
- },
- },
- },
- },
- },
- ];
-
- const newCustomUnitParam = lodashClone(newCustomUnit);
- const {pendingAction, errors, ...newRates} = newCustomUnitParam.rates ?? {};
- newCustomUnitParam.rates = newRates;
-
- const params: UpdateWorkspaceCustomUnitAndRateParams = {
- policyID,
- lastModified,
- customUnit: JSON.stringify(newCustomUnitParam),
- customUnitRate: JSON.stringify(newCustomUnitParam.rates),
- };
-
- API.write(WRITE_COMMANDS.UPDATE_WORKSPACE_CUSTOM_UNIT_AND_RATE, params, {optimisticData, successData, failureData});
-}
-
/**
* Removes an error after trying to delete a workspace
*/
@@ -2001,37 +1917,6 @@ function createDraftWorkspace(policyOwnerEmail = '', makeMeAdmin = false, policy
return params;
}
-function openWorkspaceReimburseView(policyID: string) {
- if (!policyID) {
- Log.warn('openWorkspaceReimburseView invalid params', {policyID});
- return;
- }
-
- const successData: OnyxUpdate[] = [
- {
- onyxMethod: Onyx.METHOD.MERGE,
- key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
- value: {
- isLoading: false,
- },
- },
- ];
-
- const failureData: OnyxUpdate[] = [
- {
- onyxMethod: Onyx.METHOD.MERGE,
- key: ONYXKEYS.REIMBURSEMENT_ACCOUNT,
- value: {
- isLoading: false,
- },
- },
- ];
-
- const params: OpenWorkspaceReimburseViewParams = {policyID};
-
- API.read(READ_COMMANDS.OPEN_WORKSPACE_REIMBURSE_VIEW, params, {successData, failureData});
-}
-
function openPolicyWorkflowsPage(policyID: string) {
if (!policyID) {
Log.warn('openPolicyWorkflowsPage invalid params', {policyID});
@@ -2073,22 +1958,6 @@ function openPolicyWorkflowsPage(policyID: string) {
API.read(READ_COMMANDS.OPEN_POLICY_WORKFLOWS_PAGE, params, onyxData);
}
-function setPolicyIDForReimburseView(policyID: string) {
- Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, {policyID, rate: null, unit: null});
-}
-
-function clearOnyxDataForReimburseView() {
- Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, null);
-}
-
-function setRateForReimburseView(rate: string) {
- Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, {rate});
-}
-
-function setUnitForReimburseView(unit: Unit) {
- Onyx.merge(ONYXKEYS.WORKSPACE_RATE_AND_UNIT, {unit});
-}
-
/**
* Returns the accountIDs of the members of the policy whose data is passed in the parameters
*/
@@ -4923,15 +4792,9 @@ export {
hideWorkspaceAlertMessage,
deleteWorkspace,
updateAddress,
- updateWorkspaceCustomUnitAndRate,
updateLastAccessedWorkspace,
clearDeleteWorkspaceError,
- openWorkspaceReimburseView,
- setPolicyIDForReimburseView,
- clearOnyxDataForReimburseView,
setWorkspaceDefaultSpendCategory,
- setRateForReimburseView,
- setUnitForReimburseView,
generateDefaultWorkspaceName,
updateGeneralSettings,
deleteWorkspaceAvatar,
diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts
index 89eec7835e0b..b038f16d003d 100644
--- a/src/libs/actions/Report.ts
+++ b/src/libs/actions/Report.ts
@@ -57,7 +57,6 @@ import {prepareDraftComment} from '@libs/DraftCommentUtils';
import * as EmojiUtils from '@libs/EmojiUtils';
import * as Environment from '@libs/Environment/Environment';
import * as ErrorUtils from '@libs/ErrorUtils';
-import hasCompletedGuidedSetupFlowSelector from '@libs/hasCompletedGuidedSetupFlowSelector';
import HttpUtils from '@libs/HttpUtils';
import isPublicScreenRoute from '@libs/isPublicScreenRoute';
import * as Localize from '@libs/Localize';
@@ -2694,32 +2693,29 @@ function openReportFromDeepLink(url: string) {
return;
}
- const state = navigationRef.getRootState();
- const currentFocusedRoute = findFocusedRoute(state);
- const hasCompletedGuidedSetupFlow = hasCompletedGuidedSetupFlowSelector(onboarding);
-
// We need skip deeplinking if the user hasn't completed the guided setup flow.
- if (!hasCompletedGuidedSetupFlow) {
- Welcome.isOnboardingFlowCompleted({
- onNotCompleted: () => OnboardingFlow.startOnboardingFlow(),
- });
- return;
- }
-
- if (isOnboardingFlowName(currentFocusedRoute?.name)) {
- Welcome.setOnboardingErrorMessage(Localize.translateLocal('onboarding.purpose.errorBackButton'));
- return;
- }
-
- if (shouldSkipDeepLinkNavigation(route)) {
- return;
- }
-
- if (isAuthenticated) {
- return;
- }
-
- Navigation.navigate(route as Route, CONST.NAVIGATION.ACTION_TYPE.PUSH);
+ Welcome.isOnboardingFlowCompleted({
+ onNotCompleted: () => OnboardingFlow.startOnboardingFlow(),
+ onCompleted: () => {
+ const state = navigationRef.getRootState();
+ const currentFocusedRoute = findFocusedRoute(state);
+
+ if (isOnboardingFlowName(currentFocusedRoute?.name)) {
+ Welcome.setOnboardingErrorMessage(Localize.translateLocal('onboarding.purpose.errorBackButton'));
+ return;
+ }
+
+ if (shouldSkipDeepLinkNavigation(route)) {
+ return;
+ }
+
+ if (isAuthenticated) {
+ return;
+ }
+
+ Navigation.navigate(route as Route, CONST.NAVIGATION.ACTION_TYPE.PUSH);
+ },
+ });
});
},
});
diff --git a/src/libs/actions/Search.ts b/src/libs/actions/Search.ts
index a4f0e59ef976..873603b68739 100644
--- a/src/libs/actions/Search.ts
+++ b/src/libs/actions/Search.ts
@@ -156,8 +156,12 @@ function clearAdvancedFilters() {
Onyx.merge(ONYXKEYS.FORMS.SEARCH_ADVANCED_FILTERS_FORM, values);
}
+function showSavedSearchRenameTooltip() {
+ Onyx.set(ONYXKEYS.SHOULD_SHOW_SAVED_SEARCH_RENAME_TOOLTIP, true);
+}
+
function dismissSavedSearchRenameTooltip() {
- Onyx.merge(ONYXKEYS.NVP_SHOULD_HIDE_SAVED_SEARCH_RENAME_TOOLTIP, true);
+ Onyx.set(ONYXKEYS.SHOULD_SHOW_SAVED_SEARCH_RENAME_TOOLTIP, false);
}
export {
@@ -173,4 +177,5 @@ export {
clearAdvancedFilters,
deleteSavedSearch,
dismissSavedSearchRenameTooltip,
+ showSavedSearchRenameTooltip,
};
diff --git a/src/libs/actions/connections/QuickbooksOnline.ts b/src/libs/actions/connections/QuickbooksOnline.ts
index a32363289268..c62c97aa88ca 100644
--- a/src/libs/actions/connections/QuickbooksOnline.ts
+++ b/src/libs/actions/connections/QuickbooksOnline.ts
@@ -164,6 +164,17 @@ function buildOnyxDataForQuickbooksConfiguration(policyID: string, settingValue: TSettingValue) {
+ const onyxData = buildOnyxDataForQuickbooksConfiguration(policyID, CONST.QUICKBOOKS_CONFIG.AUTO_SYNC, {enabled: settingValue}, {enabled: !settingValue});
+
+ const parameters: UpdateQuickbooksOnlineGenericTypeParams = {
+ policyID,
+ settingValue: JSON.stringify(settingValue),
+ idempotencyKey: String(CONST.QUICKBOOKS_CONFIG.AUTO_SYNC),
+ };
+ API.write(WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_AUTO_SYNC, parameters, onyxData);
+}
+
function updateQuickbooksOnlineEnableNewCategories(policyID: string, settingValue: TSettingValue) {
const onyxData = buildOnyxDataForQuickbooksConfiguration(policyID, CONST.QUICKBOOKS_CONFIG.ENABLE_NEW_CATEGORIES, settingValue, !settingValue);
@@ -192,6 +203,17 @@ function updateQuickbooksOnlineAutoCreateVendor(policyID: string, settingValue: TSettingValue) {
+ const onyxData = buildOnyxDataForQuickbooksConfiguration(policyID, CONST.QUICKBOOKS_CONFIG.SYNC_PEOPLE, settingValue, !settingValue);
+
+ const parameters: UpdateQuickbooksOnlineGenericTypeParams = {
+ policyID,
+ settingValue: JSON.stringify(settingValue),
+ idempotencyKey: String(CONST.QUICKBOOKS_CONFIG.SYNC_PEOPLE),
+ };
+ API.write(WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_SYNC_PEOPLE, parameters, onyxData);
+}
+
function updateQuickbooksOnlineReimbursableExpensesAccount(
policyID: string,
settingValue: TSettingValue,
@@ -338,11 +360,45 @@ function updateQuickbooksOnlineSyncTax(
+ policyID: string,
+ settingValue: TSettingValue,
+ oldSettingValue?: TSettingValue,
+) {
+ const onyxData = buildOnyxDataForQuickbooksConfiguration(policyID, CONST.QUICKBOOKS_CONFIG.REIMBURSEMENT_ACCOUNT_ID, settingValue, oldSettingValue);
+
+ const parameters: UpdateQuickbooksOnlineGenericTypeParams = {
+ policyID,
+ settingValue: JSON.stringify(settingValue),
+ idempotencyKey: String(CONST.QUICKBOOKS_CONFIG.REIMBURSEMENT_ACCOUNT_ID),
+ };
+ API.write(WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_REIMBURSEMENT_ACCOUNT_ID, parameters, onyxData);
+}
+
+function updateQuickbooksOnlinePreferredExporter(
+ policyID: string,
+ settingValue: TSettingValue,
+ oldSettingValue?: TSettingValue,
+) {
+ const onyxData = buildOnyxDataForQuickbooksConfiguration(policyID, CONST.QUICKBOOKS_CONFIG.EXPORT, settingValue, oldSettingValue);
+
+ const parameters: UpdateQuickbooksOnlineGenericTypeParams = {
+ policyID,
+ settingValue: JSON.stringify(settingValue),
+ idempotencyKey: String(CONST.QUICKBOOKS_CONFIG.EXPORT),
+ };
+ API.write(WRITE_COMMANDS.UPDATE_QUICKBOOKS_ONLINE_EXPORT, parameters, onyxData);
+}
+
export {
getQuickbooksOnlineSetupLink,
updateQuickbooksOnlineEnableNewCategories,
updateQuickbooksOnlineAutoCreateVendor,
updateQuickbooksOnlineReimbursableExpensesAccount,
+ updateQuickbooksOnlineAutoSync,
+ updateQuickbooksOnlineSyncPeople,
+ updateQuickbooksOnlineReimbursementAccountID,
+ updateQuickbooksOnlinePreferredExporter,
updateQuickbooksOnlineReceivableAccount,
updateQuickbooksOnlineExportDate,
updateQuickbooksOnlineNonReimbursableExpensesAccount,
diff --git a/src/libs/actions/connections/index.ts b/src/libs/actions/connections/index.ts
index 20581d40ed0b..79875a161103 100644
--- a/src/libs/actions/connections/index.ts
+++ b/src/libs/actions/connections/index.ts
@@ -307,9 +307,6 @@ function hasSynchronizationErrorMessage(policy: OnyxEntry, connectionNam
}
function isAuthenticationError(policy: OnyxEntry, connectionName: PolicyConnectionName) {
- if (connectionName === CONST.POLICY.CONNECTIONS.NAME.NETSUITE) {
- return false;
- }
const connection = policy?.connections?.[connectionName];
return connection?.lastSync?.isAuthenticationError === true;
}
diff --git a/src/libs/focusTextInputAfterAnimation/index.android.ts b/src/libs/focusTextInputAfterAnimation/index.android.ts
deleted file mode 100644
index cca8a6588103..000000000000
--- a/src/libs/focusTextInputAfterAnimation/index.android.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import type FocusTextInputAfterAnimation from './types';
-
-/**
- * Initially this file is intended for native ios but use index.native.js filename and affects android.
- *
- * Initial comment for this file is:
- * "Focus the text input with a slight delay to make sure modals are closed first.
- * Since in react-native-modal `onModalHide` is called before the modal is actually hidden.
- * It results in the keyboard being dismissed right away on both iOS and Android.
- * See this discussion for more details: https://github.com/Expensify/App/issues/18300"
- *
- * But the bug already fixed, without using setTimeout for IOS the focus seems to work properly.
- * Instead there is new IOS bug of text input content doesn't scroll to bottom if using setTimeout,
- * also there is an android keyboard doesn't show up bug when text input is focused and
- * the use of setTimeout will make the keyboard show up properly.
- *
- * @param animationLength you must use your best guess as to what a good animationLength is. It can't be too short, or the animation won't be finished. It can't be too long or
- * the user will notice that it feels sluggish
- */
-const focusTextInputAfterAnimation: FocusTextInputAfterAnimation = (inputRef, animationLength = 0) => {
- setTimeout(() => {
- inputRef?.focus();
- }, animationLength);
-};
-
-export default focusTextInputAfterAnimation;
diff --git a/src/libs/focusTextInputAfterAnimation/index.ts b/src/libs/focusTextInputAfterAnimation/index.ts
deleted file mode 100644
index 66d0c35c1a63..000000000000
--- a/src/libs/focusTextInputAfterAnimation/index.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import type FocusTextInputAfterAnimation from './types';
-
-/**
- * This library is a no-op for all platforms except for Android and iOS and will immediately focus the given input without any delays.
- */
-const focusTextInputAfterAnimation: FocusTextInputAfterAnimation = (inputRef) => {
- inputRef?.focus();
-};
-
-export default focusTextInputAfterAnimation;
diff --git a/src/libs/focusTextInputAfterAnimation/types.ts b/src/libs/focusTextInputAfterAnimation/types.ts
deleted file mode 100644
index ce5086328b33..000000000000
--- a/src/libs/focusTextInputAfterAnimation/types.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import type {TextInput} from 'react-native';
-
-type FocusTextInputAfterAnimation = (inputRef: TextInput | HTMLElement | undefined, animationLength: number) => void;
-
-export default FocusTextInputAfterAnimation;
diff --git a/src/pages/AddressPage.tsx b/src/pages/AddressPage.tsx
index 88e52409751b..fc9ae171ccf5 100644
--- a/src/pages/AddressPage.tsx
+++ b/src/pages/AddressPage.tsx
@@ -85,6 +85,7 @@ function AddressPage({title, address, updateAddress, isLoadingApp = true, backTo
title={title}
shouldShowBackButton
onBackButtonPress={() => Navigation.goBack(backTo)}
+ shouldDisplaySearchRouter
/>
{isLoadingApp ? (
diff --git a/src/pages/ReimbursementAccount/BankAccountStep.tsx b/src/pages/ReimbursementAccount/BankAccountStep.tsx
index 09a9e0053959..36ef3f4f00a9 100644
--- a/src/pages/ReimbursementAccount/BankAccountStep.tsx
+++ b/src/pages/ReimbursementAccount/BankAccountStep.tsx
@@ -147,10 +147,10 @@ function BankAccountStep({plaidLinkOAuthToken = '', policyID = '', policyName =
BankAccounts.openPlaidView();
}}
shouldShowRightIcon
- wrapperStyle={[styles.cardMenuItem]}
+ wrapperStyle={[styles.sectionMenuItemTopDescription]}
/>