Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mobile Selection Mode #46096

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e957a29
Add Onyx key
filip-solecki Jul 24, 2024
a55571b
Move translations
filip-solecki Jul 24, 2024
3b149a1
Refactor Search Selection Mode
filip-solecki Jul 24, 2024
8c4578e
Add Selection Mode to Workspace Members page
filip-solecki Jul 24, 2024
a9248e0
Add Selection Mode to Workspace Categories page
filip-solecki Jul 24, 2024
9f40d33
Add Selection Mode to Workspace Distance Rates page
filip-solecki Jul 24, 2024
d5956f8
Add Selection Mode to Workspace multi level tags page
filip-solecki Jul 24, 2024
25b0250
Clean SearchListWithHeader
filip-solecki Jul 24, 2024
e8c43ef
Add selection mode to Workspace Tags page
filip-solecki Jul 24, 2024
1fec030
Add selection mode to Workspace Taxes page
filip-solecki Jul 24, 2024
7e22023
Add selection mode to Workspace Report Fields page
filip-solecki Jul 24, 2024
382d104
Fixes for header buttons
filip-solecki Jul 24, 2024
95b6d66
Add selection mode to Group chat members page
filip-solecki Jul 24, 2024
6b0dbd9
Fix tags page
filip-solecki Jul 25, 2024
eed24fc
Fix headers in Tags and Distance Rates pages
filip-solecki Jul 25, 2024
d5393b3
Fix Workspace Members page
filip-solecki Jul 25, 2024
12693c5
Fix self selecting for group chat admin
filip-solecki Jul 25, 2024
3b19a67
Merge branch 'main' into filip-solecki/mobile-selection-mode
filip-solecki Jul 25, 2024
14feef4
Turn off selection mode on large screen
filip-solecki Jul 25, 2024
edc7547
Merge branch 'main' into filip-solecki/mobile-selection-mode
filip-solecki Jul 26, 2024
8923b83
Resolve merge changes
filip-solecki Jul 26, 2024
b4a81a3
Add mobile selection mode to add list type report field
filip-solecki Jul 26, 2024
88cb815
Merge branch 'main' into filip-solecki/mobile-selection-mode
filip-solecki Jul 29, 2024
e981e36
Merge branch 'main' into filip-solecki/mobile-selection-mode
filip-solecki Jul 30, 2024
7a0e5a6
Move turning on selection mode logic on resize to main SelectionListW…
filip-solecki Jul 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion src/components/ButtonWithDropdownMenu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ function ButtonWithDropdownMenu<IValueType>({
}}
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]}
Expand Down
63 changes: 5 additions & 58 deletions src/components/Search/SearchListWithHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -21,8 +18,6 @@ type SearchListWithHeaderProps = Omit<BaseSelectionListProps<ReportListItemType
hash: number;
data: TransactionListItemType[] | ReportListItemType[];
searchType: SearchDataTypes;
isMobileSelectionModeActive?: boolean;
setIsMobileSelectionModeActive?: (isMobileSelectionModeActive: boolean) => void;
};

function mapTransactionItemToSelectedEntry(item: TransactionListItemType): [string, SelectedTransactionInfo] {
Expand All @@ -43,14 +38,9 @@ function mapToItemWithSelectionInfo(item: TransactionListItemType | ReportListIt
};
}

function SearchListWithHeader(
{ListItem, onSelectRow, status, hash, data, searchType, isMobileSelectionModeActive, setIsMobileSelectionModeActive, ...props}: SearchListWithHeaderProps,
ref: ForwardedRef<SelectionListHandle>,
) {
function SearchListWithHeader({ListItem, onSelectRow, status, hash, data, searchType, ...props}: SearchListWithHeaderProps, ref: ForwardedRef<SelectionListHandle>) {
const {isSmallScreenWidth} = useWindowDimensions();
const {translate} = useLocalize();
const [isModalVisible, setIsModalVisible] = useState(false);
const [longPressedItem, setLongPressedItem] = useState<TransactionListItemType | ReportListItemType | null>(null);
const [selectedTransactions, setSelectedTransactions] = useState<SelectedTransactions>({});
const [selectedTransactionsToDelete, setSelectedTransactionsToDelete] = useState<string[]>([]);
const [deleteExpensesConfirmModalVisible, setDeleteExpensesConfirmModalVisible] = useState(false);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -191,23 +151,21 @@ function SearchListWithHeader(
status={status}
hash={hash}
onSelectDeleteOption={handleOnSelectDeleteOption}
isMobileSelectionModeActive={isMobileSelectionModeActive}
setIsMobileSelectionModeActive={setIsMobileSelectionModeActive}
selectedReports={selectedReports}
setOfflineModalOpen={() => setOfflineModalVisible(true)}
setDownloadErrorModalOpen={() => setDownloadErrorModalVisible(true)}
/>
<SelectionList<ReportListItemType | TransactionListItemType>
<SelectionListWithModal<ReportListItemType | TransactionListItemType>
// 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}
/>
<ConfirmModal
isVisible={deleteExpensesConfirmModalVisible}
Expand Down Expand Up @@ -237,17 +195,6 @@ function SearchListWithHeader(
isVisible={downloadErrorModalVisible}
onClose={() => setDownloadErrorModalVisible(false)}
/>
<Modal
isVisible={isModalVisible}
type={CONST.MODAL.MODAL_TYPE.BOTTOM_DOCKED}
onClose={closeBottomModal}
>
<MenuItem
title={translate('common.select')}
icon={Expensicons.Checkmark}
onPress={turnOnSelectionMode}
/>
</Modal>
</>
);
}
Expand Down
21 changes: 10 additions & 11 deletions src/components/Search/SearchPageHeader.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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';
Expand All @@ -29,8 +32,6 @@ type SearchPageHeaderProps = {
clearSelectedItems?: () => void;
hash: number;
onSelectDeleteOption?: (itemsToDelete: string[]) => void;
isMobileSelectionModeActive?: boolean;
setIsMobileSelectionModeActive?: (isMobileSelectionModeActive: boolean) => void;
setOfflineModalOpen?: () => void;
setDownloadErrorModalOpen?: () => void;
};
Expand All @@ -43,8 +44,6 @@ function SearchPageHeader({
hash,
clearSelectedItems,
onSelectDeleteOption,
isMobileSelectionModeActive,
setIsMobileSelectionModeActive,
setOfflineModalOpen,
setDownloadErrorModalOpen,
selectedReports,
Expand All @@ -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')},
Expand Down Expand Up @@ -106,8 +106,8 @@ function SearchPageHeader({
}

clearSelectedItems?.();
if (isMobileSelectionModeActive) {
setIsMobileSelectionModeActive?.(false);
if (selectionMode?.isEnabled) {
turnOffMobileSelectionMode();
}
setSelectedTransactionIDs(selectedTransactionsKeys);
Navigation.navigate(ROUTES.TRANSACTION_HOLD_REASON_RHP);
Expand All @@ -130,8 +130,8 @@ function SearchPageHeader({
}

clearSelectedItems?.();
if (isMobileSelectionModeActive) {
setIsMobileSelectionModeActive?.(false);
if (selectionMode?.isEnabled) {
turnOffMobileSelectionMode();
}
SearchActions.unholdMoneyRequestOnSearch(hash, selectedTransactionsKeys);
},
Expand Down Expand Up @@ -183,9 +183,7 @@ function SearchPageHeader({
translate,
onSelectDeleteOption,
clearSelectedItems,
isMobileSelectionModeActive,
hash,
setIsMobileSelectionModeActive,
theme.icon,
styles.colorMuted,
styles.fontWeightNormal,
Expand All @@ -196,10 +194,11 @@ function SearchPageHeader({
selectedReports,
styles.textWrap,
setSelectedTransactionIDs,
selectionMode?.isEnabled,
]);

if (isSmallScreenWidth) {
if (isMobileSelectionModeActive) {
if (selectionMode?.isEnabled) {
return (
<SearchSelectedNarrow
options={headerButtonsOptions}
Expand Down
9 changes: 3 additions & 6 deletions src/components/Search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ import type {SearchColumnType, SearchQueryJSON, SearchStatus, SortOrder} from '.
type SearchProps = {
queryJSON: SearchQueryJSON;
policyIDs?: string;
isMobileSelectionModeActive?: boolean;
setIsMobileSelectionModeActive?: (isMobileSelectionModeActive: boolean) => void;
};

const sortableSearchTabs: SearchStatus[] = [CONST.SEARCH.STATUS.ALL];
Expand 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<StackNavigationProp<AuthScreensParamList>>();
const lastSearchResultsRef = useRef<OnyxEntry<SearchResults>>();
const {setCurrentSearchHash} = useSearchContext();
const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE);
const [offset, setOffset] = React.useState(0);

const {status, sortBy, sortOrder, hash} = queryJSON;
Expand Down Expand Up @@ -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 (
<SearchListWithHeader
Expand Down Expand Up @@ -223,8 +222,6 @@ function Search({queryJSON, policyIDs, isMobileSelectionModeActive, setIsMobileS
showScrollIndicator={false}
onEndReachedThreshold={0.75}
onEndReached={fetchMoreResults}
setIsMobileSelectionModeActive={setIsMobileSelectionModeActive}
isMobileSelectionModeActive={isMobileSelectionModeActive}
listFooterContent={
shouldShowLoadingMoreItems ? (
<SearchRowSkeleton
Expand Down
2 changes: 0 additions & 2 deletions src/components/SelectionList/BaseSelectionList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,6 @@ function BaseSelectionList<TItem extends ListItem>(
shouldDelayFocus = true,
shouldUpdateFocusedIndex = false,
onLongPressRow,
isMobileSelectionModeActive,
}: BaseSelectionListProps<TItem>,
ref: ForwardedRef<SelectionListHandle>,
) {
Expand Down Expand Up @@ -458,7 +457,6 @@ function BaseSelectionList<TItem extends ListItem>(
showTooltip={showTooltip}
canSelectMultiple={canSelectMultiple}
onLongPressRow={onLongPressRow}
isMobileSelectionModeActive={isMobileSelectionModeActive}
onSelectRow={() => selectRow(item, index)}
onCheckboxPress={handleOnCheckboxPress()}
onDismissError={() => onDismissError?.(item)}
Expand Down
2 changes: 2 additions & 0 deletions src/components/SelectionList/TableListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ function TableListItem<TItem extends ListItem>({
onDismissError,
rightHandSideComponent,
onFocus,
onLongPressRow,
shouldSyncFocus,
}: TableListItemProps<TItem>) {
const styles = useThemeStyles();
Expand Down Expand Up @@ -50,6 +51,7 @@ function TableListItem<TItem extends ListItem>({
isDisabled={isDisabled}
showTooltip={showTooltip}
canSelectMultiple={canSelectMultiple}
onLongPressRow={onLongPressRow}
onSelectRow={onSelectRow}
onDismissError={onDismissError}
rightHandSideComponent={rightHandSideComponent}
Expand Down
6 changes: 0 additions & 6 deletions src/components/SelectionList/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,6 @@ type CommonListItemProps<TItem extends ListItem> = {

/** 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<TItem>;

type ListItem = {
Expand Down Expand Up @@ -492,9 +489,6 @@ type BaseSelectionListProps<TItem extends ListItem> = Partial<ChildrenProps> & {

/** 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<TItem>;

type SelectionListHandle = {
Expand Down
Loading
Loading