, policy: OnyxEntry, policy: OnyxEntry | undefined = undefined): string {
- const moneyRequestTotal = getMoneyRequestReimbursableTotal(report);
+ const moneyRequestTotal = getMoneyRequestSpendBreakdown(report).totalDisplaySpend;
const formattedAmount = CurrencyUtils.convertToDisplayString(moneyRequestTotal, report?.currency, hasOnlyDistanceRequestTransactions(report?.reportID));
const payerOrApproverName = isExpenseReport(report) ? getPolicyName(report, false, policy) : getDisplayNameForParticipant(report?.managerID) ?? '';
const payerPaidAmountMessage = Localize.translateLocal('iou.payerPaidAmount', {
@@ -2196,7 +2154,7 @@ function getReportPreviewMessage(
}
}
- const totalAmount = getMoneyRequestReimbursableTotal(report);
+ const totalAmount = getMoneyRequestSpendBreakdown(report).totalDisplaySpend;
const policyName = getPolicyName(report, false, policy);
const payerName = isExpenseReport(report) ? policyName : getDisplayNameForParticipant(report.managerID, !isPreviewMessageForParentChatReport);
@@ -2792,7 +2750,7 @@ function getIOUReportActionMessage(iouReportID: string, type: string, total: num
const report = getReport(iouReportID);
const amount =
type === CONST.IOU.REPORT_ACTION_TYPE.PAY
- ? CurrencyUtils.convertToDisplayString(getMoneyRequestReimbursableTotal(!isEmptyObject(report) ? report : null), currency)
+ ? CurrencyUtils.convertToDisplayString(getMoneyRequestSpendBreakdown(!isEmptyObject(report) ? report : null).totalDisplaySpend, currency)
: CurrencyUtils.convertToDisplayString(total, currency);
let paymentMethodMessage;
@@ -2864,7 +2822,7 @@ function buildOptimisticIOUReportAction(
comment: string,
participants: Participant[],
transactionID: string,
- paymentType: DeepValueOf,
+ paymentType: PaymentMethodType,
iouReportID = '',
isSettlingUp = false,
isSendMoneyFlow = false,
@@ -4230,9 +4188,8 @@ function shouldDisableRename(report: OnyxEntry, policy: OnyxEntry, policy: OnyxEntry {
- if (!session) {
- return;
- }
-
- currentUserAccountID = session.accountID;
- },
-});
-
let resolveSidebarIsReadyPromise: (args?: unknown[]) => void;
let sidebarIsReadyPromise = new Promise((resolve) => {
@@ -200,7 +184,7 @@ function getOrderedReportIDs(
report.displayName = ReportUtils.getReportName(report);
// eslint-disable-next-line no-param-reassign
- report.iouReportAmount = ReportUtils.getMoneyRequestReimbursableTotal(report, allReports);
+ report.iouReportAmount = ReportUtils.getMoneyRequestSpendBreakdown(report, allReports).totalDisplaySpend;
const isPinned = report.isPinned ?? false;
const reportAction = ReportActionsUtils.getReportAction(report.parentReportID ?? '', report.parentReportActionID ?? '');
@@ -239,13 +223,6 @@ function getOrderedReportIDs(
return LHNReports;
}
-type ActorDetails = {
- displayName?: string;
- firstName?: string;
- lastName?: string;
- accountID?: number;
-};
-
/**
* Gets all the data necessary for rendering an OptionRowLHN component
*/
@@ -350,12 +327,12 @@ function getOptionData({
// We only create tooltips for the first 10 users or so since some reports have hundreds of users, causing performance to degrade.
const displayNamesWithTooltips = ReportUtils.getDisplayNamesWithTooltips((participantPersonalDetailList || []).slice(0, 10), hasMultipleParticipants);
- const lastMessageTextFromReport = OptionsListUtils.getLastMessageTextForReport(report);
// If the last actor's details are not currently saved in Onyx Collection,
// then try to get that from the last report action if that action is valid
// to get data from.
- let lastActorDetails: ActorDetails | null = report.lastActorAccountID && personalDetails?.[report.lastActorAccountID] ? personalDetails[report.lastActorAccountID] : null;
+ let lastActorDetails: Partial | null = report.lastActorAccountID && personalDetails?.[report.lastActorAccountID] ? personalDetails[report.lastActorAccountID] : null;
+
if (!lastActorDetails && visibleReportActionItems[report.reportID]) {
const lastActorDisplayName = visibleReportActionItems[report.reportID]?.person?.[0]?.text;
lastActorDetails = lastActorDisplayName
@@ -366,31 +343,12 @@ function getOptionData({
: null;
}
- const shouldShowDisplayName = hasMultipleParticipants && lastActorDetails?.accountID && Number(lastActorDetails.accountID) !== currentUserAccountID;
- const lastActorName = lastActorDetails?.firstName ?? lastActorDetails?.displayName;
- const lastActorDisplayName = shouldShowDisplayName ? lastActorName : '';
+ const lastActorDisplayName = OptionsListUtils.getLastActorDisplayName(lastActorDetails, hasMultipleParticipants);
+ const lastMessageTextFromReport = OptionsListUtils.getLastMessageTextForReport(report, lastActorDetails, policy);
let lastMessageText = lastMessageTextFromReport;
const reportAction = lastReportActions?.[report.reportID];
- if (result.isArchivedRoom) {
- const archiveReason = (reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.CLOSED && reportAction?.originalMessage?.reason) || CONST.REPORT.ARCHIVE_REASON.DEFAULT;
-
- switch (archiveReason) {
- case CONST.REPORT.ARCHIVE_REASON.ACCOUNT_CLOSED:
- case CONST.REPORT.ARCHIVE_REASON.REMOVED_FROM_POLICY:
- case CONST.REPORT.ARCHIVE_REASON.POLICY_DELETED: {
- lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.${archiveReason}`, {
- policyName: ReportUtils.getPolicyName(report, false, policy),
- displayName: PersonalDetailsUtils.getDisplayNameOrDefault(lastActorDetails),
- });
- break;
- }
- default: {
- lastMessageText = Localize.translate(preferredLocale, `reportArchiveReasons.default`);
- }
- }
- }
const isThreadMessage =
ReportUtils.isThread(report) && reportAction?.actionName === CONST.REPORT.ACTIONS.TYPE.ADDCOMMENT && reportAction?.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE;
@@ -459,7 +417,7 @@ function getOptionData({
}
result.isIOUReportOwner = ReportUtils.isIOUOwnedByCurrentUser(result as Report);
- result.iouReportAmount = ReportUtils.getMoneyRequestReimbursableTotal(result as Report);
+ result.iouReportAmount = ReportUtils.getMoneyRequestSpendBreakdown(result as Report).totalDisplaySpend;
if (!hasMultipleParticipants) {
result.accountID = personalDetail?.accountID;
diff --git a/src/libs/Timers.ts b/src/libs/Timers.ts
index 21ee2a8c2914..0ca4f3d29eb0 100644
--- a/src/libs/Timers.ts
+++ b/src/libs/Timers.ts
@@ -1,9 +1,9 @@
-const timers: NodeJS.Timer[] = [];
+const timers: NodeJS.Timeout[] = [];
/**
* Register a timer so it can be cleaned up later.
*/
-function register(timerID: NodeJS.Timer): NodeJS.Timer {
+function register(timerID: NodeJS.Timeout): NodeJS.Timeout {
timers.push(timerID);
return timerID;
}
diff --git a/src/libs/TransactionUtils.ts b/src/libs/TransactionUtils.ts
index b1a900675949..68085c8d1255 100644
--- a/src/libs/TransactionUtils.ts
+++ b/src/libs/TransactionUtils.ts
@@ -36,10 +36,10 @@ Onyx.connect({
callback: (value) => (allReports = value),
});
-function isDistanceRequest(transaction: Transaction): boolean {
+function isDistanceRequest(transaction: OnyxEntry): boolean {
// This is used during the request creation flow before the transaction has been saved to the server
if (lodashHas(transaction, 'iouRequestType')) {
- return transaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE;
+ return transaction?.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE;
}
// This is the case for transaction objects once they have been saved to the server
diff --git a/src/libs/actions/AppUpdate.ts b/src/libs/actions/AppUpdate/index.ts
similarity index 71%
rename from src/libs/actions/AppUpdate.ts
rename to src/libs/actions/AppUpdate/index.ts
index 29ee2a4547ab..69c80a089831 100644
--- a/src/libs/actions/AppUpdate.ts
+++ b/src/libs/actions/AppUpdate/index.ts
@@ -1,5 +1,6 @@
import Onyx from 'react-native-onyx';
import ONYXKEYS from '@src/ONYXKEYS';
+import updateApp from './updateApp';
function triggerUpdateAvailable() {
Onyx.set(ONYXKEYS.UPDATE_AVAILABLE, true);
@@ -9,4 +10,4 @@ function setIsAppInBeta(isBeta: boolean) {
Onyx.set(ONYXKEYS.IS_BETA, isBeta);
}
-export {triggerUpdateAvailable, setIsAppInBeta};
+export {triggerUpdateAvailable, setIsAppInBeta, updateApp};
diff --git a/src/libs/actions/AppUpdate/updateApp/index.android.ts b/src/libs/actions/AppUpdate/updateApp/index.android.ts
new file mode 100644
index 000000000000..7b0022b3e970
--- /dev/null
+++ b/src/libs/actions/AppUpdate/updateApp/index.android.ts
@@ -0,0 +1,6 @@
+import {Linking} from 'react-native';
+import CONST from '@src/CONST';
+
+export default function updateApp() {
+ Linking.openURL(CONST.APP_DOWNLOAD_LINKS.ANDROID);
+}
diff --git a/src/libs/actions/AppUpdate/updateApp/index.desktop.ts b/src/libs/actions/AppUpdate/updateApp/index.desktop.ts
new file mode 100644
index 000000000000..fb3a7d649baa
--- /dev/null
+++ b/src/libs/actions/AppUpdate/updateApp/index.desktop.ts
@@ -0,0 +1,6 @@
+import {Linking} from 'react-native';
+import CONST from '@src/CONST';
+
+export default function updateApp() {
+ Linking.openURL(CONST.APP_DOWNLOAD_LINKS.DESKTOP);
+}
diff --git a/src/libs/actions/AppUpdate/updateApp/index.ios.ts b/src/libs/actions/AppUpdate/updateApp/index.ios.ts
new file mode 100644
index 000000000000..59f25888de11
--- /dev/null
+++ b/src/libs/actions/AppUpdate/updateApp/index.ios.ts
@@ -0,0 +1,6 @@
+import {Linking} from 'react-native';
+import CONST from '@src/CONST';
+
+export default function updateApp() {
+ Linking.openURL(CONST.APP_DOWNLOAD_LINKS.IOS);
+}
diff --git a/src/libs/actions/AppUpdate/updateApp/index.ts b/src/libs/actions/AppUpdate/updateApp/index.ts
new file mode 100644
index 000000000000..8c2b191029a2
--- /dev/null
+++ b/src/libs/actions/AppUpdate/updateApp/index.ts
@@ -0,0 +1,6 @@
+/**
+ * On web or mWeb we can simply refresh the page and the user should have the new version of the app downloaded.
+ */
+export default function updateApp() {
+ window.location.reload();
+}
diff --git a/src/libs/actions/BankAccounts.ts b/src/libs/actions/BankAccounts.ts
index 58509379b232..2c65804cb428 100644
--- a/src/libs/actions/BankAccounts.ts
+++ b/src/libs/actions/BankAccounts.ts
@@ -21,6 +21,7 @@ import * as PlaidDataProps from '@pages/ReimbursementAccount/plaidDataPropTypes'
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
+import type {Route} from '@src/ROUTES';
import type PlaidBankAccount from '@src/types/onyx/PlaidBankAccount';
import type {BankAccountStep, BankAccountSubStep} from '@src/types/onyx/ReimbursementAccount';
import type {OnfidoData} from '@src/types/onyx/ReimbursementAccountDraft';
@@ -75,7 +76,7 @@ function openPersonalBankAccountSetupView(exitReportID?: string) {
/**
* Whether after adding a bank account we should continue with the KYC flow. If so, we must specify the fallback route.
*/
-function setPersonalBankAccountContinueKYCOnSuccess(onSuccessFallbackRoute: string) {
+function setPersonalBankAccountContinueKYCOnSuccess(onSuccessFallbackRoute: Route) {
Onyx.merge(ONYXKEYS.PERSONAL_BANK_ACCOUNT, {onSuccessFallbackRoute});
}
diff --git a/src/libs/actions/FormActions.ts b/src/libs/actions/FormActions.ts
index 00ad3652c665..e32863cff0b1 100644
--- a/src/libs/actions/FormActions.ts
+++ b/src/libs/actions/FormActions.ts
@@ -1,5 +1,5 @@
import Onyx from 'react-native-onyx';
-import type {KeyValueMapping, NullishDeep} from 'react-native-onyx/lib/types';
+import type {KeyValueMapping, NullishDeep} from 'react-native-onyx';
import type {OnyxFormKeyWithoutDraft} from '@components/Form/types';
import FormUtils from '@libs/FormUtils';
import type {OnyxFormKey} from '@src/ONYXKEYS';
diff --git a/src/libs/actions/IOU.js b/src/libs/actions/IOU.js
index 5297d9aa4463..a9e1b09ed984 100644
--- a/src/libs/actions/IOU.js
+++ b/src/libs/actions/IOU.js
@@ -1,9 +1,9 @@
import {format} from 'date-fns';
+import fastMerge from 'expensify-common/lib/fastMerge';
import Str from 'expensify-common/lib/str';
import lodashGet from 'lodash/get';
import lodashHas from 'lodash/has';
import Onyx from 'react-native-onyx';
-import OnyxUtils from 'react-native-onyx/lib/utils';
import _ from 'underscore';
import ReceiptGeneric from '@assets/images/receipt-generic.png';
import * as API from '@libs/API';
@@ -779,7 +779,7 @@ function getMoneyRequestInformation(
// to remind me to do this.
const existingTransaction = allTransactionDrafts[`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${CONST.IOU.OPTIMISTIC_TRANSACTION_ID}`];
if (existingTransaction && existingTransaction.iouRequestType === CONST.IOU.REQUEST_TYPE.DISTANCE) {
- optimisticTransaction = OnyxUtils.fastMerge(existingTransaction, optimisticTransaction);
+ optimisticTransaction = fastMerge(existingTransaction, optimisticTransaction);
}
// STEP 4: Build optimistic reportActions. We need:
@@ -892,6 +892,7 @@ function createDistanceRequest(report, participant, comment, created, category,
// If the report is an iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function
const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report);
const currentChatReport = isMoneyRequestReport ? ReportUtils.getReport(report.chatReportID) : report;
+ const currentCreated = DateUtils.enrichMoneyRequestTimestamp(created);
const optimisticReceipt = {
source: ReceiptGeneric,
@@ -903,7 +904,7 @@ function createDistanceRequest(report, participant, comment, created, category,
comment,
amount,
currency,
- created,
+ currentCreated,
merchant,
userAccountID,
currentUserEmail,
@@ -928,7 +929,7 @@ function createDistanceRequest(report, participant, comment, created, category,
createdIOUReportActionID,
reportPreviewReportActionID: reportPreviewAction.reportActionID,
waypoints: JSON.stringify(validWaypoints),
- created,
+ created: currentCreated,
category,
tag,
billable,
@@ -1312,6 +1313,7 @@ function requestMoney(
// If the report is iou or expense report, we should get the linked chat report to be passed to the getMoneyRequestInformation function
const isMoneyRequestReport = ReportUtils.isMoneyRequestReport(report);
const currentChatReport = isMoneyRequestReport ? ReportUtils.getReport(report.chatReportID) : report;
+ const currentCreated = DateUtils.enrichMoneyRequestTimestamp(created);
const {payerAccountID, payerEmail, iouReport, chatReport, transaction, iouAction, createdChatReportActionID, createdIOUReportActionID, reportPreviewAction, onyxData} =
getMoneyRequestInformation(
currentChatReport,
@@ -1319,7 +1321,7 @@ function requestMoney(
comment,
amount,
currency,
- created,
+ currentCreated,
merchant,
payeeAccountID,
payeeEmail,
@@ -1342,7 +1344,7 @@ function requestMoney(
amount,
currency,
comment,
- created,
+ created: currentCreated,
merchant,
iouReportID: iouReport.reportID,
chatReportID: chatReport.reportID,
@@ -3497,18 +3499,18 @@ function detachReceipt(transactionID) {
* @param {String} source
*/
function replaceReceipt(transactionID, file, source) {
- const transaction = lodashGet(allTransactions, 'transactionID', {});
+ const transaction = allTransactions[`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`] || {};
const oldReceipt = lodashGet(transaction, 'receipt', {});
-
+ const receiptOptimistic = {
+ source,
+ state: CONST.IOU.RECEIPT_STATE.OPEN,
+ };
const optimisticData = [
{
onyxMethod: Onyx.METHOD.MERGE,
key: `${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`,
value: {
- receipt: {
- source,
- state: CONST.IOU.RECEIPT_STATE.OPEN,
- },
+ receipt: receiptOptimistic,
filename: file.name,
},
},
@@ -3521,6 +3523,7 @@ function replaceReceipt(transactionID, file, source) {
value: {
receipt: oldReceipt,
filename: transaction.filename,
+ errors: getReceiptError(receiptOptimistic, file.name),
},
},
];
diff --git a/src/libs/actions/MemoryOnlyKeys/MemoryOnlyKeys.ts b/src/libs/actions/MemoryOnlyKeys/MemoryOnlyKeys.ts
index 3e8c613187b4..15b9133f0aaf 100644
--- a/src/libs/actions/MemoryOnlyKeys/MemoryOnlyKeys.ts
+++ b/src/libs/actions/MemoryOnlyKeys/MemoryOnlyKeys.ts
@@ -1,5 +1,5 @@
import Onyx from 'react-native-onyx';
-import type {OnyxKey} from 'react-native-onyx/lib/types';
+import type {OnyxKey} from 'react-native-onyx';
import Log from '@libs/Log';
import ONYXKEYS from '@src/ONYXKEYS';
diff --git a/src/libs/actions/PaymentMethods.ts b/src/libs/actions/PaymentMethods.ts
index 1eebed9a9df4..cbc5778187a1 100644
--- a/src/libs/actions/PaymentMethods.ts
+++ b/src/libs/actions/PaymentMethods.ts
@@ -1,9 +1,8 @@
import {createRef} from 'react';
import type {MutableRefObject} from 'react';
import type {GestureResponderEvent} from 'react-native';
-import type {OnyxUpdate} from 'react-native-onyx';
+import type {OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
-import type {OnyxEntry} from 'react-native-onyx/lib/types';
import type {TransferMethod} from '@components/KYCWall/types';
import * as API from '@libs/API';
import type {AddPaymentCardParams, DeletePaymentCardParams, MakeDefaultPaymentMethodParams, PaymentCardParams, TransferWalletBalanceParams} from '@libs/API/parameters';
@@ -13,6 +12,7 @@ import Navigation from '@libs/Navigation/Navigation';
import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
+import type {Route} from '@src/ROUTES';
import type {BankAccountList, FundList} from '@src/types/onyx';
import type PaymentMethod from '@src/types/onyx/PaymentMethod';
import type {FilterMethodPaymentType} from '@src/types/onyx/WalletTransfer';
@@ -29,7 +29,7 @@ const kycWallRef: MutableRefObject = createRef();
/**
* When we successfully add a payment method or pass the KYC checks we will continue with our setup action if we have one set.
*/
-function continueSetup(fallbackRoute = ROUTES.HOME) {
+function continueSetup(fallbackRoute: Route = ROUTES.HOME) {
if (!kycWallRef.current?.continueAction) {
Navigation.goBack(fallbackRoute);
return;
diff --git a/src/libs/actions/Policy.ts b/src/libs/actions/Policy.ts
index d84fccfb196c..fbe92aeb378d 100644
--- a/src/libs/actions/Policy.ts
+++ b/src/libs/actions/Policy.ts
@@ -4,9 +4,8 @@ import Str from 'expensify-common/lib/str';
import {escapeRegExp} from 'lodash';
import lodashClone from 'lodash/clone';
import lodashUnion from 'lodash/union';
-import type {OnyxCollection, OnyxUpdate} from 'react-native-onyx';
+import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
-import type {NullishDeep, OnyxEntry} from 'react-native-onyx/lib/types';
import * as API from '@libs/API';
import type {
AddMembersToWorkspaceParams,
@@ -1607,12 +1606,12 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined {
const optimisticData: OnyxUpdate[] = [
{
- onyxMethod: Onyx.METHOD.MERGE,
+ onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.POLICY}${policyID}`,
value: newWorkspace,
},
{
- onyxMethod: Onyx.METHOD.MERGE,
+ onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.POLICY_MEMBERS}${policyID}`,
value: {
[sessionAccountID]: {
@@ -1626,7 +1625,7 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined {
},
},
{
- onyxMethod: Onyx.METHOD.MERGE,
+ onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.REPORT}${announceChatReportID}`,
value: {
pendingFields: {
@@ -1636,12 +1635,12 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined {
},
},
{
- onyxMethod: Onyx.METHOD.MERGE,
+ onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${announceChatReportID}`,
value: announceReportActionData,
},
{
- onyxMethod: Onyx.METHOD.MERGE,
+ onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.REPORT}${adminsChatReportID}`,
value: {
pendingFields: {
@@ -1651,12 +1650,12 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined {
},
},
{
- onyxMethod: Onyx.METHOD.MERGE,
+ onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${adminsChatReportID}`,
value: adminsReportActionData,
},
{
- onyxMethod: Onyx.METHOD.MERGE,
+ onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.REPORT}${workspaceChatReportID}`,
value: {
pendingFields: {
@@ -1666,7 +1665,7 @@ function createWorkspaceFromIOUPayment(iouReport: Report): string | undefined {
},
},
{
- onyxMethod: Onyx.METHOD.MERGE,
+ onyxMethod: Onyx.METHOD.SET,
key: `${ONYXKEYS.COLLECTION.REPORT_ACTIONS}${workspaceChatReportID}`,
value: workspaceChatReportActionData,
},
diff --git a/src/libs/actions/Report.ts b/src/libs/actions/Report.ts
index 6222c09a898e..1f6905cfb8e0 100644
--- a/src/libs/actions/Report.ts
+++ b/src/libs/actions/Report.ts
@@ -3,9 +3,8 @@ import ExpensiMark from 'expensify-common/lib/ExpensiMark';
import Str from 'expensify-common/lib/str';
import isEmpty from 'lodash/isEmpty';
import {DeviceEventEmitter, InteractionManager, Linking} from 'react-native';
-import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx';
+import type {NullishDeep, OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
-import type {NullishDeep} from 'react-native-onyx/lib/types';
import type {PartialDeep, ValueOf} from 'type-fest';
import type {Emoji} from '@assets/emojis/types';
import * as ActiveClientManager from '@libs/ActiveClientManager';
@@ -929,7 +928,7 @@ function expandURLPreview(reportID: string, reportActionID: string) {
}
/** Marks the new report actions as read */
-function readNewestAction(reportID: string, shouldEmitEvent = true) {
+function readNewestAction(reportID: string) {
const lastReadTime = DateUtils.getDBTime();
const optimisticData: OnyxUpdate[] = [
@@ -948,11 +947,6 @@ function readNewestAction(reportID: string, shouldEmitEvent = true) {
};
API.write(WRITE_COMMANDS.READ_NEWEST_ACTION, parameters, {optimisticData});
-
- if (!shouldEmitEvent) {
- return;
- }
-
DeviceEventEmitter.emit(`readNewestAction_${reportID}`, lastReadTime);
}
@@ -1531,7 +1525,7 @@ function updateWriteCapabilityAndNavigate(report: Report, newValue: WriteCapabil
*
* @param ignoreConciergeReportID - Flag to ignore conciergeChatReportID during navigation. The default behavior is to not ignore.
*/
-function navigateToConciergeChat(ignoreConciergeReportID = false) {
+function navigateToConciergeChat(ignoreConciergeReportID = false, shouldDismissModal = false) {
// If conciergeChatReportID contains a concierge report ID, we navigate to the concierge chat using the stored report ID.
// Otherwise, we would find the concierge chat and navigate to it.
// Now, when user performs sign-out and a sign-in again, conciergeChatReportID may contain a stale value.
@@ -1541,8 +1535,10 @@ function navigateToConciergeChat(ignoreConciergeReportID = false) {
// we need to ensure that the server data has been successfully pulled
Welcome.serverDataIsReadyPromise().then(() => {
// If we don't have a chat with Concierge then create it
- navigateToAndOpenReport([CONST.EMAIL.CONCIERGE], false);
+ navigateToAndOpenReport([CONST.EMAIL.CONCIERGE], shouldDismissModal);
});
+ } else if (shouldDismissModal) {
+ Navigation.dismissModal(conciergeChatReportID);
} else {
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(conciergeChatReportID));
}
diff --git a/src/libs/actions/Transaction.ts b/src/libs/actions/Transaction.ts
index 8743da7abd06..03c5d18aabb4 100644
--- a/src/libs/actions/Transaction.ts
+++ b/src/libs/actions/Transaction.ts
@@ -75,6 +75,8 @@ function saveWaypoint(transactionID: string, index: string, waypoint: RecentWayp
// Clear the existing route so that we don't show an old route
routes: {
route0: {
+ // Clear the existing distance to recalculate next time
+ distance: null,
geometry: {
coordinates: null,
},
@@ -148,6 +150,7 @@ function removeWaypoint(transaction: Transaction, currentIndex: string, isDraft:
// Clear the existing route so that we don't show an old route
routes: {
route0: {
+ // Clear the existing distance to recalculate next time
distance: null,
geometry: {
coordinates: null,
@@ -253,6 +256,7 @@ function updateWaypoints(transactionID: string, waypoints: WaypointCollection, i
// Clear the existing route so that we don't show an old route
routes: {
route0: {
+ // Clear the existing distance to recalculate next time
distance: null,
geometry: {
coordinates: null,
@@ -262,4 +266,8 @@ function updateWaypoints(transactionID: string, waypoints: WaypointCollection, i
});
}
-export {addStop, createInitialWaypoints, saveWaypoint, removeWaypoint, getRoute, getRouteForDraft, updateWaypoints};
+function clearError(transactionID: string) {
+ Onyx.merge(`${ONYXKEYS.COLLECTION.TRANSACTION}${transactionID}`, {errors: null});
+}
+
+export {addStop, createInitialWaypoints, saveWaypoint, removeWaypoint, getRoute, getRouteForDraft, updateWaypoints, clearError};
diff --git a/src/libs/actions/UpdateRequired.ts b/src/libs/actions/UpdateRequired.ts
new file mode 100644
index 000000000000..078bcc41fd71
--- /dev/null
+++ b/src/libs/actions/UpdateRequired.ts
@@ -0,0 +1,11 @@
+import Onyx from 'react-native-onyx';
+import ONYXKEYS from '@src/ONYXKEYS';
+
+function alertUser() {
+ Onyx.set(ONYXKEYS.UPDATE_REQUIRED, true);
+}
+
+export {
+ // eslint-disable-next-line import/prefer-default-export
+ alertUser,
+};
diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts
index f118797fa659..d6ed882be54a 100644
--- a/src/libs/actions/User.ts
+++ b/src/libs/actions/User.ts
@@ -1,7 +1,6 @@
import {isBefore} from 'date-fns';
-import type {OnyxCollection, OnyxUpdate} from 'react-native-onyx';
+import type {OnyxCollection, OnyxEntry, OnyxUpdate} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
-import type {OnyxEntry} from 'react-native-onyx/lib/types';
import type {ValueOf} from 'type-fest';
import * as API from '@libs/API';
import type {
diff --git a/src/libs/focusAndUpdateMultilineInputRange.ts b/src/libs/focusAndUpdateMultilineInputRange.ts
deleted file mode 100644
index 2e4a3d23631e..000000000000
--- a/src/libs/focusAndUpdateMultilineInputRange.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import type {TextInput} from 'react-native';
-
-/**
- * Focus a multiline text input and place the cursor at the end of the value (if there is a value in the input).
- *
- * When a multiline input contains a text value that goes beyond the scroll height, the cursor will be placed
- * at the end of the text value, and automatically scroll the input field to this position after the field gains
- * focus. This provides a better user experience in cases where the text in the field has to be edited. The auto-
- * scroll behaviour works on all platforms except iOS native.
- * See https://github.com/Expensify/App/issues/20836 for more details.
- */
-export default function focusAndUpdateMultilineInputRange(input: TextInput | HTMLTextAreaElement) {
- if (!input) {
- return;
- }
-
- input.focus();
- if ('setSelectionRange' in input && input.value) {
- const length = input.value.length;
- input.setSelectionRange(length, length);
- // eslint-disable-next-line no-param-reassign
- input.scrollTop = input.scrollHeight;
- }
-}
diff --git a/src/libs/migrations/PersonalDetailsByAccountID.js b/src/libs/migrations/PersonalDetailsByAccountID.js
index c08ec6fb2c43..24aece8f5a97 100644
--- a/src/libs/migrations/PersonalDetailsByAccountID.js
+++ b/src/libs/migrations/PersonalDetailsByAccountID.js
@@ -251,12 +251,6 @@ export default function () {
delete newReport.lastActorEmail;
}
- if (lodashHas(newReport, ['participants'])) {
- reportWasModified = true;
- Log.info(`[Migrate Onyx] PersonalDetailsByAccountID migration: removing participants from report ${newReport.reportID}`);
- delete newReport.participants;
- }
-
if (lodashHas(newReport, ['ownerEmail'])) {
reportWasModified = true;
Log.info(`[Migrate Onyx] PersonalDetailsByAccountID migration: removing ownerEmail from report ${newReport.reportID}`);
diff --git a/src/libs/migrations/RenameReceiptFilename.ts b/src/libs/migrations/RenameReceiptFilename.ts
index dff2be5c286d..b867024fc74e 100644
--- a/src/libs/migrations/RenameReceiptFilename.ts
+++ b/src/libs/migrations/RenameReceiptFilename.ts
@@ -1,5 +1,5 @@
import Onyx from 'react-native-onyx';
-import type {NullishDeep, OnyxCollection} from 'react-native-onyx/lib/types';
+import type {NullishDeep, OnyxCollection} from 'react-native-onyx';
import Log from '@libs/Log';
import ONYXKEYS from '@src/ONYXKEYS';
import type Transaction from '@src/types/onyx/Transaction';
diff --git a/src/libs/onyxSubscribe.ts b/src/libs/onyxSubscribe.ts
index 4572ca35a4f2..af775842fc16 100644
--- a/src/libs/onyxSubscribe.ts
+++ b/src/libs/onyxSubscribe.ts
@@ -1,6 +1,6 @@
import type {ConnectOptions} from 'react-native-onyx';
import Onyx from 'react-native-onyx';
-import type {OnyxKey} from '@src/ONYXKEYS';
+import type {OnyxCollectionKey, OnyxKey} from '@src/ONYXKEYS';
/**
* Connect to onyx data. Same params as Onyx.connect(), but returns a function to unsubscribe.
@@ -8,7 +8,7 @@ import type {OnyxKey} from '@src/ONYXKEYS';
* @param mapping Same as for Onyx.connect()
* @return Unsubscribe callback
*/
-function onyxSubscribe(mapping: ConnectOptions) {
+function onyxSubscribe(mapping: ConnectOptions) {
const connectionId = Onyx.connect(mapping);
return () => Onyx.disconnect(connectionId);
}
diff --git a/src/libs/tryResolveUrlFromApiRoot.ts b/src/libs/tryResolveUrlFromApiRoot.ts
index adf717d500be..8eb5bdba0129 100644
--- a/src/libs/tryResolveUrlFromApiRoot.ts
+++ b/src/libs/tryResolveUrlFromApiRoot.ts
@@ -1,3 +1,4 @@
+import type {ImageSourcePropType} from 'react-native';
import Config from '@src/CONFIG';
import type {Request} from '@src/types/onyx';
import * as ApiUtils from './ApiUtils';
@@ -18,12 +19,12 @@ const ORIGIN_PATTERN = new RegExp(`^(${ORIGINS_TO_REPLACE.join('|')})`);
* - Unmatched URLs (non expensify) are returned with no modifications
*/
function tryResolveUrlFromApiRoot(url: string): string;
-function tryResolveUrlFromApiRoot(url: number): number;
-function tryResolveUrlFromApiRoot(url: string | number): string | number;
-function tryResolveUrlFromApiRoot(url: string | number): string | number {
+function tryResolveUrlFromApiRoot(url: ImageSourcePropType): number;
+function tryResolveUrlFromApiRoot(url: string | ImageSourcePropType): string | ImageSourcePropType;
+function tryResolveUrlFromApiRoot(url: string | ImageSourcePropType): string | ImageSourcePropType {
// in native, when we import an image asset, it will have a number representation which can be used in `source` of Image
// in this case we can skip the url resolving
- if (typeof url === 'number') {
+ if (typeof url !== 'string') {
return url;
}
const apiRoot = ApiUtils.getApiRoot({shouldUseSecure: false} as Request);
diff --git a/src/pages/AddPersonalBankAccountPage.js b/src/pages/AddPersonalBankAccountPage.tsx
similarity index 64%
rename from src/pages/AddPersonalBankAccountPage.js
rename to src/pages/AddPersonalBankAccountPage.tsx
index 09b73ea158f9..1876992f9ced 100644
--- a/src/pages/AddPersonalBankAccountPage.js
+++ b/src/pages/AddPersonalBankAccountPage.tsx
@@ -1,8 +1,6 @@
-import lodashGet from 'lodash/get';
-import PropTypes from 'prop-types';
import React, {useCallback, useEffect, useState} from 'react';
import {withOnyx} from 'react-native-onyx';
-import _ from 'underscore';
+import type {OnyxEntry} from 'react-native-onyx';
import AddPlaidBankAccount from '@components/AddPlaidBankAccount';
import ConfirmationPage from '@components/ConfirmationPage';
import FormProvider from '@components/Form/FormProvider';
@@ -16,69 +14,37 @@ import * as BankAccounts from '@userActions/BankAccounts';
import * as PaymentMethods from '@userActions/PaymentMethods';
import ONYXKEYS from '@src/ONYXKEYS';
import ROUTES from '@src/ROUTES';
-import * as PlaidDataProps from './ReimbursementAccount/plaidDataPropTypes';
+import type {PersonalBankAccount, PlaidData} from '@src/types/onyx';
-const propTypes = {
+type AddPersonalBankAccountPageWithOnyxProps = {
/** Contains plaid data */
- plaidData: PlaidDataProps.plaidDataPropTypes,
+ plaidData: OnyxEntry;
/** The details about the Personal bank account we are adding saved in Onyx */
- personalBankAccount: PropTypes.shape({
- /** An error message to display to the user */
- error: PropTypes.string,
-
- /** Whether we should show the view that the bank account was successfully added */
- shouldShowSuccess: PropTypes.bool,
-
- /** Any reportID we should redirect to at the end of the flow */
- exitReportID: PropTypes.string,
-
- /** Whether we should continue with KYC at the end of the flow */
- shouldContinueKYCOnSuccess: PropTypes.bool,
-
- /** Whether the form is loading */
- isLoading: PropTypes.bool,
-
- /** The account ID of the selected bank account from Plaid */
- plaidAccountID: PropTypes.string,
- }),
+ personalBankAccount: OnyxEntry;
};
-const defaultProps = {
- plaidData: PlaidDataProps.plaidDataDefaultProps,
- personalBankAccount: {
- error: '',
- shouldShowSuccess: false,
- isLoading: false,
- plaidAccountID: '',
- exitReportID: '',
- shouldContinueKYCOnSuccess: false,
- },
-};
-
-function AddPersonalBankAccountPage({personalBankAccount, plaidData}) {
+function AddPersonalBankAccountPage({personalBankAccount, plaidData}: AddPersonalBankAccountPageWithOnyxProps) {
const styles = useThemeStyles();
const {translate} = useLocalize();
const [selectedPlaidAccountId, setSelectedPlaidAccountId] = useState('');
- const shouldShowSuccess = lodashGet(personalBankAccount, 'shouldShowSuccess', false);
+ const shouldShowSuccess = personalBankAccount?.shouldShowSuccess ?? false;
- /**
- * @returns {Object}
- */
const validateBankAccountForm = () => ({});
const submitBankAccountForm = useCallback(() => {
- const selectedPlaidBankAccount = _.findWhere(lodashGet(plaidData, 'bankAccounts', []), {
- plaidAccountID: selectedPlaidAccountId,
- });
+ const bankAccounts = plaidData?.bankAccounts ?? [];
+ const selectedPlaidBankAccount = bankAccounts.find((bankAccount) => bankAccount.plaidAccountID === selectedPlaidAccountId);
- BankAccounts.addPersonalBankAccount(selectedPlaidBankAccount);
+ if (selectedPlaidBankAccount) {
+ BankAccounts.addPersonalBankAccount(selectedPlaidBankAccount);
+ }
}, [plaidData, selectedPlaidAccountId]);
const exitFlow = useCallback(
(shouldContinue = false) => {
- const exitReportID = lodashGet(personalBankAccount, 'exitReportID');
- const onSuccessFallbackRoute = lodashGet(personalBankAccount, 'onSuccessFallbackRoute', '');
+ const exitReportID = personalBankAccount?.exitReportID;
+ const onSuccessFallbackRoute = personalBankAccount?.onSuccessFallbackRoute ?? '';
if (exitReportID) {
Navigation.dismissModal(exitReportID);
@@ -114,7 +80,7 @@ function AddPersonalBankAccountPage({personalBankAccount, plaidData}) {
/>
) : (
({
personalBankAccount: {
key: ONYXKEYS.PERSONAL_BANK_ACCOUNT,
},
diff --git a/src/pages/DetailsPage.js b/src/pages/DetailsPage.js
index f215b4167ab6..47ade872d25a 100755
--- a/src/pages/DetailsPage.js
+++ b/src/pages/DetailsPage.js
@@ -136,7 +136,6 @@ function DetailsPage(props) {
{({show}) => (
diff --git a/src/pages/ErrorPage/UpdateRequiredView.tsx b/src/pages/ErrorPage/UpdateRequiredView.tsx
new file mode 100644
index 000000000000..2a73215d2293
--- /dev/null
+++ b/src/pages/ErrorPage/UpdateRequiredView.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import {View} from 'react-native';
+import Button from '@components/Button';
+import Header from '@components/Header';
+import HeaderGap from '@components/HeaderGap';
+import Lottie from '@components/Lottie';
+import LottieAnimations from '@components/LottieAnimations';
+import Text from '@components/Text';
+import useLocalize from '@hooks/useLocalize';
+import useSafeAreaInsets from '@hooks/useSafeAreaInsets';
+import useStyleUtils from '@hooks/useStyleUtils';
+import useThemeStyles from '@hooks/useThemeStyles';
+import useWindowDimensions from '@hooks/useWindowDimensions';
+import * as AppUpdate from '@libs/actions/AppUpdate';
+
+function UpdateRequiredView() {
+ const insets = useSafeAreaInsets();
+ const styles = useThemeStyles();
+ const StyleUtils = useStyleUtils();
+ const {translate} = useLocalize();
+ const {isSmallScreenWidth} = useWindowDimensions();
+ return (
+
+
+
+
+
+
+
+
+
+
+ {translate('updateRequiredView.pleaseInstall')}
+
+
+ {translate('updateRequiredView.toGetLatestChanges')}
+
+
+
+
+
+ );
+}
+
+UpdateRequiredView.displayName = 'UpdateRequiredView';
+export default UpdateRequiredView;
diff --git a/src/pages/GetAssistancePage.tsx b/src/pages/GetAssistancePage.tsx
index 46963e56997a..948e0c239de9 100644
--- a/src/pages/GetAssistancePage.tsx
+++ b/src/pages/GetAssistancePage.tsx
@@ -2,7 +2,7 @@ import type {StackScreenProps} from '@react-navigation/stack';
import React from 'react';
import {ScrollView, View} from 'react-native';
import {withOnyx} from 'react-native-onyx';
-import type {OnyxEntry} from 'react-native-onyx/lib/types';
+import type {OnyxEntry} from 'react-native-onyx';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import * as Expensicons from '@components/Icon/Expensicons';
import * as Illustrations from '@components/Icon/Illustrations';
diff --git a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js
index 2069c71f3c8b..156c8a78e14d 100644
--- a/src/pages/ReimbursementAccount/ReimbursementAccountPage.js
+++ b/src/pages/ReimbursementAccount/ReimbursementAccountPage.js
@@ -315,6 +315,11 @@ function ReimbursementAccountPage({reimbursementAccount, route, onfidoToken, pol
const currentStepRouteParam = getStepToOpenFromRouteParams(route);
if (currentStepRouteParam === currentStep) {
+ // If the user is connecting online with plaid, reset any bank account errors so we don't persist old data from a potential previous connection
+ if (currentStep === CONST.BANK_ACCOUNT.STEP.BANK_ACCOUNT && achData.subStep === CONST.BANK_ACCOUNT.SETUP_TYPE.PLAID) {
+ BankAccounts.hideBankAccountErrors();
+ }
+
// The route is showing the correct step, no need to update the route param or clear errors.
return;
}
diff --git a/src/pages/ReimbursementAccount/ValidationStep.js b/src/pages/ReimbursementAccount/ValidationStep.js
index 41f63b22f8f3..1898fc8ad212 100644
--- a/src/pages/ReimbursementAccount/ValidationStep.js
+++ b/src/pages/ReimbursementAccount/ValidationStep.js
@@ -201,7 +201,7 @@ function ValidationStep({reimbursementAccount, translate, onBackButtonPress, acc
{translate('validationStep.letsChatText')}