, policy: OnyxEntry TransactionUtils.hasMissingSmartscanFields(transaction));
+ return TransactionUtils.getAllReportTransactions(iouReportID).some((transaction) => TransactionUtils.hasMissingSmartscanFields(transaction));
}
/**
@@ -3308,7 +3305,6 @@ function updateReportPreview(iouReport: OnyxEntry, reportPreviewAction:
const message = getReportPreviewMessage(iouReport, reportPreviewAction);
return {
...reportPreviewAction,
- created: DateUtils.getDBTime(),
message: [
{
html: message,
@@ -4031,10 +4027,10 @@ function shouldReportBeInOptionList({
/**
* Attempts to find a report in onyx with the provided list of participants. Does not include threads, task, money request, room, and policy expense chat.
*/
-function getChatByParticipants(newParticipantList: number[]): OnyxEntry {
+function getChatByParticipants(newParticipantList: number[], reports: OnyxCollection = allReports): OnyxEntry {
const sortedNewParticipantList = newParticipantList.sort();
return (
- Object.values(allReports ?? {}).find((report) => {
+ Object.values(reports ?? {}).find((report) => {
// If the report has been deleted, or there are no participants (like an empty #admins room) then skip it
if (
!report ||
@@ -4282,7 +4278,7 @@ function canRequestMoney(report: OnyxEntry, policy: OnyxEntry, o
if (isMoneyRequestReport(report)) {
const isOwnExpenseReport = isExpenseReport(report) && isOwnPolicyExpenseChat;
if (isOwnExpenseReport && PolicyUtils.isPaidGroupPolicy(policy)) {
- return isDraftExpenseReport(report);
+ return isDraftExpenseReport(report) || isExpenseReportWithInstantSubmittedState(report);
}
return (isOwnExpenseReport || isIOUReport(report)) && !isReportApproved(report) && !isSettled(report?.reportID);
@@ -5084,6 +5080,7 @@ export {
isPublicAnnounceRoom,
isConciergeChatReport,
isProcessingReport,
+ isExpenseReportWithInstantSubmittedState,
isCurrentUserTheOnlyParticipant,
hasAutomatedExpensifyAccountIDs,
hasExpensifyGuidesEmails,
diff --git a/src/libs/Request.ts b/src/libs/Request.ts
index aa94eccbca2e..fc31160bbc1c 100644
--- a/src/libs/Request.ts
+++ b/src/libs/Request.ts
@@ -13,7 +13,9 @@ function makeXHR(request: Request): Promise {
// If we're using the Supportal token and this is not a Supportal request
// let's just return a promise that will resolve itself.
if (NetworkStore.getSupportAuthToken() && !NetworkStore.isSupportRequest(request.command)) {
- return new Promise((resolve) => resolve());
+ return new Promise((resolve) => {
+ resolve();
+ });
}
return HttpUtils.xhr(request.command, finalParameters, request.type, request.shouldUseSecure);
diff --git a/src/libs/RequestThrottle.ts b/src/libs/RequestThrottle.ts
index 36935982afbb..4c524394cb2c 100644
--- a/src/libs/RequestThrottle.ts
+++ b/src/libs/RequestThrottle.ts
@@ -26,9 +26,10 @@ function sleep(): Promise {
requestRetryCount++;
return new Promise((resolve, reject) => {
if (requestRetryCount <= CONST.NETWORK.MAX_REQUEST_RETRIES) {
- return setTimeout(resolve, getRequestWaitTime());
+ setTimeout(resolve, getRequestWaitTime());
+ return;
}
- return reject();
+ reject();
});
}
diff --git a/src/libs/SidebarUtils.ts b/src/libs/SidebarUtils.ts
index d9298817f6b7..35cf52a5ff99 100644
--- a/src/libs/SidebarUtils.ts
+++ b/src/libs/SidebarUtils.ts
@@ -234,7 +234,7 @@ function getOptionData({
result.isExpenseRequest = ReportUtils.isExpenseRequest(report);
result.isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report);
result.shouldShowSubscript = ReportUtils.shouldReportShowSubscript(report);
- result.pendingAction = report.pendingFields ? report.pendingFields.addWorkspaceRoom || report.pendingFields.createChat : undefined;
+ result.pendingAction = report.pendingFields?.addWorkspaceRoom ?? report.pendingFields?.createChat;
result.brickRoadIndicator = hasErrors || hasViolations ? CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR : '';
result.ownerAccountID = report.ownerAccountID;
result.managerID = report.managerID;
diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts
index 3489053951b6..67e31c610369 100644
--- a/src/libs/TransactionUtils.ts
+++ b/src/libs/TransactionUtils.ts
@@ -12,6 +12,7 @@ import {isEmptyObject} from '@src/types/utils/EmptyObject';
import {isCorporateCard, isExpensifyCard} from './CardUtils';
import DateUtils from './DateUtils';
import * as NumberUtils from './NumberUtils';
+import {getCleanedTagName} from './PolicyUtils';
import type {OptimisticIOUReportAction} from './ReportUtils';
let allTransactions: OnyxCollection = {};
@@ -409,7 +410,7 @@ function getTag(transaction: OnyxEntry, tagIndex?: number): string
}
function getTagForDisplay(transaction: OnyxEntry, tagIndex?: number): string {
- return getTag(transaction, tagIndex).replace(/[\\\\]:/g, ':');
+ return getCleanedTagName(getTag(transaction, tagIndex));
}
/**
@@ -480,7 +481,7 @@ function isReceiptBeingScanned(transaction: OnyxEntry): boolean {
* Check if the transaction has a non-smartscanning receipt and is missing required fields
*/
function hasMissingSmartscanFields(transaction: OnyxEntry): boolean {
- return Boolean(transaction && hasReceipt(transaction) && !isDistanceRequest(transaction) && !isReceiptBeingScanned(transaction) && areRequiredFieldsEmpty(transaction));
+ return Boolean(transaction && !isDistanceRequest(transaction) && !isReceiptBeingScanned(transaction) && areRequiredFieldsEmpty(transaction));
}
/**
diff --git a/src/libs/Trie/index.ts b/src/libs/Trie/index.ts
index c23c7b814a22..a82c90c15772 100644
--- a/src/libs/Trie/index.ts
+++ b/src/libs/Trie/index.ts
@@ -31,7 +31,6 @@ class Trie {
}
if (!newNode.children[newWord[0]]) {
newNode.children[newWord[0]] = new TrieNode();
- this.add(newWord.substring(1), metaData, newNode.children[newWord[0]], true);
}
this.add(newWord.substring(1), metaData, newNode.children[newWord[0]], true);
}
diff --git a/src/libs/UnreadIndicatorUpdater/updateUnread/index.desktop.ts b/src/libs/UnreadIndicatorUpdater/updateUnread/index.desktop.ts
index 5cbba61542b1..75013ebe621f 100644
--- a/src/libs/UnreadIndicatorUpdater/updateUnread/index.desktop.ts
+++ b/src/libs/UnreadIndicatorUpdater/updateUnread/index.desktop.ts
@@ -1,4 +1,4 @@
-import ELECTRON_EVENTS from '../../../../desktop/ELECTRON_EVENTS';
+import ELECTRON_EVENTS from '@desktop/ELECTRON_EVENTS';
import type UpdateUnread from './types';
/**
diff --git a/src/libs/UserUtils.ts b/src/libs/UserUtils.ts
index 12b52524f113..147343e99ceb 100644
--- a/src/libs/UserUtils.ts
+++ b/src/libs/UserUtils.ts
@@ -4,6 +4,7 @@ import type {ValueOf} from 'type-fest';
import * as defaultAvatars from '@components/Icon/DefaultAvatars';
import {ConciergeAvatar, FallbackAvatar, NotificationsAvatar} from '@components/Icon/Expensicons';
import CONST from '@src/CONST';
+import type {LoginList} from '@src/types/onyx';
import type Login from '@src/types/onyx/Login';
import type IconAsset from '@src/types/utils/IconAsset';
import hashCode from './hashCode';
@@ -12,7 +13,7 @@ type AvatarRange = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
type AvatarSource = IconAsset | string;
-type LoginListIndicator = ValueOf | '';
+type LoginListIndicator = ValueOf | undefined;
/**
* Searches through given loginList for any contact method / login with an error.
@@ -35,8 +36,8 @@ type LoginListIndicator = ValueOf | ''
* }
* }}
*/
-function hasLoginListError(loginList: Record): boolean {
- return Object.values(loginList).some((loginData) => Object.values(loginData.errorFields ?? {}).some((field) => Object.keys(field ?? {}).length > 0));
+function hasLoginListError(loginList: OnyxEntry): boolean {
+ return Object.values(loginList ?? {}).some((loginData) => Object.values(loginData.errorFields ?? {}).some((field) => Object.keys(field ?? {}).length > 0));
}
/**
@@ -44,22 +45,22 @@ function hasLoginListError(loginList: Record): boolean {
* an Info brick road status indicator. Currently this only applies if the user
* has an unvalidated contact method.
*/
-function hasLoginListInfo(loginList: Record): boolean {
- return !Object.values(loginList).every((field) => field.validatedDate);
+function hasLoginListInfo(loginList: OnyxEntry): boolean {
+ return !Object.values(loginList ?? {}).every((field) => field.validatedDate);
}
/**
* Gets the appropriate brick road indicator status for a given loginList.
* Error status is higher priority, so we check for that first.
*/
-function getLoginListBrickRoadIndicator(loginList: Record): LoginListIndicator {
+function getLoginListBrickRoadIndicator(loginList: OnyxEntry): LoginListIndicator {
if (hasLoginListError(loginList)) {
return CONST.BRICK_ROAD_INDICATOR_STATUS.ERROR;
}
if (hasLoginListInfo(loginList)) {
return CONST.BRICK_ROAD_INDICATOR_STATUS.INFO;
}
- return '';
+ return undefined;
}
/**
@@ -227,4 +228,4 @@ export {
hashText,
isDefaultAvatar,
};
-export type {AvatarSource};
+export type {AvatarSource, LoginListIndicator};
diff --git a/src/libs/Visibility/index.desktop.ts b/src/libs/Visibility/index.desktop.ts
index c01b6001f456..e3cab6ec44a6 100644
--- a/src/libs/Visibility/index.desktop.ts
+++ b/src/libs/Visibility/index.desktop.ts
@@ -1,4 +1,4 @@
-import ELECTRON_EVENTS from '../../../desktop/ELECTRON_EVENTS';
+import ELECTRON_EVENTS from '@desktop/ELECTRON_EVENTS';
import type {HasFocus, IsVisible, OnVisibilityChange} from './types';
/**
diff --git a/src/libs/actions/App.ts b/src/libs/actions/App.ts
index d2c650d6f1c0..6442f2ec0eef 100644
--- a/src/libs/actions/App.ts
+++ b/src/libs/actions/App.ts
@@ -15,7 +15,7 @@ import type {
ReconnectAppParams,
UpdatePreferredLocaleParams,
} from '@libs/API/parameters';
-import {READ_COMMANDS, SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
+import {SIDE_EFFECT_REQUEST_COMMANDS, WRITE_COMMANDS} from '@libs/API/types';
import * as Browser from '@libs/Browser';
import DateUtils from '@libs/DateUtils';
import Log from '@libs/Log';
@@ -211,7 +211,7 @@ function openApp() {
getPolicyParamsForOpenOrReconnect().then((policyParams: PolicyParamsForOpenOrReconnect) => {
const params: OpenAppParams = {enablePriorityModeFilter: true, ...policyParams};
- API.read(READ_COMMANDS.OPEN_APP, params, getOnyxDataForOpenOrReconnect(true));
+ API.write(WRITE_COMMANDS.OPEN_APP, params, getOnyxDataForOpenOrReconnect(true));
});
}
diff --git a/src/libs/actions/Card.ts b/src/libs/actions/Card.ts
index 2cc32616562d..756ef902d913 100644
--- a/src/libs/actions/Card.ts
+++ b/src/libs/actions/Card.ts
@@ -178,3 +178,4 @@ function revealVirtualCardDetails(cardID: number): Promise {
}
export {requestReplacementExpensifyCard, activatePhysicalExpensifyCard, clearCardListErrors, reportVirtualExpensifyCardFraud, revealVirtualCardDetails};
+export type {ReplacementReason};
diff --git a/src/libs/actions/Device/generateDeviceID/index.desktop.ts b/src/libs/actions/Device/generateDeviceID/index.desktop.ts
index f8af0a7faa81..45daf7062a5a 100644
--- a/src/libs/actions/Device/generateDeviceID/index.desktop.ts
+++ b/src/libs/actions/Device/generateDeviceID/index.desktop.ts
@@ -1,4 +1,4 @@
-import ELECTRON_EVENTS from '../../../../../desktop/ELECTRON_EVENTS';
+import ELECTRON_EVENTS from '@desktop/ELECTRON_EVENTS';
import type GenerateDeviceID from './types';
/**
diff --git a/src/libs/actions/Device/index.ts b/src/libs/actions/Device/index.ts
index 761e27d95a78..e7c19d20e4fe 100644
--- a/src/libs/actions/Device/index.ts
+++ b/src/libs/actions/Device/index.ts
@@ -12,7 +12,8 @@ let deviceID: string | null = null;
function getDeviceID(): Promise {
return new Promise((resolve) => {
if (deviceID) {
- return resolve(deviceID);
+ resolve(deviceID);
+ return;
}
const connectionID = Onyx.connect({
diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts
index 37308c73e724..2528add01bbb 100644
--- a/src/libs/actions/IOU.ts
+++ b/src/libs/actions/IOU.ts
@@ -1,3 +1,4 @@
+import type {ParamListBase, StackNavigationState} from '@react-navigation/native';
import type {StackScreenProps} from '@react-navigation/stack';
import {format} from 'date-fns';
import fastMerge from 'expensify-common/lib/fastMerge';
@@ -43,14 +44,14 @@ import type {OptimisticChatReport, OptimisticCreatedReportAction, OptimisticIOUR
import * as TransactionUtils from '@libs/TransactionUtils';
import * as UserUtils from '@libs/UserUtils';
import ViolationsUtils from '@libs/Violations/ViolationsUtils';
-import type {MoneyRequestNavigatorParamList} from '@navigation/types';
+import type {MoneyRequestNavigatorParamList, NavigationPartialRoute} from '@navigation/types';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
import type SCREENS from '@src/SCREENS';
import type * as OnyxTypes from '@src/types/onyx';
import type {Participant, Split} from '@src/types/onyx/IOU';
-import type {ErrorFields, Errors, PendingFields} from '@src/types/onyx/OnyxCommon';
+import type {ErrorFields, Errors} from '@src/types/onyx/OnyxCommon';
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
import type ReportAction from '@src/types/onyx/ReportAction';
import type {OnyxData} from '@src/types/onyx/Request';
@@ -260,6 +261,28 @@ function clearMoneyRequest(transactionID: string) {
Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, null);
}
+/**
+ * Update money request-related pages IOU type params
+ */
+function updateMoneyRequestTypeParams(routes: StackNavigationState['routes'] | NavigationPartialRoute[], newIouType: string, tab: string) {
+ routes.forEach((route) => {
+ const tabList = [CONST.TAB_REQUEST.DISTANCE, CONST.TAB_REQUEST.MANUAL, CONST.TAB_REQUEST.SCAN] as string[];
+ if (!route.name.startsWith('Money_Request_') && !tabList.includes(route.name)) {
+ return;
+ }
+ const newParams: Record = {iouType: newIouType};
+ if (route.name === 'Money_Request_Create') {
+ // Both screen and nested params are needed to properly update the nested tab navigator
+ newParams.params = {...newParams};
+ newParams.screen = tab;
+ }
+ Navigation.setParams(newParams, route.key ?? '');
+
+ // Recursively update nested money request tab params
+ updateMoneyRequestTypeParams(route.state?.routes ?? [], newIouType, tab);
+ });
+}
+
// eslint-disable-next-line @typescript-eslint/naming-convention
function startMoneyRequest_temporaryForRefactor(iouType: ValueOf, reportID: string) {
clearMoneyRequest(CONST.IOU.OPTIMISTIC_TRANSACTION_ID);
@@ -302,7 +325,7 @@ function setMoneyRequestMerchant(transactionID: string, merchant: string, isDraf
Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {merchant});
}
-function setMoneyRequestPendingFields(transactionID: string, pendingFields: PendingFields) {
+function setMoneyRequestPendingFields(transactionID: string, pendingFields: OnyxTypes.Transaction['pendingFields']) {
Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {pendingFields});
}
@@ -324,9 +347,9 @@ function setMoneyRequestParticipants_temporaryForRefactor(transactionID: string,
Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, {participants});
}
-function setMoneyRequestReceipt(transactionID: string, source: string, filename: string, isDraft: boolean) {
+function setMoneyRequestReceipt(transactionID: string, source: string, filename: string, isDraft: boolean, type?: string) {
Onyx.merge(`${isDraft ? ONYXKEYS.COLLECTION.TRANSACTION_DRAFT : ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {
- receipt: {source},
+ receipt: {source, type: type ?? ''},
filename,
});
}
@@ -804,8 +827,8 @@ function getMoneyRequestInformation(
// If the scheduled submit is turned off on the policy, user needs to manually submit the report which is indicated by GBR in LHN
needsToBeManuallySubmitted = isFromPaidPolicy && !policy?.harvesting?.enabled;
- // If the linked expense report on paid policy is not draft, we need to create a new draft expense report
- if (iouReport && isFromPaidPolicy && !ReportUtils.isDraftExpenseReport(iouReport)) {
+ // If the linked expense report on paid policy is not draft and not instantly submitted, we need to create a new draft expense report
+ if (iouReport && isFromPaidPolicy && !ReportUtils.isDraftExpenseReport(iouReport) && !ReportUtils.isExpenseReportWithInstantSubmittedState(iouReport)) {
iouReport = null;
}
}
@@ -814,7 +837,7 @@ function getMoneyRequestInformation(
if (isPolicyExpenseChat) {
iouReport = {...iouReport};
if (iouReport?.currency === currency && typeof iouReport.total === 'number') {
- // Because of the Expense reports are stored as negative values, we substract the total from the amount
+ // Because of the Expense reports are stored as negative values, we subtract the total from the amount
iouReport.total -= amount;
}
} else {
@@ -4193,6 +4216,7 @@ function navigateToStartStepIfScanFileCannotBeRead(
iouType: ValueOf,
transactionID: string,
reportID: string,
+ receiptType: string,
) {
if (!receiptFilename || !receiptPath) {
return;
@@ -4206,7 +4230,7 @@ function navigateToStartStepIfScanFileCannotBeRead(
}
IOUUtils.navigateToStartMoneyRequestStep(requestType, iouType, transactionID, reportID);
};
- FileUtils.readFileAsync(receiptPath, receiptFilename, onSuccess, onFailure);
+ FileUtils.readFileAsync(receiptPath, receiptFilename, onSuccess, onFailure, receiptType);
}
/** Save the preferred payment method for a policy */
@@ -4233,6 +4257,8 @@ export {
initMoneyRequest,
startMoneyRequest_temporaryForRefactor,
resetMoneyRequestInfo,
+ clearMoneyRequest,
+ updateMoneyRequestTypeParams,
setMoneyRequestAmount_temporaryForRefactor,
setMoneyRequestBillable_temporaryForRefactor,
setMoneyRequestCreated,
diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts
index 57cd4a6fc071..b9a2e8535b62 100644
--- a/src/libs/actions/Policy.ts
+++ b/src/libs/actions/Policy.ts
@@ -54,6 +54,7 @@ import type {
} from '@src/types/onyx';
import type {Errors} from '@src/types/onyx/OnyxCommon';
import type {Attributes, CustomUnit, Rate, Unit} from '@src/types/onyx/Policy';
+import type {OnyxData} from '@src/types/onyx/Request';
import type {EmptyObject} from '@src/types/utils/EmptyObject';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
@@ -2178,6 +2179,60 @@ function createWorkspaceFromIOUPayment(iouReport: Report | EmptyObject): string
return policyID;
}
+const setWorkspaceRequiresCategory = (policyID: string, requiresCategory: boolean) => {
+ const onyxData: OnyxData = {
+ optimisticData: [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
+ value: {
+ requiresCategory,
+ errors: {
+ requiresCategory: null,
+ },
+ pendingFields: {
+ requiresCategory: CONST.RED_BRICK_ROAD_PENDING_ACTION.UPDATE,
+ },
+ },
+ },
+ ],
+ successData: [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
+ value: {
+ errors: {
+ requiresCategory: null,
+ },
+ pendingFields: {
+ requiresCategory: null,
+ },
+ },
+ },
+ ],
+ failureData: [
+ {
+ onyxMethod: Onyx.METHOD.MERGE,
+ key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
+ value: {
+ requiresCategory: !requiresCategory,
+ errors: ErrorUtils.getMicroSecondOnyxError('workspace.categories.genericFailureMessage'),
+ pendingFields: {
+ requiresCategory: null,
+ },
+ },
+ },
+ ],
+ };
+
+ const parameters = {
+ policyID,
+ requiresCategory,
+ };
+
+ API.write('SetWorkspaceRequiresCategory', parameters, onyxData);
+};
+
export {
removeMembers,
addMembersToWorkspace,
@@ -2221,4 +2276,5 @@ export {
setWorkspaceAutoReporting,
setWorkspaceApprovalMode,
updateWorkspaceDescription,
+ setWorkspaceRequiresCategory,
};
diff --git a/src/libs/actions/Session/clearCache/index.ts b/src/libs/actions/Session/clearCache/index.ts
index 6d288c6cbd3b..3daa8ec2d7d7 100644
--- a/src/libs/actions/Session/clearCache/index.ts
+++ b/src/libs/actions/Session/clearCache/index.ts
@@ -1,5 +1,8 @@
import type ClearCache from './types';
-const clearStorage: ClearCache = () => new Promise((res) => res());
+const clearStorage: ClearCache = () =>
+ new Promise((resolve) => {
+ resolve();
+ });
export default clearStorage;
diff --git a/src/libs/actions/Task.ts b/src/libs/actions/Task.ts
index e8d29952149a..48ba3196964c 100644
--- a/src/libs/actions/Task.ts
+++ b/src/libs/actions/Task.ts
@@ -386,7 +386,7 @@ function editTask(report: OnyxTypes.Report, {title, description}: OnyxTypes.Task
const editTaskReportAction = ReportUtils.buildOptimisticEditedTaskFieldReportAction({title, description});
// Sometimes title or description is undefined, so we need to check for that, and we provide it to multiple functions
- const reportName = (title ?? report?.reportName).trim();
+ const reportName = (title ?? report?.reportName)?.trim();
// Description can be unset, so we default to an empty string if so
const reportDescription = (description ?? report.description ?? '').trim();
diff --git a/src/libs/calculateAnchorPosition.ts b/src/libs/calculateAnchorPosition.ts
index 0f1e383522eb..3dc5924d023a 100644
--- a/src/libs/calculateAnchorPosition.ts
+++ b/src/libs/calculateAnchorPosition.ts
@@ -16,13 +16,15 @@ type AnchorOrigin = {
export default function calculateAnchorPosition(anchorComponent: View | RNText, anchorOrigin?: AnchorOrigin): Promise {
return new Promise((resolve) => {
if (!anchorComponent) {
- return resolve({horizontal: 0, vertical: 0});
+ resolve({horizontal: 0, vertical: 0});
+ return;
}
anchorComponent.measureInWindow((x, y, width, height) => {
if (anchorOrigin?.vertical === CONST.MODAL.ANCHOR_ORIGIN_VERTICAL.TOP && anchorOrigin?.horizontal === CONST.MODAL.ANCHOR_ORIGIN_HORIZONTAL.LEFT) {
- return resolve({horizontal: x, vertical: y + height + (anchorOrigin?.shiftVertical ?? 0)});
+ resolve({horizontal: x, vertical: y + height + (anchorOrigin?.shiftVertical ?? 0)});
+ return;
}
- return resolve({horizontal: x + width, vertical: y + (anchorOrigin?.shiftVertical ?? 0)});
+ resolve({horizontal: x + width, vertical: y + (anchorOrigin?.shiftVertical ?? 0)});
});
});
}
diff --git a/src/libs/fileDownload/FileUtils.ts b/src/libs/fileDownload/FileUtils.ts
index 055abf140e64..06bd47f3b39b 100644
--- a/src/libs/fileDownload/FileUtils.ts
+++ b/src/libs/fileDownload/FileUtils.ts
@@ -80,14 +80,14 @@ function showCameraPermissionsAlert() {
* Extracts a filename from a given URL and sanitizes it for file system usage.
*
* This function takes a URL as input and performs the following operations:
- * 1. Extracts the last segment of the URL, which could be a file name, a path segment,
- * or a query string parameter.
+ * 1. Extracts the last segment of the URL.
* 2. Decodes the extracted segment from URL encoding to a plain string for better readability.
* 3. Replaces any characters in the decoded string that are illegal in file names
* with underscores.
*/
function getFileName(url: string): string {
- const fileName = url.split(/[#?/]/).pop() ?? '';
+ const fileName = url.split('/').pop()?.split('?')[0].split('#')[0] ?? '';
+
if (!fileName) {
Log.warn('[FileUtils] Could not get attachment name', {url});
}
@@ -111,7 +111,7 @@ function getFileType(fileUrl: string): string | undefined {
return;
}
- const fileName = fileUrl.split('/').pop()?.split('?')[0].split('#')[0];
+ const fileName = getFileName(fileUrl);
if (!fileName) {
return;
@@ -159,7 +159,7 @@ function appendTimeToFileName(fileName: string): string {
* @param path - the blob url of the locally uploaded file
* @param fileName - name of the file to read
*/
-const readFileAsync: ReadFileAsync = (path, fileName, onSuccess, onFailure = () => {}) =>
+const readFileAsync: ReadFileAsync = (path, fileName, onSuccess, onFailure = () => {}, fileType = '') =>
new Promise((resolve) => {
if (!path) {
resolve();
@@ -176,7 +176,9 @@ const readFileAsync: ReadFileAsync = (path, fileName, onSuccess, onFailure = ()
}
res.blob()
.then((blob) => {
- const file = new File([blob], cleanFileName(fileName), {type: blob.type});
+ // On Android devices, fetching blob for a file with name containing spaces fails to retrieve the type of file.
+ // In this case, let us fallback on fileType provided by the caller of this function.
+ const file = new File([blob], cleanFileName(fileName), {type: blob.type || fileType});
file.source = path;
// For some reason, the File object on iOS does not have a uri property
// so images aren't uploaded correctly to the backend
diff --git a/src/libs/fileDownload/types.ts b/src/libs/fileDownload/types.ts
index 6d92bddd5816..f09ce495795b 100644
--- a/src/libs/fileDownload/types.ts
+++ b/src/libs/fileDownload/types.ts
@@ -8,7 +8,7 @@ type GetImageResolution = (url: File | Asset) => Promise;
type ExtensionAndFileName = {fileName: string; fileExtension: string};
type SplitExtensionFromFileName = (fileName: string) => ExtensionAndFileName;
-type ReadFileAsync = (path: string, fileName: string, onSuccess: (file: File) => void, onFailure: (error?: unknown) => void) => Promise;
+type ReadFileAsync = (path: string, fileName: string, onSuccess: (file: File) => void, onFailure: (error?: unknown) => void, fileType?: string) => Promise;
type AttachmentDetails = {
previewSourceURL: null | string;
diff --git a/src/libs/getSplashBackgroundColor/index.native.ts b/src/libs/getSplashBackgroundColor/index.native.ts
new file mode 100644
index 000000000000..1d21b7a004e1
--- /dev/null
+++ b/src/libs/getSplashBackgroundColor/index.native.ts
@@ -0,0 +1,7 @@
+import colors from '@styles/theme/colors';
+
+function getSplashBackgroundColor() {
+ return colors.green400;
+}
+
+export default getSplashBackgroundColor;
diff --git a/src/libs/getSplashBackgroundColor/index.ts b/src/libs/getSplashBackgroundColor/index.ts
new file mode 100644
index 000000000000..97484d49163e
--- /dev/null
+++ b/src/libs/getSplashBackgroundColor/index.ts
@@ -0,0 +1,7 @@
+import colors from '@styles/theme/colors';
+
+function getSplashBackgroundColor() {
+ return colors.productDark100;
+}
+
+export default getSplashBackgroundColor;
diff --git a/src/pages/EnablePayments/TermsStep.js b/src/pages/EnablePayments/TermsStep.js
index a09e1801c3b0..9fa3a4becea3 100644
--- a/src/pages/EnablePayments/TermsStep.js
+++ b/src/pages/EnablePayments/TermsStep.js
@@ -7,6 +7,7 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton';
import Text from '@components/Text';
import TextLink from '@components/TextLink';
import withLocalize, {withLocalizePropTypes} from '@components/withLocalize';
+import useLocalize from '@hooks/useLocalize';
import useThemeStyles from '@hooks/useThemeStyles';
import compose from '@libs/compose';
import * as ErrorUtils from '@libs/ErrorUtils';
@@ -32,6 +33,30 @@ const defaultProps = {
walletTerms: {},
};
+function HaveReadAndAgreeLabel() {
+ const {translate} = useLocalize();
+
+ return (
+
+ {`${translate('termsStep.haveReadAndAgree')}`}
+ {`${translate('termsStep.electronicDisclosures')}.`}
+
+ );
+}
+
+function AgreeToTheLabel() {
+ const {translate} = useLocalize();
+
+ return (
+
+ {`${translate('termsStep.agreeToThe')} `}
+ {`${translate('common.privacy')} `}
+ {`${translate('common.and')} `}
+ {`${translate('termsStep.walletAgreement')}.`}
+
+ );
+}
+
function TermsStep(props) {
const styles = useThemeStyles();
const [hasAcceptedDisclosure, setHasAcceptedDisclosure] = useState(false);
@@ -71,27 +96,12 @@ function TermsStep(props) {
accessibilityLabel={props.translate('termsStep.haveReadAndAgree')}
style={[styles.mb4, styles.mt4]}
onInputChange={toggleDisclosure}
- LabelComponent={() => (
-
- {`${props.translate('termsStep.haveReadAndAgree')}`}
- {`${props.translate('termsStep.electronicDisclosures')}.`}
-
- )}
+ LabelComponent={HaveReadAndAgreeLabel}
/>
(
-
- {`${props.translate('termsStep.agreeToThe')} `}
-
- {`${props.translate('common.privacy')} `}
-
- {`${props.translate('common.and')} `}
-
- {`${props.translate('termsStep.walletAgreement')}.`}
-
- )}
+ LabelComponent={AgreeToTheLabel}
/>