Skip to content

Commit f12e4b0

Browse files
authored
Merge pull request #95115 from hungvu193/fix-blocker-email-submitter
[Submit] Fix blockers for submit email submitter
2 parents bdbd914 + 8a07b85 commit f12e4b0

10 files changed

Lines changed: 132 additions & 93 deletions

src/components/CollapsibleHeaderOnKeyboard/index.native.tsx

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@ import React, {useEffect, useRef} from 'react';
33
import type {LayoutChangeEvent} from 'react-native';
44
import {useReanimatedKeyboardAnimation} from 'react-native-keyboard-controller';
55
import Reanimated, {Easing, useAnimatedReaction, useAnimatedStyle, useSharedValue, withTiming} from 'react-native-reanimated';
6-
import useDebounce from '@hooks/useDebounce';
76
import usePrevious from '@hooks/usePrevious';
87
import useWindowDimensions from '@hooks/useWindowDimensions';
98
import isInLandscapeModeUtil from '@libs/isInLandscapeMode';
109
import type {CollapsibleHeaderOnKeyboardProps} from './types';
1110

1211
const COLLAPSE_DURATION = 100;
1312
const RESTORE_DURATION = 300;
14-
const HEADER_CONTENT_LAYOUT_STABILIZATION_TIME_ON_ORIENTATION_CHANGE = 300;
1513
// Assumed vertical space for the focused input field — used to reserve space above the keyboard.
1614
const VERTICAL_SPACE_FOR_FOCUSED_INPUT = 120;
1715
const KEYBOARD_OPENING_PROGRESS_THRESHOLDS = [0.5, 0.7, 0.8, 0.85, 0.9, 0.95, 0.99];
@@ -62,30 +60,19 @@ function CollapsibleHeaderOnKeyboard({children, collapsibleHeaderOffset = 0}: Co
6260
isInLandscapeModeSV.set(isInLandscapeMode);
6361
}, [isInLandscapeMode, isInLandscapeModeSV]);
6462

65-
const debouncedUpdateNaturalHeightRef = useDebounce((height: number) => {
66-
if (isInLandscapeMode) {
67-
return;
68-
}
69-
naturalHeightRef.current = height;
70-
}, HEADER_CONTENT_LAYOUT_STABILIZATION_TIME_ON_ORIENTATION_CHANGE);
71-
7263
const onLayout = (e: LayoutChangeEvent) => {
7364
const height = e.nativeEvent.layout.height;
7465

7566
if (height <= 0) {
7667
return;
7768
}
7869

79-
// Header re-measured in portrait mode and the content is taller than the previous measurement,
80-
// set the natural height to the new height and animate the height to the new height.
81-
// This can happen when header has different height on portrait and landscape mode.
82-
if (!isInLandscapeMode && height > naturalHeightRef.current && naturalHeightRef.current !== -1) {
70+
// Portrait: always sync height immediately in both directions (e.g. next step appears, or
71+
// Submit shows beside More after Retract). Avoids stale clipped height from the collapse animation.
72+
if (!isInLandscapeMode) {
73+
naturalHeightRef.current = height;
8374
naturalHeight.set(height);
84-
animatedHeight.set(withTiming(height, {duration: RESTORE_DURATION}));
85-
86-
// we need to debounce the update of the natural height ref to make sure we don't block height updates
87-
// on landscape -> portrait mode change where header layout might not be stable at first.
88-
debouncedUpdateNaturalHeightRef(height);
75+
animatedHeight.set(height);
8976
return;
9077
}
9178

@@ -188,13 +175,16 @@ function CollapsibleHeaderOnKeyboard({children, collapsibleHeaderOffset = 0}: Co
188175
return {height: animatedHeight.get(), overflow: 'hidden'};
189176
});
190177

191-
// Inner wrapper slides the content upward. translateY = animatedHeight - naturalHeight,
192-
// so it goes from 0 (fully open) to -naturalHeight (fully collapsed), making the header
193-
// appear to exit through the top while the outer clip hides it progressively.
178+
// Inner wrapper slides the content upward during landscape keyboard collapse only.
194179
const innerStyle = useAnimatedStyle(() => {
195180
if (animatedHeight.get() >= naturalHeight.get()) {
196181
return {};
197182
}
183+
184+
if (!isInLandscapeModeSV.get()) {
185+
return {};
186+
}
187+
198188
return {transform: [{translateY: animatedHeight.get() - naturalHeight.get()}]};
199189
});
200190

src/components/MoneyReportHeader.tsx

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -138,17 +138,14 @@ function MoneyReportHeaderContent({reportID: reportIDProp, shouldDisplayBackButt
138138
)}
139139
</HeaderWithBackButton>
140140
{!shouldShowHeaderButtonsInHeaderRow && (
141-
<View style={[styles.w100, styles.flexColumn]}>
142-
<MoneyReportHeaderActions
143-
reportID={reportIDProp}
144-
primaryAction={primaryAction}
145-
isReportInSearch={isReportInSearch}
146-
backTo={backTo}
147-
/>
148-
<MoneyReportHeaderMoreContent reportID={reportIDProp} />
149-
</View>
141+
<MoneyReportHeaderActions
142+
reportID={reportIDProp}
143+
primaryAction={primaryAction}
144+
isReportInSearch={isReportInSearch}
145+
backTo={backTo}
146+
/>
150147
)}
151-
{shouldShowHeaderButtonsInHeaderRow && <MoneyReportHeaderMoreContent reportID={reportIDProp} />}
148+
<MoneyReportHeaderMoreContent reportID={reportIDProp} />
152149
<HeaderLoadingBar />
153150
</View>
154151
);

src/components/MoneyReportHeaderActions/MoneyReportHeaderSecondaryActions.tsx

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -443,7 +443,7 @@ function MoneyReportHeaderSecondaryActionsPlaceholder({primaryAction}: {primaryA
443443
const {shouldUseNarrowLayout, isMediumScreenWidth, isInLandscapeMode} = useResponsiveLayout();
444444
const icons = useMemoizedLazyExpensifyIcons(['DownArrow']);
445445
const shouldTakeRemainingWidth = (shouldUseNarrowLayout || isMediumScreenWidth) && !primaryAction && !isInLandscapeMode;
446-
const wrapperStyle = shouldTakeRemainingWidth ? [styles.flex1, styles.w100, styles.mnw0] : undefined;
446+
const wrapperStyle = shouldTakeRemainingWidth ? [styles.w100, styles.mnw0] : undefined;
447447
// Match the inner styles the real ButtonWithDropdownMenu applies when isSplitButton=false so text placement stays put on swap.
448448
const innerStyles = [StyleUtils.getDropDownButtonHeight(CONST.DROPDOWN_BUTTON_SIZE.MEDIUM), styles.dropDownButtonCartIconView];
449449
return (
@@ -464,31 +464,28 @@ function MoneyReportHeaderSecondaryActions({reportID, primaryAction, isReportInS
464464
const styles = useThemeStyles();
465465
const {shouldUseNarrowLayout, isMediumScreenWidth, isInLandscapeMode} = useResponsiveLayout();
466466
const shouldTakeRemainingWidth = (shouldUseNarrowLayout || isMediumScreenWidth) && !primaryAction && !isInLandscapeMode;
467-
const layoutWrapperStyle = shouldTakeRemainingWidth ? [styles.flex1, styles.w100, styles.mnw0] : undefined;
468467

469468
return (
470-
<View style={layoutWrapperStyle}>
471-
<ReportSubmitToPopoverAnchor
472-
reportID={reportID}
473-
wrapperStyle={styles.w100}
474-
anchorAlignment={MORE_MENU_SUBMIT_TO_POPOVER_ANCHOR_ALIGNMENT}
469+
<ReportSubmitToPopoverAnchor
470+
reportID={reportID}
471+
wrapperStyle={shouldTakeRemainingWidth ? styles.w100 : undefined}
472+
anchorAlignment={MORE_MENU_SUBMIT_TO_POPOVER_ANCHOR_ALIGNMENT}
473+
>
474+
<NavigationDeferredMount
475+
placeholder={<MoneyReportHeaderSecondaryActionsPlaceholder primaryAction={primaryAction} />}
476+
// RHPReportScreen remounts this tree on setParams arrow-nav without firing a transition,
477+
// so we must not wait for one — see https://github.com/Expensify/App/issues/88931.
478+
waitForUpcomingTransition={false}
475479
>
476-
<NavigationDeferredMount
477-
placeholder={<MoneyReportHeaderSecondaryActionsPlaceholder primaryAction={primaryAction} />}
478-
// RHPReportScreen remounts this tree on setParams arrow-nav without firing a transition,
479-
// so we must not wait for one — see https://github.com/Expensify/App/issues/88931.
480-
waitForUpcomingTransition={false}
481-
>
482-
<MoneyReportHeaderSecondaryActionsInner
483-
reportID={reportID}
484-
primaryAction={primaryAction}
485-
isReportInSearch={isReportInSearch}
486-
backTo={backTo}
487-
dropdownMenuRef={dropdownMenuRef}
488-
/>
489-
</NavigationDeferredMount>
490-
</ReportSubmitToPopoverAnchor>
491-
</View>
480+
<MoneyReportHeaderSecondaryActionsInner
481+
reportID={reportID}
482+
primaryAction={primaryAction}
483+
isReportInSearch={isReportInSearch}
484+
backTo={backTo}
485+
dropdownMenuRef={dropdownMenuRef}
486+
/>
487+
</NavigationDeferredMount>
488+
</ReportSubmitToPopoverAnchor>
492489
);
493490
}
494491

src/components/MoneyReportHeaderKYCDropdown.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ function MoneyReportHeaderKYCDropdown({
102102
customText={customText ?? translate('common.more')}
103103
options={applicableSecondaryActions}
104104
isSplitButton={false}
105-
wrapperStyle={shouldDisplayNarrowVersion && [!primaryAction && !customText && !isInLandscapeMode && styles.flex1, !!customText && styles.w100]}
105+
wrapperStyle={shouldDisplayNarrowVersion && [!primaryAction && !customText && !isInLandscapeMode && styles.w100, !!customText && styles.w100]}
106106
style={shouldDisplayNarrowVersion && !primaryAction && !customText && !isInLandscapeMode ? styles.w100 : undefined}
107107
shouldUseModalPaddingStyle
108108
onOptionsMenuHide={onOptionsMenuHide}

src/components/MoneyReportHeaderMoreContent.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ function MoneyReportHeaderMoreContentBody({moneyRequestReport, statusBarType, is
8080
const {iouTransactionID} = useMoneyReportTransactionThread();
8181

8282
return (
83-
<View style={[styles.flexRow, styles.gap2, styles.justifyContentStart, styles.flexNoWrap, styles.ph5, styles.pb3, shouldShowNextStep && styles.pt0]}>
83+
<View style={[styles.flexRow, styles.gap2, styles.justifyContentStart, styles.flexNoWrap, styles.ph5, styles.pb3]}>
8484
<View style={[styles.flexShrink1, styles.flexGrow1, styles.mnw0, styles.flexWrap, styles.justifyContentCenter]}>
8585
{shouldShowNextStep && <MoneyReportHeaderNextStep reportID={reportID} />}
8686
<MoneyReportHeaderStatusBarSection

src/hooks/useExpenseActions.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
isSelfDM,
3131
navigateOnDeleteExpense,
3232
} from '@libs/ReportUtils';
33+
import showConfirmModalAfterMoreMenuDismiss from '@libs/showConfirmModalAfterMoreMenuDismiss';
3334
import {shouldRestrictUserBillableActions} from '@libs/SubscriptionUtils';
3435
import {
3536
getChildTransactions,
@@ -328,7 +329,7 @@ function useExpenseActions({reportID, isReportInSearch = false, backTo, onDuplic
328329
}
329330

330331
if (hasCustomUnitOutOfPolicyViolation) {
331-
showConfirmModal({
332+
showConfirmModalAfterMoreMenuDismiss(showConfirmModal, {
332333
title: translate('common.duplicateExpense'),
333334
prompt: translate('iou.correctRateError'),
334335
confirmText: translate('common.buttonConfirm'),
@@ -338,7 +339,7 @@ function useExpenseActions({reportID, isReportInSearch = false, backTo, onDuplic
338339
}
339340

340341
if (isDistanceExpenseUnsupportedForDuplicating) {
341-
showConfirmModal({
342+
showConfirmModalAfterMoreMenuDismiss(showConfirmModal, {
342343
title: translate('common.duplicateExpense'),
343344
prompt: translate('iou.cannotDuplicateDistanceExpense'),
344345
confirmText: translate('common.buttonConfirm'),
@@ -348,7 +349,7 @@ function useExpenseActions({reportID, isReportInSearch = false, backTo, onDuplic
348349
}
349350

350351
if (isPerDiemRequestOnNonDefaultWorkspace) {
351-
showConfirmModal({
352+
showConfirmModalAfterMoreMenuDismiss(showConfirmModal, {
352353
title: translate('common.duplicateExpense'),
353354
prompt: translate('iou.duplicateNonDefaultWorkspacePerDiemError'),
354355
confirmText: translate('common.buttonConfirm'),
@@ -483,7 +484,7 @@ function useExpenseActions({reportID, isReportInSearch = false, backTo, onDuplic
483484
return;
484485
}
485486

486-
const result = await showConfirmModal({
487+
const result = await showConfirmModalAfterMoreMenuDismiss(showConfirmModal, {
487488
title: translate('iou.deleteExpense', {count: 1}),
488489
prompt: translate('iou.deleteConfirmation', {count: 1}),
489490
confirmText: translate('common.delete'),
@@ -544,7 +545,7 @@ function useExpenseActions({reportID, isReportInSearch = false, backTo, onDuplic
544545
return;
545546
}
546547

547-
const result = await showConfirmModal({
548+
const result = await showConfirmModalAfterMoreMenuDismiss(showConfirmModal, {
548549
title: translate('iou.deleteReport', {count: 1}),
549550
prompt: translate('iou.deleteReportConfirmation', {count: 1}),
550551
confirmText: translate('common.delete'),

src/hooks/useLifecycleActions.tsx

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
shouldShowMarkAsDone,
2323
} from '@libs/ReportUtils';
2424
import refreshSearchAfterReportAction from '@libs/SearchRefreshUtils';
25+
import showConfirmModalAfterMoreMenuDismiss from '@libs/showConfirmModalAfterMoreMenuDismiss';
2526
import {hasAnyPendingRTERViolation as hasAnyPendingRTERViolationTransactionUtils, hasOnlyPendingCardTransactions, showPendingCardTransactionsBlockModal} from '@libs/TransactionUtils';
2627
import {cancelPayment, markReportPaymentReceived} from '@userActions/IOU/PayMoneyRequest';
2728
import {approveMoneyRequest, reopenReport, retractReport, submitReport, unapproveExpenseReport} from '@userActions/IOU/ReportWorkflow';
@@ -292,7 +293,7 @@ function useLifecycleActions({reportID, startApprovedAnimation, startAnimation,
292293
return;
293294
}
294295

295-
const result = await showConfirmModal({
296+
const result = await showConfirmModalAfterMoreMenuDismiss(showConfirmModal, {
296297
title: translate('iou.confirmPaymentReceivedModalTitle'),
297298
prompt: translate('iou.receivedPaymentConfirmation'),
298299
confirmText: translate('iou.confirmReceivedPayment'),
@@ -338,7 +339,7 @@ function useLifecycleActions({reportID, startApprovedAnimation, startAnimation,
338339
</Text>
339340
);
340341

341-
const result = await showConfirmModal({
342+
const result = await showConfirmModalAfterMoreMenuDismiss(showConfirmModal, {
342343
title: translate('iou.unapproveReport'),
343344
prompt: unapproveWarningText,
344345
confirmText: translate('iou.unapproveReport'),
@@ -360,7 +361,7 @@ function useLifecycleActions({reportID, startApprovedAnimation, startAnimation,
360361
icon: expensifyIcons.Clear,
361362
sentryLabel: CONST.SENTRY_LABEL.MORE_MENU.CANCEL_PAYMENT,
362363
onSelected: async () => {
363-
const result = await showConfirmModal({
364+
const result = await showConfirmModalAfterMoreMenuDismiss(showConfirmModal, {
364365
title: translate('iou.cancelPayment'),
365366
prompt: translate('iou.cancelPaymentConfirmation'),
366367
confirmText: translate('iou.cancelPayment'),
@@ -393,7 +394,7 @@ function useLifecycleActions({reportID, startApprovedAnimation, startAnimation,
393394
</Text>
394395
);
395396

396-
const result = await showConfirmModal({
397+
const result = await showConfirmModalAfterMoreMenuDismiss(showConfirmModal, {
397398
title: translate('iou.reopenReport'),
398399
prompt: reopenExportedReportWarningText,
399400
confirmText: translate('iou.reopenReport'),
@@ -427,7 +428,7 @@ function useLifecycleActions({reportID, startApprovedAnimation, startAnimation,
427428
</Text>
428429
);
429430

430-
const result = await showConfirmModal({
431+
const result = await showConfirmModalAfterMoreMenuDismiss(showConfirmModal, {
431432
title: translate('iou.reopenReport'),
432433
prompt: reopenExportedReportWarningText,
433434
confirmText: translate('iou.reopenReport'),

0 commit comments

Comments
 (0)