Skip to content

Commit a5ec775

Browse files
authored
Merge pull request Expensify#78060 from lorretheboy/fix-part-1/76691
Part 1: Refactor ConfirmModal usage to useConfirmModal in workspace feature pages
2 parents 812b0ef + 719bf87 commit a5ec775

6 files changed

Lines changed: 135 additions & 173 deletions

File tree

src/pages/workspace/WorkspaceMembersPage.tsx

Lines changed: 37 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
import {useIsFocused} from '@react-navigation/native';
2-
import {deepEqual} from 'fast-equals';
32
import React, {useCallback, useContext, useEffect, useMemo, useRef, useState} from 'react';
43
import {InteractionManager, View} from 'react-native';
54
import type {ValueOf} from 'type-fest';
65
import Button from '@components/Button';
76
import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu';
87
import type {DropdownOption, WorkspaceMemberBulkActionType} from '@components/ButtonWithDropdownMenu/types';
9-
import ConfirmModal from '@components/ConfirmModal';
108
import DecisionModal from '@components/DecisionModal';
119
// eslint-disable-next-line no-restricted-imports
1210
import {Plus} from '@components/Icon/Expensicons';
1311
import {LockedAccountContext} from '@components/LockedAccountModalProvider';
1412
import MessagesRow from '@components/MessagesRow';
13+
import {ModalActions} from '@components/Modal/Global/ModalContext';
1514
import SearchBar from '@components/SearchBar';
1615
import TableListItem from '@components/SelectionList/ListItem/TableListItem';
1716
import type {ListItem, SelectionListHandle} from '@components/SelectionList/types';
1817
import SelectionListWithModal from '@components/SelectionListWithModal';
1918
import CustomListHeader from '@components/SelectionListWithModal/CustomListHeader';
2019
import Text from '@components/Text';
2120
import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types';
21+
import useConfirmModal from '@hooks/useConfirmModal';
2222
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
2323
import useFilteredSelection from '@hooks/useFilteredSelection';
2424
import {useMemoizedLazyExpensifyIcons, useMemoizedLazyIllustrations} from '@hooks/useLazyAsset';
@@ -103,14 +103,13 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
103103
const employeeListDetails = useMemo(() => policy?.employeeList ?? ({} as PolicyEmployeeList), [policy?.employeeList]);
104104
const currentUserPersonalDetails = useCurrentUserPersonalDetails();
105105
const styles = useThemeStyles();
106+
const {showConfirmModal} = useConfirmModal();
106107
const StyleUtils = useStyleUtils();
107-
const [removeMembersConfirmModalVisible, setRemoveMembersConfirmModalVisible] = useState(false);
108108
const {isOffline} = useNetwork();
109109
const prevIsOffline = usePrevious(isOffline);
110110
const accountIDs = useMemo(() => Object.values(policyMemberEmailsToAccountIDs ?? {}).map((accountID) => Number(accountID)), [policyMemberEmailsToAccountIDs]);
111111
const prevAccountIDs = usePrevious(accountIDs);
112112
const textInputRef = useRef<BaseTextInputRef>(null);
113-
const [isOfflineModalVisible, setIsOfflineModalVisible] = useState(false);
114113
const [isDownloadFailureModalVisible, setIsDownloadFailureModalVisible] = useState(false);
115114
const isOfflineAndNoMemberDataAvailable = isEmptyObject(policy?.employeeList) && isOffline;
116115
const {translate, formatPhoneNumber, localeCompare} = useLocalize();
@@ -202,14 +201,6 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
202201
getWorkspaceMembers();
203202
}, [getWorkspaceMembers]);
204203

205-
useEffect(() => {
206-
if (!removeMembersConfirmModalVisible || deepEqual(accountIDs, prevAccountIDs)) {
207-
return;
208-
}
209-
setRemoveMembersConfirmModalVisible(false);
210-
// eslint-disable-next-line react-hooks/exhaustive-deps
211-
}, [accountIDs]);
212-
213204
useEffect(() => {
214205
const isReconnecting = prevIsOffline && !isOffline;
215206
if (!isReconnecting) {
@@ -266,8 +257,6 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
266257
}
267258
}
268259

269-
setRemoveMembersConfirmModalVisible(false);
270-
271260
// eslint-disable-next-line @typescript-eslint/no-deprecated
272261
InteractionManager.runAfterInteractions(() => {
273262
setSelectedEmployees([]);
@@ -278,9 +267,26 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
278267
/**
279268
* Show the modal to confirm removal of the selected members
280269
*/
281-
const askForConfirmationToRemove = () => {
282-
setRemoveMembersConfirmModalVisible(true);
283-
};
270+
const askForConfirmationToRemove = useCallback(async () => {
271+
const result = await showConfirmModal({
272+
danger: true,
273+
title: translate('workspace.people.removeMembersTitle', {count: selectedEmployees.length}),
274+
prompt: confirmModalPrompt,
275+
confirmText: translate('common.remove'),
276+
cancelText: translate('common.cancel'),
277+
onModalHide: () => {
278+
if (!textInputRef.current) {
279+
return;
280+
}
281+
textInputRef.current.focus();
282+
},
283+
});
284+
if (result.action !== ModalActions.CONFIRM) {
285+
return;
286+
}
287+
288+
removeUsers();
289+
}, [confirmModalPrompt, removeUsers, selectedEmployees.length, showConfirmModal, translate]);
284290

285291
/**
286292
* Add or remove all users passed from the selectedEmployees list
@@ -486,6 +492,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
486492
styles.cursorDefault,
487493
styles.flex1,
488494
styles.pr3,
495+
translate,
489496
styles.alignSelfStart,
490497
styles.alignSelfEnd,
491498
isControlPolicyWithWideLayout,
@@ -667,6 +674,16 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
667674
return options;
668675
};
669676

677+
const showRequiresInternetModal = useCallback(() => {
678+
showConfirmModal({
679+
title: translate('common.youAppearToBeOffline'),
680+
prompt: translate('common.thisFeatureRequiresInternet'),
681+
confirmText: translate('common.buttonConfirm'),
682+
shouldShowCancelButton: false,
683+
shouldHandleNavigationBack: true,
684+
});
685+
}, [showConfirmModal, translate]);
686+
670687
const secondaryActions = useMemo(() => {
671688
if (!isPolicyAdmin) {
672689
return [];
@@ -682,7 +699,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
682699
return;
683700
}
684701
if (isOffline) {
685-
close(() => setIsOfflineModalVisible(true));
702+
close(showRequiresInternetModal);
686703
return;
687704
}
688705
Navigation.navigate(ROUTES.WORKSPACE_MEMBERS_IMPORT.getRoute(policyID));
@@ -694,7 +711,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
694711
text: translate('spreadsheet.downloadCSV'),
695712
onSelected: () => {
696713
if (isOffline) {
697-
close(() => setIsOfflineModalVisible(true));
714+
close(showRequiresInternetModal);
698715
return;
699716
}
700717

@@ -713,7 +730,7 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
713730
];
714731

715732
return menuItems;
716-
}, [icons.Download, icons.Table, policyID, translate, isOffline, isPolicyAdmin, isAccountLocked, showLockedAccountModal]);
733+
}, [isPolicyAdmin, icons.Table, icons.Download, translate, isAccountLocked, isOffline, policyID, showLockedAccountModal, showRequiresInternetModal]);
717734

718735
const getHeaderButtons = () => {
719736
if (!isPolicyAdmin) {
@@ -811,36 +828,6 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
811828
{() => (
812829
<>
813830
{shouldUseNarrowLayout && <View style={[styles.pl5, styles.pr5]}>{getHeaderButtons()}</View>}
814-
<ConfirmModal
815-
isVisible={isOfflineModalVisible}
816-
onConfirm={() => setIsOfflineModalVisible(false)}
817-
title={translate('common.youAppearToBeOffline')}
818-
prompt={translate('common.thisFeatureRequiresInternet')}
819-
confirmText={translate('common.buttonConfirm')}
820-
shouldShowCancelButton={false}
821-
onCancel={() => setIsOfflineModalVisible(false)}
822-
shouldHandleNavigationBack
823-
/>
824-
825-
<ConfirmModal
826-
danger
827-
title={translate('workspace.people.removeMembersTitle', {count: selectedEmployees.length})}
828-
isVisible={removeMembersConfirmModalVisible}
829-
onConfirm={removeUsers}
830-
onCancel={() => setRemoveMembersConfirmModalVisible(false)}
831-
prompt={confirmModalPrompt}
832-
confirmText={translate('common.remove')}
833-
cancelText={translate('common.cancel')}
834-
onModalHide={() => {
835-
// eslint-disable-next-line @typescript-eslint/no-deprecated
836-
InteractionManager.runAfterInteractions(() => {
837-
if (!textInputRef.current) {
838-
return;
839-
}
840-
textInputRef.current.focus();
841-
});
842-
}}
843-
/>
844831
<DecisionModal
845832
title={translate('common.downloadFailedTitle')}
846833
prompt={translate('common.downloadFailedDescription')}
@@ -861,7 +848,6 @@ function WorkspaceMembersPage({personalDetails, route, policy}: WorkspaceMembers
861848
onSelectAll={filteredData.length > 0 ? () => toggleAllUsers(filteredData) : undefined}
862849
style={{listHeaderWrapperStyle: [styles.ph9, styles.pv3, styles.pb5], listItemTitleContainerStyles: shouldUseNarrowLayout ? undefined : [styles.pr3]}}
863850
onTurnOnSelectionMode={(item) => item && toggleUser(item.login)}
864-
disableKeyboardShortcuts={removeMembersConfirmModalVisible}
865851
shouldPreventDefaultFocusOnSelectRow={!canUseTouchScreen()}
866852
onCheckboxPress={(item) => toggleUser(item.login)}
867853
shouldUseDefaultRightHandSideCheckmark={false}

src/pages/workspace/members/WorkspaceMemberDetailsPage.tsx

Lines changed: 34 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
import {Str} from 'expensify-common';
2-
import React, {useContext, useEffect, useState} from 'react';
2+
import React, {useContext, useEffect} from 'react';
33
import {View} from 'react-native';
44
import type {OnyxEntry} from 'react-native-onyx';
55
import type {ValueOf} from 'type-fest';
66
import Avatar from '@components/Avatar';
77
import Button from '@components/Button';
88
import ButtonDisabledWhenOffline from '@components/Button/ButtonDisabledWhenOffline';
9-
import ConfirmModal from '@components/ConfirmModal';
109
import HeaderWithBackButton from '@components/HeaderWithBackButton';
1110
import {LockedAccountContext} from '@components/LockedAccountModalProvider';
1211
import MenuItem from '@components/MenuItem';
1312
import MenuItemWithTopDescription from '@components/MenuItemWithTopDescription';
13+
import {ModalActions} from '@components/Modal/Global/ModalContext';
1414
import OfflineWithFeedback from '@components/OfflineWithFeedback';
1515
import ScreenWrapper from '@components/ScreenWrapper';
1616
import ScrollView from '@components/ScrollView';
1717
import Text from '@components/Text';
1818
import useCardFeeds from '@hooks/useCardFeeds';
1919
import {useCompanyCardFeedIcons} from '@hooks/useCompanyCardIcons';
20+
import useConfirmModal from '@hooks/useConfirmModal';
2021
import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails';
2122
import useExpensifyCardFeeds from '@hooks/useExpensifyCardFeeds';
2223
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
@@ -88,8 +89,7 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM
8889
const [customCardNames] = useOnyx(ONYXKEYS.NVP_EXPENSIFY_COMPANY_CARDS_CUSTOM_NAMES, {canBeMissing: true});
8990
const [fundList] = useOnyx(ONYXKEYS.FUND_LIST, {canBeMissing: true});
9091
const expensifyCardSettings = useExpensifyCardFeeds(policyID);
91-
92-
const [isRemoveMemberConfirmModalVisible, setIsRemoveMemberConfirmModalVisible] = useState(false);
92+
const {showConfirmModal} = useConfirmModal();
9393

9494
const accountID = Number(route.params.accountID);
9595
const memberLogin = personalDetails?.[accountID]?.login ?? '';
@@ -109,7 +109,6 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM
109109
const isSMSLogin = Str.isSMSLogin(memberLogin);
110110
const phoneNumber = getPhoneNumber(details);
111111
const isReimburser = policy?.achAccount?.reimburser === memberLogin;
112-
const [isCannotRemoveUser, setIsCannotRemoveUser] = useState(false);
113112
const {isAccountLocked, showLockedAccountModal} = useContext(LockedAccountContext);
114113

115114
const {approvalWorkflows} = convertPolicyEmployeesToApprovalWorkflows({
@@ -170,14 +169,6 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM
170169
navigateAfterInteraction(() => Navigation.goBack());
171170
}, [member?.pendingAction, prevMember]);
172171

173-
const askForConfirmationToRemove = () => {
174-
if (isReimburser) {
175-
setIsCannotRemoveUser(true);
176-
return;
177-
}
178-
setIsRemoveMemberConfirmModalVisible(true);
179-
};
180-
181172
// Function to remove a member and close the modal
182173
const removeMemberAndCloseModal = () => {
183174
removeMembers(policy, [memberLogin], {[memberLogin]: accountID});
@@ -187,7 +178,6 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM
187178
// We can't let the "Prevent Self Approvals" enabled if there's only one workspace user
188179
setPolicyPreventSelfApproval(policyID, false);
189180
}
190-
setIsRemoveMemberConfirmModalVisible(false);
191181
};
192182

193183
const removeUser = () => {
@@ -221,6 +211,36 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM
221211
removeMemberAndCloseModal();
222212
};
223213

214+
const showRemoveMemberModal = async () => {
215+
const result = await showConfirmModal({
216+
danger: true,
217+
title: translate('workspace.people.removeMemberTitle'),
218+
prompt: confirmModalPrompt,
219+
confirmText: translate('common.remove'),
220+
cancelText: translate('common.cancel'),
221+
});
222+
223+
if (result.action !== ModalActions.CONFIRM) {
224+
return;
225+
}
226+
removeUser();
227+
};
228+
229+
const askForConfirmationToRemove = () => {
230+
if (isReimburser) {
231+
showConfirmModal({
232+
shouldShowCancelButton: false,
233+
success: true,
234+
title: translate('workspace.people.removeMemberTitle'),
235+
prompt: confirmModalPrompt,
236+
confirmText: translate('common.buttonConfirm'),
237+
cancelText: translate('common.cancel'),
238+
});
239+
return;
240+
}
241+
showRemoveMemberModal();
242+
};
243+
224244
const navigateToProfile = () => {
225245
Navigation.navigate(ROUTES.PROFILE.getRoute(accountID, Navigation.getActiveRoute()));
226246
};
@@ -311,27 +331,6 @@ function WorkspaceMemberDetailsPage({personalDetails, policy, route}: WorkspaceM
311331
style={styles.mb5}
312332
/>
313333
)}
314-
<ConfirmModal
315-
danger
316-
title={translate('workspace.people.removeMemberTitle')}
317-
isVisible={isRemoveMemberConfirmModalVisible}
318-
onConfirm={removeUser}
319-
onCancel={() => setIsRemoveMemberConfirmModalVisible(false)}
320-
prompt={confirmModalPrompt}
321-
confirmText={translate('common.remove')}
322-
cancelText={translate('common.cancel')}
323-
/>
324-
<ConfirmModal
325-
title={translate('workspace.people.removeMemberTitle')}
326-
isVisible={isCannotRemoveUser}
327-
onConfirm={() => {
328-
setIsCannotRemoveUser(false);
329-
}}
330-
prompt={confirmModalPrompt}
331-
confirmText={translate('common.buttonConfirm')}
332-
success
333-
shouldShowCancelButton={false}
334-
/>
335334
</View>
336335
<View style={styles.w100}>
337336
<MenuItemWithTopDescription

0 commit comments

Comments
 (0)