Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 21 additions & 21 deletions src/libs/NavigationFocusReturn.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import {findFocusedRoute} from '@react-navigation/core';
import type {NavigationState, PartialState} from '@react-navigation/native';
// eslint-disable-next-line no-restricted-imports -- idiomatic defer primitive past navigation transitions.
import {InteractionManager} from 'react-native';
import compoundParamsKey, {COMPOUND_KEY_DELIMITER} from './compoundParamsKey';
import FOCUSABLE_SELECTOR from './focusableSelector';
import hasFocusableAttributes from './focusGuards';
import getHadTabNavigation from './hadTabNavigation';
import {consumeLauncher, pickLauncher, resetLauncherStackForTests} from './LauncherStack';
import navigationRef from './Navigation/navigationRef';
import TransitionTracker from './Navigation/TransitionTracker';
import {isCycleIdle, Priorities, resetCycle, tryClaim} from './ScreenFocusArbiter';

/** focusin tracks the last keyboard-focused element; a nav state listener captures it against the outgoing route and restores it on backward nav. */
Expand Down Expand Up @@ -318,28 +317,29 @@ function scheduleRestore(routeKey: string): void {

const attempt = () => {
// Defer past the transition so useAutoFocusInput and React Navigation's own focus work settle first.
// eslint-disable-next-line @typescript-eslint/no-deprecated -- idiomatic defer primitive despite type-def deprecation.
imHandle = InteractionManager.runAfterInteractions(() => {
if (cancelled) {
return;
}
frameId = requestAnimationFrame(() => {
imHandle = TransitionTracker.runAfterTransitions({
callback: () => {
if (cancelled) {
return;
}
attempts += 1;
const restored = restoreTriggerForRoute(routeKey);
if (restored || !triggerMap.has(routeKey)) {
pendingRestore = null;
return;
}
if (attempts >= MAX_RESTORE_ATTEMPTS) {
triggerMap.delete(routeKey);
pendingRestore = null;
return;
}
retryTimerId = setTimeout(attempt, RESTORE_RETRY_MS);
});
frameId = requestAnimationFrame(() => {
if (cancelled) {
return;
}
attempts += 1;
const restored = restoreTriggerForRoute(routeKey);
if (restored || !triggerMap.has(routeKey)) {
pendingRestore = null;
return;
}
if (attempts >= MAX_RESTORE_ATTEMPTS) {
triggerMap.delete(routeKey);
pendingRestore = null;
return;
}
retryTimerId = setTimeout(attempt, RESTORE_RETRY_MS);
});
},
});
};

Expand Down
254 changes: 128 additions & 126 deletions src/libs/actions/replaceOptimisticReportWithActualReport.ts

Large diffs are not rendered by default.

8 changes: 3 additions & 5 deletions src/pages/DynamicReportDetailsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@ import {delegateEmailSelector} from '@selectors/Account';
import {validTransactionDraftIDsSelector} from '@selectors/TransactionDraft';
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import type {StyleProp, ViewStyle} from 'react-native';
// eslint-disable-next-line no-restricted-imports
import {InteractionManager, View} from 'react-native';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import type {ValueOf} from 'type-fest';
import AvatarWithImagePicker from '@components/AvatarWithImagePicker';
Expand Down Expand Up @@ -50,6 +49,7 @@ import createDynamicRoute from '@libs/Navigation/helpers/dynamicRoutesUtils/crea
import isReportTopmostSplitNavigator from '@libs/Navigation/helpers/isReportTopmostSplitNavigator';
import Navigation, {navigationRef} from '@libs/Navigation/Navigation';
import type {PlatformStackScreenProps} from '@libs/Navigation/PlatformStackNavigation/types';
import TransitionTracker from '@libs/Navigation/TransitionTracker';
import type {ReportDetailsNavigatorParamList, RightModalNavigatorParamList} from '@libs/Navigation/types';
import Parser from '@libs/Parser';
import Permissions from '@libs/Permissions';
Expand Down Expand Up @@ -1086,9 +1086,7 @@ function DynamicReportDetailsPage({policy, report, route, reportMetadata, report
navigateToTargetUrl();
// Delay deletion until the RHP close animation finishes to prevent a brief
// "Not Found" flash inside the animating-out panel on slower devices.
InteractionManager.runAfterInteractions(() => {
deleteTransaction();
});
TransitionTracker.runAfterTransitions({callback: deleteTransaction, waitForUpcomingTransition: true});
});
}, [showConfirmModal, translate, caseID, iouTransactionID, shouldOpenSplitExpenseEditFlowOnDelete, navigateToTargetUrl, deleteTransaction]);

Expand Down
8 changes: 1 addition & 7 deletions src/pages/Search/SearchTransactionsChangeReport.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import React, {useEffect, useMemo} from 'react';
// eslint-disable-next-line no-restricted-imports
import {InteractionManager} from 'react-native';
import Onyx from 'react-native-onyx';
import {usePersonalDetails, useSession} from '@components/OnyxListItemProvider';
import {useSearchResultsContext, useSearchSelectionActions, useSearchSelectionContext} from '@components/Search/SearchContext';
Expand Down Expand Up @@ -235,11 +233,7 @@ function SearchTransactionsChangeReport() {
allTransactionViolation: transactionViolations,
allReports,
});
InteractionManager.runAfterInteractions(() => {
clearSelectedTransactions();
});

Navigation.goBack();
Navigation.goBack(undefined, {afterTransition: clearSelectedTransactions});
};

const removeFromReport = () => {
Expand Down
15 changes: 9 additions & 6 deletions src/pages/inbox/ReportFetchHandler.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import {useIsFocused, useNavigation, useRoute} from '@react-navigation/native';
import {useEffect, useEffectEvent, useRef} from 'react';
// eslint-disable-next-line no-restricted-imports
import {InteractionManager} from 'react-native';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import useIsAnonymousUser from '@hooks/useIsAnonymousUser';
import useIsInSidePanel from '@hooks/useIsInSidePanel';
Expand All @@ -16,6 +14,8 @@ import useResponsiveLayout from '@hooks/useResponsiveLayout';
import getNonEmptyStringOnyxID from '@libs/getNonEmptyStringOnyxID';
import {getAllNonDeletedTransactions} from '@libs/MoneyRequestReportUtils';
import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types';
import TransitionTracker from '@libs/Navigation/TransitionTracker';
import type {CancelHandle} from '@libs/Navigation/TransitionTracker';
import {getFilteredReportActionsForReportView, getIOUActionForReportID, getOneTransactionThreadReportID, isCreatedAction} from '@libs/ReportActionsUtils';
import {
isChatThread,
Expand Down Expand Up @@ -335,11 +335,14 @@ function ReportFetchHandler() {
// any `pendingFields.createChat` or `pendingFields.addWorkspaceRoom` fields are set to null.
// Existing reports created will have empty fields for `pendingFields`.
const didCreateReportSuccessfully = !report?.pendingFields || (!report?.pendingFields.addWorkspaceRoom && !report?.pendingFields.createChat);
let interactionTask: ReturnType<typeof InteractionManager.runAfterInteractions> | null = null;
let interactionTask: CancelHandle | null = null;
if (!didSubscribeToReportLeavingEvents.current && didCreateReportSuccessfully) {
interactionTask = InteractionManager.runAfterInteractions(() => {
subscribeToReportLeavingEvents(reportIDFromRoute, currentUserAccountID);
didSubscribeToReportLeavingEvents.current = true;
interactionTask = TransitionTracker.runAfterTransitions({
callback: () => {
subscribeToReportLeavingEvents(reportIDFromRoute, currentUserAccountID);
didSubscribeToReportLeavingEvents.current = true;
},
waitForUpcomingTransition: true,
Comment thread
collectioneur marked this conversation as resolved.
});
}
return () => {
Expand Down
85 changes: 43 additions & 42 deletions src/pages/inbox/report/useReportActionsNewActionLiveTail.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import {useCallback, useEffect, useEffectEvent, useRef, useState} from 'react';
// eslint-disable-next-line no-restricted-imports
import {InteractionManager} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import type useReportScrollManager from '@hooks/useReportScrollManager';
import type {OpenReportActionParams} from '@libs/actions/Report';
import {openReport, pruneReportActionPagesToNewestWindow, subscribeToNewActionEvent} from '@libs/actions/Report';
import isReportTopmostSplitNavigator from '@libs/Navigation/helpers/isReportTopmostSplitNavigator';
import Navigation from '@libs/Navigation/Navigation';
import TransitionTracker from '@libs/Navigation/TransitionTracker';
import CONST from '@src/CONST';
import ROUTES from '@src/ROUTES';
import type * as OnyxTypes from '@src/types/onyx';
Expand Down Expand Up @@ -76,56 +75,58 @@ function useReportActionsNewActionLiveTail({
const [isScrollToBottomEnabled, setIsScrollToBottomEnabled] = useState(false);

const scrollToBottomForCurrentUserAction = useEffectEvent((isFromCurrentUser: boolean, action?: OnyxTypes.ReportAction) => {
InteractionManager.runAfterInteractions(() => {
// If a new comment is added and it's from the current user scroll to the bottom otherwise leave the user positioned where
// they are now in the list.
if (!isFromCurrentUser || (!isReportTopmostSplitNavigator() && !Navigation.getReportRHPActiveRoute())) {
return;
}
if (!hasNewestReportAction && !isFromCurrentUser) {
if (Navigation.getReportRHPActiveRoute()) {
TransitionTracker.runAfterTransitions({
callback: () => {
// If a new comment is added and it's from the current user scroll to the bottom otherwise leave the user positioned where
// they are now in the list.
if (!isFromCurrentUser || (!isReportTopmostSplitNavigator() && !Navigation.getReportRHPActiveRoute())) {
return;
}
Navigation.setNavigationActionToMicrotaskQueue(() => {
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(reportID));
});
return;
}

const shouldJumpToLiveTail =
!isOffline && action?.actionName !== CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && (hasNewerActions || !!linkedReportActionID || unreadMarkerReportActionID);

if (shouldJumpToLiveTail) {
if (liveTailJumpRef.current.stage === 'idle') {
liveTailJumpRef.current = {stage: 'open_report'};
openReport({
reportID,
introSelected,
betas,
if (!hasNewestReportAction && !isFromCurrentUser) {
if (Navigation.getReportRHPActiveRoute()) {
return;
}
Navigation.setNavigationActionToMicrotaskQueue(() => {
Navigation.navigate(ROUTES.REPORT_WITH_ID.getRoute(reportID));
});
return;
}

const shouldJumpToLiveTail =
!isOffline && action?.actionName !== CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW && (hasNewerActions || !!linkedReportActionID || unreadMarkerReportActionID);

if (shouldJumpToLiveTail) {
if (liveTailJumpRef.current.stage === 'idle') {
liveTailJumpRef.current = {stage: 'open_report'};
openReport({
reportID,
introSelected,
betas,
});
}
return;
}
return;
}

const index = sortedVisibleReportActions.findIndex((item) => item.reportActionID === action?.reportActionID);
if (action?.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW) {
if (index > 0) {
setTimeout(() => {
reportScrollManager.scrollToIndex(index);
}, 100);
const index = sortedVisibleReportActions.findIndex((item) => item.reportActionID === action?.reportActionID);
if (action?.actionName === CONST.REPORT.ACTIONS.TYPE.REPORT_PREVIEW) {
if (index > 0) {
setTimeout(() => {
reportScrollManager.scrollToIndex(index);
}, 100);
} else {
setIsFloatingMessageCounterVisible(false);
reportScrollManager.scrollToBottom();
}
if (action?.reportActionID) {
setActionIdToHighlight(action.reportActionID);
}
} else {
setIsFloatingMessageCounterVisible(false);
reportScrollManager.scrollToBottom();
}
if (action?.reportActionID) {
setActionIdToHighlight(action.reportActionID);
}
} else {
setIsFloatingMessageCounterVisible(false);
reportScrollManager.scrollToBottom();
}

setIsScrollToBottomEnabled(true);
setIsScrollToBottomEnabled(true);
},
});
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {useIsFocused} from '@react-navigation/native';
import lodashIsEmpty from 'lodash/isEmpty';
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
// eslint-disable-next-line no-restricted-imports
import {InteractionManager, View} from 'react-native';
import {View} from 'react-native';
import type {OnyxEntry} from 'react-native-onyx';
import Button from '@components/Button';
import FormHelpMessage from '@components/FormHelpMessage';
Expand Down Expand Up @@ -513,9 +512,7 @@ function IOURequestStepDistanceOdometer({
};

const restoreLastInputFocus = useCallback(() => {
InteractionManager.runAfterInteractions(() => {
lastFocusedInputRef.current?.focus();
});
lastFocusedInputRef.current?.focus();
}, []);

useDiscardChangesConfirmation({
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
// eslint-disable-next-line no-restricted-imports
import {InteractionManager} from 'react-native';
import type {OnyxCollection, OnyxEntry} from 'react-native-onyx';
import {useSearchSelectionActions} from '@components/Search/SearchContext';
import type {ListItem} from '@components/SelectionList/types';
Expand All @@ -9,6 +7,7 @@ import {setCustomUnitID, setCustomUnitRateID} from '@libs/actions/IOU/MoneyReque
import {clearSubrates} from '@libs/actions/IOU/PerDiem';
import {changeTransactionsReport, setTransactionReport} from '@libs/actions/Transaction';
import Navigation from '@libs/Navigation/Navigation';
import TransitionTracker from '@libs/Navigation/TransitionTracker';
import {getPerDiemCustomUnit} from '@libs/PolicyUtils';
import {getReportOrDraftReport} from '@libs/ReportUtils';
import CONST from '@src/CONST';
Expand Down Expand Up @@ -177,8 +176,9 @@ function useReportSelectionActions({
}

handleGoBack();
InteractionManager.runAfterInteractions(() => {
Navigation.setNavigationActionToMicrotaskQueue(() => {
TransitionTracker.runAfterTransitions({
waitForUpcomingTransition: true,
callback: () => {
const participants = buildParticipants(report);

setTransactionReport(
Expand Down Expand Up @@ -208,29 +208,30 @@ function useReportSelectionActions({
});
removeTransaction(transaction.transactionID);
}
});
},
});
};

const removeFromReport = () => {
if (!transaction) {
return;
}
Navigation.dismissToSuperWideRHP();
InteractionManager.runAfterInteractions(() => {
const policyTagList = personalPolicyID ? allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${personalPolicyID}`] : {};
changeTransactionsReport({
transactionIDs: targetTransactionIDs,
isASAPSubmitBetaEnabled,
accountID: session?.accountID ?? CONST.DEFAULT_NUMBER_ID,
email: session?.email ?? '',
policy: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${personalPolicyID}`],
policyTagList,
transactions: targetTransactions,
allTransactionViolation: transactionViolations,
allReports,
});
removeTransaction(transaction.transactionID);
Navigation.dismissToSuperWideRHP({
afterTransition: () => {
const policyTagList = personalPolicyID ? allPolicyTags?.[`${ONYXKEYS.COLLECTION.POLICY_TAGS}${personalPolicyID}`] : {};
changeTransactionsReport({
transactionIDs: targetTransactionIDs,
isASAPSubmitBetaEnabled,
accountID: session?.accountID ?? CONST.DEFAULT_NUMBER_ID,
email: session?.email ?? '',
policy: allPolicies?.[`${ONYXKEYS.COLLECTION.POLICY}${personalPolicyID}`],
policyTagList,
transactions: targetTransactions,
allTransactionViolation: transactionViolations,
allReports,
});
removeTransaction(transaction.transactionID);
},
});
};

Expand Down
12 changes: 8 additions & 4 deletions src/pages/settings/Security/MergeAccounts/MergeResultPage.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import {useRoute} from '@react-navigation/native';
import {emailSelector} from '@selectors/Session';
import React, {useEffect, useMemo} from 'react';
// eslint-disable-next-line no-restricted-imports
import {InteractionManager, View} from 'react-native';
import {View} from 'react-native';
import type {ValueOf} from 'type-fest';
import ConfirmationPage from '@components/ConfirmationPage';
import type {ConfirmationPageProps} from '@components/ConfirmationPage';
Expand All @@ -18,6 +17,7 @@ import useOnyx from '@hooks/useOnyx';
import useThemeStyles from '@hooks/useThemeStyles';
import Navigation from '@libs/Navigation/Navigation';
import type {PlatformStackRouteProp} from '@libs/Navigation/PlatformStackNavigation/types';
import TransitionTracker from '@libs/Navigation/TransitionTracker';
import type {SettingsNavigatorParamList} from '@libs/Navigation/types';
import {shouldHideOldAppRedirect} from '@libs/TryNewDotUtils';
import {closeReactNativeApp} from '@userActions/HybridApp';
Expand Down Expand Up @@ -197,9 +197,13 @@ function MergeResultPage() {
return;
}

InteractionManager.runAfterInteractions(() => {
Navigation.removeScreenFromNavigationState(SCREENS.SETTINGS.MERGE_ACCOUNTS.ACCOUNT_DETAILS);
const handle = TransitionTracker.runAfterTransitions({
callback: () => {
Navigation.removeScreenFromNavigationState(SCREENS.SETTINGS.MERGE_ACCOUNTS.ACCOUNT_DETAILS);
},
waitForUpcomingTransition: true,
});
return () => handle.cancel();
}, [result]);

const {
Expand Down
Loading
Loading