diff --git a/apps/agent/app/src/components/useFilteredTransactions.js b/apps/agent/app/src/components/useFilteredTransactions.js index 04bd35fbf1..bf42f98dd6 100644 --- a/apps/agent/app/src/components/useFilteredTransactions.js +++ b/apps/agent/app/src/components/useFilteredTransactions.js @@ -1,5 +1,5 @@ import { useState, useCallback, useMemo } from 'react' -import { endOfDay, isAfter, isWithinInterval, startOfDay } from 'date-fns' +import { endOfDay, isWithinInterval, startOfDay } from 'date-fns' import { TRANSACTION_TYPES } from '../transaction-types' import { addressesEqual } from '../lib/web3-utils' diff --git a/apps/finance/app/src/components/Transfers.js b/apps/finance/app/src/components/Transfers.js index c295b01ba1..6d55fce295 100644 --- a/apps/finance/app/src/components/Transfers.js +++ b/apps/finance/app/src/components/Transfers.js @@ -1,12 +1,6 @@ -import React, { useMemo, useState, useCallback, useContext } from 'react' +import React, { useMemo, useCallback, useContext } from 'react' import PropTypes from 'prop-types' -import { - compareDesc, - endOfDay, - format, - isWithinInterval, - startOfDay, -} from 'date-fns' +import { compareDesc, format } from 'date-fns' import { Button, ContextMenu, @@ -29,54 +23,15 @@ import { useNetwork, } from '@aragon/api-react' import { saveAs } from 'file-saver' -import * as TransferTypes from '../transfer-types' import { addressesEqual, toChecksumAddress } from '../lib/web3-utils' import { formatTokenAmount } from '../lib/utils' import TransfersFilters from './TransfersFilters' import { useIdentity, IdentityContext } from './IdentityManager/IdentityManager' import LocalIdentityBadge from './LocalIdentityBadge/LocalIdentityBadge' +import useFilteredTransfers from './useFilteredTransfers' -const UNSELECTED_TOKEN_FILTER = -1 -const UNSELECTED_TRANSFER_TYPE_FILTER = -1 -const INITIAL_DATE_RANGE = { start: null, end: null } -const TRANSFER_TYPES = [ - TransferTypes.All, - TransferTypes.Incoming, - TransferTypes.Outgoing, -] -const TRANSFER_TYPES_STRING = TRANSFER_TYPES.map(TransferTypes.convertToString) const formatDate = date => format(date, 'dd/MM/yy') -const getTokenDetails = (details, { address, decimals, symbol }) => { - details[toChecksumAddress(address)] = { - decimals, - symbol, - } - return details -} -// Filter transfer based on the selected filters -const getFilteredTransfers = ({ - transactions, - selectedToken, - selectedTransferType, - selectedDateRange, -}) => { - const transferType = TRANSFER_TYPES[selectedTransferType] - return transactions.filter( - ({ token, isIncoming, date }) => - (!selectedDateRange.start || - !selectedDateRange.end || - isWithinInterval(new Date(date), { - start: startOfDay(selectedDateRange.start), - end: endOfDay(selectedDateRange.end), - })) && - (selectedToken === null || - addressesEqual(token, selectedToken.address)) && - (transferType === TransferTypes.All || - selectedTransferType === UNSELECTED_TRANSFER_TYPE_FILTER || - (transferType === TransferTypes.Incoming && isIncoming) || - (transferType === TransferTypes.Outgoing && !isIncoming)) - ) -} + const getDownloadData = async (transfers, tokenDetails, resolveAddress) => { const mappedData = await Promise.all( transfers.map( @@ -123,49 +78,37 @@ const Transfers = React.memo(({ tokens, transactions }) => { const { appState } = useAragonApi() const connectedAccount = useConnectedAccount() const currentApp = useCurrentApp() - const toast = useToast() - const theme = useTheme() const { layoutName } = useLayout() + const theme = useTheme() + const toast = useToast() - const [page, setPage] = useState(0) - const [selectedToken, setSelectedToken] = useState(UNSELECTED_TOKEN_FILTER) - const [selectedTransferType, setSelectedTransferType] = useState( - UNSELECTED_TRANSFER_TYPE_FILTER - ) - const [selectedDateRange, setSelectedDateRange] = useState(INITIAL_DATE_RANGE) - const handleSelectedDateRangeChange = range => { - setPage(0) - setSelectedDateRange(range) - } - const handleTokenChange = useCallback( - index => { - setPage(0) - setSelectedToken(index || UNSELECTED_TOKEN_FILTER) - }, - [setPage, setSelectedToken] - ) - const handleTransferTypeChange = useCallback( - index => { - setPage(0) - setSelectedTransferType(index || UNSELECTED_TOKEN_FILTER) - }, - [setPage, setSelectedTransferType] - ) - const handleClearFilters = useCallback(() => { - setPage(0) - setSelectedTransferType(UNSELECTED_TRANSFER_TYPE_FILTER) - setSelectedToken(UNSELECTED_TOKEN_FILTER) - setSelectedDateRange(INITIAL_DATE_RANGE) - }, [setPage, setSelectedTransferType, setSelectedToken, setSelectedDateRange]) - const filteredTransfers = getFilteredTransfers({ - transactions, - selectedToken: selectedToken > 0 ? tokens[selectedToken - 1] : null, - selectedTransferType, + const { + emptyResultsViaFilters, + filteredTransfers, + handleClearFilters, + handleSelectedDateRangeChange, + handleTokenChange, + handleTransferTypeChange, + page, + setPage, selectedDateRange, - }) + selectedToken, + selectedTransferType, + symbols, + transferTypes, + } = useFilteredTransfers({ transactions, tokens }) + const { isSyncing } = appState - const symbols = tokens.map(({ symbol }) => symbol) - const tokenDetails = tokens.reduce(getTokenDetails, {}) + const tokenDetails = tokens.reduce( + (details, { address, decimals, symbol }) => { + details[toChecksumAddress(address)] = { + decimals, + symbol, + } + return details + }, + {} + ) const { resolve: resolveAddress } = useContext(IdentityContext) const handleDownload = useCallback(async () => { if (!currentApp || !currentApp.appAddress) { @@ -184,12 +127,6 @@ const Transfers = React.memo(({ tokens, transactions }) => { saveAs(new Blob([data], { type: 'text/csv;charset=utf-8' }), filename) toast('Transfers data exported') }, [currentApp, filteredTransfers, tokenDetails, resolveAddress]) - const emptyResultsViaFilters = - !filteredTransfers.length && - (selectedToken !== 0 || - selectedTransferType !== 0 || - selectedDateRange.start || - selectedDateRange.end) const compactMode = layoutName === 'small' @@ -259,13 +196,12 @@ const Transfers = React.memo(({ tokens, transactions }) => { )} diff --git a/apps/finance/app/src/components/TransfersFilters.js b/apps/finance/app/src/components/TransfersFilters.js index 544889257a..8105744009 100644 --- a/apps/finance/app/src/components/TransfersFilters.js +++ b/apps/finance/app/src/components/TransfersFilters.js @@ -2,8 +2,6 @@ import React from 'react' import { DropDown, GU, DateRangePicker } from '@aragon/ui' const TransfersFilters = ({ - compactMode, - opened, dateRangeFilter, onDateRangeChange, onTokenChange, diff --git a/apps/finance/app/src/components/useFilteredTransfers.js b/apps/finance/app/src/components/useFilteredTransfers.js new file mode 100644 index 0000000000..b9e7289e20 --- /dev/null +++ b/apps/finance/app/src/components/useFilteredTransfers.js @@ -0,0 +1,118 @@ +import { useCallback, useEffect, useMemo, useState } from 'react' +import { endOfDay, isAfter, isBefore, startOfDay } from 'date-fns' +import { + TRANSFER_TYPES, + TRANSFER_TYPES_LABELS, + Incoming, + Outgoing, +} from '../transfer-types' +import { addressesEqual } from '../lib/web3-utils' + +const UNSELECTED_TOKEN_FILTER = -1 +const UNSELECTED_TRANSFER_TYPE_FILTER = -1 +const UNSELECTED_DATE_RANGE_FILTER = { start: null, end: null } + +function useFilteredTransfers({ transactions, tokens }) { + const [page, setPage] = useState(0) + const [selectedDateRange, setSelectedDateRange] = useState( + UNSELECTED_DATE_RANGE_FILTER + ) + const [selectedTransferType, setSelectedTransferType] = useState( + UNSELECTED_TRANSFER_TYPE_FILTER + ) + const [selectedToken, setSelectedToken] = useState(UNSELECTED_TOKEN_FILTER) + + useEffect(() => setPage(0), [ + selectedDateRange, + selectedTransferType, + selectedToken, + ]) + + const handleSelectedDateRangeChange = useCallback(range => { + setSelectedDateRange(range) + }, []) + const handleTokenChange = useCallback(index => { + const tokenIndex = index === 0 ? UNSELECTED_TOKEN_FILTER : index + setSelectedToken(tokenIndex) + }, []) + const handleTransferTypeChange = useCallback(index => { + const transferTypeIndex = + index === 0 ? UNSELECTED_TRANSFER_TYPE_FILTER : index + setSelectedTransferType(transferTypeIndex) + }, []) + const handleClearFilters = useCallback(() => { + setSelectedTransferType(UNSELECTED_TRANSFER_TYPE_FILTER) + setSelectedToken(UNSELECTED_TOKEN_FILTER) + setSelectedDateRange(UNSELECTED_DATE_RANGE_FILTER) + }, []) + + const tokensToFilter = useMemo(() => [{ symbol: 'All tokens' }, ...tokens], [ + tokens, + ]) + + const filteredTransfers = useMemo( + () => + transactions.filter(({ date, isIncoming, token }) => { + const type = isIncoming ? Incoming : Outgoing + // Exclude by transaction type + if ( + selectedTransferType !== -1 && + TRANSFER_TYPES[selectedTransferType] !== type + ) { + return false + } + // Filter separately by start and end date. + if ( + selectedDateRange.start && + isBefore(new Date(date), startOfDay(selectedDateRange.start)) + ) { + return false + } + if ( + selectedDateRange.end && + isAfter(new Date(date), endOfDay(selectedDateRange.end)) + ) { + return false + } + // Exclude by token + if ( + selectedToken > 0 && + !addressesEqual(token, tokensToFilter[selectedToken].address) + ) { + return false + } + + // All good, we can include the transaction ✌️ + return true + }), + [ + selectedDateRange, + selectedTransferType, + selectedToken, + tokensToFilter, + transactions, + ] + ) + const symbols = tokensToFilter.map(({ symbol }) => symbol) + const emptyResultsViaFilters = + !filteredTransfers && + (selectedToken > 0 || selectedTransferType > 0 || selectedDateRange.start) + + return { + emptyResultsViaFilters, + filteredTransfers, + handleClearFilters, + handleSelectedDateRangeChange, + handleTokenChange, + handleTransferTypeChange, + page, + setPage, + selectedDateRange, + selectedToken, + selectedTransferType, + symbols, + transferTypes: TRANSFER_TYPES_LABELS, + } +} + +export default useFilteredTransfers diff --git a/apps/finance/app/src/transfer-types.js b/apps/finance/app/src/transfer-types.js index decf87497d..eb8f36b617 100644 --- a/apps/finance/app/src/transfer-types.js +++ b/apps/finance/app/src/transfer-types.js @@ -1,21 +1,14 @@ -export const All = Symbol('All') -export const Incoming = Symbol('Incoming') -export const Outgoing = Symbol('Outgoing') +export const All = Symbol('ALL_TRANSFER') +export const Incoming = Symbol('INCOMING_TRANSFER') +export const Outgoing = Symbol('OUTGOING_TRANSFER') -const symbolMapping = { - All, - Incoming, - Outgoing, -} -const stringMapping = { - [All]: 'All', - [Incoming]: 'Incoming', - [Outgoing]: 'Outgoing', -} +const AVAILABLE_TRANSFER_TYPES = [ + [All, 'All'], + [Incoming, 'Incoming'], + [Outgoing, 'Outgoing'], +] -export function convertFromString(str) { - return symbolMapping[str] -} -export function convertToString(symbol) { - return stringMapping[symbol] -} +export const TRANSFER_TYPES = AVAILABLE_TRANSFER_TYPES.map(([type]) => type) +export const TRANSFER_TYPES_LABELS = AVAILABLE_TRANSFER_TYPES.map( + ([_, label]) => label +)