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
9 changes: 7 additions & 2 deletions src/components/DestinationPicker.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React, {useMemo} from 'react';
import type {ForwardedRef} from 'react';
import useDebouncedState from '@hooks/useDebouncedState';
import useLocalize from '@hooks/useLocalize';
import useOnyx from '@hooks/useOnyx';
Expand All @@ -12,15 +13,17 @@ import ONYXKEYS from '@src/ONYXKEYS';
// eslint-disable-next-line no-restricted-imports
import SelectionList from './SelectionListWithSections';
import RadioListItem from './SelectionListWithSections/RadioListItem';
import type {ListItem} from './SelectionListWithSections/types';
import type {ListItem, SelectionListHandle} from './SelectionListWithSections/types';

type DestinationPickerProps = {
policyID: string;
selectedDestination?: string;
onSubmit: (item: ListItem & {currency: string}) => void;
ref?: ForwardedRef<SelectionListHandle>;
textInputAutoFocus?: boolean;
};

function DestinationPicker({selectedDestination, policyID, onSubmit}: DestinationPickerProps) {
function DestinationPicker({selectedDestination, policyID, onSubmit, ref, textInputAutoFocus = false}: DestinationPickerProps) {
const policy = usePolicy(policyID);
const customUnit = getPerDiemCustomUnit(policy);
const [policyRecentlyUsedDestinations] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_RECENTLY_USED_DESTINATIONS}${policyID}`, {canBeMissing: true});
Expand Down Expand Up @@ -71,6 +74,7 @@ function DestinationPicker({selectedDestination, policyID, onSubmit}: Destinatio

return (
<SelectionList
ref={ref}
key={selectedDestination}
sections={sections}
headerMessage={headerMessage}
Expand All @@ -81,6 +85,7 @@ function DestinationPicker({selectedDestination, policyID, onSubmit}: Destinatio
ListItem={RadioListItem}
initiallyFocusedOptionKey={selectedOptionKey ?? undefined}
shouldHideKeyboardOnScroll={false}
textInputAutoFocus={textInputAutoFocus}
shouldUpdateFocusedIndex
/>
);
Expand Down
22 changes: 21 additions & 1 deletion src/pages/iou/request/IOURequestStartPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import DragAndDropProvider from '@components/DragAndDrop/Provider';
import FocusTrapContainerElement from '@components/FocusTrap/FocusTrapContainerElement';
import HeaderWithBackButton from '@components/HeaderWithBackButton';
import {useProductTrainingContext} from '@components/ProductTrainingContext';
import type {AnimatedTextInputRef} from '@components/RNTextInput';
import ScreenWrapper from '@components/ScreenWrapper';
import TabSelector from '@components/TabSelector/TabSelector';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
Expand Down Expand Up @@ -87,6 +88,8 @@ function IOURequestStartPage({
const [nvpDismissedProductTraining] = useOnyx(ONYXKEYS.NVP_DISMISSED_PRODUCT_TRAINING, {canBeMissing: true});
const hasOnlyPersonalPolicies = useMemo(() => hasOnlyPersonalPoliciesUtil(allPolicies), [allPolicies]);

const perDiemInputRef = useRef<AnimatedTextInputRef | null>(null);

const tabTitles = {
[CONST.IOU.TYPE.REQUEST]: translate('iou.createExpense'),
[CONST.IOU.TYPE.SUBMIT]: translate('iou.createExpense'),
Expand All @@ -99,6 +102,18 @@ function IOURequestStartPage({
[CONST.IOU.TYPE.CREATE]: translate('iou.createExpense'),
};

const onTabSelectFocusHandler = ({index}: {index: number}) => {
// We requestAnimationFrame since the function is called in the animate block in the web implementation
// which fixes a locked animation glitch when swiping between tabs, and aligns with the native implementation internal delay
requestAnimationFrame(() => {
// 2 - PerDiem
if (index !== 2) {
return;
}
perDiemInputRef.current?.focus?.();
});
};

const isFromGlobalCreate = isEmptyObject(report?.reportID);
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
const policiesWithPerDiemEnabledAndHasRates = useMemo(
Expand Down Expand Up @@ -241,6 +256,8 @@ function IOURequestStartPage({

useHandleBackButton(onBackButtonPress);

const shouldShowWorkspaceSelectForPerDiem = moreThanOnePerDiemExist && !hasCurrentPolicyPerDiemEnabled;

return (
<AccessOrNotFoundWrapper
reportID={reportID}
Expand Down Expand Up @@ -273,6 +290,7 @@ function IOURequestStartPage({
id={CONST.TAB.IOU_REQUEST_TYPE}
defaultSelectedTab={defaultSelectedTab}
onTabSelected={onTabSelected}
onTabSelect={onTabSelectFocusHandler}
tabBar={TabSelector}
onTabBarFocusTrapContainerElementChanged={setTabBarContainerElement}
onActiveTabFocusTrapContainerElementChanged={setActiveTabContainerElement}
Expand Down Expand Up @@ -324,14 +342,16 @@ function IOURequestStartPage({
<TopTab.Screen name={CONST.TAB_REQUEST.PER_DIEM}>
{() => (
<TabScreenWithFocusTrapWrapper>
{moreThanOnePerDiemExist && !hasCurrentPolicyPerDiemEnabled ? (
{shouldShowWorkspaceSelectForPerDiem ? (
<IOURequestStepPerDiemWorkspace
route={route}
navigation={navigation}
/>
) : (
<IOURequestStepDestination
shouldAutoFocusInput={false}
openedFromStartPage
ref={perDiemInputRef}
explicitPolicyID={moreThanOnePerDiemExist ? undefined : policiesWithPerDiemEnabledAndHasRates.at(0)?.id}
route={route}
navigation={navigation}
Expand Down
21 changes: 19 additions & 2 deletions src/pages/iou/request/step/IOURequestStepDestination.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React, {useEffect} from 'react';
import React, {useEffect, useImperativeHandle, useRef} from 'react';
import type {ForwardedRef} from 'react';
import {InteractionManager, View} from 'react-native';
import ActivityIndicator from '@components/ActivityIndicator';
import FullPageOfflineBlockingView from '@components/BlockingViews/FullPageOfflineBlockingView';
import Button from '@components/Button';
import DestinationPicker from '@components/DestinationPicker';
import FixedFooter from '@components/FixedFooter';
import ScreenWrapper from '@components/ScreenWrapper';
import type {ListItem} from '@components/SelectionListWithSections/types';
import type {ListItem, SelectionListHandle} from '@components/SelectionListWithSections/types';
import WorkspaceEmptyStateSection from '@components/WorkspaceEmptyStateSection';
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
import {useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
Expand Down Expand Up @@ -43,10 +44,16 @@ import withFullTransactionOrNotFound from './withFullTransactionOrNotFound';
import type {WithWritableReportOrNotFoundProps} from './withWritableReportOrNotFound';
import withWritableReportOrNotFound from './withWritableReportOrNotFound';

type IOURequestStepDestinationRef = {
focus?: () => void;
};

type IOURequestStepDestinationProps = WithWritableReportOrNotFoundProps<typeof SCREENS.MONEY_REQUEST.STEP_DESTINATION | typeof SCREENS.MONEY_REQUEST.CREATE> &
WithFullTransactionOrNotFoundProps<typeof SCREENS.MONEY_REQUEST.STEP_DESTINATION | typeof SCREENS.MONEY_REQUEST.CREATE> & {
openedFromStartPage?: boolean;
explicitPolicyID?: string;
shouldAutoFocusInput?: boolean;
ref: ForwardedRef<IOURequestStepDestinationRef>;
};

function IOURequestStepDestination({
Expand All @@ -57,6 +64,8 @@ function IOURequestStepDestination({
transaction,
openedFromStartPage = false,
explicitPolicyID,
shouldAutoFocusInput = true,
ref,
}: IOURequestStepDestinationProps) {
const [policy, policyMetadata] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${explicitPolicyID ?? getIOURequestPolicyID(transaction, report)}`, {canBeMissing: false});
const personalPolicy = usePersonalPolicy();
Expand All @@ -70,6 +79,12 @@ function IOURequestStepDestination({
const illustrations = useMemoizedLazyIllustrations(['EmptyStateExpenses']);
const {translate} = useLocalize();

const destinationSelectionListRef = useRef<SelectionListHandle | null>(null);

useImperativeHandle(ref, () => ({
focus: destinationSelectionListRef.current?.focusTextInput,
}));

// eslint-disable-next-line rulesdir/no-negated-variables
const shouldShowNotFoundPage = isEmptyObject(policy);

Expand Down Expand Up @@ -196,6 +211,8 @@ function IOURequestStepDestination({
)}
{!shouldShowEmptyState && !isLoading && !shouldShowOfflineView && !!policy?.id && (
<DestinationPicker
textInputAutoFocus={shouldAutoFocusInput}
ref={destinationSelectionListRef}
selectedDestination={selectedDestination}
policyID={policy.id}
onSubmit={updateDestination}
Expand Down
Loading