From f2396eaa039a58ea39cf0623e1ce991697ed793d Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Wed, 19 Mar 2025 14:05:07 +0100 Subject: [PATCH 1/6] Add sorting functionality --- .../MoneyRequestReportTableHeader.tsx | 2 +- .../MoneyRequestReportTransactionList.tsx | 21 ++++++-- src/libs/sortMoneyRequestViewTable.ts | 50 +++++++++++++++++++ 3 files changed, 67 insertions(+), 6 deletions(-) create mode 100644 src/libs/sortMoneyRequestViewTable.ts diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx index af970a2b92f5f..4265e2135cb52 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx @@ -52,7 +52,7 @@ const columnConfig: ColumnConfig[] = [ ]; type SearchTableHeaderProps = { - sortBy?: SearchColumnType; + sortBy?: SortableColumnName; sortOrder?: SortOrder; onSortPress: (column: SortableColumnName, order: SortOrder) => void; shouldShowSorting: boolean; diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx index a1fa22a7863c9..d72d4e0db3c3a 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx @@ -1,8 +1,10 @@ -import React from 'react'; +import React, {useState} from 'react'; import {View} from 'react-native'; import TransactionItemRow from '@components/TransactionItemRow'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; +import {compareFactory, isMoneyRequestReportSortableColumn} from '@libs/sortMoneyRequestViewTable'; +import type {SortingProperties} from '@libs/sortMoneyRequestViewTable'; import type * as OnyxTypes from '@src/types/onyx'; import MoneyRequestReportTableHeader from './MoneyRequestReportTableHeader'; @@ -15,6 +17,10 @@ function MoneyRequestReportTransactionList({transactions}: MoneyRequestReportTra const styles = useThemeStyles(); const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); + const [sortingProperties, setSortingProperties] = useState({ + sortBy: 'date', + sortOrder: 'desc', + }); const displayNarrowVersion = isMediumScreenWidth || shouldUseNarrowLayout; return ( @@ -22,13 +28,18 @@ function MoneyRequestReportTransactionList({transactions}: MoneyRequestReportTra {!displayNarrowVersion && ( {}} + sortBy={sortingProperties.sortBy} + sortOrder={sortingProperties.sortOrder} + onSortPress={(sortBy, sortOrder) => { + if (!isMoneyRequestReportSortableColumn(sortBy)) { + return; + } + setSortingProperties({sortBy, sortOrder}); + }} /> )} - {transactions.map((transaction) => { + {transactions.toSorted(compareFactory(sortingProperties)).map((transaction) => { return ( ; +type SortingProperties = { + sortBy: ValidSortableColumns; + sortOrder: SortOrder; +}; + +const isMoneyRequestReportSortableColumn = (value: SortableColumnName): value is ValidSortableColumns => { + return validSortableColumns.find((val) => val === value) !== undefined; +}; + +function compareFactory({sortBy, sortOrder}: {sortBy: ValidSortableColumns; sortOrder: SortOrder}) { + return (valueA: OnyxTypes.Transaction, valueB: OnyxTypes.Transaction) => { + const key = sortBy === CONST.SEARCH.TABLE_COLUMNS.DATE ? 'created' : sortBy; + const isAsc = sortOrder === CONST.SEARCH.SORT_ORDER.ASC; + + let a = valueA[key]; + let b = valueB[key]; + + if (typeof a === 'string' && typeof b === 'string') { + return isAsc ? a.localeCompare(b) : b.localeCompare(a); + } + + if (typeof a === 'number' && typeof b === 'number') { + if (sortBy === 'amount') { + a = Math.abs(a); + b = Math.abs(b); + } + return isAsc ? a - b : b - a; + } + + return 0; + }; +} + +export {isMoneyRequestReportSortableColumn, compareFactory}; +export type {SortingProperties}; From f48c1e1814129b70d5d1c2e12295dbfa5a2bc5b3 Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Wed, 19 Mar 2025 14:10:27 +0100 Subject: [PATCH 2/6] Remove unused import from MoneyRequestReportTableHeader --- .../MoneyRequestReportView/MoneyRequestReportTableHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx index 4265e2135cb52..3722a08aafaf3 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx @@ -1,6 +1,6 @@ import React from 'react'; import {View} from 'react-native'; -import type {SearchColumnType, SortOrder} from '@components/Search/types'; +import type {SortOrder} from '@components/Search/types'; import SortableTableHeader from '@components/SelectionList/SortableTableHeader'; import type {SortableColumnName} from '@components/SelectionList/types'; import useThemeStyles from '@hooks/useThemeStyles'; From f62d19c8848742e790e943b6706331fd52eb91b8 Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Wed, 19 Mar 2025 22:02:00 +0100 Subject: [PATCH 3/6] Improve sorting logic code --- .../MoneyRequestReportTableHeader.tsx | 5 +- .../MoneyRequestReportTransactionList.tsx | 43 ++++++++++++---- src/libs/SearchUIUtils.ts | 39 ++++++++++----- src/libs/sortMoneyRequestViewTable.ts | 50 ------------------- 4 files changed, 63 insertions(+), 74 deletions(-) delete mode 100644 src/libs/sortMoneyRequestViewTable.ts diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx index 3722a08aafaf3..18002cec52e58 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx @@ -13,7 +13,7 @@ type ColumnConfig = { isColumnSortable?: boolean; }; -const columnConfig: ColumnConfig[] = [ +const columnConfig = [ { columnName: CONST.SEARCH.TABLE_COLUMNS.RECEIPT, translationKey: 'common.receipt', @@ -49,7 +49,7 @@ const columnConfig: ColumnConfig[] = [ columnName: CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT, translationKey: 'common.total', }, -]; +] satisfies ColumnConfig[]; type SearchTableHeaderProps = { sortBy?: SortableColumnName; @@ -82,3 +82,4 @@ function MoneyRequestReportTableHeader({sortBy, sortOrder, onSortPress, shouldSh MoneyRequestReportTableHeader.displayName = 'MoneyRequestReportTableHeader'; export default MoneyRequestReportTableHeader; +export {columnConfig} diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx index d72d4e0db3c3a..eab2f9a85c976 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx @@ -1,28 +1,52 @@ -import React, {useState} from 'react'; +import React, {useMemo, useState} from 'react'; import {View} from 'react-native'; +import type {TupleToUnion} from 'type-fest'; +import type {SortOrder} from '@components/Search/types'; +import type {SortableColumnName} from '@components/SelectionList/types'; import TransactionItemRow from '@components/TransactionItemRow'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; -import {compareFactory, isMoneyRequestReportSortableColumn} from '@libs/sortMoneyRequestViewTable'; -import type {SortingProperties} from '@libs/sortMoneyRequestViewTable'; +import {compareValues} from '@libs/SearchUIUtils'; +import CONST from '@src/CONST'; import type * as OnyxTypes from '@src/types/onyx'; -import MoneyRequestReportTableHeader from './MoneyRequestReportTableHeader'; +import MoneyRequestReportTableHeader, {columnConfig} from './MoneyRequestReportTableHeader'; type MoneyRequestReportTransactionListProps = { /** List of transactions belonging to one report */ transactions: OnyxTypes.Transaction[]; }; +const columnConfigNames = columnConfig.map(({columnName}) => columnName); +const unwantedColumnConfigNames = [CONST.SEARCH.TABLE_COLUMNS.RECEIPT, CONST.SEARCH.TABLE_COLUMNS.TYPE, CONST.REPORT.TRANSACTION_LIST.COLUMNS.COMMENTS]; + +type SortableColumnConfigName = Exclude, TupleToUnion>; + +const isSortableColumnConfigName = (key: SortableColumnName): key is SortableColumnConfigName => { + const isInColumnConfig = !!columnConfigNames.find((val) => val === key); + const isInUnwanted = unwantedColumnConfigNames.find((val) => val === key); + return isInColumnConfig && !isInUnwanted; +}; + function MoneyRequestReportTransactionList({transactions}: MoneyRequestReportTransactionListProps) { const styles = useThemeStyles(); const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); - const [sortingProperties, setSortingProperties] = useState({ - sortBy: 'date', - sortOrder: 'desc', + const [sortingProperties, setSortingProperties] = useState<{ + sortBy: SortableColumnConfigName; + sortOrder: SortOrder; + }>({ + sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE, + sortOrder: CONST.SEARCH.SORT_ORDER.DESC, }); const displayNarrowVersion = isMediumScreenWidth || shouldUseNarrowLayout; + const sortedTransactions = useMemo(() => { + return transactions.toSorted((valueA: OnyxTypes.Transaction, valueB: OnyxTypes.Transaction) => { + const key = sortingProperties.sortBy === CONST.SEARCH.TABLE_COLUMNS.DATE ? 'created' : sortingProperties.sortBy; + return compareValues(valueA[key], valueB[key], sortingProperties.sortOrder, key); + }); + }, [sortingProperties, transactions]); + return ( <> {!displayNarrowVersion && ( @@ -31,15 +55,16 @@ function MoneyRequestReportTransactionList({transactions}: MoneyRequestReportTra sortBy={sortingProperties.sortBy} sortOrder={sortingProperties.sortOrder} onSortPress={(sortBy, sortOrder) => { - if (!isMoneyRequestReportSortableColumn(sortBy)) { + if (!isSortableColumnConfigName(sortBy)) { return; } + setSortingProperties({sortBy, sortOrder}); }} /> )} - {transactions.toSorted(compareFactory(sortingProperties)).map((transaction) => { + {sortedTransactions.map((transaction) => { return ( ; -type SortingProperties = { - sortBy: ValidSortableColumns; - sortOrder: SortOrder; -}; - -const isMoneyRequestReportSortableColumn = (value: SortableColumnName): value is ValidSortableColumns => { - return validSortableColumns.find((val) => val === value) !== undefined; -}; - -function compareFactory({sortBy, sortOrder}: {sortBy: ValidSortableColumns; sortOrder: SortOrder}) { - return (valueA: OnyxTypes.Transaction, valueB: OnyxTypes.Transaction) => { - const key = sortBy === CONST.SEARCH.TABLE_COLUMNS.DATE ? 'created' : sortBy; - const isAsc = sortOrder === CONST.SEARCH.SORT_ORDER.ASC; - - let a = valueA[key]; - let b = valueB[key]; - - if (typeof a === 'string' && typeof b === 'string') { - return isAsc ? a.localeCompare(b) : b.localeCompare(a); - } - - if (typeof a === 'number' && typeof b === 'number') { - if (sortBy === 'amount') { - a = Math.abs(a); - b = Math.abs(b); - } - return isAsc ? a - b : b - a; - } - - return 0; - }; -} - -export {isMoneyRequestReportSortableColumn, compareFactory}; -export type {SortingProperties}; From a04c75ba0b786e44c358e83d72088d129be44e1b Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Wed, 19 Mar 2025 22:11:26 +0100 Subject: [PATCH 4/6] Run prettier --- .../MoneyRequestReportView/MoneyRequestReportTableHeader.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx index 18002cec52e58..4e4f9f8cef124 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx @@ -82,4 +82,4 @@ function MoneyRequestReportTableHeader({sortBy, sortOrder, onSortPress, shouldSh MoneyRequestReportTableHeader.displayName = 'MoneyRequestReportTableHeader'; export default MoneyRequestReportTableHeader; -export {columnConfig} +export {columnConfig}; From 4afbac828c42af9cf2e9ba90bf49cff247558908 Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Thu, 20 Mar 2025 15:39:50 +0100 Subject: [PATCH 5/6] Make sort ignore columns with equal values --- .../MoneyRequestReportTableHeader.tsx | 5 +- .../MoneyRequestReportTransactionList.tsx | 63 ++++++++++++------- 2 files changed, 44 insertions(+), 24 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx index 4e4f9f8cef124..3722a08aafaf3 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTableHeader.tsx @@ -13,7 +13,7 @@ type ColumnConfig = { isColumnSortable?: boolean; }; -const columnConfig = [ +const columnConfig: ColumnConfig[] = [ { columnName: CONST.SEARCH.TABLE_COLUMNS.RECEIPT, translationKey: 'common.receipt', @@ -49,7 +49,7 @@ const columnConfig = [ columnName: CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT, translationKey: 'common.total', }, -] satisfies ColumnConfig[]; +]; type SearchTableHeaderProps = { sortBy?: SortableColumnName; @@ -82,4 +82,3 @@ function MoneyRequestReportTableHeader({sortBy, sortOrder, onSortPress, shouldSh MoneyRequestReportTableHeader.displayName = 'MoneyRequestReportTableHeader'; export default MoneyRequestReportTableHeader; -export {columnConfig}; diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx index eab2f9a85c976..f9909baeb1260 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx @@ -9,57 +9,78 @@ import useThemeStyles from '@hooks/useThemeStyles'; import {compareValues} from '@libs/SearchUIUtils'; import CONST from '@src/CONST'; import type * as OnyxTypes from '@src/types/onyx'; -import MoneyRequestReportTableHeader, {columnConfig} from './MoneyRequestReportTableHeader'; +import MoneyRequestReportTableHeader from './MoneyRequestReportTableHeader'; type MoneyRequestReportTransactionListProps = { /** List of transactions belonging to one report */ transactions: OnyxTypes.Transaction[]; }; -const columnConfigNames = columnConfig.map(({columnName}) => columnName); -const unwantedColumnConfigNames = [CONST.SEARCH.TABLE_COLUMNS.RECEIPT, CONST.SEARCH.TABLE_COLUMNS.TYPE, CONST.REPORT.TRANSACTION_LIST.COLUMNS.COMMENTS]; +const moneyRequestReportSortableColumnNames = [ + CONST.SEARCH.TABLE_COLUMNS.DATE, + CONST.SEARCH.TABLE_COLUMNS.MERCHANT, + CONST.SEARCH.TABLE_COLUMNS.CATEGORY, + CONST.SEARCH.TABLE_COLUMNS.TAG, + CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT, +]; -type SortableColumnConfigName = Exclude, TupleToUnion>; +type MoneyRequestReportSortableColumnName = TupleToUnion; -const isSortableColumnConfigName = (key: SortableColumnName): key is SortableColumnConfigName => { - const isInColumnConfig = !!columnConfigNames.find((val) => val === key); - const isInUnwanted = unwantedColumnConfigNames.find((val) => val === key); - return isInColumnConfig && !isInUnwanted; +const initialSortingProperties: { + sortBy: MoneyRequestReportSortableColumnName; + sortOrder: SortOrder; +} = { + sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE, + sortOrder: CONST.SEARCH.SORT_ORDER.DESC, }; +const isMoneyRequestReportSortableColumnName = (key: SortableColumnName): key is MoneyRequestReportSortableColumnName => !!moneyRequestReportSortableColumnNames.find((val) => val === key); +const areTransactionValuesEqual = (transactions: OnyxTypes.Transaction[], key: keyof OnyxTypes.Transaction) => { + const firstValidTransaction = transactions.find((transaction) => transaction !== undefined); + return !firstValidTransaction || transactions.every((transaction: OnyxTypes.Transaction) => transaction[key] === firstValidTransaction[key]); +}; +const getTransactionKey = (key: MoneyRequestReportSortableColumnName) => (key === CONST.SEARCH.TABLE_COLUMNS.DATE ? 'created' : key); + function MoneyRequestReportTransactionList({transactions}: MoneyRequestReportTransactionListProps) { const styles = useThemeStyles(); const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); - const [sortingProperties, setSortingProperties] = useState<{ - sortBy: SortableColumnConfigName; - sortOrder: SortOrder; - }>({ - sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE, - sortOrder: CONST.SEARCH.SORT_ORDER.DESC, + // We don't want to sort the array again if all column values are the same, + // but we still want to show the user that the table is sorted by selected column + // so there are 2 state properties + const [sortingProperties, setSortingProperties] = useState({ + visualIndicator: initialSortingProperties, + operationalProperties: initialSortingProperties, }); const displayNarrowVersion = isMediumScreenWidth || shouldUseNarrowLayout; const sortedTransactions = useMemo(() => { + const {sortBy, sortOrder} = sortingProperties.operationalProperties; return transactions.toSorted((valueA: OnyxTypes.Transaction, valueB: OnyxTypes.Transaction) => { - const key = sortingProperties.sortBy === CONST.SEARCH.TABLE_COLUMNS.DATE ? 'created' : sortingProperties.sortBy; - return compareValues(valueA[key], valueB[key], sortingProperties.sortOrder, key); + const key = getTransactionKey(sortBy); + return compareValues(valueA[key], valueB[key], sortOrder, key); }); - }, [sortingProperties, transactions]); + }, [sortingProperties.operationalProperties, transactions]); return ( <> {!displayNarrowVersion && ( { - if (!isSortableColumnConfigName(sortBy)) { + if (!isMoneyRequestReportSortableColumnName(sortBy)) { return; } - setSortingProperties({sortBy, sortOrder}); + const newSortingProperties = {sortBy, sortOrder}; + const shouldUpdateOperationalProperties = !areTransactionValuesEqual(transactions, getTransactionKey(sortBy)); + + setSortingProperties(({operationalProperties}) => ({ + visualIndicator: newSortingProperties, + operationalProperties: shouldUpdateOperationalProperties ? newSortingProperties : operationalProperties, + })); }} /> )} From b23681d7b2c52a544d45ea49812f38e6396f39a0 Mon Sep 17 00:00:00 2001 From: Jakub Korytko Date: Fri, 21 Mar 2025 11:15:29 +0100 Subject: [PATCH 6/6] Clean the code according to PR comments --- .../MoneyRequestReportTransactionList.tsx | 82 ++++++++++--------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx index f9909baeb1260..25b7351769a87 100644 --- a/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx +++ b/src/components/MoneyRequestReportView/MoneyRequestReportTransactionList.tsx @@ -1,8 +1,7 @@ -import React, {useMemo, useState} from 'react'; +import React, {useEffect, useState} from 'react'; import {View} from 'react-native'; import type {TupleToUnion} from 'type-fest'; import type {SortOrder} from '@components/Search/types'; -import type {SortableColumnName} from '@components/SelectionList/types'; import TransactionItemRow from '@components/TransactionItemRow'; import useResponsiveLayout from '@hooks/useResponsiveLayout'; import useThemeStyles from '@hooks/useThemeStyles'; @@ -16,7 +15,7 @@ type MoneyRequestReportTransactionListProps = { transactions: OnyxTypes.Transaction[]; }; -const moneyRequestReportSortableColumnNames = [ +const sortableColumnNames = [ CONST.SEARCH.TABLE_COLUMNS.DATE, CONST.SEARCH.TABLE_COLUMNS.MERCHANT, CONST.SEARCH.TABLE_COLUMNS.CATEGORY, @@ -24,68 +23,73 @@ const moneyRequestReportSortableColumnNames = [ CONST.SEARCH.TABLE_COLUMNS.TOTAL_AMOUNT, ]; -type MoneyRequestReportSortableColumnName = TupleToUnion; +type SortableColumnName = TupleToUnion; -const initialSortingProperties: { - sortBy: MoneyRequestReportSortableColumnName; +type SortedTransactions = { + transactions: OnyxTypes.Transaction[]; + sortBy: SortableColumnName; sortOrder: SortOrder; -} = { - sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE, - sortOrder: CONST.SEARCH.SORT_ORDER.DESC, }; -const isMoneyRequestReportSortableColumnName = (key: SortableColumnName): key is MoneyRequestReportSortableColumnName => !!moneyRequestReportSortableColumnNames.find((val) => val === key); -const areTransactionValuesEqual = (transactions: OnyxTypes.Transaction[], key: keyof OnyxTypes.Transaction) => { +const isSortableColumnName = (key: unknown): key is SortableColumnName => !!sortableColumnNames.find((val) => val === key); + +const getTransactionKey = (transaction: OnyxTypes.Transaction, key: SortableColumnName) => { + const dateKey = transaction.modifiedCreated ? 'modifiedCreated' : 'created'; + return key === CONST.SEARCH.TABLE_COLUMNS.DATE ? dateKey : key; +}; + +const areTransactionValuesEqual = (transactions: OnyxTypes.Transaction[], key: SortableColumnName) => { const firstValidTransaction = transactions.find((transaction) => transaction !== undefined); - return !firstValidTransaction || transactions.every((transaction: OnyxTypes.Transaction) => transaction[key] === firstValidTransaction[key]); + if (!firstValidTransaction) { + return true; + } + + const keyOfFirstValidTransaction = getTransactionKey(firstValidTransaction, key); + return transactions.every((transaction) => transaction[getTransactionKey(transaction, key)] === firstValidTransaction[keyOfFirstValidTransaction]); }; -const getTransactionKey = (key: MoneyRequestReportSortableColumnName) => (key === CONST.SEARCH.TABLE_COLUMNS.DATE ? 'created' : key); function MoneyRequestReportTransactionList({transactions}: MoneyRequestReportTransactionListProps) { const styles = useThemeStyles(); const {shouldUseNarrowLayout, isMediumScreenWidth} = useResponsiveLayout(); + const displayNarrowVersion = isMediumScreenWidth || shouldUseNarrowLayout; - // We don't want to sort the array again if all column values are the same, - // but we still want to show the user that the table is sorted by selected column - // so there are 2 state properties - const [sortingProperties, setSortingProperties] = useState({ - visualIndicator: initialSortingProperties, - operationalProperties: initialSortingProperties, + const [sortedData, setSortedData] = useState({ + transactions, + sortBy: CONST.SEARCH.TABLE_COLUMNS.DATE, + sortOrder: CONST.SEARCH.SORT_ORDER.DESC, }); - const displayNarrowVersion = isMediumScreenWidth || shouldUseNarrowLayout; - const sortedTransactions = useMemo(() => { - const {sortBy, sortOrder} = sortingProperties.operationalProperties; - return transactions.toSorted((valueA: OnyxTypes.Transaction, valueB: OnyxTypes.Transaction) => { - const key = getTransactionKey(sortBy); - return compareValues(valueA[key], valueB[key], sortOrder, key); - }); - }, [sortingProperties.operationalProperties, transactions]); + const {sortBy, sortOrder} = sortedData; + + useEffect(() => { + if (areTransactionValuesEqual(transactions, sortBy)) { + return; + } + + setSortedData((prevState) => ({ + ...prevState, + transactions: [...transactions].sort((a, b) => compareValues(a[getTransactionKey(a, sortBy)], b[getTransactionKey(b, sortBy)], sortOrder, sortBy)), + })); + }, [sortBy, sortOrder, transactions]); return ( <> {!displayNarrowVersion && ( { - if (!isMoneyRequestReportSortableColumnName(sortBy)) { + sortBy={sortBy} + sortOrder={sortOrder} + onSortPress={(selectedSortBy, selectedSortOrder) => { + if (!isSortableColumnName(selectedSortBy)) { return; } - const newSortingProperties = {sortBy, sortOrder}; - const shouldUpdateOperationalProperties = !areTransactionValuesEqual(transactions, getTransactionKey(sortBy)); - - setSortingProperties(({operationalProperties}) => ({ - visualIndicator: newSortingProperties, - operationalProperties: shouldUpdateOperationalProperties ? newSortingProperties : operationalProperties, - })); + setSortedData((prevState) => ({...prevState, sortBy: selectedSortBy, sortOrder: selectedSortOrder})); }} /> )} - {sortedTransactions.map((transaction) => { + {sortedData.transactions.map((transaction) => { return (