Skip to content

Commit

Permalink
Finance: port agent filtering to finance (#1102)
Browse files Browse the repository at this point in the history
* Port agent filtering to finance

* Revert transfer types to symbols, rename variable for readability

* Make TRANSFER_TYPES_LABELS be provided by useFilteredTransfers()

* Move set page to effect, use Reflect for iterating over object with symbol keys

* Make index conversion to unselected standard clearer on filtering hook

* Add more robust case checking for dates

* Add individual date filtering, transform transfer types to tuple array, make handlers clearer
  • Loading branch information
Evalir authored and facuspagnuolo committed May 22, 2020
1 parent af28e21 commit 1e5ba8f
Show file tree
Hide file tree
Showing 5 changed files with 166 additions and 121 deletions.
2 changes: 1 addition & 1 deletion apps/agent/app/src/components/useFilteredTransactions.js
Original file line number Diff line number Diff line change
@@ -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'

Expand Down
134 changes: 35 additions & 99 deletions apps/finance/app/src/components/Transfers.js
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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(
Expand Down Expand Up @@ -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) {
Expand All @@ -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'

Expand Down Expand Up @@ -259,13 +196,12 @@ const Transfers = React.memo(({ tokens, transactions }) => {
<TransfersFilters
dateRangeFilter={selectedDateRange}
onDateRangeChange={handleSelectedDateRangeChange}
tokenFilter={selectedToken}
onTokenChange={handleTokenChange}
transferTypeFilter={selectedTransferType}
onTransferTypeChange={handleTransferTypeChange}
compactMode={compactMode}
symbols={['All tokens', ...symbols]}
transferTypes={TRANSFER_TYPES_STRING}
tokenFilter={selectedToken}
transferTypeFilter={selectedTransferType}
transferTypes={transferTypes}
symbols={symbols}
/>
)}
</React.Fragment>
Expand Down
2 changes: 0 additions & 2 deletions apps/finance/app/src/components/TransfersFilters.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ import React from 'react'
import { DropDown, GU, DateRangePicker } from '@aragon/ui'

const TransfersFilters = ({
compactMode,
opened,
dateRangeFilter,
onDateRangeChange,
onTokenChange,
Expand Down
118 changes: 118 additions & 0 deletions apps/finance/app/src/components/useFilteredTransfers.js
Original file line number Diff line number Diff line change
@@ -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
31 changes: 12 additions & 19 deletions apps/finance/app/src/transfer-types.js
Original file line number Diff line number Diff line change
@@ -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
)

0 comments on commit 1e5ba8f

Please sign in to comment.