Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1156,7 +1156,7 @@ type OnyxValuesMapping = {
[ONYXKEYS.REVIEW_DUPLICATES]: OnyxTypes.ReviewDuplicates;
[ONYXKEYS.ADD_NEW_COMPANY_CARD]: OnyxTypes.AddNewCompanyCardFeed;
[ONYXKEYS.ASSIGN_CARD]: OnyxTypes.AssignCard;
[ONYXKEYS.MOBILE_SELECTION_MODE]: OnyxTypes.MobileSelectionMode;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you remove this type as its not used anymore?

[ONYXKEYS.MOBILE_SELECTION_MODE]: boolean;
[ONYXKEYS.NVP_FIRST_DAY_FREE_TRIAL]: string;
[ONYXKEYS.NVP_LAST_DAY_FREE_TRIAL]: string;
[ONYXKEYS.NVP_BILLING_FUND_ID]: number;
Expand Down
4 changes: 2 additions & 2 deletions src/components/MoneyReportHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -880,9 +880,9 @@ function MoneyReportHeader({
[connectedIntegrationName, styles.noWrap, styles.textStrong, translate],
);

const {selectionMode} = useMobileSelectionMode();
const isMobileSelectionModeEnabled = useMobileSelectionMode();

if (selectionMode?.isEnabled) {
if (isMobileSelectionModeEnabled) {
return (
<HeaderWithBackButton
title={translate('common.selectMultiple')}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ function MoneyRequestReportActionsList({

const {selectedTransactionIDs, setSelectedTransactions, clearSelectedTransactions} = useSearchContext();

const {selectionMode} = useMobileSelectionMode();
const isMobileSelectionModeEnabled = useMobileSelectionMode();
const {
options: selectedTransactionsOptions,
handleDeleteTransactions,
Expand Down Expand Up @@ -557,7 +557,7 @@ function MoneyRequestReportActionsList({
style={[styles.flex1]}
ref={wrapperViewRef}
>
{shouldUseNarrowLayout && !!selectionMode?.isEnabled && (
{shouldUseNarrowLayout && isMobileSelectionModeEnabled && (
<>
<ButtonWithDropdownMenu
onPress={() => null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ function MoneyRequestReportTransactionList({
const {isMouseDownOnInput, setMouseUp} = useMouseContext();

const {selectedTransactionIDs, setSelectedTransactions, clearSelectedTransactions} = useSearchContext();
const {selectionMode} = useMobileSelectionMode();
const isMobileSelectionModeEnabled = useMobileSelectionMode();

const toggleTransaction = useCallback(
(transactionID: string) => {
Expand Down Expand Up @@ -273,7 +273,7 @@ function MoneyRequestReportTransactionList({
return;
}

if (selectionMode?.isEnabled) {
if (isMobileSelectionModeEnabled) {
toggleTransaction(transaction.transactionID);
return;
}
Expand All @@ -295,7 +295,7 @@ function MoneyRequestReportTransactionList({
if (!isSmallScreenWidth) {
return;
}
if (selectionMode?.isEnabled) {
if (isMobileSelectionModeEnabled) {
toggleTransaction(transaction.transactionID);
return;
}
Expand All @@ -312,7 +312,7 @@ function MoneyRequestReportTransactionList({
taxAmountColumnSize={taxAmountColumnSize}
shouldShowTooltip
shouldUseNarrowLayout={shouldUseNarrowLayout || isMediumScreenWidth}
shouldShowCheckbox={!!selectionMode?.isEnabled || !isSmallScreenWidth}
shouldShowCheckbox={isMobileSelectionModeEnabled || !isSmallScreenWidth}
onCheckboxPress={toggleTransaction}
columns={allReportColumns}
scrollToNewTransaction={transaction.transactionID === newTransactions?.at(0)?.transactionID ? scrollToNewTransaction : undefined}
Expand Down Expand Up @@ -355,7 +355,7 @@ function MoneyRequestReportTransactionList({
title={translate('common.select')}
icon={Expensicons.CheckSquare}
onPress={() => {
if (!selectionMode?.isEnabled) {
if (!isMobileSelectionModeEnabled) {
turnOnMobileSelectionMode();
}
toggleTransaction(selectedTransactionID);
Expand Down
2 changes: 1 addition & 1 deletion src/components/Navigation/SearchSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function SearchSidebar({state}: SearchSidebarProps) {
}
}, [lastSearchType, queryJSON, setLastSearchType, currentSearchResults]);

const isDataLoaded = isSearchDataLoaded(currentSearchResults, lastNonEmptySearchResults, queryJSON);
const isDataLoaded = isSearchDataLoaded(currentSearchResults?.data ? currentSearchResults : lastNonEmptySearchResults, queryJSON);
const shouldShowLoadingState = route?.name === SCREENS.SEARCH.MONEY_REQUEST_REPORT ? false : !isOffline && !isDataLoaded;

if (shouldUseNarrowLayout) {
Expand Down
26 changes: 21 additions & 5 deletions src/components/Search/SearchContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {isMoneyRequestReport} from '@libs/ReportUtils';
import {isTransactionCardGroupListItemType, isTransactionListItemType, isTransactionMemberGroupListItemType, isTransactionReportGroupListItemType} from '@libs/SearchUIUtils';
import CONST from '@src/CONST';
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import {isEmptyObject} from '@src/types/utils/EmptyObject';
import type {SearchContext, SearchContextData} from './types';

const defaultSearchContextData: SearchContextData = {
Expand Down Expand Up @@ -41,10 +42,15 @@ function SearchContextProvider({children}: ChildrenProps) {
const areTransactionsEmpty = useRef(true);

const setCurrentSearchHash = useCallback((searchHash: number) => {
setSearchContextData((prevState) => ({
...prevState,
currentSearchHash: searchHash,
}));
setSearchContextData((prevState) => {
if (searchHash === prevState.currentSearchHash) {
return prevState;
}
return {
...prevState,
currentSearchHash: searchHash,
};
});
}, []);

const setSelectedTransactions: SearchContext['setSelectedTransactions'] = useCallback((selectedTransactions, data = []) => {
Expand Down Expand Up @@ -107,6 +113,10 @@ function SearchContextProvider({children}: ChildrenProps) {
if (searchHashOrClearIDsFlag === searchContextData.currentSearchHash) {
return;
}

if (searchContextData.selectedReports.length === 0 && isEmptyObject(searchContextData.selectedTransactions) && !searchContextData.shouldTurnOffSelectionMode) {
return;
}
setSearchContextData((prevState) => ({
...prevState,
shouldTurnOffSelectionMode,
Expand All @@ -116,7 +126,13 @@ function SearchContextProvider({children}: ChildrenProps) {
setShouldShowExportModeOption(false);
setExportMode(false);
},
[searchContextData.currentSearchHash, setSelectedTransactions],
[
searchContextData.currentSearchHash,
searchContextData.selectedReports.length,
searchContextData.selectedTransactions,
searchContextData.shouldTurnOffSelectionMode,
setSelectedTransactions,
],
);

const removeTransaction: SearchContext['removeTransaction'] = useCallback(
Expand Down
54 changes: 12 additions & 42 deletions src/components/Search/SearchList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@ import useArrowKeyFocusManager from '@hooks/useArrowKeyFocusManager';
import useKeyboardShortcut from '@hooks/useKeyboardShortcut';
import useKeyboardState from '@hooks/useKeyboardState';
import useLocalize from '@hooks/useLocalize';
import useMobileSelectionMode from '@hooks/useMobileSelectionMode';
import useOnyx from '@hooks/useOnyx';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import useSafeAreaPaddings from '@hooks/useSafeAreaPaddings';
import useThemeStyles from '@hooks/useThemeStyles';
import {turnOffMobileSelectionMode, turnOnMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
import {turnOnMobileSelectionMode} from '@libs/actions/MobileSelectionMode';
import {isMobileChrome} from '@libs/Browser';
import {addKeyDownPressListener, removeKeyDownPressListener} from '@libs/KeyboardShortcut/KeyDownPressListener';
import variables from '@styles/variables';
Expand Down Expand Up @@ -78,6 +77,13 @@ type SearchListProps = Pick<FlatListPropsWithLayout<SearchListItem>, 'onScroll'

/** Invoked on mount and layout changes */
onLayout?: () => void;

/** Whether mobile selection mode is enabled */
isMobileSelectionModeEnabled: boolean;
};

const keyExtractor = (item: SearchListItem, index: number) => {
return item.keyForList ?? `${index}`;
};

const onScrollToIndexFailed = () => {};
Expand All @@ -102,6 +108,7 @@ function SearchList(
queryJSON,
onViewableItemsChanged,
onLayout,
isMobileSelectionModeEnabled,
}: SearchListProps,
ref: ForwardedRef<SearchListHandle>,
) {
Expand All @@ -126,51 +133,14 @@ function SearchList(
const {isSmallScreenWidth} = useResponsiveLayout();

const [isModalVisible, setIsModalVisible] = useState(false);
const {selectionMode} = useMobileSelectionMode();
const [longPressedItem, setLongPressedItem] = useState<SearchListItem>();
// Check if selection should be on when the modal is opened
const wasSelectionOnRef = useRef(false);
// Keep track of the number of selected items to determine if we should turn off selection mode
const selectionRef = useRef(0);

const [policies] = useOnyx(ONYXKEYS.COLLECTION.POLICY, {
canBeMissing: true,
});

const [allReports] = useOnyx(ONYXKEYS.COLLECTION.REPORT, {canBeMissing: false});

useEffect(() => {
selectionRef.current = selectedItemsLength;

if (!isSmallScreenWidth) {
if (selectedItemsLength === 0) {
turnOffMobileSelectionMode();
}
return;
}
if (!isFocused) {
return;
}
if (!wasSelectionOnRef.current && selectedItemsLength > 0) {
wasSelectionOnRef.current = true;
}
if (selectedItemsLength > 0 && !selectionMode?.isEnabled) {
turnOnMobileSelectionMode();
} else if (selectedItemsLength === 0 && selectionMode?.isEnabled && !wasSelectionOnRef.current) {
turnOffMobileSelectionMode();
}
Comment on lines -159 to -161
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coming from the #66027 checklist: we’ve removed this logic, so in case the user navigates back using the Android back button, we need to call turnOffMobileSelectionMode

}, [selectionMode, isSmallScreenWidth, isFocused, selectedItemsLength]);

useEffect(
() => () => {
if (selectionRef.current !== 0) {
return;
}
turnOffMobileSelectionMode();
},
[],
);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

amazing 👏
Can you tell me in short what part of code is responsible for disabling selection mode now in these cases?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. For the first useEffect:
    this useEffect and this one from Search/index are doing the same thing, so these effects were redundant.
  2. For the second one: now when everything uses useMobileSelctionMode we don't need it to clear it after leaving because it is cleared on entering a screen that uses useMobileSelctionMode.

const handleLongPressRow = useCallback(
(item: SearchListItem) => {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
Expand All @@ -181,14 +151,14 @@ function SearchList(
if ('transactions' in item && item.transactions.length === 0) {
return;
}
if (selectionMode?.isEnabled) {
if (isMobileSelectionModeEnabled) {
onCheckboxPress(item);
return;
}
setLongPressedItem(item);
setIsModalVisible(true);
},
[isFocused, isSmallScreenWidth, onCheckboxPress, selectionMode?.isEnabled, shouldPreventLongPressRow],
[isFocused, isSmallScreenWidth, onCheckboxPress, isMobileSelectionModeEnabled, shouldPreventLongPressRow],
);

const turnOnSelectionMode = useCallback(() => {
Expand Down Expand Up @@ -398,7 +368,7 @@ function SearchList(
<Animated.FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item, index) => item.keyForList ?? `${index}`}
keyExtractor={keyExtractor}
onScroll={onScroll}
contentContainerStyle={contentContainerStyle}
showsVerticalScrollIndicator={false}
Expand Down
6 changes: 3 additions & 3 deletions src/components/Search/SearchPageHeader/SearchFiltersBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ import type {SearchHeaderOptionValue} from './SearchPageHeader';
type SearchFiltersBarProps = {
queryJSON: SearchQueryJSON;
headerButtonsOptions: Array<DropdownOption<SearchHeaderOptionValue>>;
isMobileSelectionModeEnabled: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NAB: I'm not sure if we need to introduce this prop, since all the places using this component already pass the value from the useMobileSelectionMode hook

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each useMobileSelectionMode is adding additional overhead with useOnyx, useRef and the useEfffect which it calls.
Since the performance is our number one concern and it's so easy to pass isMobileSelectionModeEnabled as it is in the SearchFiltersBar parent I think we should keep it that way.

};

function SearchFiltersBar({queryJSON, headerButtonsOptions}: SearchFiltersBarProps) {
function SearchFiltersBar({queryJSON, headerButtonsOptions, isMobileSelectionModeEnabled}: SearchFiltersBarProps) {
const scrollRef = useRef<RNScrollView>(null);

// type, groupBy and status values are not guaranteed to respect the ts type as they come from user input
Expand All @@ -70,7 +71,6 @@ function SearchFiltersBar({queryJSON, headerButtonsOptions}: SearchFiltersBarPro
const [policyTagsLists] = useOnyx(ONYXKEYS.COLLECTION.POLICY_TAGS, {canBeMissing: true});
const [policyCategories] = useOnyx(ONYXKEYS.COLLECTION.POLICY_CATEGORIES, {canBeMissing: true});
const [workspaceCardFeeds] = useOnyx(ONYXKEYS.COLLECTION.WORKSPACE_CARDS_LIST, {canBeMissing: true});
const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE, {canBeMissing: true});
const [allFeeds] = useOnyx(ONYXKEYS.COLLECTION.SHARED_NVP_PRIVATE_DOMAIN_MEMBER, {canBeMissing: true});
const [searchResultsErrors] = useOnyx(`${ONYXKEYS.COLLECTION.SNAPSHOT}${hash}`, {canBeMissing: true, selector: (data) => data?.errors});

Expand All @@ -79,7 +79,7 @@ function SearchFiltersBar({queryJSON, headerButtonsOptions}: SearchFiltersBarPro
const selectedTransactionsKeys = useMemo(() => Object.keys(selectedTransactions ?? {}), [selectedTransactions]);

const hasErrors = Object.keys(searchResultsErrors ?? {}).length > 0 && !isOffline;
const shouldShowSelectedDropdown = headerButtonsOptions.length > 0 && (!shouldUseNarrowLayout || (!!selectionMode && selectionMode.isEnabled));
const shouldShowSelectedDropdown = headerButtonsOptions.length > 0 && (!shouldUseNarrowLayout || isMobileSelectionModeEnabled);

const [typeOptions, type] = useMemo(() => {
const options = getTypeOptions(allPolicies, email);
Expand Down
16 changes: 11 additions & 5 deletions src/components/Search/SearchPageHeader/SearchPageHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ import {View} from 'react-native';
import type {DropdownOption} from '@components/ButtonWithDropdownMenu/types';
import {useSearchContext} from '@components/Search/SearchContext';
import type {SearchQueryJSON} from '@components/Search/types';
import useOnyx from '@hooks/useOnyx';
import useResponsiveLayout from '@hooks/useResponsiveLayout';
import SearchSelectedNarrow from '@pages/Search/SearchSelectedNarrow';
import type CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import type DeepValueOf from '@src/types/utils/DeepValueOf';
import SearchPageHeaderInput from './SearchPageHeaderInput';

Expand All @@ -18,18 +16,26 @@ type SearchPageHeaderProps = {
onSearchRouterFocus?: () => void;
headerButtonsOptions: Array<DropdownOption<SearchHeaderOptionValue>>;
handleSearch: (value: string) => void;
isMobileSelectionModeEnabled: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NAB: same above all the places using this component already pass the value from the useMobileSelectionMode hook so i think we don't need to introduce this props

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same answear as above ☝️

};

type SearchHeaderOptionValue = DeepValueOf<typeof CONST.SEARCH.BULK_ACTION_TYPES> | undefined;

function SearchPageHeader({queryJSON, searchRouterListVisible, hideSearchRouterList, onSearchRouterFocus, headerButtonsOptions, handleSearch}: SearchPageHeaderProps) {
function SearchPageHeader({
queryJSON,
searchRouterListVisible,
hideSearchRouterList,
onSearchRouterFocus,
headerButtonsOptions,
handleSearch,
isMobileSelectionModeEnabled,
}: SearchPageHeaderProps) {
const {shouldUseNarrowLayout} = useResponsiveLayout();
const {selectedTransactions} = useSearchContext();
const [selectionMode] = useOnyx(ONYXKEYS.MOBILE_SELECTION_MODE, {canBeMissing: true});

const selectedTransactionsKeys = Object.keys(selectedTransactions ?? {});

if (shouldUseNarrowLayout && selectionMode?.isEnabled) {
if (shouldUseNarrowLayout && isMobileSelectionModeEnabled) {
return (
<View>
<SearchSelectedNarrow
Expand Down
Loading
Loading