Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Finance: port agent filtering to finance #1102

Merged
merged 7 commits into from
Apr 9, 2020
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
},
{}
)
sohkai marked this conversation as resolved.
Show resolved Hide resolved
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)
Evalir marked this conversation as resolved.
Show resolved Hide resolved
const [selectedDateRange, setSelectedDateRange] = useState(
UNSELECTED_DATE_RANGE_FILTER
)
const [selectedTransferType, setSelectedTransferType] = useState(
UNSELECTED_TRANSFER_TYPE_FILTER
)
const [selectedToken, setSelectedToken] = useState(UNSELECTED_TOKEN_FILTER)
Evalir marked this conversation as resolved.
Show resolved Hide resolved

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
Evalir marked this conversation as resolved.
Show resolved Hide resolved
// Exclude by transaction type
if (
selectedTransferType !== -1 &&
TRANSFER_TYPES[selectedTransferType] !== type
Evalir marked this conversation as resolved.
Show resolved Hide resolved
) {
return false
}
// Filter separately by start and end date.
if (
selectedDateRange.start &&
Evalir marked this conversation as resolved.
Show resolved Hide resolved
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
)