diff --git a/src/ONYXKEYS.ts b/src/ONYXKEYS.ts index 4bf7987f9ceb..cecb02c2ca87 100755 --- a/src/ONYXKEYS.ts +++ b/src/ONYXKEYS.ts @@ -385,6 +385,9 @@ const ONYXKEYS = { /** Stores the information about the state of issuing a new card */ ISSUE_NEW_EXPENSIFY_CARD: 'issueNewExpensifyCard', + /** Stores the information if mobile selection mode is active */ + MOBILE_SELECTION_MODE: 'mobileSelectionMode', + NVP_PRIVATE_CANCELLATION_DETAILS: 'nvp_private_cancellationDetails', /** Stores the information about currently edited advanced approval workflow */ @@ -853,6 +856,7 @@ type OnyxValuesMapping = { [ONYXKEYS.NVP_TRAVEL_SETTINGS]: OnyxTypes.TravelSettings; [ONYXKEYS.REVIEW_DUPLICATES]: OnyxTypes.ReviewDuplicates; [ONYXKEYS.ISSUE_NEW_EXPENSIFY_CARD]: OnyxTypes.IssueNewCard; + [ONYXKEYS.MOBILE_SELECTION_MODE]: OnyxTypes.MobileSelectionMode; [ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL]: string; [ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL]: string; [ONYXKEYS.NVP_BILLING_FUND_ID]: number; diff --git a/src/components/ButtonWithDropdownMenu/index.tsx b/src/components/ButtonWithDropdownMenu/index.tsx index d4ee3e10914a..12802476ed0d 100644 --- a/src/components/ButtonWithDropdownMenu/index.tsx +++ b/src/components/ButtonWithDropdownMenu/index.tsx @@ -79,7 +79,7 @@ function ButtonWithDropdownMenu({ }} onPress={(event) => (!isSplitButton ? setIsMenuVisible(!isMenuVisible) : onPress(event, selectedItem.value))} text={customText ?? selectedItem.text} - isDisabled={isDisabled || !!selectedItem.disabled} + isDisabled={isDisabled || !!selectedItem?.disabled} isLoading={isLoading} shouldRemoveRightBorderRadius style={[styles.flex1, styles.pr0]} diff --git a/src/components/Search/SearchListWithHeader.tsx b/src/components/Search/SearchListWithHeader.tsx index 172685377f97..abb1bc45aebe 100644 --- a/src/components/Search/SearchListWithHeader.tsx +++ b/src/components/Search/SearchListWithHeader.tsx @@ -2,11 +2,8 @@ import type {ForwardedRef} from 'react'; import React, {forwardRef, useCallback, useEffect, useMemo, useState} from 'react'; import ConfirmModal from '@components/ConfirmModal'; import DecisionModal from '@components/DecisionModal'; -import * as Expensicons from '@components/Icon/Expensicons'; -import MenuItem from '@components/MenuItem'; -import Modal from '@components/Modal'; -import SelectionList from '@components/SelectionList'; import type {BaseSelectionListProps, ReportListItemType, SelectionListHandle, TransactionListItemType} from '@components/SelectionList/types'; +import SelectionListWithModal from '@components/SelectionListWithModal'; import useLocalize from '@hooks/useLocalize'; import useWindowDimensions from '@hooks/useWindowDimensions'; import * as SearchActions from '@libs/actions/Search'; @@ -21,8 +18,6 @@ type SearchListWithHeaderProps = Omit void; }; function mapTransactionItemToSelectedEntry(item: TransactionListItemType): [string, SelectedTransactionInfo] { @@ -43,14 +38,9 @@ function mapToItemWithSelectionInfo(item: TransactionListItemType | ReportListIt }; } -function SearchListWithHeader( - {ListItem, onSelectRow, status, hash, data, searchType, isMobileSelectionModeActive, setIsMobileSelectionModeActive, ...props}: SearchListWithHeaderProps, - ref: ForwardedRef, -) { +function SearchListWithHeader({ListItem, onSelectRow, status, hash, data, searchType, ...props}: SearchListWithHeaderProps, ref: ForwardedRef) { const {isSmallScreenWidth} = useWindowDimensions(); const {translate} = useLocalize(); - const [isModalVisible, setIsModalVisible] = useState(false); - const [longPressedItem, setLongPressedItem] = useState(null); const [selectedTransactions, setSelectedTransactions] = useState({}); const [selectedTransactionsToDelete, setSelectedTransactionsToDelete] = useState([]); const [deleteExpensesConfirmModalVisible, setDeleteExpensesConfirmModalVisible] = useState(false); @@ -132,36 +122,6 @@ function SearchListWithHeader( [selectedTransactions], ); - const openBottomModal = (item: TransactionListItemType | ReportListItemType | null) => { - if (!isSmallScreenWidth) { - return; - } - - setLongPressedItem(item); - setIsModalVisible(true); - }; - - const turnOnSelectionMode = useCallback(() => { - setIsMobileSelectionModeActive?.(true); - setIsModalVisible(false); - - if (longPressedItem) { - toggleTransaction(longPressedItem); - } - }, [longPressedItem, setIsMobileSelectionModeActive, toggleTransaction]); - - const closeBottomModal = useCallback(() => { - setIsModalVisible(false); - }, []); - - useEffect(() => { - if (isMobileSelectionModeActive) { - return; - } - - setSelectedTransactions({}); - }, [setSelectedTransactions, isMobileSelectionModeActive]); - const toggleAllTransactions = () => { const areItemsOfReportType = searchType === CONST.SEARCH.DATA_TYPES.REPORT; const flattenedItems = areItemsOfReportType ? (data as ReportListItemType[]).flatMap((item) => item.transactions) : data; @@ -191,23 +151,21 @@ function SearchListWithHeader( status={status} hash={hash} onSelectDeleteOption={handleOnSelectDeleteOption} - isMobileSelectionModeActive={isMobileSelectionModeActive} - setIsMobileSelectionModeActive={setIsMobileSelectionModeActive} selectedReports={selectedReports} setOfflineModalOpen={() => setOfflineModalVisible(true)} setDownloadErrorModalOpen={() => setDownloadErrorModalVisible(true)} /> - + // eslint-disable-next-line react/jsx-props-no-spreading {...props} sections={[{data: sortedSelectedData, isDisabled: false}]} ListItem={ListItem} onSelectRow={onSelectRow} - onLongPressRow={openBottomModal} + turnOnSelectionModeOnLongPress + onTurnOnSelectionMode={(item) => item && toggleTransaction(item)} ref={ref} onCheckboxPress={toggleTransaction} onSelectAll={toggleAllTransactions} - isMobileSelectionModeActive={isMobileSelectionModeActive} /> setDownloadErrorModalVisible(false)} /> - - - ); } diff --git a/src/components/Search/SearchPageHeader.tsx b/src/components/Search/SearchPageHeader.tsx index 01f80f5bab56..0dd83853456d 100644 --- a/src/components/Search/SearchPageHeader.tsx +++ b/src/components/Search/SearchPageHeader.tsx @@ -1,4 +1,5 @@ import React, {useMemo} from 'react'; +import {useOnyx} from 'react-native-onyx'; import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu'; import type {DropdownOption} from '@components/ButtonWithDropdownMenu/types'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; @@ -10,11 +11,13 @@ import useNetwork from '@hooks/useNetwork'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import * as SearchActions from '@libs/actions/Search'; import Navigation from '@libs/Navigation/Navigation'; import SearchSelectedNarrow from '@pages/Search/SearchSelectedNarrow'; import variables from '@styles/variables'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type {SearchReport} from '@src/types/onyx/SearchResults'; import type DeepValueOf from '@src/types/utils/DeepValueOf'; @@ -29,8 +32,6 @@ type SearchPageHeaderProps = { clearSelectedItems?: () => void; hash: number; onSelectDeleteOption?: (itemsToDelete: string[]) => void; - isMobileSelectionModeActive?: boolean; - setIsMobileSelectionModeActive?: (isMobileSelectionModeActive: boolean) => void; setOfflineModalOpen?: () => void; setDownloadErrorModalOpen?: () => void; }; @@ -43,8 +44,6 @@ function SearchPageHeader({ hash, clearSelectedItems, onSelectDeleteOption, - isMobileSelectionModeActive, - setIsMobileSelectionModeActive, setOfflineModalOpen, setDownloadErrorModalOpen, selectedReports, @@ -56,6 +55,7 @@ function SearchPageHeader({ const {activeWorkspaceID} = useActiveWorkspace(); const {isSmallScreenWidth} = useResponsiveLayout(); const {setSelectedTransactionIDs} = useSearchContext(); + const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); const headerContent: {[key in SearchStatus]: {icon: IconAsset; title: string}} = { all: {icon: Illustrations.MoneyReceipts, title: translate('common.expenses')}, @@ -106,8 +106,8 @@ function SearchPageHeader({ } clearSelectedItems?.(); - if (isMobileSelectionModeActive) { - setIsMobileSelectionModeActive?.(false); + if (selectionMode?.isEnabled) { + turnOffMobileSelectionMode(); } setSelectedTransactionIDs(selectedTransactionsKeys); Navigation.navigate(ROUTES.TRANSACTION_HOLD_REASON_RHP); @@ -130,8 +130,8 @@ function SearchPageHeader({ } clearSelectedItems?.(); - if (isMobileSelectionModeActive) { - setIsMobileSelectionModeActive?.(false); + if (selectionMode?.isEnabled) { + turnOffMobileSelectionMode(); } SearchActions.unholdMoneyRequestOnSearch(hash, selectedTransactionsKeys); }, @@ -183,9 +183,7 @@ function SearchPageHeader({ translate, onSelectDeleteOption, clearSelectedItems, - isMobileSelectionModeActive, hash, - setIsMobileSelectionModeActive, theme.icon, styles.colorMuted, styles.fontWeightNormal, @@ -196,10 +194,11 @@ function SearchPageHeader({ selectedReports, styles.textWrap, setSelectedTransactionIDs, + selectionMode?.isEnabled, ]); if (isSmallScreenWidth) { - if (isMobileSelectionModeActive) { + if (selectionMode?.isEnabled) { return ( void; }; const sortableSearchTabs: SearchStatus[] = [CONST.SEARCH.STATUS.ALL]; @@ -42,13 +40,14 @@ const reportItemTransactionHeight = 52; const listItemPadding = 12; // this is equivalent to 'mb3' on every transaction/report list item const searchHeaderHeight = 54; -function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileSelectionModeActive}: SearchProps) { +function Search({queryJSON, policyIDs}: SearchProps) { const {isOffline} = useNetwork(); const styles = useThemeStyles(); const {isLargeScreenWidth, isSmallScreenWidth} = useWindowDimensions(); const navigation = useNavigation>(); const lastSearchResultsRef = useRef>(); const {setCurrentSearchHash} = useSearchContext(); + const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); const [offset, setOffset] = React.useState(0); const {status, sortBy, sortOrder, hash} = queryJSON; @@ -179,7 +178,7 @@ function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileS const shouldShowYear = SearchUtils.shouldShowYear(searchResults?.data); - const canSelectMultiple = isSmallScreenWidth ? isMobileSelectionModeActive : true; + const canSelectMultiple = isSmallScreenWidth ? selectionMode?.isEnabled : true; return ( ( shouldDelayFocus = true, shouldUpdateFocusedIndex = false, onLongPressRow, - isMobileSelectionModeActive, }: BaseSelectionListProps, ref: ForwardedRef, ) { @@ -458,7 +457,6 @@ function BaseSelectionList( showTooltip={showTooltip} canSelectMultiple={canSelectMultiple} onLongPressRow={onLongPressRow} - isMobileSelectionModeActive={isMobileSelectionModeActive} onSelectRow={() => selectRow(item, index)} onCheckboxPress={handleOnCheckboxPress()} onDismissError={() => onDismissError?.(item)} diff --git a/src/components/SelectionList/TableListItem.tsx b/src/components/SelectionList/TableListItem.tsx index 83bc8df36571..ee1ce4672529 100644 --- a/src/components/SelectionList/TableListItem.tsx +++ b/src/components/SelectionList/TableListItem.tsx @@ -23,6 +23,7 @@ function TableListItem({ onDismissError, rightHandSideComponent, onFocus, + onLongPressRow, shouldSyncFocus, }: TableListItemProps) { const styles = useThemeStyles(); @@ -50,6 +51,7 @@ function TableListItem({ isDisabled={isDisabled} showTooltip={showTooltip} canSelectMultiple={canSelectMultiple} + onLongPressRow={onLongPressRow} onSelectRow={onSelectRow} onDismissError={onDismissError} rightHandSideComponent={rightHandSideComponent} diff --git a/src/components/SelectionList/types.ts b/src/components/SelectionList/types.ts index 41097ba83cb4..80f8e6dda1a3 100644 --- a/src/components/SelectionList/types.ts +++ b/src/components/SelectionList/types.ts @@ -66,9 +66,6 @@ type CommonListItemProps = { /** Callback to fire when the item is long pressed */ onLongPressRow?: (item: TItem) => void; - - /** Whether Selection Mode is active - used only on small screens */ - isMobileSelectionModeActive?: boolean; } & TRightHandSideComponent; type ListItem = { @@ -492,9 +489,6 @@ type BaseSelectionListProps = Partial & { /** Callback to fire when the item is long pressed */ onLongPressRow?: (item: TItem) => void; - - /** Whether Selection Mode is active - used only on small screens */ - isMobileSelectionModeActive?: boolean; } & TRightHandSideComponent; type SelectionListHandle = { diff --git a/src/components/SelectionListWithModal/index.tsx b/src/components/SelectionListWithModal/index.tsx new file mode 100644 index 000000000000..45b90f17ac46 --- /dev/null +++ b/src/components/SelectionListWithModal/index.tsx @@ -0,0 +1,91 @@ +import React, {forwardRef, useEffect, useState} from 'react'; +import type {ForwardedRef} from 'react'; +import {useOnyx} from 'react-native-onyx'; +import * as Expensicons from '@components/Icon/Expensicons'; +import MenuItem from '@components/MenuItem'; +import Modal from '@components/Modal'; +import SelectionList from '@components/SelectionList'; +import type {BaseSelectionListProps, ListItem, SelectionListHandle} from '@components/SelectionList/types'; +import useLocalize from '@hooks/useLocalize'; +import useResponsiveLayout from '@hooks/useResponsiveLayout'; +import {turnOffMobileSelectionMode, turnOnMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; +import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; + +type SelectionListWithModalProps = BaseSelectionListProps & { + turnOnSelectionModeOnLongPress?: boolean; + onTurnOnSelectionMode?: (item: TItem | null) => void; +}; + +function SelectionListWithModal( + {turnOnSelectionModeOnLongPress, onTurnOnSelectionMode, onLongPressRow, sections, ...rest}: SelectionListWithModalProps, + ref: ForwardedRef, +) { + const [isModalVisible, setIsModalVisible] = useState(false); + const [longPressedItem, setLongPressedItem] = useState(null); + const {translate} = useLocalize(); + const {isSmallScreenWidth} = useResponsiveLayout(); + const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); + + useEffect(() => { + // We can access 0 index safely as we are not displaying multiple sections in table view + const selectedItems = sections[0].data.filter((item) => item.isSelected); + if (!isSmallScreenWidth) { + if (selectedItems.length === 0) { + turnOffMobileSelectionMode(); + } + return; + } + if (selectedItems.length > 0 && !selectionMode?.isEnabled) { + turnOnMobileSelectionMode(); + } + }, [sections, selectionMode, isSmallScreenWidth]); + + const handleLongPressRow = (item: TItem) => { + if (!turnOnSelectionModeOnLongPress || !isSmallScreenWidth) { + return; + } + setLongPressedItem(item); + setIsModalVisible(true); + + if (onLongPressRow) { + onLongPressRow(item); + } + }; + + const turnOnSelectionMode = () => { + turnOnMobileSelectionMode(); + setIsModalVisible(false); + + if (onTurnOnSelectionMode) { + onTurnOnSelectionMode(longPressedItem); + } + }; + + useEffect(() => turnOffMobileSelectionMode(), []); + + return ( + <> + + setIsModalVisible(false)} + > + + + + ); +} + +export default forwardRef(SelectionListWithModal); diff --git a/src/languages/en.ts b/src/languages/en.ts index 32b9a9eff2b6..0118aed32f4c 100755 --- a/src/languages/en.ts +++ b/src/languages/en.ts @@ -159,6 +159,7 @@ export default { resend: 'Resend', save: 'Save', select: 'Select', + selectMultiple: 'Select multiple', saveChanges: 'Save changes', submit: 'Submit', rotate: 'Rotate', @@ -3577,7 +3578,6 @@ export default { screenShareRequest: 'Expensify is inviting you to a screen share', }, search: { - selectMultiple: 'Select multiple', resultsAreLimited: 'Search results are limited.', viewResults: 'View results', searchResults: { diff --git a/src/languages/es.ts b/src/languages/es.ts index 1636512a6fa4..e117534e5ac7 100644 --- a/src/languages/es.ts +++ b/src/languages/es.ts @@ -142,6 +142,7 @@ export default { find: 'Encontrar', searchWithThreeDots: 'Buscar...', select: 'Seleccionar', + selectMultiple: 'Seleccionar varios', next: 'Siguiente', create: 'Crear', previous: 'Anterior', @@ -3634,7 +3635,6 @@ export default { screenShareRequest: 'Expensify te está invitando a compartir la pantalla', }, search: { - selectMultiple: 'Seleccionar varios', resultsAreLimited: 'Los resultados de búsqueda están limitados.', viewResults: 'Ver resultados', searchResults: { diff --git a/src/libs/actions/MobileSelectionMode.ts b/src/libs/actions/MobileSelectionMode.ts new file mode 100644 index 000000000000..65a51b834901 --- /dev/null +++ b/src/libs/actions/MobileSelectionMode.ts @@ -0,0 +1,12 @@ +import Onyx from 'react-native-onyx'; +import ONYXKEYS from '@src/ONYXKEYS'; + +const turnOnMobileSelectionMode = () => { + Onyx.merge(ONYXKEYS.MOBILE_SELECTION_MODE, {isEnabled: true}); +}; + +const turnOffMobileSelectionMode = () => { + Onyx.merge(ONYXKEYS.MOBILE_SELECTION_MODE, {isEnabled: false}); +}; + +export {turnOnMobileSelectionMode, turnOffMobileSelectionMode}; diff --git a/src/pages/ReportParticipantsPage.tsx b/src/pages/ReportParticipantsPage.tsx index 2a1fca17abbf..97d05a81e308 100755 --- a/src/pages/ReportParticipantsPage.tsx +++ b/src/pages/ReportParticipantsPage.tsx @@ -2,8 +2,7 @@ import {useIsFocused} from '@react-navigation/native'; import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import {InteractionManager, View} from 'react-native'; import type {TextInput} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; -import {useOnyx, withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import type {ValueOf} from 'type-fest'; import Badge from '@components/Badge'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; @@ -14,14 +13,15 @@ import ConfirmModal from '@components/ConfirmModal'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import ScreenWrapper from '@components/ScreenWrapper'; -import SelectionList from '@components/SelectionList'; import TableListItem from '@components/SelectionList/TableListItem'; import type {ListItem, SelectionListHandle} from '@components/SelectionList/types'; +import SelectionListWithModal from '@components/SelectionListWithModal'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; +import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import * as Report from '@libs/actions/Report'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; @@ -30,37 +30,30 @@ import * as ReportUtils from '@libs/ReportUtils'; import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; -import type {PersonalDetailsList, Session} from '@src/types/onyx'; import type {WithReportOrNotFoundProps} from './home/report/withReportOrNotFound'; import withReportOrNotFound from './home/report/withReportOrNotFound'; -type ReportParticipantsPageOnyxProps = { - /** Personal details of all the users */ - personalDetails: OnyxEntry; - - /** Session info for the currently logged in user. */ - session: OnyxEntry; -}; - -type ReportParticipantsPageProps = ReportParticipantsPageOnyxProps & WithReportOrNotFoundProps; - type MemberOption = Omit & {accountID: number}; -function ReportParticipantsPage({report, personalDetails, session}: ReportParticipantsPageProps) { +function ReportParticipantsPage({report}: WithReportOrNotFoundProps) { const [selectedMembers, setSelectedMembers] = useState([]); const [removeMembersConfirmModalVisible, setRemoveMembersConfirmModalVisible] = useState(false); const {translate, formatPhoneNumber} = useLocalize(); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); - const {shouldUseNarrowLayout} = useResponsiveLayout(); + const {shouldUseNarrowLayout, isSmallScreenWidth} = useResponsiveLayout(); const selectionListRef = useRef(null); const textInputRef = useRef(null); - const currentUserAccountID = Number(session?.accountID); const [reportNameValuePairs] = useOnyx(`${ONYXKEYS.COLLECTION.REPORT_NAME_VALUE_PAIRS}${report?.reportID ?? -1}`); + const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); + const [session] = useOnyx(ONYXKEYS.SESSION); + const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST); + const currentUserAccountID = Number(session?.accountID); const isCurrentUserAdmin = ReportUtils.isGroupChatAdmin(report, currentUserAccountID); const isGroupChat = useMemo(() => ReportUtils.isGroupChat(report), [report]); const isIOUReport = ReportUtils.isIOUReport(report); const isFocused = useIsFocused(); + const canSelectMultiple = isGroupChat && isCurrentUserAdmin && (isSmallScreenWidth ? selectionMode?.isEnabled : true); useEffect(() => { if (isFocused) { @@ -180,15 +173,19 @@ function ReportParticipantsPage({report, personalDetails, session}: ReportPartic * Toggle user from the selectedMembers list */ const toggleUser = useCallback( - (accountID: number) => { + (user: MemberOption) => { + if (user.accountID === currentUserAccountID) { + return; + } + // Add or remove the user if the checkbox is enabled - if (selectedMembers.includes(accountID)) { - removeUser(accountID); + if (selectedMembers.includes(user.accountID)) { + removeUser(user.accountID); } else { - addUser(accountID); + addUser(user.accountID); } }, - [selectedMembers, addUser, removeUser], + [selectedMembers, addUser, removeUser, currentUserAccountID], ); const headerContent = useMemo(() => { @@ -215,12 +212,12 @@ function ReportParticipantsPage({report, personalDetails, session}: ReportPartic ); - if (isCurrentUserAdmin) { + if (canSelectMultiple) { return header; } return {header}; - }, [styles, translate, isGroupChat, isCurrentUserAdmin, StyleUtils]); + }, [styles, translate, isGroupChat, isCurrentUserAdmin, StyleUtils, canSelectMultiple]); const bulkActionsButtonOptions = useMemo(() => { const options: Array> = [ @@ -264,7 +261,7 @@ function ReportParticipantsPage({report, personalDetails, session}: ReportPartic return ( - {selectedMembers.length > 0 ? ( + {(isSmallScreenWidth ? canSelectMultiple : selectedMembers.length > 0) ? ( shouldAlwaysShowDropdownMenu pressOnEnter @@ -287,7 +284,7 @@ function ReportParticipantsPage({report, personalDetails, session}: ReportPartic )} ); - }, [bulkActionsButtonOptions, inviteUser, shouldUseNarrowLayout, selectedMembers, styles, translate, isGroupChat]); + }, [bulkActionsButtonOptions, inviteUser, isSmallScreenWidth, selectedMembers, styles, translate, isGroupChat, canSelectMultiple, shouldUseNarrowLayout]); /** Opens the member details page */ const openMemberDetails = useCallback( @@ -313,6 +310,9 @@ function ReportParticipantsPage({report, personalDetails, session}: ReportPartic } return translate('common.details'); }, [report, translate, isGroupChat]); + + const selectionModeHeader = selectionMode?.isEnabled && isSmallScreenWidth; + return ( Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report.reportID)) : undefined} - shouldShowBackButton + title={selectionModeHeader ? translate('common.selectMultiple') : headerTitle} + onBackButtonPress={() => { + if (selectionMode?.isEnabled) { + setSelectedMembers([]); + turnOffMobileSelectionMode(); + return; + } + + if (report) { + Navigation.goBack(ROUTES.REPORT_WITH_ID_DETAILS.getRoute(report.reportID)); + } + }} guidesCallTaskID={CONST.GUIDES_CALL_TASK_IDS.WORKSPACE_MEMBERS} /> {headerButtons} @@ -346,15 +355,17 @@ function ReportParticipantsPage({report, personalDetails, session}: ReportPartic }} /> - item && toggleUser(item)} sections={[{data: participants}]} ListItem={TableListItem} headerContent={headerContent} onSelectRow={openMemberDetails} shouldDebounceRowSelect={!(isGroupChat && isCurrentUserAdmin)} - onCheckboxPress={(item) => toggleUser(item.accountID)} + onCheckboxPress={(item) => toggleUser(item)} onSelectAll={() => toggleAllUsers(participants)} showScrollIndicator textInputRef={textInputRef} @@ -369,13 +380,4 @@ function ReportParticipantsPage({report, personalDetails, session}: ReportPartic ReportParticipantsPage.displayName = 'ReportParticipantsPage'; -export default withReportOrNotFound()( - withOnyx({ - personalDetails: { - key: ONYXKEYS.PERSONAL_DETAILS_LIST, - }, - session: { - key: ONYXKEYS.SESSION, - }, - })(ReportParticipantsPage), -); +export default withReportOrNotFound()(ReportParticipantsPage); diff --git a/src/pages/Search/SearchPageBottomTab.tsx b/src/pages/Search/SearchPageBottomTab.tsx index 35679b158b7f..4f3138aef110 100644 --- a/src/pages/Search/SearchPageBottomTab.tsx +++ b/src/pages/Search/SearchPageBottomTab.tsx @@ -1,4 +1,5 @@ -import React, {useMemo, useState} from 'react'; +import React, {useMemo} from 'react'; +import {useOnyx} from 'react-native-onyx'; import FullPageNotFoundView from '@components/BlockingViews/FullPageNotFoundView'; import HeaderWithBackButton from '@components/HeaderWithBackButton'; import ScreenWrapper from '@components/ScreenWrapper'; @@ -7,11 +8,13 @@ import useActiveCentralPaneRoute from '@hooks/useActiveCentralPaneRoute'; import useLocalize from '@hooks/useLocalize'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; +import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import Navigation from '@libs/Navigation/Navigation'; import type {AuthScreensParamList} from '@libs/Navigation/types'; import {buildSearchQueryJSON} from '@libs/SearchUtils'; import TopBar from '@navigation/AppNavigator/createCustomBottomTabNavigator/TopBar'; import CONST from '@src/CONST'; +import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import SCREENS from '@src/SCREENS'; import SearchStatusMenu from './SearchStatusMenu'; @@ -21,7 +24,7 @@ function SearchPageBottomTab() { const {shouldUseNarrowLayout} = useResponsiveLayout(); const activeCentralPaneRoute = useActiveCentralPaneRoute(); const styles = useThemeStyles(); - const [isMobileSelectionModeActive, setIsMobileSelectionModeActive] = useState(false); + const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); const {queryJSON, policyIDs} = useMemo(() => { if (!activeCentralPaneRoute || activeCentralPaneRoute.name !== SCREENS.SEARCH.CENTRAL_PANE) { @@ -50,7 +53,7 @@ function SearchPageBottomTab() { onBackButtonPress={handleOnBackButtonPress} shouldShowLink={false} > - {!isMobileSelectionModeActive && queryJSON ? ( + {!selectionMode?.isEnabled && queryJSON ? ( <> ) : ( setIsMobileSelectionModeActive(false)} + title={translate('common.selectMultiple')} + onBackButtonPress={turnOffMobileSelectionMode} /> )} {shouldUseNarrowLayout && queryJSON && ( )} diff --git a/src/pages/workspace/WorkspaceMembersPage.tsx b/src/pages/workspace/WorkspaceMembersPage.tsx index f7e5c4933ac8..d6fdb4bcd71c 100644 --- a/src/pages/workspace/WorkspaceMembersPage.tsx +++ b/src/pages/workspace/WorkspaceMembersPage.tsx @@ -5,7 +5,7 @@ import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'; import type {TextInput} from 'react-native'; import {InteractionManager, View} from 'react-native'; import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import Badge from '@components/Badge'; import Button from '@components/Button'; import ButtonWithDropdownMenu from '@components/ButtonWithDropdownMenu'; @@ -14,9 +14,9 @@ import ConfirmModal from '@components/ConfirmModal'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import MessagesRow from '@components/MessagesRow'; -import SelectionList from '@components/SelectionList'; import TableListItem from '@components/SelectionList/TableListItem'; import type {ListItem, SelectionListHandle} from '@components/SelectionList/types'; +import SelectionListWithModal from '@components/SelectionListWithModal'; import Text from '@components/Text'; import type {WithCurrentUserPersonalDetailsProps} from '@components/withCurrentUserPersonalDetails'; import withCurrentUserPersonalDetails from '@components/withCurrentUserPersonalDetails'; @@ -26,6 +26,7 @@ import usePrevious from '@hooks/usePrevious'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useStyleUtils from '@hooks/useStyleUtils'; import useThemeStyles from '@hooks/useThemeStyles'; +import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import Log from '@libs/Log'; import Navigation from '@libs/Navigation/Navigation'; @@ -40,24 +41,14 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type {InvitedEmailsToAccountIDs, PersonalDetailsList, PolicyEmployeeList, Session} from '@src/types/onyx'; +import type {PersonalDetailsList, PolicyEmployeeList} from '@src/types/onyx'; import type {Errors, PendingAction} from '@src/types/onyx/OnyxCommon'; import {isEmptyObject} from '@src/types/utils/EmptyObject'; import type {WithPolicyAndFullscreenLoadingProps} from './withPolicyAndFullscreenLoading'; import withPolicyAndFullscreenLoading from './withPolicyAndFullscreenLoading'; import WorkspacePageWithSections from './WorkspacePageWithSections'; -type WorkspaceMembersPageOnyxProps = { - /** Session info for the currently logged in user. */ - session: OnyxEntry; - - /** An object containing the accountID for every invited user email */ - invitedEmailsToAccountIDsDraft: OnyxEntry; -}; -type WorkspaceMembersPageProps = WithPolicyAndFullscreenLoadingProps & - WithCurrentUserPersonalDetailsProps & - WorkspaceMembersPageOnyxProps & - StackScreenProps; +type WorkspaceMembersPageProps = WithPolicyAndFullscreenLoadingProps & WithCurrentUserPersonalDetailsProps & StackScreenProps; /** * Inverts an object, equivalent of _.invert @@ -69,7 +60,7 @@ function invertObject(object: Record): Record { type MemberOption = Omit & {accountID: number}; -function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, route, policy, session, currentUserPersonalDetails}: WorkspaceMembersPageProps) { +function WorkspaceMembersPage({personalDetails, route, policy, currentUserPersonalDetails}: WorkspaceMembersPageProps) { const policyMemberEmailsToAccountIDs = useMemo(() => PolicyUtils.getMemberAccountIDsForWorkspace(policy?.employeeList, true), [policy?.employeeList]); const styles = useThemeStyles(); const StyleUtils = useStyleUtils(); @@ -90,10 +81,16 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, () => !isOfflineAndNoMemberDataAvailable && (!OptionsListUtils.isPersonalDetailsReady(personalDetails) || isEmptyObject(policy?.employeeList)), [isOfflineAndNoMemberDataAvailable, personalDetails, policy?.employeeList], ); + + const [invitedEmailsToAccountIDsDraft] = useOnyx(`${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${route.params.policyID.toString()}`); + const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); + const [session] = useOnyx(ONYXKEYS.SESSION); const selectionListRef = useRef(null); const isFocused = useIsFocused(); const policyID = route.params.policyID; + const canSelectMultiple = isPolicyAdmin && (shouldUseNarrowLayout ? selectionMode?.isEnabled : true); + const confirmModalPrompt = useMemo(() => { const approverAccountID = selectedEmployees.find((selectedEmployee) => Member.isApprover(policy, selectedEmployee)); if (!approverAccountID) { @@ -261,6 +258,10 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, */ const toggleUser = useCallback( (accountID: number, pendingAction?: PendingAction) => { + if (accountID === policy?.ownerAccountID && accountID !== session?.accountID) { + return; + } + if (pendingAction === CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE) { return; } @@ -272,7 +273,7 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, addUser(accountID); } }, - [selectedEmployees, addUser, removeUser], + [selectedEmployees, addUser, removeUser, policy?.ownerAccountID, session?.accountID], ); /** Opens the member details page */ @@ -422,18 +423,26 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, ); + useEffect(() => { + if (selectionMode?.isEnabled) { + return; + } + + setSelectedEmployees([]); + }, [setSelectedEmployees, selectionMode?.isEnabled]); + const getCustomListHeader = () => { const header = ( - {translate('common.member')} + {translate('common.member')} {translate('common.role')} ); - if (isPolicyAdmin) { + if (canSelectMultiple) { return header; } return {header}; @@ -497,7 +506,7 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, } return ( - {selectedEmployees.length > 0 ? ( + {(shouldUseNarrowLayout ? canSelectMultiple : selectedEmployees.length > 0) ? ( shouldAlwaysShowDropdownMenu pressOnEnter @@ -523,17 +532,27 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, ); }; + const selectionModeHeader = selectionMode?.isEnabled && shouldUseNarrowLayout; + return ( { + if (selectionMode?.isEnabled) { + setSelectedEmployees([]); + turnOffMobileSelectionMode(); + return; + } + Navigation.goBack(); + }} > {() => ( <> @@ -557,11 +576,13 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, }} /> - item && toggleUser(item?.accountID)} shouldUseUserSkeletonView disableKeyboardShortcuts={removeMembersConfirmModalVisible} headerMessage={getHeaderMessage()} @@ -588,15 +609,4 @@ function WorkspaceMembersPage({personalDetails, invitedEmailsToAccountIDsDraft, WorkspaceMembersPage.displayName = 'WorkspaceMembersPage'; -export default withCurrentUserPersonalDetails( - withPolicyAndFullscreenLoading( - withOnyx({ - invitedEmailsToAccountIDsDraft: { - key: ({route}) => `${ONYXKEYS.COLLECTION.WORKSPACE_INVITE_MEMBERS_DRAFT}${route.params.policyID.toString()}`, - }, - session: { - key: ONYXKEYS.SESSION, - }, - })(WorkspaceMembersPage), - ), -); +export default withCurrentUserPersonalDetails(withPolicyAndFullscreenLoading(WorkspaceMembersPage)); diff --git a/src/pages/workspace/WorkspacePageWithSections.tsx b/src/pages/workspace/WorkspacePageWithSections.tsx index 277c77c53582..e46e7071333f 100644 --- a/src/pages/workspace/WorkspacePageWithSections.tsx +++ b/src/pages/workspace/WorkspacePageWithSections.tsx @@ -90,6 +90,9 @@ type WorkspacePageWithSectionsProps = WithPolicyAndFullscreenLoadingProps & /** Whether the page is loading, example any other API call in progres */ isLoading?: boolean; + + /** Callback to be called when the back button is pressed */ + onBackButtonPress?: () => void; }; function fetchData(policyID: string, skipVBBACal?: boolean) { @@ -122,6 +125,7 @@ function WorkspacePageWithSections({ testID, shouldShowNotFoundPage = false, isLoading: isPageLoading = false, + onBackButtonPress, }: WorkspacePageWithSectionsProps) { const styles = useThemeStyles(); const policyID = route.params?.policyID ?? '-1'; @@ -181,8 +185,8 @@ function WorkspacePageWithSections({ (onBackButtonPress ? onBackButtonPress() : Navigation.goBack(backButtonRoute))} shouldShowBackButton={shouldUseNarrowLayout || shouldShowBackButton} - onBackButtonPress={() => Navigation.goBack(backButtonRoute)} icon={icon ?? undefined} style={styles.headerBarDesktopHeight} > diff --git a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx index f8b539ca61db..d9179f7317b3 100644 --- a/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx +++ b/src/pages/workspace/categories/WorkspaceCategoriesPage.tsx @@ -13,10 +13,10 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; -import SelectionList from '@components/SelectionList'; import ListItemRightCaretWithLabel from '@components/SelectionList/ListItemRightCaretWithLabel'; import TableListItem from '@components/SelectionList/TableListItem'; import type {ListItem} from '@components/SelectionList/types'; +import SelectionListWithModal from '@components/SelectionListWithModal'; import TableListItemSkeleton from '@components/Skeletons/TableRowSkeleton'; import Text from '@components/Text'; import TextLink from '@components/TextLink'; @@ -26,6 +26,7 @@ import useNetwork from '@hooks/useNetwork'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import localeCompare from '@libs/LocaleCompare'; import Navigation from '@libs/Navigation/Navigation'; @@ -61,9 +62,12 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { const backTo = route.params?.backTo; const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyId}`); const [policyCategories] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY_CATEGORIES}${policyId}`); + const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); const isConnectedToAccounting = Object.keys(policy?.connections ?? {}).length > 0; const currentConnectionName = PolicyUtils.getCurrentConnectionName(policy); + const canSelectMultiple = shouldUseNarrowLayout ? selectionMode?.isEnabled : true; + const fetchCategories = useCallback(() => { Category.openPolicyCategoriesPage(policyId); }, [policyId]); @@ -100,7 +104,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { [policyCategories, selectedCategories, translate], ); - const toggleCategory = (category: PolicyOption) => { + const toggleCategory = useCallback((category: PolicyOption) => { setSelectedCategories((prev) => { if (prev[category.keyForList]) { const {[category.keyForList]: omittedCategory, ...newCategories} = prev; @@ -108,7 +112,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { } return {...prev, [category.keyForList]: true}; }); - }; + }, []); const toggleAllCategories = () => { const availableCategories = categoryList.filter((category) => category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE); @@ -117,7 +121,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { }; const getCustomListHeader = () => ( - + {translate('common.name')} {translate('statusPage.status')} @@ -163,7 +167,7 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { const options: Array>> = []; const isThereAnyAccountingConnection = Object.keys(policy?.connections ?? {}).length !== 0; - if (selectedCategoriesArray.length > 0) { + if (shouldUseNarrowLayout ? canSelectMultiple : selectedCategoriesArray.length > 0) { if (!isThereAnyAccountingConnection) { options.push({ icon: Expensicons.Trashcan, @@ -257,6 +261,14 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { const isLoading = !isOffline && policyCategories === undefined; + useEffect(() => { + if (selectionMode?.isEnabled) { + return; + } + + setSelectedCategories({}); + }, [setSelectedCategories, selectionMode?.isEnabled]); + const hasVisibleCategories = categoryList.some((category) => category.pendingAction !== CONST.RED_BRICK_ROAD_PENDING_ACTION.DELETE || isOffline); const getHeaderText = () => ( @@ -278,6 +290,8 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { ); + const selectionModeHeader = selectionMode?.isEnabled && shouldUseNarrowLayout; + return ( Navigation.goBack(backTo)} + title={selectionModeHeader ? translate('common.selectMultiple') : translate('workspace.common.categories')} + icon={!selectionModeHeader ? Illustrations.FolderOpen : undefined} + onBackButtonPress={() => { + if (selectionMode?.isEnabled) { + setSelectedCategories({}); + turnOffMobileSelectionMode(); + return; + } + Navigation.goBack(backTo); + }} > {!shouldUseNarrowLayout && getHeaderButtons()} @@ -331,8 +352,10 @@ function WorkspaceCategoriesPage({route}: WorkspaceCategoriesPageProps) { /> )} {hasVisibleCategories && !isLoading && ( - item && toggleCategory(item)} sections={[{data: categoryList, isDisabled: false}]} onCheckboxPress={toggleCategory} onSelectRow={navigateToCategorySettings} diff --git a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx index c11a7473e3ab..6c0c282d0c24 100644 --- a/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx +++ b/src/pages/workspace/distanceRates/PolicyDistanceRatesPage.tsx @@ -2,8 +2,7 @@ import {useFocusEffect, useIsFocused} from '@react-navigation/native'; import type {StackScreenProps} from '@react-navigation/stack'; import React, {useCallback, useEffect, useMemo, useState} from 'react'; import {ActivityIndicator, View} from 'react-native'; -import type {OnyxEntry} from 'react-native-onyx'; -import {withOnyx} from 'react-native-onyx'; +import {useOnyx} from 'react-native-onyx'; import Button from '@components/Button'; import type {DropdownOption, WorkspaceDistanceRatesBulkActionType} from '@components/ButtonWithDropdownMenu/types'; import ConfirmModal from '@components/ConfirmModal'; @@ -11,16 +10,17 @@ import HeaderWithBackButton from '@components/HeaderWithBackButton'; import * as Expensicons from '@components/Icon/Expensicons'; import * as Illustrations from '@components/Icon/Illustrations'; import ScreenWrapper from '@components/ScreenWrapper'; -import SelectionList from '@components/SelectionList'; import ListItemRightCaretWithLabel from '@components/SelectionList/ListItemRightCaretWithLabel'; import TableListItem from '@components/SelectionList/TableListItem'; import type {ListItem} from '@components/SelectionList/types'; +import SelectionListWithModal from '@components/SelectionListWithModal'; import Text from '@components/Text'; import useLocalize from '@hooks/useLocalize'; import useNetwork from '@hooks/useNetwork'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useTheme from '@hooks/useTheme'; import useThemeStyles from '@hooks/useThemeStyles'; +import {turnOffMobileSelectionMode} from '@libs/actions/MobileSelectionMode'; import * as CurrencyUtils from '@libs/CurrencyUtils'; import * as DeviceCapabilities from '@libs/DeviceCapabilities'; import Navigation from '@libs/Navigation/Navigation'; @@ -32,19 +32,17 @@ import CONST from '@src/CONST'; import ONYXKEYS from '@src/ONYXKEYS'; import ROUTES from '@src/ROUTES'; import type SCREENS from '@src/SCREENS'; -import type * as OnyxTypes from '@src/types/onyx'; import type {CustomUnit, Rate} from '@src/types/onyx/Policy'; type RateForList = ListItem & {value: string}; -type PolicyDistanceRatesPageOnyxProps = { - /** Policy details */ - policy: OnyxEntry; -}; +type PolicyDistanceRatesPageProps = StackScreenProps; -type PolicyDistanceRatesPageProps = PolicyDistanceRatesPageOnyxProps & StackScreenProps; - -function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) { +function PolicyDistanceRatesPage({ + route: { + params: {policyID}, + }, +}: PolicyDistanceRatesPageProps) { const {shouldUseNarrowLayout} = useResponsiveLayout(); const styles = useThemeStyles(); const theme = useTheme(); @@ -52,8 +50,11 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) const [selectedDistanceRates, setSelectedDistanceRates] = useState([]); const [isWarningModalVisible, setIsWarningModalVisible] = useState(false); const [isDeleteModalVisible, setIsDeleteModalVisible] = useState(false); - const policyID = route.params.policyID; const isFocused = useIsFocused(); + const [policy] = useOnyx(`${ONYXKEYS.COLLECTION.POLICY}${policyID}`); + const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE); + + const canSelectMultiple = shouldUseNarrowLayout ? selectionMode?.isEnabled : true; const customUnit: CustomUnit | undefined = useMemo( () => (policy?.customUnits !== undefined ? policy?.customUnits[Object.keys(policy?.customUnits)[0]] : undefined), @@ -191,7 +192,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) }; const getCustomListHeader = () => ( - + {translate('workspace.distanceRates.rate')} {translate('statusPage.status')} @@ -234,7 +235,7 @@ function PolicyDistanceRatesPage({policy, route}: PolicyDistanceRatesPageProps) const headerButtons = ( - {selectedDistanceRates.length === 0 ? ( + {(shouldUseNarrowLayout ? !selectionMode?.isEnabled : selectedDistanceRates.length === 0) ? ( <>