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
3 changes: 1 addition & 2 deletions config/eslint/eslint.seatbelt.tsv
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,6 @@
"../../src/components/Search/SearchList/ListItem/TransactionListItem/TransactionListItemWide.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1
"../../src/components/Search/SearchList/ListItem/TransactionListItem/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 4
"../../src/components/Search/SearchList/ListItem/WithdrawalIDListItemHeader.tsx" "@typescript-eslint/no-unsafe-type-assertion" 2
"../../src/components/Search/SearchList/index.tsx" "@typescript-eslint/no-unsafe-type-assertion" 7
"../../src/components/Search/SearchMultipleSelectionPicker.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1
"../../src/components/Search/SearchPageHeader/SearchActionsBarCreateButton.tsx" "@typescript-eslint/no-unsafe-type-assertion" 1
"../../src/components/Search/SearchPageHeader/SearchActionsBarCreateButton.tsx" "no-restricted-imports" 1
Expand Down Expand Up @@ -790,7 +789,7 @@
"../../src/libs/ReportUtils.ts" "rulesdir/no-onyx-connect" 16
"../../src/libs/SearchAutocompleteUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 4
"../../src/libs/SearchQueryUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 72
"../../src/libs/SearchUIUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 69
"../../src/libs/SearchUIUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 76
"../../src/libs/SearchUIUtils.ts" "no-restricted-imports" 1
"../../src/libs/SelectionScraper/index.ts" "@typescript-eslint/no-unsafe-type-assertion" 4
"../../src/libs/SidebarUtils.ts" "@typescript-eslint/no-unsafe-type-assertion" 1
Expand Down
70 changes: 3 additions & 67 deletions src/components/Search/ChatSearchView.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import type {ExtendedTargetedEvent} from '@components/SelectionList/ListItem/types';

import type {TransactionPreviewData} from '@libs/actions/Search';
import type {ModifiedMouseEvent} from '@libs/Navigation/helpers/openInternalRouteInNewTab';

import CONST from '@src/CONST';
import type {Transaction} from '@src/types/onyx';

import type {ForwardedRef} from 'react';
import type {NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native';
import type {NativeSyntheticEvent} from 'react-native';

import React, {useImperativeHandle} from 'react';

import type {SearchListItem} from './SearchList/ListItem/types';
import type {SearchColumnType, SearchQueryJSON} from './types';
import type {CommonSearchViewProps} from './searchViewProps';

import useSearchListViewState from './hooks/useSearchListViewState';
import AnimatedExitRow from './primitives/AnimatedExitRow';
Expand All @@ -21,66 +16,7 @@ import BaseSearchList from './SearchList/BaseSearchList';
import ChatListItem from './SearchList/ListItem/ChatListItem';
import SearchListViewLayout from './SearchListViewLayout';

/** Imperative handle the router uses for highlight-driven scrolling (mirrors SearchList's handle). */
type SearchListHandle = {
scrollToIndex: (index: number, animated?: boolean) => void;
};

type ChatSearchViewProps = {
/** The chat search query. */
queryJSON: SearchQueryJSON;

/** The sorted rows to render (from the router's useSearchSnapshot). */
data: SearchListItem[];

/** The columns to render in the list (drives the table min-width and header). */
columns: SearchColumnType[];

/** Whether the list supports multi-select. */
canSelectMultiple: boolean;

/** Whether the action column uses its wider variant. */
isActionColumnWide: boolean;

/** Whether mobile selection mode is on. */
isMobileSelectionModeEnabled: boolean;

/** The column header element (undefined on narrow layouts). */
SearchTableHeader?: React.JSX.Element;

/** Whether a table header bar is shown above the list. */
tableHeaderVisible: boolean;

/** Whether everything has been loaded (gates the fully-checked select-all state). */
hasLoadedAllTransactions?: boolean;

/** Rows flagged for the post-create highlight animation (feeds BaseSearchList extraData). */
newTransactions: Transaction[];

/** The navigation handler for a row tap (owned by the router). */
onSelectRow: (item: SearchListItem, transactionPreviewData?: TransactionPreviewData, event?: ModifiedMouseEvent) => void;

/** The list footer (pagination / pending skeleton). */
ListFooterComponent?: React.JSX.Element;

/** Fires when the list scrolls near its end (router's fetchMoreResults). */
onEndReached: () => void;

/** Fires on the list's first layout and on layout changes. */
onLayout: () => void;

/** Scroll handler forwarded to the list. */
onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;

/** Content container style for the list. */
contentContainerStyle: StyleProp<ViewStyle>;

/** Outer container style for the list wrapper. */
containerStyle: StyleProp<ViewStyle>;

/** Imperative handle for highlight-driven scrolling, set by the router. */
ref?: ForwardedRef<SearchListHandle>;
};
type ChatSearchViewProps = CommonSearchViewProps;

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

Expand Down
80 changes: 5 additions & 75 deletions src/components/Search/ExpenseFlatSearchView.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import type {ExtendedTargetedEvent} from '@components/SelectionList/ListItem/types';

import type {TransactionPreviewData} from '@libs/actions/Search';
import type {ModifiedMouseEvent} from '@libs/Navigation/helpers/openInternalRouteInNewTab';

import CONST from '@src/CONST';
import type {CardList, Transaction} from '@src/types/onyx';

import type {ForwardedRef} from 'react';
import type {NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native';
import type {NativeSyntheticEvent} from 'react-native';

import React, {useImperativeHandle} from 'react';

import type {SearchListItem} from './SearchList/ListItem/types';
import type {SearchColumnType, SearchQueryJSON} from './types';
import type {CommonSearchViewProps, TransactionViewExtras} from './searchViewProps';

import useSearchListViewState from './hooks/useSearchListViewState';
import AnimatedExitRow from './primitives/AnimatedExitRow';
Expand All @@ -21,72 +16,7 @@ import BaseSearchList from './SearchList/BaseSearchList';
import TransactionListItem from './SearchList/ListItem/TransactionListItem';
import SearchListViewLayout from './SearchListViewLayout';

/** Imperative handle the router uses for highlight-driven scrolling (mirrors SearchList's handle). */
type SearchListHandle = {
scrollToIndex: (index: number, animated?: boolean) => void;
};

type ExpenseFlatSearchViewProps = {
/** The flat-expense search query. */
queryJSON: SearchQueryJSON;

/** The sorted, optimistic-stabilized rows to render (from the router's useSearchSnapshot). */
data: SearchListItem[];

/** The columns to render in the list (fade-stabilized by the router). */
columns: SearchColumnType[];

/** Whether the list supports multi-select. */
canSelectMultiple: boolean;

/** Whether the action column uses its wider variant (a deleted transaction is present). */
isActionColumnWide: boolean;

/** Precomputed attendee-tracking boolean (derived from policy-for-moving-expenses). */
isAttendeesEnabledForMovingPolicy?: boolean;

/** Non-personal and workspace cards for row rendering (subscribed once by the router). */
nonPersonalAndWorkspaceCards?: CardList;

/** Whether mobile selection mode is on. */
isMobileSelectionModeEnabled: boolean;

/** The column header element for the flat table (undefined on narrow layouts). */
SearchTableHeader?: React.JSX.Element;

/** Whether a table header bar is shown above the list. */
tableHeaderVisible: boolean;

/** Whether every transaction has been loaded (gates the fully-checked select-all state). */
hasLoadedAllTransactions?: boolean;

/** Transactions flagged for the post-create highlight animation (feeds BaseSearchList extraData). */
newTransactions: Transaction[];

/** The navigation/thread-creation handler for a row tap (owned by the router). */
onSelectRow: (item: SearchListItem, transactionPreviewData?: TransactionPreviewData, event?: ModifiedMouseEvent) => void;

/** The list footer (pagination / pending-expense skeleton). */
ListFooterComponent?: React.JSX.Element;

/** Fires when the list scrolls near its end (router's fetchMoreResults). */
onEndReached: () => void;

/** Fires on the list's first layout and on layout changes. */
onLayout: () => void;

/** Scroll handler forwarded to the list. */
onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;

/** Content container style for the list. */
contentContainerStyle: StyleProp<ViewStyle>;

/** Outer container style for the list wrapper. */
containerStyle: StyleProp<ViewStyle>;

/** Imperative handle for highlight-driven scrolling, set by the router. */
ref?: ForwardedRef<SearchListHandle>;
};
type ExpenseFlatSearchViewProps = CommonSearchViewProps & TransactionViewExtras;

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

Expand Down Expand Up @@ -164,8 +94,8 @@ function ExpenseFlatSearchView({

const renderItem = (item: SearchListItem, index: number, isItemFocused: boolean, onFocus?: (event: NativeSyntheticEvent<ExtendedTargetedEvent>) => void) => {
const isDisabled = isRowDeleted(item);
// Flat view always animates row exits, so the only gate is excluding the last row.
const shouldApplyAnimation = index < data.length - 1;
// Only expense row exits animate; invoice and trip transaction lists do not (matches the legacy per-type gate).
const shouldApplyAnimation = type === CONST.SEARCH.DATA_TYPES.EXPENSE && index < data.length - 1;

return (
<AnimatedExitRow
Expand Down
87 changes: 11 additions & 76 deletions src/components/Search/ExpenseGroupedSearchView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,103 +3,35 @@ import type {ExtendedTargetedEvent} from '@components/SelectionList/ListItem/typ
import useOnyx from '@hooks/useOnyx';
import useResponsiveLayout from '@hooks/useResponsiveLayout';

import type {TransactionPreviewData} from '@libs/actions/Search';
import getPlatform from '@libs/getPlatform';
import type {ModifiedMouseEvent} from '@libs/Navigation/helpers/openInternalRouteInNewTab';
import {isTransactionGroupListItemType, splitGroupsIntoPairs} from '@libs/SearchUIUtils';
import {isTransactionGroupListItemType, isTransactionMatchWithGroupItem, splitGroupsIntoPairs} from '@libs/SearchUIUtils';

import variables from '@styles/variables';

import CONST from '@src/CONST';
import ONYXKEYS from '@src/ONYXKEYS';
import {columnsSelector} from '@src/selectors/AdvancedSearchFiltersForm';
import type {CardList, Transaction} from '@src/types/onyx';
import type {Transaction} from '@src/types/onyx';

import type {ForwardedRef} from 'react';
import type {NativeScrollEvent, NativeSyntheticEvent, StyleProp, ViewStyle} from 'react-native';
import type {NativeSyntheticEvent} from 'react-native';

import React, {useImperativeHandle, useState} from 'react';

import type {SearchListItem} from './SearchList/ListItem/types';
import type {SearchColumnType, SearchQueryJSON, SelectedTransactions} from './types';
import type {CommonSearchViewProps, TransactionViewExtras} from './searchViewProps';
import type {SearchQueryJSON, SelectedTransactions} from './types';

import useSearchListViewState from './hooks/useSearchListViewState';
import AnimatedExitRow from './primitives/AnimatedExitRow';
import SelectionTopBar from './primitives/SelectionTopBar';
import {isTransactionMatchWithGroupItem} from './SearchList';
import BaseSearchList from './SearchList/BaseSearchList';
import GroupChildrenContainer from './SearchList/ListItem/GroupChildrenContainer';
import GroupHeader from './SearchList/ListItem/GroupHeader';
import TransactionGroupListItem from './SearchList/ListItem/TransactionGroupListItem';
import {isGroupChildrenContainerItem, isGroupHeaderItem} from './SearchList/ListItem/types';
import SearchListViewLayout from './SearchListViewLayout';

/** Imperative handle the router uses for highlight-driven scrolling (mirrors SearchList's handle). */
type SearchListHandle = {
scrollToIndex: (index: number, animated?: boolean) => void;
};

type ExpenseGroupedSearchViewProps = {
/** The grouped-expense search query (a valid groupBy is set). */
queryJSON: SearchQueryJSON;

/** The sorted group rows to render (from the router's useSearchSnapshot). */
data: SearchListItem[];

/** The columns to render in the list. */
columns: SearchColumnType[];

/** Whether the list supports multi-select. */
canSelectMultiple: boolean;

/** Whether the action column uses its wider variant. */
isActionColumnWide: boolean;

/** Precomputed attendee-tracking boolean (derived from policy-for-moving-expenses). */
isAttendeesEnabledForMovingPolicy?: boolean;

/** Non-personal and workspace cards for row rendering (subscribed once by the router). */
nonPersonalAndWorkspaceCards?: CardList;

/** Whether mobile selection mode is on. */
isMobileSelectionModeEnabled: boolean;

/** The column header element (undefined on narrow layouts). */
SearchTableHeader?: React.JSX.Element;

/** Whether a table header bar is shown above the list. */
tableHeaderVisible: boolean;

/** Whether everything has been loaded (gates the fully-checked select-all state). */
hasLoadedAllTransactions?: boolean;

/** Transactions flagged for the post-create highlight animation. */
newTransactions: Transaction[];

/** The navigation handler for a row tap (owned by the router). */
onSelectRow: (item: SearchListItem, transactionPreviewData?: TransactionPreviewData, event?: ModifiedMouseEvent) => void;

/** The list footer (pagination / pending skeleton). */
ListFooterComponent?: React.JSX.Element;

/** Fires when the list scrolls near its end (router's fetchMoreResults). */
onEndReached: () => void;

/** Fires on the list's first layout and on layout changes. */
onLayout: () => void;

/** Scroll handler forwarded to the list. */
onScroll?: (event: NativeSyntheticEvent<NativeScrollEvent>) => void;

/** Content container style for the list. */
contentContainerStyle: StyleProp<ViewStyle>;

/** Outer container style for the list wrapper. */
containerStyle: StyleProp<ViewStyle>;

/** Imperative handle for highlight-driven scrolling, set by the router. */
ref?: ForwardedRef<SearchListHandle>;
};
type ExpenseGroupedSearchViewProps = CommonSearchViewProps & TransactionViewExtras;

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

Expand Down Expand Up @@ -251,7 +183,10 @@ function ExpenseGroupedSearchView({
return;
}
const item = data.at(index);
const splitIndex = item?.keyForList ? listData.findIndex((listItem) => listItem.keyForList === `header_${item.keyForList}`) : -1;
if (!item) {
return;
}
const splitIndex = item.keyForList ? listData.findIndex((listItem) => listItem.keyForList === `header_${item.keyForList}`) : -1;
scrollToListIndex(splitIndex !== -1 ? splitIndex : index, animated);
},
}),
Expand Down Expand Up @@ -337,7 +272,7 @@ function ExpenseGroupedSearchView({
const newTransactionID = item.keyForList ? newTransactionIDByItemKey.get(item.keyForList) : undefined;
return (
<AnimatedExitRow
shouldApplyAnimation={index < listData.length - 1}
shouldApplyAnimation={type === CONST.SEARCH.DATA_TYPES.EXPENSE && index < listData.length - 1}
hasItemsBeingRemoved={hasItemsBeingRemoved}
>
<TransactionGroupListItem
Expand Down
Loading
Loading