1
1
import { useRoute } from '@react-navigation/native' ;
2
2
import type { ReactNode } from 'react' ;
3
- import React , { useCallback , useEffect } from 'react' ;
3
+ import React , { useCallback , useEffect , useMemo , useState } from 'react' ;
4
4
import { View } from 'react-native' ;
5
5
import type { OnyxEntry } from 'react-native-onyx' ;
6
6
import { useOnyx } from 'react-native-onyx' ;
7
+ import type { ValueOf } from 'type-fest' ;
7
8
import useLocalize from '@hooks/useLocalize' ;
8
9
import useResponsiveLayout from '@hooks/useResponsiveLayout' ;
9
10
import useTheme from '@hooks/useTheme' ;
10
11
import useThemeStyles from '@hooks/useThemeStyles' ;
11
12
import useTransactionViolations from '@hooks/useTransactionViolations' ;
13
+ import { deleteMoneyRequest , getNavigationUrlOnMoneyRequestDelete } from '@libs/actions/IOU' ;
12
14
import Navigation from '@libs/Navigation/Navigation' ;
13
15
import { getOriginalMessage , isMoneyRequestAction } from '@libs/ReportActionsUtils' ;
16
+ import { getTransactionThreadPrimaryAction } from '@libs/ReportPrimaryActionUtils' ;
17
+ import { getSecondaryTransactionThreadActions } from '@libs/ReportSecondaryActionUtils' ;
18
+ import { changeMoneyRequestHoldStatus , navigateBackOnDeleteTransaction , navigateToDetailsPage } from '@libs/ReportUtils' ;
14
19
import {
15
- checkIfShouldShowMarkAsCashButton ,
16
20
hasPendingRTERViolation as hasPendingRTERViolationTransactionUtils ,
17
21
hasReceipt ,
18
22
isDuplicate as isDuplicateTransactionUtils ,
@@ -33,6 +37,10 @@ import type IconAsset from '@src/types/utils/IconAsset';
33
37
import isLoadingOnyxValue from '@src/types/utils/isLoadingOnyxValue' ;
34
38
import BrokenConnectionDescription from './BrokenConnectionDescription' ;
35
39
import Button from './Button' ;
40
+ import ButtonWithDropdownMenu from './ButtonWithDropdownMenu' ;
41
+ import type { DropdownOption } from './ButtonWithDropdownMenu/types' ;
42
+ import ConfirmModal from './ConfirmModal' ;
43
+ import DecisionModal from './DecisionModal' ;
36
44
import HeaderWithBackButton from './HeaderWithBackButton' ;
37
45
import Icon from './Icon' ;
38
46
import * as Expensicons from './Icon/Expensicons' ;
@@ -68,6 +76,8 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
68
76
) ;
69
77
const transactionViolations = useTransactionViolations ( transaction ?. transactionID ) ;
70
78
79
+ const [ isDeleteModalVisible , setIsDeleteModalVisible ] = useState ( false ) ;
80
+ const [ downloadErrorModalVisible , setDownloadErrorModalVisible ] = useState ( false ) ;
71
81
const [ dismissedHoldUseExplanation , dismissedHoldUseExplanationResult ] = useOnyx ( ONYXKEYS . NVP_DISMISSED_HOLD_USE_EXPLANATION , { initialValue : true } ) ;
72
82
const [ isLoadingReportData ] = useOnyx ( ONYXKEYS . IS_LOADING_REPORT_DATA ) ;
73
83
const isLoadingHoldUseExplained = isLoadingOnyxValue ( dismissedHoldUseExplanationResult ) ;
@@ -85,7 +95,6 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
85
95
const hasPendingRTERViolation = hasPendingRTERViolationTransactionUtils ( transactionViolations ) ;
86
96
87
97
const shouldShowBrokenConnectionViolation = shouldShowBrokenConnectionViolationTransactionUtils ( parentReport , policy , transactionViolations ) ;
88
- const shouldShowMarkAsCashButton = checkIfShouldShowMarkAsCashButton ( hasPendingRTERViolation , shouldShowBrokenConnectionViolation , parentReport , policy ) ;
89
98
90
99
const markAsCash = useCallback ( ( ) => {
91
100
markAsCashAction ( transaction ?. transactionID , reportID ) ;
@@ -144,12 +153,89 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
144
153
Navigation . navigate ( ROUTES . PROCESS_MONEY_REQUEST_HOLD . getRoute ( Navigation . getReportRHPActiveRoute ( ) ) ) ;
145
154
} , [ dismissedHoldUseExplanation , isLoadingHoldUseExplained , isOnHold ] ) ;
146
155
156
+ const primaryAction = useMemo ( ( ) => {
157
+ if ( ! report || ! parentReport || ! transaction ) {
158
+ return '' ;
159
+ }
160
+ return getTransactionThreadPrimaryAction ( report , parentReport , transaction , transactionViolations , policy ) ;
161
+ } , [ parentReport , policy , report , transaction , transactionViolations ] ) ;
162
+
163
+ const primaryActionImplementation = {
164
+ [ CONST . REPORT . TRANSACTION_PRIMARY_ACTIONS . REMOVE_HOLD ] : (
165
+ < Button
166
+ success
167
+ text = { translate ( 'iou.unhold' ) }
168
+ onPress = { ( ) => {
169
+ changeMoneyRequestHoldStatus ( parentReportAction ) ;
170
+ } }
171
+ />
172
+ ) ,
173
+ [ CONST . REPORT . TRANSACTION_PRIMARY_ACTIONS . REVIEW_DUPLICATES ] : (
174
+ < Button
175
+ success
176
+ text = { translate ( 'iou.reviewDuplicates' ) }
177
+ onPress = { ( ) => {
178
+ if ( ! reportID ) {
179
+ return ;
180
+ }
181
+ Navigation . navigate ( ROUTES . TRANSACTION_DUPLICATE_REVIEW_PAGE . getRoute ( reportID , Navigation . getReportRHPActiveRoute ( ) ) ) ;
182
+ } }
183
+ />
184
+ ) ,
185
+ [ CONST . REPORT . TRANSACTION_PRIMARY_ACTIONS . MARK_AS_CASH ] : (
186
+ < Button
187
+ success
188
+ text = { translate ( 'iou.markAsCash' ) }
189
+ onPress = { markAsCash }
190
+ />
191
+ ) ,
192
+ } ;
193
+
194
+ const secondaryActions = useMemo ( ( ) => {
195
+ if ( ! parentReport || ! transaction ) {
196
+ return [ ] ;
197
+ }
198
+ return getSecondaryTransactionThreadActions ( parentReport , transaction ) ;
199
+ } , [ parentReport , transaction ] ) ;
200
+
201
+ const secondaryActionsImplementation : Record < ValueOf < typeof CONST . REPORT . TRANSACTION_SECONDARY_ACTIONS > , DropdownOption < ValueOf < typeof CONST . REPORT . TRANSACTION_SECONDARY_ACTIONS > > > = {
202
+ [ CONST . REPORT . TRANSACTION_SECONDARY_ACTIONS . HOLD ] : {
203
+ text : translate ( 'iou.hold' ) ,
204
+ icon : Expensicons . Stopwatch ,
205
+ value : CONST . REPORT . TRANSACTION_SECONDARY_ACTIONS . HOLD ,
206
+ onSelected : ( ) => {
207
+ if ( ! parentReportAction ) {
208
+ throw new Error ( 'Parent action does not exist' ) ;
209
+ }
210
+
211
+ changeMoneyRequestHoldStatus ( parentReportAction ) ;
212
+ } ,
213
+ } ,
214
+ [ CONST . REPORT . TRANSACTION_SECONDARY_ACTIONS . VIEW_DETAILS ] : {
215
+ value : CONST . REPORT . SECONDARY_ACTIONS . VIEW_DETAILS ,
216
+ text : translate ( 'iou.viewDetails' ) ,
217
+ icon : Expensicons . Info ,
218
+ onSelected : ( ) => {
219
+ navigateToDetailsPage ( report , Navigation . getReportRHPActiveRoute ( ) ) ;
220
+ } ,
221
+ } ,
222
+ [ CONST . REPORT . TRANSACTION_SECONDARY_ACTIONS . DELETE ] : {
223
+ text : translate ( 'common.delete' ) ,
224
+ icon : Expensicons . Trashcan ,
225
+ value : CONST . REPORT . SECONDARY_ACTIONS . DELETE ,
226
+ onSelected : ( ) => {
227
+ setIsDeleteModalVisible ( true ) ;
228
+ } ,
229
+ } ,
230
+ } ;
231
+
232
+ const applicableSecondaryActions = secondaryActions . map ( ( action ) => secondaryActionsImplementation [ action ] ) ;
233
+
147
234
return (
148
235
< View style = { [ styles . pl0 , styles . borderBottom ] } >
149
236
< HeaderWithBackButton
150
237
shouldShowBorderBottom = { false }
151
238
shouldShowReportAvatarWithDisplay
152
- shouldEnableDetailPageNavigation
153
239
shouldShowPinButton = { false }
154
240
report = {
155
241
reportID
@@ -164,53 +250,39 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
164
250
shouldShowBackButton = { shouldUseNarrowLayout }
165
251
shouldDisplaySearchRouter = { shouldDisplaySearchRouter }
166
252
onBackButtonPress = { onBackButtonPress }
253
+ shouldEnableDetailPageNavigation
167
254
>
168
- { shouldShowMarkAsCashButton && ! shouldUseNarrowLayout && (
169
- < Button
170
- success
171
- text = { translate ( 'iou.markAsCash' ) }
172
- style = { [ styles . p0 ] }
173
- onPress = { markAsCash }
174
- />
175
- ) }
176
- { isDuplicate && ! shouldUseNarrowLayout && (
177
- < Button
178
- success
179
- text = { translate ( 'iou.reviewDuplicates' ) }
180
- style = { [ styles . p0 , styles . ml2 ] }
181
- onPress = { ( ) => {
182
- if ( ! reportID ) {
183
- return ;
184
- }
185
- Navigation . navigate ( ROUTES . TRANSACTION_DUPLICATE_REVIEW_PAGE . getRoute ( reportID , Navigation . getReportRHPActiveRoute ( ) ) ) ;
186
- } }
187
- />
255
+ { ! shouldUseNarrowLayout && (
256
+ < View style = { [ styles . flexRow , styles . gap2 ] } >
257
+ { ! ! primaryAction && primaryActionImplementation [ primaryAction ] }
258
+ { ! ! applicableSecondaryActions . length && (
259
+ < ButtonWithDropdownMenu
260
+ success = { false }
261
+ onPress = { ( ) => { } }
262
+ shouldAlwaysShowDropdownMenu
263
+ customText = { translate ( 'common.more' ) }
264
+ options = { applicableSecondaryActions }
265
+ isSplitButton = { false }
266
+ />
267
+ ) }
268
+ </ View >
188
269
) }
189
270
{ shouldDisplayTransactionNavigation && < MoneyRequestReportTransactionsNavigation currentReportID = { reportID } /> }
190
271
</ HeaderWithBackButton >
191
- { shouldShowMarkAsCashButton && shouldUseNarrowLayout && (
192
- < View style = { [ styles . ph5 , styles . pb3 ] } >
193
- < Button
194
- success
195
- text = { translate ( 'iou.markAsCash' ) }
196
- style = { [ styles . w100 , styles . pr0 ] }
197
- onPress = { markAsCash }
198
- />
199
- </ View >
200
- ) }
201
- { isDuplicate && shouldUseNarrowLayout && (
202
- < View style = { [ styles . ph5 , styles . pb3 ] } >
203
- < Button
204
- success
205
- text = { translate ( 'iou.reviewDuplicates' ) }
206
- style = { [ styles . w100 , styles . pr0 ] }
207
- onPress = { ( ) => {
208
- if ( ! reportID ) {
209
- return ;
210
- }
211
- Navigation . navigate ( ROUTES . TRANSACTION_DUPLICATE_REVIEW_PAGE . getRoute ( reportID , Navigation . getReportRHPActiveRoute ( ) ) ) ;
212
- } }
213
- />
272
+ { shouldUseNarrowLayout && (
273
+ < View style = { [ styles . flexRow , styles . gap2 , styles . pb3 , styles . ph5 , styles . w100 , styles . alignItemsCenter , styles . justifyContentCenter ] } >
274
+ { ! ! primaryAction && < View style = { [ styles . flexGrow4 ] } > { primaryActionImplementation [ primaryAction ] } </ View > }
275
+ { ! ! applicableSecondaryActions . length && (
276
+ < ButtonWithDropdownMenu
277
+ success = { false }
278
+ onPress = { ( ) => { } }
279
+ shouldAlwaysShowDropdownMenu
280
+ customText = { translate ( 'common.more' ) }
281
+ options = { applicableSecondaryActions }
282
+ isSplitButton = { false }
283
+ wrapperStyle = { [ ! primaryAction && styles . flexGrow4 ] }
284
+ />
285
+ ) }
214
286
</ View >
215
287
) }
216
288
{ ! ! statusBarProps && (
@@ -222,6 +294,36 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
222
294
</ View >
223
295
) }
224
296
< LoadingBar shouldShow = { ( isLoadingReportData && shouldUseNarrowLayout ) ?? false } />
297
+ < DecisionModal
298
+ title = { translate ( 'common.downloadFailedTitle' ) }
299
+ prompt = { translate ( 'common.downloadFailedDescription' ) }
300
+ isSmallScreenWidth = { isSmallScreenWidth }
301
+ onSecondOptionSubmit = { ( ) => setDownloadErrorModalVisible ( false ) }
302
+ secondOptionText = { translate ( 'common.buttonConfirm' ) }
303
+ isVisible = { downloadErrorModalVisible }
304
+ onClose = { ( ) => setDownloadErrorModalVisible ( false ) }
305
+ />
306
+ < ConfirmModal
307
+ title = { translate ( 'iou.deleteExpense' , { count : 1 } ) }
308
+ isVisible = { isDeleteModalVisible }
309
+ onConfirm = { ( ) => {
310
+ setIsDeleteModalVisible ( false ) ;
311
+ if ( ! parentReportAction || ! transaction ) {
312
+ throw new Error ( 'Data missing' ) ;
313
+ }
314
+
315
+ deleteMoneyRequest ( transaction ?. transactionID , parentReportAction ) ;
316
+
317
+ const goBackRoute = getNavigationUrlOnMoneyRequestDelete ( transaction . transactionID , parentReportAction , true ) ;
318
+ navigateBackOnDeleteTransaction ( goBackRoute ) ;
319
+ } }
320
+ onCancel = { ( ) => setIsDeleteModalVisible ( false ) }
321
+ prompt = { translate ( 'iou.deleteConfirmation' , { count : 1 } ) }
322
+ confirmText = { translate ( 'common.delete' ) }
323
+ cancelText = { translate ( 'common.cancel' ) }
324
+ danger
325
+ shouldEnableNewFocusManagement
326
+ />
225
327
</ View >
226
328
) ;
227
329
}
0 commit comments