diff --git a/__pycache__/queries.cpython-311.pyc b/__pycache__/queries.cpython-311.pyc
new file mode 100644
index 0000000..9d68442
Binary files /dev/null and b/__pycache__/queries.cpython-311.pyc differ
diff --git a/src/components/Map/TokenStreamsMap.tsx b/src/components/Map/TokenStreamsMap.tsx
new file mode 100644
index 0000000..b5ee5f3
--- /dev/null
+++ b/src/components/Map/TokenStreamsMap.tsx
@@ -0,0 +1,143 @@
+import 'reactflow/dist/style.css'
+import { table } from 'console'
+import { FC, memo, ReactElement, useCallback, useEffect } from 'react'
+import ReactFlow, {
+ Controls,
+ Background,
+ Position,
+ useEdgesState,
+ useNodesState,
+ addEdge
+} from 'reactflow'
+import { Network } from '../../redux/networks'
+import UserBlock from './UserBlock'
+import { useCall } from 'wagmi'
+
+interface Edge {
+ id: string
+ source: string
+ target: string
+ label?: string
+ animated?: boolean
+ sourceHandle?: string
+ targetHandle?: string // Add targetHandle property
+ type?: string // Add type property
+}
+
+interface Node {
+ id: string
+ position: { x: number; y: number }
+ data: { label: ReactElement }
+ flowRate?: string
+ sourcePosition?: Position
+ targetPosition?: Position
+}
+
+const TokenStreamsMap: FC<{
+ network: Network
+ tableRows: any[]
+}> = ({ network, tableRows }): ReactElement => {
+ // initialize initialNodes and initialEdges with empty arrays
+ // use placeholder values for nodes
+ // need 20 nodes and 10 edges
+ let initialNodes: Node[] = []
+ let initialEdges: Edge[] = []
+ for (let i = 0; i < 10; i++) {
+ initialNodes.push({
+ id: `${2 * i}`,
+ position: { x: -200, y: -i * 200 },
+ data: {
+ label:
+ },
+ sourcePosition: Position.Right,
+ targetPosition: Position.Left
+ })
+ initialNodes.push({
+ id: `${2 * i + 1}`,
+ position: { x: 200, y: -i * 200 },
+ data: {
+ label:
+ },
+ sourcePosition: Position.Right,
+ targetPosition: Position.Left
+ })
+ initialEdges.push({
+ id: `${i}`,
+ source: `${2 * i}`,
+ target: `${2 * i + 1}`,
+ animated: true,
+ sourceHandle: 'left',
+ targetHandle: 'right'
+ })
+ }
+
+ const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)
+ const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
+
+ const onConnect = useCallback(
+ (params: any) => setEdges((edges) => addEdge(params, edges)),
+ [setEdges]
+ )
+
+ useEffect(() => {
+ // create newNodes without duplicates
+ // edges can point to the same node
+ const newNodes = tableRows
+ .map((row, index) => [
+ {
+ id: `${row.sender}`,
+ position: { x: -200, y: -index * 200 },
+ data: {
+ label:
+ },
+ sourcePosition: Position.Right,
+ targetPosition: Position.Left
+ },
+ {
+ id: `${row.receiver}`,
+ position: { x: 200, y: -index * 200 },
+ data: {
+ label:
+ },
+ sourcePosition: Position.Right,
+ targetPosition: Position.Left
+ }
+ ])
+ .flat()
+ const newEdges = tableRows.map((row, index) => {
+ return {
+ id: `${row.id}`,
+ source: `${row.sender}`,
+ target: `${row.receiver}`,
+ animated: true,
+ sourceHandle: 'left',
+ targetHandle: 'right'
+ }
+ })
+ setNodes(newNodes)
+ setEdges(newEdges)
+ }, [tableRows])
+
+ console.log('nodes', nodes)
+ console.log('edges', edges)
+
+ return (
+
+
+
+
+ )
+}
+
+export default memo(TokenStreamsMap)
diff --git a/src/components/SimpleStream/SimpleStream.tsx b/src/components/SimpleStream/SimpleStream.tsx
new file mode 100644
index 0000000..9d929ad
--- /dev/null
+++ b/src/components/SimpleStream/SimpleStream.tsx
@@ -0,0 +1,89 @@
+import { FC, ReactElement, useCallback } from 'react'
+import ReactFlow, {
+ Background,
+ Controls,
+ OnConnect,
+ Position,
+ addEdge,
+ useEdgesState,
+ useNodesState
+} from 'reactflow'
+
+interface StreamProps {
+ id: string
+ sender: string
+ receiver: string
+ token: string
+ flowRate: string
+}
+
+const SimpleStream: FC = ({
+ id,
+ sender,
+ receiver,
+ token,
+ flowRate
+}): ReactElement => {
+ const initialNodes = [
+ {
+ id: `${sender}`,
+ type: 'input',
+ position: { x: -200, y: 0 },
+ data: { label: sender.slice(0, 6) + '...' + sender.slice(-4) },
+ sourcePosition: Position.Right,
+ targetPosition: Position.Left
+ },
+ {
+ id: `${receiver}`,
+ position: { x: 150, y: 0 },
+ data: { label: receiver.slice(0, 6) + '...' + receiver.slice(-4) },
+ sourcePosition: Position.Right,
+ targetPosition: Position.Left
+ }
+ ]
+
+ const initialEdges = [
+ {
+ id: `${id}-a->b`,
+ source: `${sender}`,
+ target: `${receiver}`,
+ animated: true,
+ sourceHandle: 'left',
+ targetHandle: 'right',
+ type: 'straight'
+ }
+ ]
+
+ const [nodes, , onNodesChange] = useNodesState(initialNodes)
+ const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
+ const onConnect: OnConnect = useCallback(
+ (connection) => setEdges((edges) => addEdge(connection, edges)),
+ [setEdges]
+ )
+
+ console.log('nodes', nodes)
+ console.log('edges', edges)
+ console.log('sender', sender)
+ console.log('receiver', receiver)
+
+ return (
+
+
+
+
+ )
+}
+
+export default SimpleStream
diff --git a/src/pages/NetworkStreams.tsx b/src/pages/NetworkStreams.tsx
index 2b4258c..4c92cf3 100644
--- a/src/pages/NetworkStreams.tsx
+++ b/src/pages/NetworkStreams.tsx
@@ -6,6 +6,8 @@ import {
TableCell,
TableFooter,
TableRow,
+ ToggleButton,
+ ToggleButtonGroup,
Typography
} from '@mui/material'
import {
@@ -14,7 +16,7 @@ import {
SkipPaging,
Stream_OrderBy
} from '@superfluid-finance/sdk-core'
-import { FC, useState } from 'react'
+import React, { FC, useCallback, useEffect, useState } from 'react'
import AccountAddress from '../components/Address/AccountAddress'
import FlowingBalanceWithToken from '../components/Amount/FlowingBalanceWithToken'
@@ -23,6 +25,19 @@ import TableLoader from '../components/Table/TableLoader'
import TimeAgo from '../components/TimeAgo/TimeAgo'
import { Network } from '../redux/networks'
import { sfSubgraph } from '../redux/store'
+import SimpleStream from '../components/SimpleStream/SimpleStream'
+import ReactFlow, {
+ OnConnect,
+ Position,
+ addEdge,
+ useEdgesState,
+ useNodesState
+} from 'reactflow'
+
+export enum ViewMode {
+ Table,
+ Map
+}
export const defaultStreamQueryOrdering: Ordering = {
orderBy: 'createdAtTimestamp',
@@ -39,6 +54,10 @@ interface NetworkStreamsProps {
export const NetworkStreams: FC = ({ network }) => {
const [paging, setPaging] = useState(defaultStreamQueryPaging)
+ const [viewMode, setViewMode] = useState(ViewMode.Table)
+
+ const onViewModeChange = (_event: any, newViewMode: ViewMode) =>
+ setViewMode(newViewMode)
const query = sfSubgraph.useStreamsQuery({
chainId: network.chainId,
@@ -54,59 +73,127 @@ export const NetworkStreams: FC = ({ network }) => {
const streams = query.data?.data ?? []
+ const initialNodes = streams
+ .map((stream, index) => [
+ {
+ id: `${stream.sender}`,
+ type: 'input',
+ position: { x: -200, y: 0 },
+ data: {
+ label: stream.sender.slice(0, 6) + '...' + stream.sender.slice(-4)
+ },
+ sourcePosition: Position.Right,
+ targetPosition: Position.Left
+ },
+ {
+ id: `${stream.receiver}`,
+ position: { x: 150, y: 0 },
+ data: {
+ label: stream.receiver.slice(0, 6) + '...' + stream.receiver.slice(-4)
+ },
+ sourcePosition: Position.Right,
+ targetPosition: Position.Left
+ }
+ ])
+ .flat()
+
+ const initialEdges = streams
+ .map((stream, index) => [
+ {
+ id: `${stream.id}`,
+ source: `${stream.sender}`,
+ target: `${stream.receiver}`,
+ animated: true,
+ sourceHandle: 'left',
+ targetHandle: 'right',
+ type: 'straight'
+ }
+ ])
+ .flat()
+
+ const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes)
+ const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges)
+
+ useEffect(() => {
+ // need to update nodes and edges when streams change
+ }, [paging])
+
+ const onConnect: OnConnect = useCallback(
+ (connection) => setEdges((edges) => addEdge(connection, edges)),
+ [setEdges]
+ )
+
return (
-
-
- {streams.map((stream) => (
-
-
-
-
-
-
-
-
-
-
-
- ))}
-
-
-
-
- {streams.length > 0 && (
-
-
-
-
-
-
-
- )}
-
+
+ {/*
+
+ Table view
+ Map view
+
+ */}
+
+
+ {streams.map((stream) => (
+ *': { borderBottom: 'none' } }}
+ key={stream.id}
+ >
+
+
+
+
+
+
+
+
+
+
+ ))}
+
+
+
+
+ {streams.length > 0 && (
+
+
+
+
+
+
+
+ )}
+
+
)
}
diff --git a/src/pages/[_network]/supertokens/[_id]/SuperTokenStreamsTable.tsx b/src/pages/[_network]/supertokens/[_id]/SuperTokenStreamsTable.tsx
index 6bb51ee..83b6eca 100644
--- a/src/pages/[_network]/supertokens/[_id]/SuperTokenStreamsTable.tsx
+++ b/src/pages/[_network]/supertokens/[_id]/SuperTokenStreamsTable.tsx
@@ -30,7 +30,16 @@ import { StreamsQuery } from '@superfluid-finance/sdk-redux'
import omit from 'lodash/fp/omit'
import set from 'lodash/fp/set'
import isEqual from 'lodash/isEqual'
-import { ChangeEvent, FC, FormEvent, useEffect, useRef, useState } from 'react'
+import {
+ ChangeEvent,
+ FC,
+ FormEvent,
+ ReactElement,
+ useCallback,
+ useEffect,
+ useRef,
+ useState
+} from 'react'
import AccountAddress from '../../../../components/Address/AccountAddress'
import FlowRate from '../../../../components/Amount/FlowRate'
@@ -44,6 +53,13 @@ import { Network } from '../../../../redux/networks'
import { sfSubgraph } from '../../../../redux/store'
import { StreamStatus } from '../../accounts/AccountIncomingStreamsTable'
import { StreamDetailsDialog } from '../../streams/StreamDetails'
+import UserBlock from '../../../../components/Map/UserBlock'
+import TokenStreamsMap from '../../../../components/Map/TokenStreamsMap'
+
+export enum ViewMode {
+ Table,
+ Map
+}
const defaultOrdering = {
orderBy: 'createdAtTimestamp',
@@ -71,6 +87,11 @@ const SuperTokenStreamsTable: FC = ({
const [streamStatus, setStreamStatus] = useState(null)
+ const [viewMode, setViewMode] = useState(ViewMode.Table)
+
+ const onViewModeChange = (_event: any, newViewMode: ViewMode) =>
+ setViewMode(newViewMode)
+
const defaultFilter = {
token: tokenAddress
}
@@ -377,115 +398,132 @@ const SuperTokenStreamsTable: FC = ({
-
-
-
- Sender
- Receiver
-
-
- Flow Rate
-
-
-
-
-
- Created
-
-
-
-
-
-
- {tableRows.map((stream) => (
-
-
-
-
-
-
-
-
-
-
+
+
+ Table view
+ Map view
+
+
+ {viewMode === ViewMode.Table && (
+
+
+
+ Sender
+ Receiver
- {new Date(stream.createdAtTimestamp * 1000).toLocaleString()}
-
-
-
-
- {(onClick) => }
-
+
+ Flow Rate
+
+
-
- ))}
-
- {queryResult.isSuccess && tableRows.length === 0 && (
-
-
-
- No results
-
+
+
+ Created
+
+
+
+
+ {tableRows.map((stream) => (
+
+
+
+
+
+
+
+
+
+
+
+ {new Date(stream.createdAtTimestamp * 1000).toLocaleString()}
+
+
+
+
+ {(onClick) => }
+
+
+
+ ))}
+
+ {queryResult.isSuccess && tableRows.length === 0 && (
+
+
+
+ No results
+
+
+
+ )}
+
+
+
+ {tableRows.length > 0 && (
+
+
+
+
+
+
+
)}
-
-
-
- {tableRows.length > 0 && (
-
-
-
-
-
-
-
- )}
-
+
+ )}
+ {viewMode === ViewMode.Map && (
+
+ )}
>
)
}
diff --git a/src/pages/[_network]/supertokens/[_id]/SuperTokensTable.tsx b/src/pages/[_network]/supertokens/[_id]/SuperTokensTable.tsx
index 94598dc..80f1251 100644
--- a/src/pages/[_network]/supertokens/[_id]/SuperTokensTable.tsx
+++ b/src/pages/[_network]/supertokens/[_id]/SuperTokensTable.tsx
@@ -1,11 +1,15 @@
import FilterListIcon from '@mui/icons-material/FilterList'
+import IosShareIcon from '@mui/icons-material/IosShare'
import {
Box,
Button,
Chip,
+ FormControlLabel,
IconButton,
OutlinedInput,
Popover,
+ Radio,
+ RadioGroup,
Stack,
Table,
TableBody,
@@ -24,9 +28,14 @@ import {
createSkipPaging,
Ordering,
Token_Filter,
- Token_OrderBy
+ Token_OrderBy,
+ TokenStatistic_OrderBy,
+ TokenStatistic_Filter
} from '@superfluid-finance/sdk-core'
-import { TokensQuery } from '@superfluid-finance/sdk-redux'
+import {
+ TokenStatisticsQuery,
+ TokensQuery
+} from '@superfluid-finance/sdk-redux'
import isEqual from 'lodash/fp/isEqual'
import omit from 'lodash/fp/omit'
import set from 'lodash/fp/set'
@@ -52,33 +61,60 @@ interface SuperTokensTableProps {
}
type RequiredTokensQuery = Required>
+type RequiredTokenStatisticsQuery = Required<
+ Omit
+>
const defaultOrdering = {
orderBy: 'isListed',
orderDirection: 'desc'
} as Ordering
+const defaultStatisticsOrdering = {
+ orderBy: 'token__isListed',
+ orderDirection: 'desc'
+} as Ordering
+
const defaultFilter: Token_Filter = {
isSuperToken: true
}
+const defaultStatisticsFilter: TokenStatistic_Filter = {
+ token_: defaultFilter
+}
+
export const defaultPaging = createSkipPaging({
take: 10
})
+export const tokensPaging = createSkipPaging({
+ take: 500
+})
+
const SuperTokensTable: FC = ({ network }) => {
const filterAnchorRef = useRef(null)
const [showFilterMenu, setShowFilterMenu] = useState(false)
+ const exportAnchorRef = useRef(null)
+ const [showExportMenu, setShowExportMenu] = useState(false)
+ const [exportValue, setExportValue] = useState('json')
+
const [listedStatus, setListedStatus] = useState(null)
const createDefaultArg = (): RequiredTokensQuery => ({
chainId: network.chainId,
filter: defaultFilter,
- pagination: defaultPaging,
+ pagination: tokensPaging,
order: defaultOrdering
})
+ const createDefaultStatisticsArg = (): RequiredTokenStatisticsQuery => ({
+ chainId: network.chainId,
+ filter: defaultStatisticsFilter,
+ pagination: defaultPaging,
+ order: defaultStatisticsOrdering
+ })
+
const [queryArg, setQueryArg] =
useState(createDefaultArg())
@@ -96,8 +132,27 @@ const SuperTokensTable: FC = ({ network }) => {
}
}), [queriedTokens.data?.items]);
+ const [queryStatisticsArg, setQueryStatisticsArg] =
+ useState(createDefaultStatisticsArg())
+
+ const [queryStatisticsTrigger, queryStatisticsResult] =
+ sfSubgraph.useLazyTokenStatisticsQuery()
+
+ // const [queryStatisticsArg, setQueryStatisticsArg] =
+ // useState(createDefaultStatisticsArg())
+
+ // const [queryStatisticsTrigger, queryStatisticsResult] =
+ // sfSubgraph.useLazyTokenStatisticsQuery()
+
const queryTriggerDebounced = useDebounce(queryTrigger, 250)
+ const [dailyOutflowRateGte, setDailyOutflowRateGte] = useState('')
+ const [dailyOutflowRateLte, setDailyOutflowRateLte] = useState('')
+ const [perSecondOutflowRateGte, setPerSecondOutflowRateGte] =
+ useState('')
+ const [perSecondOutflowRateLte, setPerSecondOutflowRateLte] =
+ useState('')
+
const onQueryArgsChanged = (newArgs: RequiredTokensQuery) => {
setQueryArg(newArgs)
@@ -111,86 +166,228 @@ const SuperTokensTable: FC = ({ network }) => {
}
}
+ const onQueryStatisticsArgsChanged = (
+ newStatisticsArgs: RequiredTokenStatisticsQuery
+ ) => {
+ setQueryStatisticsArg(newStatisticsArgs)
+ queryStatisticsTrigger(newStatisticsArgs, true)
+ }
+
useEffect(() => {
onQueryArgsChanged(createDefaultArg())
+ onQueryStatisticsArgsChanged(createDefaultStatisticsArg())
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [network])
- const setPage = (newPage: number) =>
- onQueryArgsChanged(
- set('pagination.skip', (newPage - 1) * queryArg.pagination.take, queryArg)
+ const setPage = (newPage: number) => {
+ onQueryStatisticsArgsChanged(
+ set(
+ 'pagination.skip',
+ (newPage - 1) * queryStatisticsArg.pagination.take,
+ queryStatisticsArg
+ )
)
+ }
- const setPageSize = (newPageSize: number) =>
- onQueryArgsChanged(set('pagination.take', newPageSize, queryArg))
+ const setPageSize = (newPageSize: number) => {
+ onQueryStatisticsArgsChanged(
+ set('pagination.take', newPageSize, queryStatisticsArg)
+ )
+ }
- const onOrderingChanged = (newOrdering: Ordering) =>
- onQueryArgsChanged({ ...queryArg, order: newOrdering })
+ const onStatisticsOrderingChanged = (
+ newStatisticsOrdering: Ordering
+ ) =>
+ onQueryStatisticsArgsChanged({
+ ...queryStatisticsArg,
+ order: newStatisticsOrdering
+ })
- const onSortClicked = (field: Token_OrderBy) => () => {
- if (queryArg.order?.orderBy !== field) {
- onOrderingChanged({
+ const onStatisticsSortClicked = (field: TokenStatistic_OrderBy) => () => {
+ if (queryStatisticsArg.order?.orderBy !== field) {
+ onStatisticsOrderingChanged({
orderBy: field,
orderDirection: 'desc'
})
- } else if (queryArg.order.orderDirection === 'desc') {
- onOrderingChanged({
+ } else if (queryStatisticsArg.order.orderDirection === 'desc') {
+ onStatisticsOrderingChanged({
orderBy: field,
orderDirection: 'asc'
})
} else {
- onOrderingChanged(defaultOrdering)
+ onStatisticsOrderingChanged(defaultStatisticsOrdering)
}
}
- const onFilterChange = (newFilter: Token_Filter) => {
- onQueryArgsChanged({
- ...queryArg,
- pagination: { ...queryArg.pagination, skip: 0 },
+ const onFilterChange = (newFilter: TokenStatistic_Filter) => {
+ onQueryStatisticsArgsChanged({
+ ...queryStatisticsArg,
+ pagination: { ...queryStatisticsArg.pagination, skip: 0 },
filter: newFilter
})
}
const clearFilterField =
- (...fields: Array) =>
- () =>
- onFilterChange(omit(fields, queryArg.filter))
+ (...fields: Array | string[]) =>
+ () => {
+ if (fields.includes('totalOutflowRate_gte')) {
+ setDailyOutflowRateGte('')
+ setPerSecondOutflowRateGte('')
+ } else if (fields.includes('totalOutflowRate_lte')) {
+ setDailyOutflowRateLte('')
+ setPerSecondOutflowRateLte('')
+ }
+ onFilterChange(omit(fields, queryStatisticsArg.filter))
+ }
const onNameChange = (e: ChangeEvent) => {
+ const { token_, ...newFilter } = queryStatisticsArg.filter
if (e.target.value) {
onFilterChange({
- ...queryArg.filter,
- name_contains_nocase: e.target.value
+ ...queryStatisticsArg.filter,
+ token_: {
+ name_contains_nocase: e.target.value,
+ isListed: token_?.isListed,
+ symbol_contains_nocase: token_?.symbol_contains_nocase
+ }
})
} else {
- onFilterChange(omit('name_contains_nocase', queryArg.filter))
+ onFilterChange(
+ omit('token_.name_contains_nocase', queryStatisticsArg.filter)
+ )
}
}
const onSymbolChange = (e: ChangeEvent) => {
+ const { token_, ...newFilter } = queryStatisticsArg.filter
if (e.target.value) {
onFilterChange({
- ...queryArg.filter,
- symbol_contains_nocase: e.target.value
+ ...queryStatisticsArg.filter,
+ token_: {
+ symbol_contains_nocase: e.target.value,
+ isListed: token_?.isListed,
+ name_contains_nocase: token_?.name_contains_nocase
+ }
})
} else {
- onFilterChange(omit('symbol_contains_nocase', queryArg.filter))
+ onFilterChange(
+ omit('token_.symbol_contains_nocase', queryStatisticsArg.filter)
+ )
}
}
- const getListedStatusFilter = (status: ListedStatus | null): Token_Filter => {
+ const onActiveStreamsGteChange = (e: ChangeEvent) => {
+ if (e.target.value) {
+ onFilterChange({
+ ...queryStatisticsArg.filter,
+ totalNumberOfActiveStreams_gte: Number(e.target.value)
+ })
+ } else {
+ onFilterChange(
+ omit('totalNumberOfActiveStreams_gte', queryStatisticsArg.filter)
+ )
+ }
+ }
+
+ const onActiveStreamsLteChange = (e: ChangeEvent) => {
+ if (e.target.value) {
+ onFilterChange({
+ ...queryStatisticsArg.filter,
+ totalNumberOfActiveStreams_lte: Number(e.target.value)
+ })
+ } else {
+ onFilterChange(
+ omit('totalNumberOfActiveStreams_lte', queryStatisticsArg.filter)
+ )
+ }
+ }
+
+ const queryOutflowRateGte = (normalizedOutflowRate: string) => {
+ if (normalizedOutflowRate) {
+ onFilterChange({
+ ...queryStatisticsArg.filter,
+ totalOutflowRate_gte: normalizedOutflowRate
+ })
+ } else {
+ onFilterChange(omit('totalOutflowRate_gte', queryStatisticsArg.filter))
+ }
+ }
+
+ const onOutflowRateGteChange = (e: ChangeEvent) => {
+ setDailyOutflowRateGte(e.target.value)
+ }
+
+ useEffect(() => {
+ if (dailyOutflowRateGte) {
+ const dailyOutflowRateGteValue = Number(dailyOutflowRateGte)
+ if (!isNaN(dailyOutflowRateGteValue)) {
+ const normalizedOutflowRate = (
+ (dailyOutflowRateGteValue * 1e18) /
+ (60 * 60 * 24)
+ ).toFixed(0)
+ setPerSecondOutflowRateGte(normalizedOutflowRate)
+ queryOutflowRateGte(normalizedOutflowRate)
+ }
+ }
+ }, [dailyOutflowRateGte])
+
+ const queryOutflowRateLte = (normalizedOutflowRate: string) => {
+ if (normalizedOutflowRate) {
+ onFilterChange({
+ ...queryStatisticsArg.filter,
+ totalOutflowRate_lte: normalizedOutflowRate
+ })
+ } else {
+ onFilterChange(omit('totalOutflowRate_lte', queryStatisticsArg.filter))
+ }
+ }
+
+ const onOutflowRateLteChange = (e: ChangeEvent) => {
+ setDailyOutflowRateLte(e.target.value)
+ }
+
+ useEffect(() => {
+ if (dailyOutflowRateLte) {
+ const dailyOutflowRateLteValue = Number(dailyOutflowRateLte)
+ if (!isNaN(dailyOutflowRateLteValue)) {
+ const normalizedOutflowRate = (
+ (dailyOutflowRateLteValue * 1e18) /
+ (60 * 60 * 24)
+ ).toFixed(0)
+ setPerSecondOutflowRateLte(normalizedOutflowRate)
+ queryOutflowRateLte(normalizedOutflowRate)
+ }
+ }
+ }, [dailyOutflowRateLte])
+
+ const getListedStatusFilter = (
+ status: ListedStatus | null
+ ): TokenStatistic_Filter => {
+ const { token_, ...newFilter } = queryStatisticsArg.filter
switch (status) {
case ListedStatus.Listed:
- return { isListed: true }
+ return {
+ token_: {
+ isListed: true,
+ name_contains_nocase: token_?.name_contains_nocase,
+ symbol_contains_nocase: token_?.symbol_contains_nocase
+ }
+ }
case ListedStatus.NotListed:
- return { isListed: false }
+ return {
+ token_: {
+ isListed: false,
+ name_contains_nocase: token_?.name_contains_nocase,
+ symbol_contains_nocase: token_?.symbol_contains_nocase
+ }
+ }
default:
return {}
}
}
const changeListedStatus = (newStatus: ListedStatus | null) => {
- const { isListed, ...newFilter } = queryArg.filter
+ const { token_, ...newFilter } = queryStatisticsArg.filter
setListedStatus(newStatus)
onFilterChange({
@@ -214,17 +411,94 @@ const SuperTokensTable: FC = ({ network }) => {
const resetFilter = () => {
clearListedStatusFilter()
- onFilterChange(defaultFilter)
+ onFilterChange(defaultStatisticsFilter)
closeFilter()
}
const hasNextPage = !!queriedTokens.data?.nextPaging
+ // const tokens = queryResult.data?.data || []
+
+ const tokenStatistics = queryStatisticsResult.data?.data || []
+
+ const resultsToShow = tokenStatistics.map((stat) => ({
+ ...stat,
+ ...tokens.find((token) => token.id === stat.id)
+ }))
+
+ const {
+ filter: statisticsFilter,
+ order: statisticsOrder,
+ pagination: statisticsPagination
+ } = queryStatisticsArg
+
const { filter, order, pagination } = queryArg
const { skip = defaultPaging.skip, take = defaultPaging.take } =
queriedTokens.data?.paging || {}
+ const openExport = () => setShowExportMenu(true)
+ const closeExport = () => setShowExportMenu(false)
+ const onExportChange = (event: ChangeEvent) => {
+ setExportValue((event.target as HTMLInputElement).value)
+ }
+
+ const jsonToCSV = (jsonData: any[]) => {
+ const keys = Object.keys(jsonData[0])
+ const csvRows = [keys.join(',')]
+
+ jsonData.forEach((item) => {
+ const values = keys.map((key) => item[key])
+ csvRows.push(values.join(','))
+ })
+
+ return csvRows.join('\n')
+ }
+
+ const transformData = (originalData: any[]) => {
+ return originalData.map((item) => {
+ const dailyOutflowRateRaw =
+ (parseFloat(item.totalOutflowRate) * 60 * 60 * 24) / 1e18
+ const dailyOutflowRate =
+ dailyOutflowRateRaw < 0.01
+ ? 0
+ : parseFloat(dailyOutflowRateRaw.toFixed(2))
+
+ return {
+ name: item.name,
+ symbol: item.symbol,
+ isListed: item.isListed,
+ totalNumberOfActiveStreams: item.totalNumberOfActiveStreams,
+ dailyOutflowRate: dailyOutflowRate,
+ address: item.id
+ }
+ })
+ }
+
+ const exportData = () => {
+ let blob
+ let fileName
+ const dataToExport = transformData(resultsToShow)
+ if (exportValue === 'json') {
+ const jsonData = JSON.stringify(dataToExport, null, 2)
+ blob = new Blob([jsonData], { type: 'application/json' })
+ fileName = 'tokens.json'
+ } else {
+ const csvData = jsonToCSV(dataToExport)
+ blob = new Blob([csvData], { type: 'text/csv' })
+ fileName = 'tokens.csv'
+ }
+ const url = window.URL.createObjectURL(blob)
+ const a = document.createElement('a')
+ a.setAttribute('hidden', '')
+ a.setAttribute('href', url)
+ a.setAttribute('download', `${fileName}`)
+ document.body.appendChild(a)
+ a.click()
+ document.body.removeChild(a)
+ closeExport()
+ }
+
return (
<>
@@ -232,30 +506,40 @@ const SuperTokensTable: FC = ({ network }) => {
Super tokens
+
+
+
+
+
+
- {filter.name_contains_nocase && (
+ {statisticsFilter.token_?.name_contains_nocase && (
Name:{' '}
- {filter.name_contains_nocase}
+
+ {statisticsFilter.token_?.name_contains_nocase}
+
>
}
size="small"
- onDelete={clearFilterField('name_contains_nocase')}
+ onDelete={clearFilterField('token_.name_contains_nocase')}
/>
)}
- {filter.symbol_contains_nocase && (
+ {statisticsFilter.token_?.symbol_contains_nocase && (
Symbol:{' '}
- {filter.symbol_contains_nocase}
+
+ {statisticsFilter.token_?.symbol_contains_nocase}
+
>
}
size="small"
- onDelete={clearFilterField('symbol_contains_nocase')}
+ onDelete={clearFilterField('token_.symbol_contains_nocase')}
/>
)}
@@ -273,6 +557,62 @@ const SuperTokensTable: FC = ({ network }) => {
onDelete={clearListedStatusFilter}
/>
)}
+
+ {statisticsFilter.totalNumberOfActiveStreams_gte && (
+
+ Active Streams ≥{' '}
+
+ {statisticsFilter.totalNumberOfActiveStreams_gte}
+
+ >
+ }
+ size="small"
+ onDelete={clearFilterField('totalNumberOfActiveStreams_gte')}
+ />
+ )}
+
+ {statisticsFilter.totalNumberOfActiveStreams_lte && (
+
+ Active Streams ≤{' '}
+
+ {statisticsFilter.totalNumberOfActiveStreams_lte}
+
+ >
+ }
+ size="small"
+ onDelete={clearFilterField('totalNumberOfActiveStreams_lte')}
+ />
+ )}
+
+ {statisticsFilter.totalOutflowRate_gte && (
+
+ Outflow Rate ≥{' '}
+ {dailyOutflowRateGte}
+ >
+ }
+ size="small"
+ onDelete={clearFilterField('totalOutflowRate_gte')}
+ />
+ )}
+
+ {statisticsFilter.totalOutflowRate_lte && (
+
+ Outflow Rate ≤{' '}
+ {dailyOutflowRateLte}
+ >
+ }
+ size="small"
+ onDelete={clearFilterField('totalOutflowRate_lte')}
+ />
+ )}
@@ -281,6 +621,59 @@ const SuperTokensTable: FC = ({ network }) => {
+
+
+
+
+ Export as
+
+
+ } label="CSV" />
+ }
+ label="JSON"
+ />
+
+
+
+
+
+
+
+
+
+
= ({ network }) => {
autoFocus
fullWidth
size="small"
- value={filter.name_contains_nocase || ''}
+ value={statisticsFilter.token_?.name_contains_nocase || ''}
onChange={onNameChange}
endAdornment={
- filter.name_contains_nocase && (
+ statisticsFilter.token_?.name_contains_nocase && (
)
}
@@ -324,12 +717,14 @@ const SuperTokensTable: FC = ({ network }) => {
data-cy={'filter-symbol-input'}
fullWidth
size="small"
- value={filter.symbol_contains_nocase || ''}
+ value={statisticsFilter.token_?.symbol_contains_nocase || ''}
onChange={onSymbolChange}
endAdornment={
- filter.symbol_contains_nocase && (
+ statisticsFilter.token_?.symbol_contains_nocase && (
)
}
@@ -363,18 +758,122 @@ const SuperTokensTable: FC = ({ network }) => {
+
+
+ Active Streams
+
+
+ <>≤>
+
+ )
+ }
+ />
+
+
+ <>≥>
+
+ )
+ }
+ />
+
+
+
+
+
+ Daily Outflow Rate
+
+
+ <>≤>
+
+ )
+ }
+ />
+
+
+ <>≥>
+
+ )
+ }
+ />
+
+
+
- {(filter.name_contains_nocase ||
- filter.symbol_contains_nocase ||
+ {(statisticsFilter.token_?.name_contains_nocase ||
+ statisticsFilter.token_?.symbol_contains_nocase ||
listedStatus !== null) && (
-
- )}
+
+ )}
@@ -387,11 +886,13 @@ const SuperTokensTable: FC = ({ network }) => {
Token name
@@ -399,11 +900,13 @@ const SuperTokensTable: FC = ({ network }) => {
Symbol
Listed
= ({ network }) => {
/>
+
+
+ Active Streams
+
+
+ {/* Holders */}
+
+
+ Daily Outflow Rate
+
+
Address
- {tokens.map((token) => (
+ {resultsToShow.map((token) => (
+ {/* {tokens.find((stat) => stat.id === token.id)?.name || (
+ <>—>
+ )} */}
{token.name || <>—>}
@@ -446,7 +981,10 @@ const SuperTokensTable: FC = ({ network }) => {
–>}
+ label={
+ // } // ) // <>—> // tokens.find((stat) => stat.id === token.id)?.symbol || ( // {
+ token.symbol || <>—>
+ }
sx={{
cursor: 'pointer',
lineHeight: '24px'
@@ -455,8 +993,28 @@ const SuperTokensTable: FC = ({ network }) => {
+ {/* {tokens.find((stat) => stat.id === token.id)?.isListed
+ ? 'Yes'
+ : 'No'} */}
{token.isListed ? 'Yes' : 'No'}
+
+ {token.totalNumberOfActiveStreams}
+
+ {/* */}
+
+ {(Number(token.totalOutflowRate) * 60 * 60 * 24) / 1e18 >
+ 0.01 ? (
+ <>
+ {(
+ (Number(token.totalOutflowRate) * 60 * 60 * 24) /
+ 1e18
+ ).toFixed(2)}
+ >
+ ) : (
+ <>0>
+ )}
+
{token.id}
))}
@@ -479,10 +1037,10 @@ const SuperTokensTable: FC = ({ network }) => {
/>
- {tokens.length > 0 && (
+ {resultsToShow.length > 0 && (
-
+