Skip to content

Commit 40ede1b

Browse files
authored
Merge pull request #79369 from mukhrr/fix/unpprove-unhold-copilot-limited-access
Add delegate access restriction checks to unhold expense actions
2 parents 6cb3ffa + dd4320b commit 40ede1b

12 files changed

+106
-10
lines changed

src/Expensify.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {AppState, Linking, Platform} from 'react-native';
77
import type {OnyxEntry} from 'react-native-onyx';
88
import Onyx from 'react-native-onyx';
99
import ConfirmModal from './components/ConfirmModal';
10+
import DelegateNoAccessModalProvider from './components/DelegateNoAccessModalProvider';
1011
import EmojiPicker from './components/EmojiPicker/EmojiPicker';
1112
import GrowlNotification from './components/GrowlNotification';
1213
import {InitialURLContext} from './components/InitialURLContextProvider';
@@ -362,7 +363,9 @@ function Expensify() {
362363
{shouldInit && (
363364
<>
364365
<GrowlNotification ref={growlRef} />
365-
<PopoverReportActionContextMenu ref={ReportActionContextMenu.contextMenuRef} />
366+
<DelegateNoAccessModalProvider>
367+
<PopoverReportActionContextMenu ref={ReportActionContextMenu.contextMenuRef} />
368+
</DelegateNoAccessModalProvider>
366369
<EmojiPicker ref={EmojiPickerAction.emojiPickerRef} />
367370
{/* We include the modal for showing a new update at the top level so the option is always present. */}
368371
{updateAvailable && !updateRequired ? <UpdateAppModal /> : null}

src/components/MoneyReportHeader.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -998,6 +998,11 @@ function MoneyReportHeader({
998998
success
999999
text={translate('iou.unhold')}
10001000
onPress={() => {
1001+
if (isDelegateAccessRestricted) {
1002+
showDelegateNoAccessModal();
1003+
return;
1004+
}
1005+
10011006
const parentReportAction = getReportAction(moneyRequestReport?.parentReportID, moneyRequestReport?.parentReportActionID);
10021007

10031008
const IOUActions = getAllExpensesToHoldIfApplicable(moneyRequestReport, reportActions, transactions, policy);
@@ -1275,6 +1280,11 @@ function MoneyReportHeader({
12751280
throw new Error('Parent action does not exist');
12761281
}
12771282

1283+
if (isDelegateAccessRestricted) {
1284+
showDelegateNoAccessModal();
1285+
return;
1286+
}
1287+
12781288
changeMoneyRequestHoldStatus(requestParentReportAction);
12791289
},
12801290
},

src/components/MoneyRequestHeader.tsx

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,11 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
264264
success
265265
text={translate('iou.unhold')}
266266
onPress={() => {
267+
if (isDelegateAccessRestricted) {
268+
showDelegateNoAccessModal();
269+
return;
270+
}
271+
267272
changeMoneyRequestHoldStatus(parentReportAction);
268273
}}
269274
/>
@@ -388,6 +393,11 @@ function MoneyRequestHeader({report, parentReportAction, policy, onBackButtonPre
388393
throw new Error('Parent action does not exist');
389394
}
390395

396+
if (isDelegateAccessRestricted) {
397+
showDelegateNoAccessModal();
398+
return;
399+
}
400+
391401
changeMoneyRequestHoldStatus(parentReportAction);
392402
},
393403
},

src/components/ProcessMoneyReportHoldMenu.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, {useMemo} from 'react';
1+
import React, {useContext, useMemo} from 'react';
22
import type {OnyxEntry} from 'react-native-onyx';
33
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
44
import useLocalize from '@hooks/useLocalize';
@@ -14,6 +14,7 @@ import type * as OnyxTypes from '@src/types/onyx';
1414
import type {PaymentMethodType} from '@src/types/onyx/OriginalMessage';
1515
import type DeepValueOf from '@src/types/utils/DeepValueOf';
1616
import DecisionModal from './DecisionModal';
17+
import {DelegateNoAccessContext} from './DelegateNoAccessModalProvider';
1718

1819
type ActionHandledType = DeepValueOf<typeof CONST.IOU.REPORT_ACTION_TYPE.PAY | typeof CONST.IOU.REPORT_ACTION_TYPE.APPROVE>;
1920

@@ -81,7 +82,13 @@ function ProcessMoneyReportHoldMenu({
8182
const currentUserDetails = useCurrentUserPersonalDetails();
8283
const hasViolations = hasViolationsReportUtils(moneyRequestReport?.reportID, transactionViolations, currentUserDetails.accountID, currentUserDetails.email ?? '');
8384

85+
const {isDelegateAccessRestricted, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext);
8486
const onSubmit = (full: boolean) => {
87+
if (isDelegateAccessRestricted) {
88+
showDelegateNoAccessModal();
89+
return;
90+
}
91+
8592
if (isApprove) {
8693
if (startAnimation) {
8794
startAnimation();

src/hooks/useSelectedTransactionsActions.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import {useState} from 'react';
1+
import {useContext, useState} from 'react';
22
import type {DropdownOption} from '@components/ButtonWithDropdownMenu/types';
3+
import {DelegateNoAccessContext} from '@components/DelegateNoAccessModalProvider';
34
import type {PopoverMenuItem} from '@components/PopoverMenu';
45
import {useSearchContext} from '@components/Search/SearchContext';
56
import {initSplitExpense} from '@libs/actions/IOU';
@@ -67,6 +68,7 @@ function useSelectedTransactionsActions({
6768
isOnSearch?: boolean;
6869
}) {
6970
const {isOffline} = useNetworkWithOfflineStatus();
71+
const {isDelegateAccessRestricted, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext);
7072
const {selectedTransactionIDs, clearSelectedTransactions, currentSearchHash, selectedTransactions: selectedTransactionsMeta} = useSearchContext();
7173
const allTransactions = useAllTransactions();
7274
const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false});
@@ -184,6 +186,11 @@ function useSelectedTransactionsActions({
184186
icon: expensifyIcons.Stopwatch,
185187
value: HOLD,
186188
onSelected: () => {
189+
if (isDelegateAccessRestricted) {
190+
showDelegateNoAccessModal();
191+
return;
192+
}
193+
187194
if (!report?.reportID) {
188195
return;
189196
}
@@ -198,6 +205,11 @@ function useSelectedTransactionsActions({
198205
icon: expensifyIcons.Stopwatch,
199206
value: UNHOLD,
200207
onSelected: () => {
208+
if (isDelegateAccessRestricted) {
209+
showDelegateNoAccessModal();
210+
return;
211+
}
212+
201213
for (const transactionID of selectedTransactionIDs) {
202214
const action = getIOUActionForTransactionID(reportActions, transactionID);
203215
if (!action?.childReportID) {
@@ -219,6 +231,11 @@ function useSelectedTransactionsActions({
219231
icon: expensifyIcons.ThumbsDown,
220232
value: CONST.REPORT.SECONDARY_ACTIONS.REJECT,
221233
onSelected: () => {
234+
if (isDelegateAccessRestricted) {
235+
showDelegateNoAccessModal();
236+
return;
237+
}
238+
222239
Navigation.navigate(ROUTES.SEARCH_MONEY_REQUEST_REPORT_REJECT_TRANSACTIONS.getRoute({reportID: report.reportID}));
223240
},
224241
});

src/pages/Search/SearchHoldReasonPage.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import React, {useCallback, useEffect} from 'react';
1+
import React, {useCallback, useContext, useEffect} from 'react';
2+
import {DelegateNoAccessContext} from '@components/DelegateNoAccessModalProvider';
23
import type {FormInputErrors, FormOnyxValues} from '@components/Form/types';
34
import {useSearchContext} from '@components/Search/SearchContext';
45
import useAncestors from '@hooks/useAncestors';
@@ -29,8 +30,14 @@ function SearchHoldReasonPage({route}: SearchHoldReasonPageProps) {
2930
const ancestors = useAncestors(report);
3031

3132
const [allReportActions] = useOnyx(ONYXKEYS.COLLECTION.REPORT_ACTIONS, {canBeMissing: true});
33+
const {isDelegateAccessRestricted, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext);
3234
const onSubmit = useCallback(
3335
({comment}: FormOnyxValues<typeof ONYXKEYS.FORMS.MONEY_REQUEST_HOLD_FORM>) => {
36+
if (isDelegateAccessRestricted) {
37+
showDelegateNoAccessModal();
38+
return;
39+
}
40+
3441
if (route.name === SCREENS.SEARCH.MONEY_REQUEST_REPORT_HOLD_TRANSACTIONS) {
3542
putTransactionsOnHold(context.selectedTransactionIDs, comment, reportID, ancestors);
3643
context.clearSelectedTransactions(true);
@@ -41,7 +48,7 @@ function SearchHoldReasonPage({route}: SearchHoldReasonPageProps) {
4148

4249
Navigation.goBack();
4350
},
44-
[route.name, context, reportID, allTransactions, allReportActions, ancestors],
51+
[route.name, context, reportID, allTransactions, allReportActions, ancestors, isDelegateAccessRestricted, showDelegateNoAccessModal],
4552
);
4653

4754
const validate = useCallback(

src/pages/Search/SearchPage.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,11 @@ function SearchPage({route}: SearchPageProps) {
844844
return;
845845
}
846846

847+
if (isDelegateAccessRestricted) {
848+
showDelegateNoAccessModal();
849+
return;
850+
}
851+
847852
unholdMoneyRequestOnSearch(hash, selectedTransactionsKeys);
848853
// eslint-disable-next-line @typescript-eslint/no-deprecated
849854
InteractionManager.runAfterInteractions(() => {

src/pages/Search/SearchRejectReasonPage.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import React, {useCallback, useEffect, useMemo} from 'react';
1+
import React, {useCallback, useContext, useEffect, useMemo} from 'react';
2+
import {DelegateNoAccessContext} from '@components/DelegateNoAccessModalProvider';
23
import type {FormInputErrors, FormOnyxValues} from '@components/Form/types';
34
import {useSearchContext} from '@components/Search/SearchContext';
45
import useOnyx from '@hooks/useOnyx';
@@ -35,8 +36,14 @@ function SearchRejectReasonPage({route}: SearchRejectReasonPageProps) {
3536
return context.selectedTransactions;
3637
}, [route.name, reportID, context.selectedTransactionIDs, context.selectedTransactions]);
3738

39+
const {isDelegateAccessRestricted, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext);
3840
const onSubmit = useCallback(
3941
({comment}: FormOnyxValues<typeof ONYXKEYS.FORMS.MONEY_REQUEST_REJECT_FORM>) => {
42+
if (isDelegateAccessRestricted) {
43+
showDelegateNoAccessModal();
44+
return;
45+
}
46+
4047
const urlToNavigateBack = rejectMoneyRequestsOnSearch(context.currentSearchHash, selectedTransactionsForReject, comment, allPolicies, allReports);
4148
if (route.name === SCREENS.SEARCH.MONEY_REQUEST_REPORT_REJECT_TRANSACTIONS) {
4249
context.clearSelectedTransactions(true);
@@ -48,7 +55,7 @@ function SearchRejectReasonPage({route}: SearchRejectReasonPageProps) {
4855
Navigation.isNavigationReady().then(() => Navigation.goBack(urlToNavigateBack as Route));
4956
}
5057
},
51-
[context, allPolicies, allReports, route.name, selectedTransactionsForReject],
58+
[context, allPolicies, allReports, route.name, selectedTransactionsForReject, isDelegateAccessRestricted, showDelegateNoAccessModal],
5259
);
5360

5461
const validate = useCallback((values: FormOnyxValues<typeof ONYXKEYS.FORMS.MONEY_REQUEST_REJECT_FORM>) => {

src/pages/home/report/ContextMenu/BaseReportActionContextMenu.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {OnyxEntry} from 'react-native-onyx';
88
import * as ActionSheetAwareScrollView from '@components/ActionSheetAwareScrollView';
99
import type {ContextMenuItemHandle} from '@components/ContextMenuItem';
1010
import ContextMenuItem from '@components/ContextMenuItem';
11+
import {DelegateNoAccessContext} from '@components/DelegateNoAccessModalProvider';
1112
import FocusTrapForModal from '@components/FocusTrap/FocusTrapForModal';
1213
import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager';
1314
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
@@ -134,6 +135,7 @@ function BaseReportActionContextMenu({
134135
setIsEmojiPickerActive,
135136
}: BaseReportActionContextMenuProps) {
136137
const actionSheetAwareScrollViewContext = useContext(ActionSheetAwareScrollView.ActionSheetAwareScrollViewContext);
138+
const {isDelegateAccessRestricted, showDelegateNoAccessModal} = useContext(DelegateNoAccessContext);
137139
const icons = useMemoizedLazyExpensifyIcons([
138140
'Download',
139141
'ThreeDots',
@@ -407,6 +409,8 @@ function BaseReportActionContextMenu({
407409
policyTags,
408410
translate,
409411
harvestReport,
412+
isDelegateAccessRestricted,
413+
showDelegateNoAccessModal,
410414
currentUserAccountID,
411415
};
412416

src/pages/home/report/ContextMenu/ContextMenuActions.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,8 @@ type ContextMenuActionPayload = {
245245
policyTags: OnyxEntry<PolicyTagLists>;
246246
translate: LocalizedTranslate;
247247
harvestReport?: OnyxEntry<ReportType>;
248+
isDelegateAccessRestricted?: boolean;
249+
showDelegateNoAccessModal?: () => void;
248250
};
249251

250252
type OnPress = (closePopover: boolean, payload: ContextMenuActionPayload, selection?: string, reportID?: string, draftMessage?: string) => void;
@@ -465,7 +467,12 @@ const ContextMenuActions: ContextMenuAction[] = [
465467
const holdReportAction = getReportAction(moneyRequestAction?.childReportID, `${iouTransaction?.comment?.hold ?? ''}`);
466468
return canHoldUnholdReportAction(moneyRequestReport, moneyRequestAction, holdReportAction, iouTransaction, moneyRequestPolicy).canUnholdRequest;
467469
},
468-
onPress: (closePopover, {moneyRequestAction}) => {
470+
onPress: (closePopover, {moneyRequestAction, isDelegateAccessRestricted, showDelegateNoAccessModal}) => {
471+
if (isDelegateAccessRestricted) {
472+
hideContextMenu(false, showDelegateNoAccessModal);
473+
return;
474+
}
475+
469476
if (closePopover) {
470477
hideContextMenu(false, () => changeMoneyRequestHoldStatus(moneyRequestAction));
471478
return;
@@ -488,7 +495,12 @@ const ContextMenuActions: ContextMenuAction[] = [
488495
const holdReportAction = getReportAction(moneyRequestAction?.childReportID, `${iouTransaction?.comment?.hold ?? ''}`);
489496
return canHoldUnholdReportAction(moneyRequestReport, moneyRequestAction, holdReportAction, iouTransaction, moneyRequestPolicy).canHoldRequest;
490497
},
491-
onPress: (closePopover, {moneyRequestAction}) => {
498+
onPress: (closePopover, {moneyRequestAction, isDelegateAccessRestricted, showDelegateNoAccessModal}) => {
499+
if (isDelegateAccessRestricted) {
500+
hideContextMenu(false, showDelegateNoAccessModal);
501+
return;
502+
}
503+
492504
if (closePopover) {
493505
hideContextMenu(false, () => changeMoneyRequestHoldStatus(moneyRequestAction));
494506
return;

0 commit comments

Comments
 (0)