Skip to content

Commit

Permalink
[coin/fa] Add holders tab (#917)
Browse files Browse the repository at this point in the history
  • Loading branch information
gregnazario authored Nov 15, 2024
1 parent 652df02 commit 461c57b
Show file tree
Hide file tree
Showing 10 changed files with 215 additions and 15 deletions.
37 changes: 37 additions & 0 deletions src/api/hooks/useGetCoinHolders.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {useQuery as useGraphqlQuery} from "@apollo/client/react/hooks/useQuery";
import {gql} from "@apollo/client";

export type CoinHolder = {
owner_address: string;
amount: number;
};

export function useGetCoinHolders(coin_type: string): {
isLoading: boolean;
error: any;
data: CoinHolder[] | undefined;
} {
const {loading, error, data} = useGraphqlQuery<{
current_fungible_asset_balances: CoinHolder[];
}>(
gql`
query GetFungibleAssetBalances($coin_type: String!) {
current_fungible_asset_balances(
where: {asset_type: {_eq: $coin_type}}
limit: 100
order_by: {amount: desc}
) {
owner_address
amount
}
}
`,
{variables: {coin_type}},
);

return {
isLoading: loading,
error,
data: data?.current_fungible_asset_balances,
};
}
2 changes: 1 addition & 1 deletion src/api/hooks/useGetFaMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {useViewFunction} from "./useViewFunction";

type FaMetadata = {
export type FaMetadata = {
name: string;
symbol: string;
decimals: number;
Expand Down
4 changes: 2 additions & 2 deletions src/pages/Coin/Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import {useGetCoinSupplyLimit} from "../../api/hooks/useGetCoinSupplyLimit";
import {useGetCoinList} from "../../api/hooks/useGetCoinList";
import {findCoinData} from "../Transaction/Tabs/BalanceChangeTab";

const TAB_VALUES_FULL: TabValue[] = ["info"];
const TAB_VALUES_FULL: TabValue[] = ["info", "holders"];

const TAB_VALUES: TabValue[] = ["info", "transactions"];
const TAB_VALUES: TabValue[] = ["info"];

export default function CoinPage() {
const isGraphqlClientSupported = useGetIsGraphqlClientSupported();
Expand Down
8 changes: 7 additions & 1 deletion src/pages/Coin/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import {useParams} from "react-router-dom";
import {useNavigate} from "../../routing";
import {CoinData} from "./Components/CoinData";
import {CoinDescription} from "../../api/hooks/useGetCoinList";
import HoldersTab from "./Tabs/HoldersTab";

const TAB_VALUES: TabValue[] = ["info", "transactions"];
const TAB_VALUES: TabValue[] = ["info", "holders", "transactions"];

const TabComponents = Object.freeze({
transactions: TransactionsTab,
holders: HoldersTab,
info: InfoTab,
});

Expand All @@ -27,6 +29,8 @@ function getTabLabel(value: TabValue): string {
return "Info";
case "transactions":
return "Transactions";
case "holders":
return "Holders";
default:
return assertNever(value);
}
Expand All @@ -38,6 +42,8 @@ function getTabIcon(value: TabValue): JSX.Element {
return <DescriptionOutlinedIcon fontSize="small" />;
case "transactions":
return <WysiwygIcon fontSize="small" />;
case "holders":
return <WysiwygIcon fontSize="small" />;
default:
return assertNever(value);
}
Expand Down
71 changes: 71 additions & 0 deletions src/pages/Coin/Tabs/HoldersTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import React from "react";
import EmptyTabContent from "../../../components/IndividualPageContent/EmptyTabContent";
import {CoinData} from "../Components/CoinData";
import {
CoinHolder,
useGetCoinHolders,
} from "../../../api/hooks/useGetCoinHolders";
import Table from "@mui/material/Table";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import GeneralTableBody from "../../../components/Table/GeneralTableBody";
import GeneralTableHeaderCell from "../../../components/Table/GeneralTableHeaderCell";
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";

type HoldersTabProps = {
struct: string;
data: CoinData | undefined;
};

export default function HoldersTab({struct, data}: HoldersTabProps) {
const holderData = useGetCoinHolders(struct);
if (!data || Array.isArray(data) || !holderData?.data) {
return <EmptyTabContent />;
}

return <HoldersTable data={data} holders={holderData.data} />;
}

export function HoldersTable({
data,
holders,
}: {
holders: CoinHolder[];
data: CoinData;
}) {
return (
<Table>
<TableHead>
<TableRow>
<GeneralTableHeaderCell header="address" />
<GeneralTableHeaderCell header="amount" textAlignRight={true} />
</TableRow>
</TableHead>
<GeneralTableBody>
{holders.map((holder) => {
return (
<GeneralTableRow>
<GeneralTableCell>
<HashButton
hash={holder.owner_address}
type={HashType.ACCOUNT}
/>
</GeneralTableCell>
<GeneralTableCell align={"right"}>
{getFormattedBalanceStr(
holder.amount.toString(),
data.data.decimals,
) +
" " +
data.data.symbol}
</GeneralTableCell>
</GeneralTableRow>
);
})}
</GeneralTableBody>
</Table>
);
}
14 changes: 10 additions & 4 deletions src/pages/FungibleAsset/Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,20 @@ import Error from "./Error";
import {ResponseError, ResponseErrorType} from "../../api/client";
import {isValidAccountAddress} from "../utils";
import FATitle from "./Title";
import {useGetFaMetadata} from "../../api/hooks/useGetFaMetadata";
import {FaMetadata, useGetFaMetadata} from "../../api/hooks/useGetFaMetadata";
import {useGetFASupply} from "../../api/hooks/useGetFaSupply";
import {useGetCoinList} from "../../api/hooks/useGetCoinList";
import {CoinDescription, useGetCoinList} from "../../api/hooks/useGetCoinList";
import {findCoinData} from "../Transaction/Tabs/BalanceChangeTab";

const TAB_VALUES_FULL: TabValue[] = ["info"];
const TAB_VALUES_FULL: TabValue[] = ["info", "holders"];

const TAB_VALUES: TabValue[] = ["info", "transactions"];
const TAB_VALUES: TabValue[] = ["info"];

export type FACombinedData = {
coinData: CoinDescription | undefined;
metadata: FaMetadata | null;
supply: bigint | null;
};

export default function FAPage() {
const isGraphqlClientSupported = useGetIsGraphqlClientSupported();
Expand Down
14 changes: 10 additions & 4 deletions src/pages/FungibleAsset/Tabs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import StyledTabs from "../../components/StyledTabs";
import StyledTab from "../../components/StyledTab";
import {useParams} from "react-router-dom";
import {useNavigate} from "../../routing";
import {FAData} from "./Components/FAData";
import HoldersTab from "./Tabs/HoldersTab";
import {FACombinedData} from "./Index";

const TAB_VALUES: TabValue[] = ["info", "transactions"];
const TAB_VALUES: TabValue[] = ["info", "holders", "transactions"];

const TabComponents = Object.freeze({
transactions: TransactionsTab,
holders: HoldersTab,
info: InfoTab,
});

Expand All @@ -24,6 +26,8 @@ function getTabLabel(value: TabValue): string {
switch (value) {
case "info":
return "Info";
case "holders":
return "Holders";
case "transactions":
return "Transactions";
default:
Expand All @@ -35,6 +39,8 @@ function getTabIcon(value: TabValue): JSX.Element {
switch (value) {
case "info":
return <DescriptionOutlinedIcon fontSize="small" />;
case "holders":
return <WysiwygIcon fontSize="small" />;
case "transactions":
return <WysiwygIcon fontSize="small" />;
default:
Expand All @@ -45,7 +51,7 @@ function getTabIcon(value: TabValue): JSX.Element {
type TabPanelProps = {
value: TabValue;
address: string;
data: FAData | undefined;
data: FACombinedData | undefined;
};

function TabPanel({value, address, data}: TabPanelProps): JSX.Element {
Expand All @@ -55,7 +61,7 @@ function TabPanel({value, address, data}: TabPanelProps): JSX.Element {

type FATabsProps = {
address: string;
data: any | undefined;
data: FACombinedData | undefined;
tabValues?: TabValue[];
};

Expand Down
73 changes: 73 additions & 0 deletions src/pages/FungibleAsset/Tabs/HoldersTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React from "react";
import EmptyTabContent from "../../../components/IndividualPageContent/EmptyTabContent";
import {
CoinHolder,
useGetCoinHolders,
} from "../../../api/hooks/useGetCoinHolders";
import Table from "@mui/material/Table";
import TableHead from "@mui/material/TableHead";
import TableRow from "@mui/material/TableRow";
import GeneralTableBody from "../../../components/Table/GeneralTableBody";
import GeneralTableHeaderCell from "../../../components/Table/GeneralTableHeaderCell";
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 {FACombinedData} from "../Index";

type HoldersTabProps = {
address: string;
data: FACombinedData | undefined;
};

export default function HoldersTab({address, data}: HoldersTabProps) {
const holderData = useGetCoinHolders(address);
if (!data || Array.isArray(data) || !holderData?.data) {
return <EmptyTabContent />;
}

return <HoldersTable data={data} holders={holderData.data} />;
}

export function HoldersTable({
data,
holders,
}: {
holders: CoinHolder[];
data: FACombinedData;
}) {
return (
<Table>
<TableHead>
<TableRow>
<GeneralTableHeaderCell header="rank" />
<GeneralTableHeaderCell header="holder address" />
<GeneralTableHeaderCell header="amount" textAlignRight={true} />
</TableRow>
</TableHead>
<GeneralTableBody>
{holders.map((holder, i) => {
return (
<GeneralTableRow>
<GeneralTableCell>{i}</GeneralTableCell>
<GeneralTableCell>
<HashButton
hash={holder.owner_address}
type={HashType.ACCOUNT}
/>
</GeneralTableCell>
<GeneralTableCell align={"right"}>
{getFormattedBalanceStr(
holder.amount.toString(),
data.coinData?.decimals,
) +
" " +
data.coinData?.symbol}
</GeneralTableCell>
</GeneralTableRow>
);
})}
</GeneralTableBody>
</Table>
);
}
3 changes: 2 additions & 1 deletion src/pages/FungibleAsset/Tabs/InfoTab.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@ import EmptyTabContent from "../../../components/IndividualPageContent/EmptyTabC
import {getFormattedBalanceStr} from "../../../components/IndividualPageContent/ContentValue/CurrencyValue";
import {getAssetSymbol} from "../../../utils";
import HashButton, {HashType} from "../../../components/HashButton";
import {FACombinedData} from "../Index";

type InfoTabProps = {
address: string;
data: any | undefined;
data: FACombinedData | undefined;
};

// TODO: put this extra information somewhere else
Expand Down
4 changes: 2 additions & 2 deletions src/pages/FungibleAsset/Tabs/TransactionsTab.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React from "react";
import EmptyTabContent from "../../../components/IndividualPageContent/EmptyTabContent";
import {FAData} from "../Components/FAData";
import {FACombinedData} from "../Index";

type TransactionsTabProps = {
address: string;
data: FAData | undefined;
data: FACombinedData | undefined;
};

export default function TransactionsTab({}: TransactionsTabProps) {
Expand Down

0 comments on commit 461c57b

Please sign in to comment.