diff --git a/src/components/Avatar.tsx b/src/components/Avatar.tsx index 358f5333bfba..bf48894beaab 100644 --- a/src/components/Avatar.tsx +++ b/src/components/Avatar.tsx @@ -90,7 +90,7 @@ function Avatar({ if (isWorkspace) { iconColors = StyleUtils.getDefaultWorkspaceAvatarColor(name); } else if (useFallBackAvatar) { - iconColors = StyleUtils.getBackgroundColorAndFill(theme.border, theme.icon); + iconColors = StyleUtils.getBackgroundColorAndFill(theme.buttonHoveredBG, theme.icon); } else { iconColors = null; } diff --git a/src/libs/Notification/PushNotification/index.native.ts b/src/libs/Notification/PushNotification/index.native.ts index d45d076a8adb..43ddff2508ee 100644 --- a/src/libs/Notification/PushNotification/index.native.ts +++ b/src/libs/Notification/PushNotification/index.native.ts @@ -10,7 +10,7 @@ import NotificationType from './NotificationType'; import type {ClearNotifications, Deregister, Init, OnReceived, OnSelected, Register} from './types'; import type PushNotificationType from './types'; -type NotificationEventActionCallback = (data: NotificationData) => void; +type NotificationEventActionCallback = (data: NotificationData) => Promise; type NotificationEventActionMap = Partial>>; @@ -56,7 +56,13 @@ function pushNotificationEventCallback(eventType: EventType, notification: PushP }); return; } - action(data); + + /** + * The action callback should return a promise. It's very important we return that promise so that + * when these callbacks are run in Android's background process (via Headless JS), the process waits + * for the promise to resolve before quitting + */ + return action(data); } /** @@ -83,15 +89,11 @@ function refreshNotificationOptInStatus() { */ const init: Init = () => { // Setup event listeners - Airship.addListener(EventType.PushReceived, (notification) => { - pushNotificationEventCallback(EventType.PushReceived, notification.pushPayload); - }); + Airship.addListener(EventType.PushReceived, (notification) => pushNotificationEventCallback(EventType.PushReceived, notification.pushPayload)); // Note: the NotificationResponse event has a nested PushReceived event, // so event.notification refers to the same thing as notification above ^ - Airship.addListener(EventType.NotificationResponse, (event) => { - pushNotificationEventCallback(EventType.NotificationResponse, event.pushPayload); - }); + Airship.addListener(EventType.NotificationResponse, (event) => pushNotificationEventCallback(EventType.NotificationResponse, event.pushPayload)); // Keep track of which users have enabled push notifications via an NVP. Airship.addListener(EventType.PushNotificationStatusChangedStatus, refreshNotificationOptInStatus); diff --git a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts index 4b17adf86841..a9b8fc0b64f4 100644 --- a/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts +++ b/src/libs/Notification/PushNotification/subscribeToReportCommentPushNotifications.ts @@ -1,5 +1,5 @@ import Onyx from 'react-native-onyx'; -import * as OnyxUpdates from '@libs/actions/OnyxUpdates'; +import applyOnyxUpdatesReliably from '@libs/actions/applyOnyxUpdatesReliably'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; @@ -26,35 +26,51 @@ Onyx.connect({ }, }); +function getLastUpdateIDAppliedToClient(): Promise { + return new Promise((resolve) => { + Onyx.connect({ + key: ONYXKEYS.ONYX_UPDATES_LAST_UPDATE_ID_APPLIED_TO_CLIENT, + callback: (value) => resolve(value ?? 0), + }); + }); +} + /** * Setup reportComment push notification callbacks. */ export default function subscribeToReportCommentPushNotifications() { PushNotification.onReceived(PushNotification.TYPE.REPORT_COMMENT, ({reportID, reportActionID, onyxData, lastUpdateID, previousUpdateID}) => { + Log.info(`[PushNotification] received report comment notification in the ${Visibility.isVisible() ? 'foreground' : 'background'}`, false, {reportID, reportActionID}); + if (!ActiveClientManager.isClientTheLeader()) { Log.info('[PushNotification] received report comment notification, but ignoring it since this is not the active client'); - return; + return Promise.resolve(); } - Log.info(`[PushNotification] received report comment notification in the ${Visibility.isVisible() ? 'foreground' : 'background'}`, false, {reportID, reportActionID}); - - if (onyxData && lastUpdateID && previousUpdateID) { - Log.info('[PushNotification] reliable onyx update received', false, {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); - const updates: OnyxUpdatesFromServer = { - type: CONST.ONYX_UPDATE_TYPES.AIRSHIP, - lastUpdateID, - previousUpdateID, - updates: [ - { - eventType: 'eventType', - data: onyxData, - }, - ], - }; - OnyxUpdates.applyOnyxUpdatesReliably(updates); - } else { - Log.hmmm("[PushNotification] Didn't apply onyx updates because some data is missing", {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); + if (!onyxData || !lastUpdateID || !previousUpdateID) { + Log.hmmm("[PushNotification] didn't apply onyx updates because some data is missing", {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); + return Promise.resolve(); } + + Log.info('[PushNotification] reliable onyx update received', false, {lastUpdateID, previousUpdateID, onyxDataCount: onyxData?.length ?? 0}); + const updates: OnyxUpdatesFromServer = { + type: CONST.ONYX_UPDATE_TYPES.AIRSHIP, + lastUpdateID, + previousUpdateID, + updates: [ + { + eventType: 'eventType', + data: onyxData, + }, + ], + }; + + /** + * When this callback runs in the background on Android (via Headless JS), no other Onyx.connect callbacks will run. This means that + * lastUpdateIDAppliedToClient will NOT be populated in other libs. To workaround this, we manually read the value here + * and pass it as a param + */ + return getLastUpdateIDAppliedToClient().then((lastUpdateIDAppliedToClient) => applyOnyxUpdatesReliably(updates, true, lastUpdateIDAppliedToClient)); }); // Open correct report when push notification is clicked @@ -96,5 +112,7 @@ export default function subscribeToReportCommentPushNotifications() { } }); }); + + return Promise.resolve(); }); } diff --git a/src/libs/Notification/PushNotification/types.ts b/src/libs/Notification/PushNotification/types.ts index 4399c10b4a95..cf7e54abd094 100644 --- a/src/libs/Notification/PushNotification/types.ts +++ b/src/libs/Notification/PushNotification/types.ts @@ -5,8 +5,8 @@ import type NotificationType from './NotificationType'; type Init = () => void; type Register = (notificationID: string | number) => void; type Deregister = () => void; -type OnReceived = >(notificationType: T, callback: (data: NotificationDataMap[T]) => void) => void; -type OnSelected = >(notificationType: T, callback: (data: NotificationDataMap[T]) => void) => void; +type OnReceived = >(notificationType: T, callback: (data: NotificationDataMap[T]) => Promise) => void; +type OnSelected = >(notificationType: T, callback: (data: NotificationDataMap[T]) => Promise) => void; type ClearNotifications = () => void; type PushNotification = { diff --git a/src/libs/actions/IOU.ts b/src/libs/actions/IOU.ts index 7fed15335e2a..5217be1686c2 100644 --- a/src/libs/actions/IOU.ts +++ b/src/libs/actions/IOU.ts @@ -1,4 +1,3 @@ -import type {ParamListBase, StackNavigationState} from '@react-navigation/native'; import {format} from 'date-fns'; import fastMerge from 'expensify-common/lib/fastMerge'; import Str from 'expensify-common/lib/str'; @@ -45,7 +44,6 @@ import * as ReportUtils from '@libs/ReportUtils'; import * as TransactionUtils from '@libs/TransactionUtils'; import * as UserUtils from '@libs/UserUtils'; import ViolationsUtils from '@libs/Violations/ViolationsUtils'; -import type {NavigationPartialRoute} from '@navigation/types'; import type {IOUAction, IOUType} from '@src/CONST'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; @@ -357,28 +355,6 @@ function clearMoneyRequest(transactionID: string, skipConfirmation = false) { Onyx.set(`${ONYXKEYS.COLLECTION.TRANSACTION_DRAFT}${transactionID}`, null); } -/** - * Update money expense-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 expense tab params - updateMoneyRequestTypeParams(route.state?.routes ?? [], newIouType, tab); - }); -} - // eslint-disable-next-line @typescript-eslint/naming-convention function startMoneyRequest(iouType: ValueOf, reportID: string, requestType?: IOURequestType, skipConfirmation = false) { clearMoneyRequest(CONST.IOU.OPTIMISTIC_TRANSACTION_ID, skipConfirmation); @@ -6061,6 +6037,5 @@ export { updateMoneyRequestTag, updateMoneyRequestTaxAmount, updateMoneyRequestTaxRate, - updateMoneyRequestTypeParams, }; export type {GPSPoint as GpsPoint, IOURequestType}; diff --git a/src/libs/actions/OnyxUpdateManager.ts b/src/libs/actions/OnyxUpdateManager.ts index f1f26e259ab1..8d7f299160be 100644 --- a/src/libs/actions/OnyxUpdateManager.ts +++ b/src/libs/actions/OnyxUpdateManager.ts @@ -1,9 +1,11 @@ +import type {OnyxEntry} from 'react-native-onyx'; import Onyx from 'react-native-onyx'; import * as ActiveClientManager from '@libs/ActiveClientManager'; import Log from '@libs/Log'; import * as SequentialQueue from '@libs/Network/SequentialQueue'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; +import type {OnyxUpdatesFromServer} from '@src/types/onyx'; import * as App from './App'; import * as OnyxUpdates from './OnyxUpdates'; @@ -36,83 +38,94 @@ Onyx.connect({ }, }); -export default () => { - console.debug('[OnyxUpdateManager] Listening for updates from the server'); - Onyx.connect({ - key: ONYXKEYS.ONYX_UPDATES_FROM_SERVER, - callback: (value) => { - // When there's no value, there's nothing to process, so let's return early. - if (!value) { - return; - } - // If isLoadingApp is positive it means that OpenApp command hasn't finished yet, and in that case - // we don't have base state of the app (reports, policies, etc) setup. If we apply this update, - // we'll only have them overriten by the openApp response. So let's skip it and return. - if (isLoadingApp) { - // When ONYX_UPDATES_FROM_SERVER is set, we pause the queue. Let's unpause - // it so the app is not stuck forever without processing requests. - SequentialQueue.unpause(); - console.debug(`[OnyxUpdateManager] Ignoring Onyx updates while OpenApp hans't finished yet.`); - return; - } - // This key is shared across clients, thus every client/tab will have a copy and try to execute this method. - // It is very important to only process the missing onyx updates from leader client otherwise requests we'll execute - // several duplicated requests that are not controlled by the SequentialQueue. - if (!ActiveClientManager.isClientTheLeader()) { - return; - } +/** + * + * @param onyxUpdatesFromServer + * @param clientLastUpdateID an optional override for the lastUpdateIDAppliedToClient + * @returns + */ +function handleOnyxUpdateGap(onyxUpdatesFromServer: OnyxEntry, clientLastUpdateID = 0) { + // When there's no value, there's nothing to process, so let's return early. + if (!onyxUpdatesFromServer) { + return; + } + // If isLoadingApp is positive it means that OpenApp command hasn't finished yet, and in that case + // we don't have base state of the app (reports, policies, etc) setup. If we apply this update, + // we'll only have them overriten by the openApp response. So let's skip it and return. + if (isLoadingApp) { + // When ONYX_UPDATES_FROM_SERVER is set, we pause the queue. Let's unpause + // it so the app is not stuck forever without processing requests. + SequentialQueue.unpause(); + console.debug(`[OnyxUpdateManager] Ignoring Onyx updates while OpenApp hans't finished yet.`); + return; + } + // This key is shared across clients, thus every client/tab will have a copy and try to execute this method. + // It is very important to only process the missing onyx updates from leader client otherwise requests we'll execute + // several duplicated requests that are not controlled by the SequentialQueue. + if (!ActiveClientManager.isClientTheLeader()) { + return; + } - // Since we used the same key that used to store another object, let's confirm that the current object is - // following the new format before we proceed. If it isn't, then let's clear the object in Onyx. - if ( - !(typeof value === 'object' && !!value) || - !('type' in value) || - (!(value.type === CONST.ONYX_UPDATE_TYPES.HTTPS && value.request && value.response) && - !((value.type === CONST.ONYX_UPDATE_TYPES.PUSHER || value.type === CONST.ONYX_UPDATE_TYPES.AIRSHIP) && value.updates)) - ) { - console.debug('[OnyxUpdateManager] Invalid format found for updates, cleaning and unpausing the queue'); - Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, null); - SequentialQueue.unpause(); - return; - } + // Since we used the same key that used to store another object, let's confirm that the current object is + // following the new format before we proceed. If it isn't, then let's clear the object in Onyx. + if ( + !(typeof onyxUpdatesFromServer === 'object' && !!onyxUpdatesFromServer) || + !('type' in onyxUpdatesFromServer) || + (!(onyxUpdatesFromServer.type === CONST.ONYX_UPDATE_TYPES.HTTPS && onyxUpdatesFromServer.request && onyxUpdatesFromServer.response) && + !((onyxUpdatesFromServer.type === CONST.ONYX_UPDATE_TYPES.PUSHER || onyxUpdatesFromServer.type === CONST.ONYX_UPDATE_TYPES.AIRSHIP) && onyxUpdatesFromServer.updates)) + ) { + console.debug('[OnyxUpdateManager] Invalid format found for updates, cleaning and unpausing the queue'); + Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, null); + SequentialQueue.unpause(); + return; + } - const updateParams = value; - const lastUpdateIDFromServer = value.lastUpdateID; - const previousUpdateIDFromServer = value.previousUpdateID; + const updateParams = onyxUpdatesFromServer; + const lastUpdateIDFromServer = onyxUpdatesFromServer.lastUpdateID; + const previousUpdateIDFromServer = onyxUpdatesFromServer.previousUpdateID; + const lastUpdateIDFromClient = clientLastUpdateID || lastUpdateIDAppliedToClient; - // In cases where we received a previousUpdateID and it doesn't match our lastUpdateIDAppliedToClient - // we need to perform one of the 2 possible cases: - // - // 1. This is the first time we're receiving an lastUpdateID, so we need to do a final reconnectApp before - // fully migrating to the reliable updates mode. - // 2. This client already has the reliable updates mode enabled, but it's missing some updates and it - // needs to fetch those. - let canUnpauseQueuePromise; + // In cases where we received a previousUpdateID and it doesn't match our lastUpdateIDAppliedToClient + // we need to perform one of the 2 possible cases: + // + // 1. This is the first time we're receiving an lastUpdateID, so we need to do a final reconnectApp before + // fully migrating to the reliable updates mode. + // 2. This client already has the reliable updates mode enabled, but it's missing some updates and it + // needs to fetch those. + let canUnpauseQueuePromise; - // The flow below is setting the promise to a reconnect app to address flow (1) explained above. - if (!lastUpdateIDAppliedToClient) { - Log.info('Client has not gotten reliable updates before so reconnecting the app to start the process'); + // The flow below is setting the promise to a reconnect app to address flow (1) explained above. + if (!lastUpdateIDFromClient) { + Log.info('Client has not gotten reliable updates before so reconnecting the app to start the process'); - // Since this is a full reconnectApp, we'll not apply the updates we received - those will come in the reconnect app request. - canUnpauseQueuePromise = App.finalReconnectAppAfterActivatingReliableUpdates(); - } else { - // The flow below is setting the promise to a getMissingOnyxUpdates to address flow (2) explained above. - console.debug(`[OnyxUpdateManager] Client is behind the server by ${Number(previousUpdateIDFromServer) - lastUpdateIDAppliedToClient} so fetching incremental updates`); - Log.info('Gap detected in update IDs from server so fetching incremental updates', true, { - lastUpdateIDFromServer, - previousUpdateIDFromServer, - lastUpdateIDAppliedToClient, - }); - canUnpauseQueuePromise = App.getMissingOnyxUpdates(lastUpdateIDAppliedToClient, previousUpdateIDFromServer); - } + // Since this is a full reconnectApp, we'll not apply the updates we received - those will come in the reconnect app request. + canUnpauseQueuePromise = App.finalReconnectAppAfterActivatingReliableUpdates(); + } else { + // The flow below is setting the promise to a getMissingOnyxUpdates to address flow (2) explained above. + console.debug(`[OnyxUpdateManager] Client is behind the server by ${Number(previousUpdateIDFromServer) - lastUpdateIDFromClient} so fetching incremental updates`); + Log.info('Gap detected in update IDs from server so fetching incremental updates', true, { + lastUpdateIDFromServer, + previousUpdateIDFromServer, + lastUpdateIDFromClient, + }); + canUnpauseQueuePromise = App.getMissingOnyxUpdates(lastUpdateIDFromClient, previousUpdateIDFromServer); + } - canUnpauseQueuePromise.finally(() => { - OnyxUpdates.apply(updateParams).finally(() => { - console.debug('[OnyxUpdateManager] Done applying all updates'); - Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, null); - SequentialQueue.unpause(); - }); - }); - }, + canUnpauseQueuePromise.finally(() => { + OnyxUpdates.apply(updateParams).finally(() => { + console.debug('[OnyxUpdateManager] Done applying all updates'); + Onyx.set(ONYXKEYS.ONYX_UPDATES_FROM_SERVER, null); + SequentialQueue.unpause(); + }); + }); +} + +export default () => { + console.debug('[OnyxUpdateManager] Listening for updates from the server'); + Onyx.connect({ + key: ONYXKEYS.ONYX_UPDATES_FROM_SERVER, + callback: (value) => handleOnyxUpdateGap(value), }); }; + +export {handleOnyxUpdateGap}; diff --git a/src/libs/actions/OnyxUpdates.ts b/src/libs/actions/OnyxUpdates.ts index bb486d97b33b..04656f1adfec 100644 --- a/src/libs/actions/OnyxUpdates.ts +++ b/src/libs/actions/OnyxUpdates.ts @@ -132,29 +132,23 @@ function saveUpdateInformation(updateParams: OnyxUpdatesFromServer) { * This function will receive the previousUpdateID from any request/pusher update that has it, compare to our current app state * and return if an update is needed * @param previousUpdateID The previousUpdateID contained in the response object + * @param clientLastUpdateID an optional override for the lastUpdateIDAppliedToClient */ -function doesClientNeedToBeUpdated(previousUpdateID = 0): boolean { +function doesClientNeedToBeUpdated(previousUpdateID = 0, clientLastUpdateID = 0): boolean { // If no previousUpdateID is sent, this is not a WRITE request so we don't need to update our current state if (!previousUpdateID) { return false; } - // If we don't have any value in lastUpdateIDAppliedToClient, this is the first time we're receiving anything, so we need to do a last reconnectApp - if (!lastUpdateIDAppliedToClient) { + const lastUpdateIDFromClient = clientLastUpdateID || lastUpdateIDAppliedToClient; + + // If we don't have any value in lastUpdateIDFromClient, this is the first time we're receiving anything, so we need to do a last reconnectApp + if (!lastUpdateIDFromClient) { return true; } - return lastUpdateIDAppliedToClient < previousUpdateID; -} - -function applyOnyxUpdatesReliably(updates: OnyxUpdatesFromServer) { - const previousUpdateID = Number(updates.previousUpdateID) || 0; - if (!doesClientNeedToBeUpdated(previousUpdateID)) { - apply(updates); - return; - } - saveUpdateInformation(updates); + return lastUpdateIDFromClient < previousUpdateID; } // eslint-disable-next-line import/prefer-default-export -export {saveUpdateInformation, doesClientNeedToBeUpdated, apply, applyOnyxUpdatesReliably}; +export {saveUpdateInformation, doesClientNeedToBeUpdated, apply}; diff --git a/src/libs/actions/User.ts b/src/libs/actions/User.ts index f347655b6a4d..5dab277f07fa 100644 --- a/src/libs/actions/User.ts +++ b/src/libs/actions/User.ts @@ -44,8 +44,8 @@ import type ReportAction from '@src/types/onyx/ReportAction'; import type {OriginalMessage} from '@src/types/onyx/ReportAction'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {EmptyObject} from '@src/types/utils/EmptyObject'; +import applyOnyxUpdatesReliably from './applyOnyxUpdatesReliably'; import * as Link from './Link'; -import * as OnyxUpdates from './OnyxUpdates'; import * as Report from './Report'; import * as Session from './Session'; @@ -597,7 +597,7 @@ function subscribeToUserEvents() { updates: pushJSON.updates ?? [], previousUpdateID: Number(pushJSON.previousUpdateID || 0), }; - OnyxUpdates.applyOnyxUpdatesReliably(updates); + applyOnyxUpdatesReliably(updates); }); // Handles Onyx updates coming from Pusher through the mega multipleEvents. diff --git a/src/libs/actions/applyOnyxUpdatesReliably.ts b/src/libs/actions/applyOnyxUpdatesReliably.ts new file mode 100644 index 000000000000..17754712cdc8 --- /dev/null +++ b/src/libs/actions/applyOnyxUpdatesReliably.ts @@ -0,0 +1,26 @@ +import type {OnyxUpdatesFromServer} from '@src/types/onyx'; +import {handleOnyxUpdateGap} from './OnyxUpdateManager'; +import * as OnyxUpdates from './OnyxUpdates'; + +/** + * Checks for and handles gaps of onyx updates between the client and the given server updates before applying them + * + * This is in it's own lib to fix a dependency cycle from OnyxUpdateManager + * + * @param updates + * @param shouldRunSync + * @returns + */ +export default function applyOnyxUpdatesReliably(updates: OnyxUpdatesFromServer, shouldRunSync = false, clientLastUpdateID = 0) { + const previousUpdateID = Number(updates.previousUpdateID) || 0; + if (!OnyxUpdates.doesClientNeedToBeUpdated(previousUpdateID, clientLastUpdateID)) { + OnyxUpdates.apply(updates); + return; + } + + if (shouldRunSync) { + handleOnyxUpdateGap(updates, clientLastUpdateID); + } else { + OnyxUpdates.saveUpdateInformation(updates); + } +} diff --git a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx index 60fed8e7af2e..f9f069a2172a 100644 --- a/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx +++ b/src/pages/home/sidebar/SidebarScreen/FloatingActionButtonAndPopover.tsx @@ -316,6 +316,19 @@ function FloatingActionButtonAndPopover( ), ), }, + { + icon: Expensicons.Transfer, + text: translate('iou.splitExpense'), + onSelected: () => + interceptAnonymousUser(() => + IOU.startMoneyRequest( + CONST.IOU.TYPE.SPLIT, + // When starting to create a money request from the global FAB, there is not an existing report yet. A random optimistic reportID is generated and used + // for all of the routes in the creation flow. + ReportUtils.generateReportID(), + ), + ), + }, { icon: Expensicons.Send, text: translate('iou.paySomeone', {}), diff --git a/src/pages/iou/request/IOURequestStartPage.tsx b/src/pages/iou/request/IOURequestStartPage.tsx index 8d64598ed838..f807038d9cd1 100644 --- a/src/pages/iou/request/IOURequestStartPage.tsx +++ b/src/pages/iou/request/IOURequestStartPage.tsx @@ -1,4 +1,4 @@ -import {useFocusEffect, useNavigation} from '@react-navigation/native'; +import {useFocusEffect} from '@react-navigation/native'; import React, {useCallback, useEffect, useRef, useState} from 'react'; import {View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; @@ -11,7 +11,6 @@ import ScreenWrapper from '@components/ScreenWrapper'; import TabSelector from '@components/TabSelector/TabSelector'; import useLocalize from '@hooks/useLocalize'; import usePermissions from '@hooks/usePermissions'; -import usePrevious from '@hooks/usePrevious'; import useThemeStyles from '@hooks/useThemeStyles'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import * as IOUUtils from '@libs/IOUUtils'; @@ -60,7 +59,6 @@ function IOURequestStartPage({ }: IOURequestStartPageProps) { const styles = useThemeStyles(); const {translate} = useLocalize(); - const navigation = useNavigation(); const [isDraggingOver, setIsDraggingOver] = useState(false); const tabTitles = { [CONST.IOU.TYPE.REQUEST]: translate('iou.submitExpense'), @@ -69,7 +67,6 @@ function IOURequestStartPage({ [CONST.IOU.TYPE.TRACK_EXPENSE]: translate('iou.trackExpense'), }; const transactionRequestType = useRef(TransactionUtils.getRequestType(transaction)); - const previousIOURequestType = usePrevious(transactionRequestType.current); const {canUseP2PDistanceRequests} = usePermissions(iouType); const isFromGlobalCreate = isEmptyObject(report?.reportID); @@ -98,7 +95,7 @@ function IOURequestStartPage({ const isExpenseChat = ReportUtils.isPolicyExpenseChat(report); const isExpenseReport = ReportUtils.isExpenseReport(report); - const shouldDisplayDistanceRequest = !!canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || isFromGlobalCreate; + const shouldDisplayDistanceRequest = (!!canUseP2PDistanceRequests || isExpenseChat || isExpenseReport || isFromGlobalCreate) && iouType !== CONST.IOU.TYPE.SPLIT; // Allow the user to submit the expense if we are submitting the expense in global menu or the report can create the exoense const isAllowedToCreateRequest = isEmptyObject(report?.reportID) || ReportUtils.canCreateRequest(report, policy, iouType); @@ -108,17 +105,10 @@ function IOURequestStartPage({ }; const resetIOUTypeIfChanged = useCallback( - (newIouType: IOURequestType) => { - if (newIouType === previousIOURequestType) { - return; - } - if (iouType === CONST.IOU.TYPE.SPLIT && transaction?.isFromGlobalCreate) { - IOU.updateMoneyRequestTypeParams(navigation.getState()?.routes ?? [], CONST.IOU.TYPE.REQUEST, newIouType); - } - IOU.initMoneyRequest(reportID, policy, isFromGlobalCreate, newIouType); - transactionRequestType.current = newIouType; + (newIOUType: IOURequestType) => { + IOU.initMoneyRequest(reportID, policy, isFromGlobalCreate, newIOUType); }, - [policy, previousIOURequestType, reportID, isFromGlobalCreate, iouType, navigation, transaction?.isFromGlobalCreate], + [policy, reportID, isFromGlobalCreate], ); if (!transaction?.transactionID) { diff --git a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js index 9cfa4ba2ac5a..efd98d935a01 100644 --- a/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js +++ b/src/pages/iou/request/MoneyTemporaryForRefactorRequestParticipantsSelector.js @@ -16,11 +16,9 @@ import * as Illustrations from '@components/Icon/Illustrations'; import OfflineIndicator from '@components/OfflineIndicator'; import {usePersonalDetails} from '@components/OnyxProvider'; import {useOptionsList} from '@components/OptionListContextProvider'; -import {PressableWithFeedback} from '@components/Pressable'; import ReferralProgramCTA from '@components/ReferralProgramCTA'; -import SelectCircle from '@components/SelectCircle'; import SelectionList from '@components/SelectionList'; -import UserListItem from '@components/SelectionList/UserListItem'; +import InviteMemberListItem from '@components/SelectionList/InviteMemberListItem'; import useDebouncedState from '@hooks/useDebouncedState'; import useDismissedReferralBanners from '@hooks/useDismissedReferralBanners'; import useLocalize from '@hooks/useLocalize'; @@ -91,6 +89,8 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF const maxParticipantsReached = participants.length === CONST.REPORT.MAXIMUM_PARTICIPANTS; const {isSmallScreenWidth} = useWindowDimensions(); + const isIOUSplit = iouType === CONST.IOU.TYPE.SPLIT; + useEffect(() => { Report.searchInServer(debouncedSearchTerm.trim()); }, [debouncedSearchTerm]); @@ -115,7 +115,7 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF // If we are using this component in the "Submit expense" flow then we pass the includeOwnedWorkspaceChats argument so that the current user // sees the option to submit an expense from their admin on their own Workspace Chat. - iouType === CONST.IOU.TYPE.REQUEST && action !== CONST.IOU.ACTION.REQUEST, + (iouType === CONST.IOU.TYPE.REQUEST || iouType === CONST.IOU.TYPE.SPLIT) && action !== CONST.IOU.ACTION.REQUEST, (canUseP2PDistanceRequests || iouRequestType !== CONST.IOU.REQUEST_TYPE.DISTANCE) && ![CONST.IOU.ACTION.CATEGORIZE, CONST.IOU.ACTION.SHARE].includes(action), false, @@ -328,40 +328,6 @@ function MoneyTemporaryForRefactorRequestParticipantsSelector({participants, onF ); }, [handleConfirmSelection, participants.length, isDismissed, referralContentType, shouldShowSplitBillErrorMessage, styles, translate]); - const itemRightSideComponent = useCallback( - (item) => { - if (!isAllowedToSplit) { - return null; - } - if (item.isSelected) { - return ( - addParticipantToSelection(item)} - disabled={item.isDisabled} - role={CONST.ACCESSIBILITY_ROLE.CHECKBOX} - accessibilityLabel={CONST.ACCESSIBILITY_ROLE.CHECKBOX} - style={[styles.flexRow, styles.alignItemsCenter, styles.ml5, styles.optionSelectCircle]} - > - - - ); - } - - return ( -