From 59d9ac887e90d4185b1aebfbac1145be383893a9 Mon Sep 17 00:00:00 2001 From: Greg Nazario Date: Fri, 15 Nov 2024 17:24:45 -0500 Subject: [PATCH] Fixup coin holders page / Add transactinos (#918) * [holders] Add loading spinner * [transactions] Add coin transactions bare minimum table --- src/api/hooks/useGetCoinActivities.ts | 48 ++++++++++++++ src/api/hooks/useGetCoinHolders.ts | 10 ++- src/pages/Coin/Index.tsx | 2 +- src/pages/Coin/Tabs/HoldersTab.tsx | 8 ++- src/pages/Coin/Tabs/TransactionsTab.tsx | 64 ++++++++++++++++++- src/pages/FungibleAsset/Index.tsx | 2 +- src/pages/FungibleAsset/Tabs/HoldersTab.tsx | 4 ++ .../FungibleAsset/Tabs/TransactionsTab.tsx | 64 ++++++++++++++++++- 8 files changed, 190 insertions(+), 12 deletions(-) create mode 100644 src/api/hooks/useGetCoinActivities.ts diff --git a/src/api/hooks/useGetCoinActivities.ts b/src/api/hooks/useGetCoinActivities.ts new file mode 100644 index 00000000..b261c707 --- /dev/null +++ b/src/api/hooks/useGetCoinActivities.ts @@ -0,0 +1,48 @@ +import {useQuery as useGraphqlQuery} from "@apollo/client/react/hooks/useQuery"; +import {gql} from "@apollo/client"; + +export type FAActivity = { + transaction_version: number; + transaction_timestamp: string; + owner_address: string; +}; + +export function useGetCoinActivities( + asset: string, + offset?: number, +): { + isLoading: boolean; + error: any; + data: FAActivity[] | undefined; +} { + const {loading, error, data} = useGraphqlQuery<{ + fungible_asset_activities: FAActivity[]; + }>( + // Exclude gas fees from the list + gql` + query GetFungibleAssetActivities($asset: String, $offset: Int) { + fungible_asset_activities( + where: { + asset_type: {_eq: $asset} + type: {_neq: "0x1::aptos_coin::GasFeeEvent"} + } + offset: $offset + limit: 100 + order_by: {transaction_version: desc} + distinct_on: transaction_version + ) { + transaction_version + owner_address + transaction_timestamp + } + } + `, + {variables: {asset, offset: offset ?? 0}}, + ); + + return { + isLoading: loading, + error, + data: data?.fungible_asset_activities, + }; +} diff --git a/src/api/hooks/useGetCoinHolders.ts b/src/api/hooks/useGetCoinHolders.ts index 95bac48b..285be613 100644 --- a/src/api/hooks/useGetCoinHolders.ts +++ b/src/api/hooks/useGetCoinHolders.ts @@ -6,7 +6,10 @@ export type CoinHolder = { amount: number; }; -export function useGetCoinHolders(coin_type: string): { +export function useGetCoinHolders( + coin_type: string, + offset?: number, +): { isLoading: boolean; error: any; data: CoinHolder[] | undefined; @@ -15,10 +18,11 @@ export function useGetCoinHolders(coin_type: string): { current_fungible_asset_balances: CoinHolder[]; }>( gql` - query GetFungibleAssetBalances($coin_type: String!) { + query GetFungibleAssetBalances($coin_type: String!, $offset: Int!) { current_fungible_asset_balances( where: {asset_type: {_eq: $coin_type}} limit: 100 + offset: $offset order_by: {amount: desc} ) { owner_address @@ -26,7 +30,7 @@ export function useGetCoinHolders(coin_type: string): { } } `, - {variables: {coin_type}}, + {variables: {coin_type, offset: offset ?? 0}}, ); return { diff --git a/src/pages/Coin/Index.tsx b/src/pages/Coin/Index.tsx index b7d8aedf..69f4b6ba 100644 --- a/src/pages/Coin/Index.tsx +++ b/src/pages/Coin/Index.tsx @@ -15,7 +15,7 @@ import {useGetCoinSupplyLimit} from "../../api/hooks/useGetCoinSupplyLimit"; import {useGetCoinList} from "../../api/hooks/useGetCoinList"; import {findCoinData} from "../Transaction/Tabs/BalanceChangeTab"; -const TAB_VALUES_FULL: TabValue[] = ["info", "holders"]; +const TAB_VALUES_FULL: TabValue[] = ["info", "holders", "transactions"]; const TAB_VALUES: TabValue[] = ["info"]; diff --git a/src/pages/Coin/Tabs/HoldersTab.tsx b/src/pages/Coin/Tabs/HoldersTab.tsx index 9fef97e1..cea74613 100644 --- a/src/pages/Coin/Tabs/HoldersTab.tsx +++ b/src/pages/Coin/Tabs/HoldersTab.tsx @@ -14,6 +14,7 @@ import GeneralTableRow from "../../../components/Table/GeneralTableRow"; import GeneralTableCell from "../../../components/Table/GeneralTableCell"; import HashButton, {HashType} from "../../../components/HashButton"; import {getFormattedBalanceStr} from "../../../components/IndividualPageContent/ContentValue/CurrencyValue"; +import LoadingModal from "../../../components/LoadingModal"; type HoldersTabProps = { struct: string; @@ -22,6 +23,9 @@ type HoldersTabProps = { export default function HoldersTab({struct, data}: HoldersTabProps) { const holderData = useGetCoinHolders(struct); + if (holderData?.isLoading) { + return ; + } if (!data || Array.isArray(data) || !holderData?.data) { return ; } @@ -40,14 +44,16 @@ export function HoldersTable({ + - {holders.map((holder) => { + {holders.map((holder, i) => { return ( + {i} ; +export default function TransactionsTab({struct, data}: TransactionsTabProps) { + const holderData = useGetCoinActivities(struct); + if (holderData?.isLoading) { + return ; + } + if (!data || Array.isArray(data) || !holderData?.data) { + return ; + } + + return ; +} + +export function FAActivityTable({ + activities, +}: { + activities: FAActivity[]; + data: CoinData; +}) { + return ( +
+ + + + + + + + {activities.map((activity) => { + return ( + + + + + + + + + ); + })} + +
+ ); } diff --git a/src/pages/FungibleAsset/Index.tsx b/src/pages/FungibleAsset/Index.tsx index f9e5d647..9c469d33 100644 --- a/src/pages/FungibleAsset/Index.tsx +++ b/src/pages/FungibleAsset/Index.tsx @@ -14,7 +14,7 @@ import {useGetFASupply} from "../../api/hooks/useGetFaSupply"; import {CoinDescription, useGetCoinList} from "../../api/hooks/useGetCoinList"; import {findCoinData} from "../Transaction/Tabs/BalanceChangeTab"; -const TAB_VALUES_FULL: TabValue[] = ["info", "holders"]; +const TAB_VALUES_FULL: TabValue[] = ["info", "holders", "transactions"]; const TAB_VALUES: TabValue[] = ["info"]; diff --git a/src/pages/FungibleAsset/Tabs/HoldersTab.tsx b/src/pages/FungibleAsset/Tabs/HoldersTab.tsx index 39ba77ab..fb44392d 100644 --- a/src/pages/FungibleAsset/Tabs/HoldersTab.tsx +++ b/src/pages/FungibleAsset/Tabs/HoldersTab.tsx @@ -14,6 +14,7 @@ import GeneralTableCell from "../../../components/Table/GeneralTableCell"; import HashButton, {HashType} from "../../../components/HashButton"; import {getFormattedBalanceStr} from "../../../components/IndividualPageContent/ContentValue/CurrencyValue"; import {FACombinedData} from "../Index"; +import LoadingModal from "../../../components/LoadingModal"; type HoldersTabProps = { address: string; @@ -22,6 +23,9 @@ type HoldersTabProps = { export default function HoldersTab({address, data}: HoldersTabProps) { const holderData = useGetCoinHolders(address); + if (holderData?.isLoading) { + return ; + } if (!data || Array.isArray(data) || !holderData?.data) { return ; } diff --git a/src/pages/FungibleAsset/Tabs/TransactionsTab.tsx b/src/pages/FungibleAsset/Tabs/TransactionsTab.tsx index 948923ad..9a472586 100644 --- a/src/pages/FungibleAsset/Tabs/TransactionsTab.tsx +++ b/src/pages/FungibleAsset/Tabs/TransactionsTab.tsx @@ -1,13 +1,71 @@ import React from "react"; import EmptyTabContent from "../../../components/IndividualPageContent/EmptyTabContent"; import {FACombinedData} from "../Index"; +import LoadingModal from "../../../components/LoadingModal"; +import Table from "@mui/material/Table"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import GeneralTableHeaderCell from "../../../components/Table/GeneralTableHeaderCell"; +import GeneralTableBody from "../../../components/Table/GeneralTableBody"; +import GeneralTableRow from "../../../components/Table/GeneralTableRow"; +import GeneralTableCell from "../../../components/Table/GeneralTableCell"; +import HashButton, {HashType} from "../../../components/HashButton"; +import { + FAActivity, + useGetCoinActivities, +} from "../../../api/hooks/useGetCoinActivities"; type TransactionsTabProps = { address: string; data: FACombinedData | undefined; }; -export default function TransactionsTab({}: TransactionsTabProps) { - // TODO: Lookup transactions by coin - return ; +export default function TransactionsTab({address, data}: TransactionsTabProps) { + const holderData = useGetCoinActivities(address); + if (holderData?.isLoading) { + return ; + } + if (!data || Array.isArray(data) || !holderData?.data) { + return ; + } + + return ; +} + +export function FAActivityTable({ + activities, +}: { + activities: FAActivity[]; + data: FACombinedData; +}) { + return ( + + + + + + + + + {activities.map((activity) => { + return ( + + + + + + + + + ); + })} + +
+ ); }