From 5733396754ed5d31674b3f738b85f801ba33bb2f Mon Sep 17 00:00:00 2001 From: Karan Pargal Date: Thu, 16 May 2024 17:46:45 +0530 Subject: [PATCH 1/8] feat: Add approval tabs --- .../NFTApproval/NFTApprovals.stories.tsx | 18 ++ .../Address/NFTApproval/NFTApprovals.tsx | 50 ++++++ .../TokenApproval/TokenApprovals.stories.tsx | 18 ++ .../Address/TokenApproval/TokenApprovals.tsx | 50 ++++++ src/components/Molecules/index.ts | 1 + src/components/Shared/NFTTokenApprovals.tsx | 138 +++++++++++++++ src/components/Shared/TokenApprovalsTable.tsx | 159 ++++++++++++++++++ src/utils/types/molecules.types.ts | 10 ++ src/utils/types/shared.types.ts | 17 +- 9 files changed, 460 insertions(+), 1 deletion(-) create mode 100644 src/components/Molecules/Address/NFTApproval/NFTApprovals.stories.tsx create mode 100644 src/components/Molecules/Address/NFTApproval/NFTApprovals.tsx create mode 100644 src/components/Molecules/Address/TokenApproval/TokenApprovals.stories.tsx create mode 100644 src/components/Molecules/Address/TokenApproval/TokenApprovals.tsx create mode 100644 src/components/Shared/NFTTokenApprovals.tsx create mode 100644 src/components/Shared/TokenApprovalsTable.tsx diff --git a/src/components/Molecules/Address/NFTApproval/NFTApprovals.stories.tsx b/src/components/Molecules/Address/NFTApproval/NFTApprovals.stories.tsx new file mode 100644 index 00000000..9236137c --- /dev/null +++ b/src/components/Molecules/Address/NFTApproval/NFTApprovals.stories.tsx @@ -0,0 +1,18 @@ +import { type Meta, type StoryObj } from "@storybook/react"; +import { NFTApprovals as NFTApprovalsComponent } from "./NFTApprovals"; + +type Story = StoryObj; + +const meta: Meta = { + title: "Molecules/Address/Approval", + component: NFTApprovalsComponent, +}; + +export default meta; + +export const NFTApprovals: Story = { + args: { + chain_name: "eth-mainnet", + address: "demo.eth", + }, +}; diff --git a/src/components/Molecules/Address/NFTApproval/NFTApprovals.tsx b/src/components/Molecules/Address/NFTApproval/NFTApprovals.tsx new file mode 100644 index 00000000..a956ad64 --- /dev/null +++ b/src/components/Molecules/Address/NFTApproval/NFTApprovals.tsx @@ -0,0 +1,50 @@ +import { type Option, None, Some } from "@/utils/option"; +import { NftApprovalsItem, type TokensApprovalItem } from "@covalenthq/client-sdk"; +import { useEffect, useState } from "react"; +import { type NFTApprovalsProps } from "@/utils/types/molecules.types"; +import { useGoldRush } from "@/utils/store"; +import { type CovalentAPIError } from "@/utils/types/shared.types"; +import { defaultErrorMessage } from "@/utils/constants/shared.constants"; +import { NFTApprovalsTable } from "@/components/Shared/NFTTokenApprovals"; + +export const NFTApprovals: React.FC = ({ + chain_name, + address, + ...props +}) => { + const { covalentClient } = useGoldRush(); + + const [maybeResult, setMaybeResult] = + useState>(None); + const [errorMessage, setErrorMessage] = useState(null); + + useEffect(() => { + (async () => { + setMaybeResult(None); + setErrorMessage(null); + try { + const { data, ...error } = + await covalentClient.SecurityService.getNftApprovals( + chain_name, + address.trim() + ); + if (error.error) { + throw error; + } + setMaybeResult(new Some(data.items)); + } catch (error: CovalentAPIError | any) { + setErrorMessage(error?.error_message ?? defaultErrorMessage); + setMaybeResult(new Some(null)); + console.error(error); + } + })(); + }, [chain_name, address]); + + return ( + + ); +}; diff --git a/src/components/Molecules/Address/TokenApproval/TokenApprovals.stories.tsx b/src/components/Molecules/Address/TokenApproval/TokenApprovals.stories.tsx new file mode 100644 index 00000000..d69be12a --- /dev/null +++ b/src/components/Molecules/Address/TokenApproval/TokenApprovals.stories.tsx @@ -0,0 +1,18 @@ +import { type Meta, type StoryObj } from "@storybook/react"; +import { TokenApprovals as TokenApprovalsComponent } from "./TokenApprovals"; + +type Story = StoryObj; + +const meta: Meta = { + title: "Molecules/Address/Approval", + component: TokenApprovalsComponent, +}; + +export default meta; + +export const TokenApprovals: Story = { + args: { + chain_name: "eth-mainnet", + address: "demo.eth", + }, +}; diff --git a/src/components/Molecules/Address/TokenApproval/TokenApprovals.tsx b/src/components/Molecules/Address/TokenApproval/TokenApprovals.tsx new file mode 100644 index 00000000..71009dc7 --- /dev/null +++ b/src/components/Molecules/Address/TokenApproval/TokenApprovals.tsx @@ -0,0 +1,50 @@ +import { type Option, None, Some } from "@/utils/option"; +import { type TokensApprovalItem } from "@covalenthq/client-sdk"; +import { useEffect, useState } from "react"; +import { type TokenApprovalsProps } from "@/utils/types/molecules.types"; +import { useGoldRush } from "@/utils/store"; +import { type CovalentAPIError } from "@/utils/types/shared.types"; +import { defaultErrorMessage } from "@/utils/constants/shared.constants"; +import { TokenApprovalsTable } from "@/components/Shared/TokenApprovalsTable"; + +export const TokenApprovals: React.FC = ({ + chain_name, + address, + ...props +}) => { + const { covalentClient } = useGoldRush(); + + const [maybeResult, setMaybeResult] = + useState>(None); + const [errorMessage, setErrorMessage] = useState(null); + + useEffect(() => { + (async () => { + setMaybeResult(None); + setErrorMessage(null); + try { + const { data, ...error } = + await covalentClient.SecurityService.getApprovals( + chain_name, + address.trim() + ); + if (error.error) { + throw error; + } + setMaybeResult(new Some(data.items)); + } catch (error: CovalentAPIError | any) { + setErrorMessage(error?.error_message ?? defaultErrorMessage); + setMaybeResult(new Some(null)); + console.error(error); + } + })(); + }, [chain_name, address]); + + return ( + + ); +}; diff --git a/src/components/Molecules/index.ts b/src/components/Molecules/index.ts index a689dfe9..ce223eec 100644 --- a/src/components/Molecules/index.ts +++ b/src/components/Molecules/index.ts @@ -8,6 +8,7 @@ export { LatestBlocks } from "./Block/LatestBlocks/LatestBlocks"; export { ChainSelector } from "./ChainSelector/ChainSelector"; export { GasCard } from "./GasCard/GasCard"; export { LatestPrice } from "./LatestPrice/LatestPrice"; +export { TokenApprovals } from "./Address/TokenApproval/TokenApprovals"; export { NFTCollectionDetails } from "./NFT/NFTCollectionDetails/NFTCollectionDetails"; export { NFTCollectionTokensList } from "./NFT/NFTCollectionTokensList/NFTCollectionTokensList"; export { NFTFloorPrice } from "./NFT/NFTFloorPrice/NFTFloorPrice"; diff --git a/src/components/Shared/NFTTokenApprovals.tsx b/src/components/Shared/NFTTokenApprovals.tsx new file mode 100644 index 00000000..968e3cca --- /dev/null +++ b/src/components/Shared/NFTTokenApprovals.tsx @@ -0,0 +1,138 @@ +import { type NftApprovalsItem } from "@covalenthq/client-sdk"; +import { type ColumnDef } from "@tanstack/react-table"; +import { TableHeaderSorting, TableList } from "."; +import { type NFTApprovalsTableProps } from "@/utils/types/shared.types"; +import { Address } from "@/components/Atoms"; + +export const NFTApprovalsTable: React.FC = ({ + errorMessage, + maybeResult, +}) => { + const columns: ColumnDef[] = [ + { + id: "token_details", + accessorKey: "token_details", + header: ({ column }) => ( + + align="left" + header={"Token"} + column={column} + /> + ), + cell: ({ row }) => { + return ( +
+
+ {/* {row.original.contract_ticker_symbol} */} + {row.original.contract_ticker_symbol || ( +
+ )} +
+

+

+

+
+ ); + }, + }, + { + id: "token_balance", + accessorKey: "token_balance", + header: ({ column }) => ( + + align="left" + header={"Wallet Balance"} + column={column} + /> + ), + cell: ({ row }) => { + return ( +
+

{row.original.token_balances.length}

+

+ ID:{" "} + {row.original.token_balances.map((balance) => ( + + {balance.token_id?.toString()} + + )) || "N/A"} +

+
+ ); + }, + }, + { + id: "spender_address_label", + accessorKey: "spender_address_label", + header: ({ column }) => ( + + align="left" + header={"Spender"} + column={column} + /> + ), + cell: ({ row }) => { + return ( +

+ {row.original.spenders.map((spender) => + spender.spender_address_label ? ( + spender.spender_address_label + ) : ( +

+ ) + )} +

+ ); + }, + }, + { + id: "risk_factor", + accessorKey: "risk_factor", + header: ({ column }) => ( + + align="left" + header={"Risk Factor"} + column={column} + /> + ), + cell: ({ row }) => { + return ( + + {row.original.spenders[0].allowance === "Unlimited" + ? "High" + : "Low"} + + ); + }, + }, + ]; + + return ( + + columns={columns} + errorMessage={errorMessage} + maybeData={maybeResult} + sorting_state={[ + { + id: "value_at_risk", + desc: true, + }, + ]} + /> + ); +}; diff --git a/src/components/Shared/TokenApprovalsTable.tsx b/src/components/Shared/TokenApprovalsTable.tsx new file mode 100644 index 00000000..06dc5940 --- /dev/null +++ b/src/components/Shared/TokenApprovalsTable.tsx @@ -0,0 +1,159 @@ +import { type TokensApprovalItem } from "@covalenthq/client-sdk"; +import { type ColumnDef } from "@tanstack/react-table"; +import { TableHeaderSorting, TableList } from "."; +import { type TokenApprovalsTableProps } from "@/utils/types/shared.types"; +import { Address } from "@/components/Atoms"; + +export const TokenApprovalsTable: React.FC = ({ + errorMessage, + maybeResult, +}) => { + const columns: ColumnDef[] = [ + { + id: "token_details", + accessorKey: "token_details", + header: ({ column }) => ( + + align="left" + header={"Token"} + column={column} + /> + ), + cell: ({ row }) => { + return ( +
+
+ {row.original.ticker_symbol} + {row.original.ticker_symbol || ( +
+ )} +
+

+

+

+
+ ); + }, + }, + { + id: "balance_quote", + accessorKey: "balance_quote", + header: ({ column }) => ( + + align="left" + header={"Wallet Balance"} + column={column} + /> + ), + cell: ({ row }) => { + return ( +
+

+ {Number(row.original.balance) / + Math.pow(10, row.original.contract_decimals)} +

+

+ {row.original.pretty_balance_quote} +

+
+ ); + }, + }, + { + id: "pretty_value_at_risk_quote", + accessorKey: "pretty_value_at_risk_quote", + header: ({ column }) => ( + + align="left" + header={"Value at Risk"} + column={column} + /> + ), + cell: ({ row }) => { + return ( +
+

+ {row.original.pretty_value_at_risk_quote + ? row.original.pretty_value_at_risk_quote + : Number(row.original.value_at_risk) / + Math.pow(10, row.original.contract_decimals)} +

+
+ ); + }, + }, + { + id: "spender_address_label", + accessorKey: "spender_address_label", + header: ({ column }) => ( + + align="left" + header={"Spender(s)"} + column={column} + /> + ), + cell: ({ row }) => { + return ( +

+ {row.original.spenders.map((spender) => + spender.spender_address_label ? ( + spender.spender_address_label + ) : ( +

+ ) + )} +

+ ); + }, + }, + { + id: "risk_factor", + accessorKey: "risk_factor", + header: ({ column }) => ( + + align="left" + header={"Risk Factor"} + column={column} + /> + ), + cell: ({ row }) => { + return ( + + {row.original.spenders[0].risk_factor === + "CONSIDER REVOKING" + ? "High" + : "Low"} + + ); + }, + }, + ]; + + return ( + + columns={columns} + errorMessage={errorMessage} + maybeData={maybeResult} + sorting_state={[ + { + id: "value_at_risk", + desc: true, + }, + ]} + /> + ); +}; diff --git a/src/utils/types/molecules.types.ts b/src/utils/types/molecules.types.ts index 7e2c8730..e6cb07d3 100644 --- a/src/utils/types/molecules.types.ts +++ b/src/utils/types/molecules.types.ts @@ -30,6 +30,16 @@ export interface AddressActivityListProps { errorMessage?: string | null; } +export interface NFTApprovalsProps { + chain_name: Chain; + address: string; +} + +export interface TokenApprovalsProps { + chain_name: Chain; + address: string; +} + export interface BlockDetailsProps { chain_name: Chain; height: number; diff --git a/src/utils/types/shared.types.ts b/src/utils/types/shared.types.ts index cfde739a..061aa2b7 100644 --- a/src/utils/types/shared.types.ts +++ b/src/utils/types/shared.types.ts @@ -1,5 +1,10 @@ import { type Option } from "@/utils/option"; -import { type Pagination, type Transaction } from "@covalenthq/client-sdk"; +import { + NftApprovalsItem, + TokensApprovalItem, + type Pagination, + type Transaction, +} from "@covalenthq/client-sdk"; import { type Column, type ColumnDef, @@ -45,6 +50,16 @@ export interface TransactionsProps { errorMessage: string | null; } +export interface TokenApprovalsTableProps { + maybeResult: Option; + errorMessage: string | null; +} + +export interface NFTApprovalsTableProps { + maybeResult: Option; + errorMessage: string | null; +} + export interface SkeletonTableProps { rows?: number; cols?: number; From b81ad849fd0e25009b29f834d60a06d9a67e5daf Mon Sep 17 00:00:00 2001 From: Karan Pargal Date: Thu, 16 May 2024 17:56:16 +0530 Subject: [PATCH 2/8] fix: lint --- .../Molecules/Address/NFTApproval/NFTApprovals.tsx | 3 ++- src/components/Shared/NFTTokenApprovals.tsx | 5 ++++- src/components/Shared/TokenApprovalsTable.tsx | 5 ++++- src/utils/types/shared.types.ts | 5 ++--- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/components/Molecules/Address/NFTApproval/NFTApprovals.tsx b/src/components/Molecules/Address/NFTApproval/NFTApprovals.tsx index a956ad64..69a8add2 100644 --- a/src/components/Molecules/Address/NFTApproval/NFTApprovals.tsx +++ b/src/components/Molecules/Address/NFTApproval/NFTApprovals.tsx @@ -1,5 +1,6 @@ import { type Option, None, Some } from "@/utils/option"; -import { NftApprovalsItem, type TokensApprovalItem } from "@covalenthq/client-sdk"; +import type { NftApprovalsItem } from "@covalenthq/client-sdk"; +import { type TokensApprovalItem } from "@covalenthq/client-sdk"; import { useEffect, useState } from "react"; import { type NFTApprovalsProps } from "@/utils/types/molecules.types"; import { useGoldRush } from "@/utils/store"; diff --git a/src/components/Shared/NFTTokenApprovals.tsx b/src/components/Shared/NFTTokenApprovals.tsx index 968e3cca..729bc1d1 100644 --- a/src/components/Shared/NFTTokenApprovals.tsx +++ b/src/components/Shared/NFTTokenApprovals.tsx @@ -87,7 +87,10 @@ export const NFTApprovalsTable: React.FC = ({ spender.spender_address_label ? ( spender.spender_address_label ) : ( -
+
) )}

diff --git a/src/components/Shared/TokenApprovalsTable.tsx b/src/components/Shared/TokenApprovalsTable.tsx index 06dc5940..6191cb5e 100644 --- a/src/components/Shared/TokenApprovalsTable.tsx +++ b/src/components/Shared/TokenApprovalsTable.tsx @@ -106,7 +106,10 @@ export const TokenApprovalsTable: React.FC = ({ spender.spender_address_label ? ( spender.spender_address_label ) : ( -
+
) )}

diff --git a/src/utils/types/shared.types.ts b/src/utils/types/shared.types.ts index 061aa2b7..1b7eff6c 100644 --- a/src/utils/types/shared.types.ts +++ b/src/utils/types/shared.types.ts @@ -1,10 +1,9 @@ import { type Option } from "@/utils/option"; -import { +import type { NftApprovalsItem, TokensApprovalItem, - type Pagination, - type Transaction, } from "@covalenthq/client-sdk"; +import { type Pagination, type Transaction } from "@covalenthq/client-sdk"; import { type Column, type ColumnDef, From 7804f00e9e558cc2c44db94ee3fbc9dd241608f7 Mon Sep 17 00:00:00 2001 From: Karan Pargal Date: Thu, 16 May 2024 18:08:15 +0530 Subject: [PATCH 3/8] remove comments --- src/components/Shared/NFTTokenApprovals.tsx | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/components/Shared/NFTTokenApprovals.tsx b/src/components/Shared/NFTTokenApprovals.tsx index 729bc1d1..d11f7141 100644 --- a/src/components/Shared/NFTTokenApprovals.tsx +++ b/src/components/Shared/NFTTokenApprovals.tsx @@ -23,14 +23,6 @@ export const NFTApprovalsTable: React.FC = ({ return (
- {/* {row.original.contract_ticker_symbol} */} {row.original.contract_ticker_symbol || (
Date: Thu, 16 May 2024 18:12:45 +0530 Subject: [PATCH 4/8] fix uneccessary divs --- src/components/Shared/TokenApprovalsTable.tsx | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/Shared/TokenApprovalsTable.tsx b/src/components/Shared/TokenApprovalsTable.tsx index 6191cb5e..c06ce44d 100644 --- a/src/components/Shared/TokenApprovalsTable.tsx +++ b/src/components/Shared/TokenApprovalsTable.tsx @@ -78,14 +78,12 @@ export const TokenApprovalsTable: React.FC = ({ ), cell: ({ row }) => { return ( -
-

- {row.original.pretty_value_at_risk_quote - ? row.original.pretty_value_at_risk_quote - : Number(row.original.value_at_risk) / - Math.pow(10, row.original.contract_decimals)} -

-
+

+ {row.original.pretty_value_at_risk_quote + ? row.original.pretty_value_at_risk_quote + : Number(row.original.value_at_risk) / + Math.pow(10, row.original.contract_decimals)} +

); }, }, From 256a582d47a0dcfc95306ee0cb1e164fd970b3ee Mon Sep 17 00:00:00 2001 From: Karan Pargal Date: Thu, 16 May 2024 18:24:07 +0530 Subject: [PATCH 5/8] fix: component changes --- .../Address/NFTApproval/NFTApprovals.tsx | 51 ------------------- .../NFTApprovalList.stories.tsx} | 2 +- .../NFTApprovalList/NFTApprovalList.tsx} | 48 ++++++++++++++--- .../Address/TokenApproval/TokenApprovals.tsx | 50 ------------------ .../TokenApprovalList.stories.tsx} | 2 +- .../TokenApprovalList/TokenApprovalList.tsx} | 46 ++++++++++++++--- src/components/Molecules/index.ts | 3 +- src/utils/types/molecules.types.ts | 4 +- 8 files changed, 87 insertions(+), 119 deletions(-) delete mode 100644 src/components/Molecules/Address/NFTApproval/NFTApprovals.tsx rename src/components/Molecules/Address/{NFTApproval/NFTApprovals.stories.tsx => NFTApprovalList/NFTApprovalList.stories.tsx} (97%) rename src/components/{Shared/NFTTokenApprovals.tsx => Molecules/Address/NFTApprovalList/NFTApprovalList.tsx} (72%) delete mode 100644 src/components/Molecules/Address/TokenApproval/TokenApprovals.tsx rename src/components/Molecules/Address/{TokenApproval/TokenApprovals.stories.tsx => TokenApprovalList/TokenApprovalList.stories.tsx} (96%) rename src/components/{Shared/TokenApprovalsTable.tsx => Molecules/Address/TokenApprovalList/TokenApprovalList.tsx} (77%) diff --git a/src/components/Molecules/Address/NFTApproval/NFTApprovals.tsx b/src/components/Molecules/Address/NFTApproval/NFTApprovals.tsx deleted file mode 100644 index 69a8add2..00000000 --- a/src/components/Molecules/Address/NFTApproval/NFTApprovals.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { type Option, None, Some } from "@/utils/option"; -import type { NftApprovalsItem } from "@covalenthq/client-sdk"; -import { type TokensApprovalItem } from "@covalenthq/client-sdk"; -import { useEffect, useState } from "react"; -import { type NFTApprovalsProps } from "@/utils/types/molecules.types"; -import { useGoldRush } from "@/utils/store"; -import { type CovalentAPIError } from "@/utils/types/shared.types"; -import { defaultErrorMessage } from "@/utils/constants/shared.constants"; -import { NFTApprovalsTable } from "@/components/Shared/NFTTokenApprovals"; - -export const NFTApprovals: React.FC = ({ - chain_name, - address, - ...props -}) => { - const { covalentClient } = useGoldRush(); - - const [maybeResult, setMaybeResult] = - useState>(None); - const [errorMessage, setErrorMessage] = useState(null); - - useEffect(() => { - (async () => { - setMaybeResult(None); - setErrorMessage(null); - try { - const { data, ...error } = - await covalentClient.SecurityService.getNftApprovals( - chain_name, - address.trim() - ); - if (error.error) { - throw error; - } - setMaybeResult(new Some(data.items)); - } catch (error: CovalentAPIError | any) { - setErrorMessage(error?.error_message ?? defaultErrorMessage); - setMaybeResult(new Some(null)); - console.error(error); - } - })(); - }, [chain_name, address]); - - return ( - - ); -}; diff --git a/src/components/Molecules/Address/NFTApproval/NFTApprovals.stories.tsx b/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.stories.tsx similarity index 97% rename from src/components/Molecules/Address/NFTApproval/NFTApprovals.stories.tsx rename to src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.stories.tsx index 9236137c..8a4a4091 100644 --- a/src/components/Molecules/Address/NFTApproval/NFTApprovals.stories.tsx +++ b/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.stories.tsx @@ -1,5 +1,5 @@ import { type Meta, type StoryObj } from "@storybook/react"; -import { NFTApprovals as NFTApprovalsComponent } from "./NFTApprovals"; +import { NFTApprovals as NFTApprovalsComponent } from "./NFTApprovalList"; type Story = StoryObj; diff --git a/src/components/Shared/NFTTokenApprovals.tsx b/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.tsx similarity index 72% rename from src/components/Shared/NFTTokenApprovals.tsx rename to src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.tsx index d11f7141..50c45fbf 100644 --- a/src/components/Shared/NFTTokenApprovals.tsx +++ b/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.tsx @@ -1,13 +1,47 @@ -import { type NftApprovalsItem } from "@covalenthq/client-sdk"; -import { type ColumnDef } from "@tanstack/react-table"; -import { TableHeaderSorting, TableList } from "."; -import { type NFTApprovalsTableProps } from "@/utils/types/shared.types"; +import { type Option, None, Some } from "@/utils/option"; +import type { NftApprovalsItem } from "@covalenthq/client-sdk"; +import { useEffect, useState } from "react"; +import { type NFTApprovalListProps } from "@/utils/types/molecules.types"; +import { useGoldRush } from "@/utils/store"; +import { type CovalentAPIError } from "@/utils/types/shared.types"; +import { defaultErrorMessage } from "@/utils/constants/shared.constants"; +import { TableHeaderSorting, TableList } from "@/components/Shared"; +import { ColumnDef } from "@tanstack/react-table"; import { Address } from "@/components/Atoms"; -export const NFTApprovalsTable: React.FC = ({ - errorMessage, - maybeResult, +export const NFTApprovalList: React.FC = ({ + chain_name, + address, + ...props }) => { + const { covalentClient } = useGoldRush(); + + const [maybeResult, setMaybeResult] = + useState>(None); + const [errorMessage, setErrorMessage] = useState(null); + + useEffect(() => { + (async () => { + setMaybeResult(None); + setErrorMessage(null); + try { + const { data, ...error } = + await covalentClient.SecurityService.getNftApprovals( + chain_name, + address.trim() + ); + if (error.error) { + throw error; + } + setMaybeResult(new Some(data.items)); + } catch (error: CovalentAPIError | any) { + setErrorMessage(error?.error_message ?? defaultErrorMessage); + setMaybeResult(new Some(null)); + console.error(error); + } + })(); + }, [chain_name, address]); + const columns: ColumnDef[] = [ { id: "token_details", diff --git a/src/components/Molecules/Address/TokenApproval/TokenApprovals.tsx b/src/components/Molecules/Address/TokenApproval/TokenApprovals.tsx deleted file mode 100644 index 71009dc7..00000000 --- a/src/components/Molecules/Address/TokenApproval/TokenApprovals.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { type Option, None, Some } from "@/utils/option"; -import { type TokensApprovalItem } from "@covalenthq/client-sdk"; -import { useEffect, useState } from "react"; -import { type TokenApprovalsProps } from "@/utils/types/molecules.types"; -import { useGoldRush } from "@/utils/store"; -import { type CovalentAPIError } from "@/utils/types/shared.types"; -import { defaultErrorMessage } from "@/utils/constants/shared.constants"; -import { TokenApprovalsTable } from "@/components/Shared/TokenApprovalsTable"; - -export const TokenApprovals: React.FC = ({ - chain_name, - address, - ...props -}) => { - const { covalentClient } = useGoldRush(); - - const [maybeResult, setMaybeResult] = - useState>(None); - const [errorMessage, setErrorMessage] = useState(null); - - useEffect(() => { - (async () => { - setMaybeResult(None); - setErrorMessage(null); - try { - const { data, ...error } = - await covalentClient.SecurityService.getApprovals( - chain_name, - address.trim() - ); - if (error.error) { - throw error; - } - setMaybeResult(new Some(data.items)); - } catch (error: CovalentAPIError | any) { - setErrorMessage(error?.error_message ?? defaultErrorMessage); - setMaybeResult(new Some(null)); - console.error(error); - } - })(); - }, [chain_name, address]); - - return ( - - ); -}; diff --git a/src/components/Molecules/Address/TokenApproval/TokenApprovals.stories.tsx b/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.stories.tsx similarity index 96% rename from src/components/Molecules/Address/TokenApproval/TokenApprovals.stories.tsx rename to src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.stories.tsx index d69be12a..cf231e3d 100644 --- a/src/components/Molecules/Address/TokenApproval/TokenApprovals.stories.tsx +++ b/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.stories.tsx @@ -1,5 +1,5 @@ import { type Meta, type StoryObj } from "@storybook/react"; -import { TokenApprovals as TokenApprovalsComponent } from "./TokenApprovals"; +import { TokenApprovals as TokenApprovalsComponent } from "./TokenApprovalList"; type Story = StoryObj; diff --git a/src/components/Shared/TokenApprovalsTable.tsx b/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx similarity index 77% rename from src/components/Shared/TokenApprovalsTable.tsx rename to src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx index c06ce44d..e9cdbc5b 100644 --- a/src/components/Shared/TokenApprovalsTable.tsx +++ b/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx @@ -1,13 +1,47 @@ +import { type Option, None, Some } from "@/utils/option"; import { type TokensApprovalItem } from "@covalenthq/client-sdk"; -import { type ColumnDef } from "@tanstack/react-table"; -import { TableHeaderSorting, TableList } from "."; -import { type TokenApprovalsTableProps } from "@/utils/types/shared.types"; +import { useEffect, useState } from "react"; +import { type TokenApprovalListProps } from "@/utils/types/molecules.types"; +import { useGoldRush } from "@/utils/store"; +import { type CovalentAPIError } from "@/utils/types/shared.types"; +import { defaultErrorMessage } from "@/utils/constants/shared.constants"; +import { TableHeaderSorting, TableList } from "@/components/Shared"; +import { ColumnDef } from "@tanstack/react-table"; import { Address } from "@/components/Atoms"; -export const TokenApprovalsTable: React.FC = ({ - errorMessage, - maybeResult, +export const TokenApprovalList: React.FC = ({ + chain_name, + address, + ...props }) => { + const { covalentClient } = useGoldRush(); + + const [maybeResult, setMaybeResult] = + useState>(None); + const [errorMessage, setErrorMessage] = useState(null); + + useEffect(() => { + (async () => { + setMaybeResult(None); + setErrorMessage(null); + try { + const { data, ...error } = + await covalentClient.SecurityService.getApprovals( + chain_name, + address.trim() + ); + if (error.error) { + throw error; + } + setMaybeResult(new Some(data.items)); + } catch (error: CovalentAPIError | any) { + setErrorMessage(error?.error_message ?? defaultErrorMessage); + setMaybeResult(new Some(null)); + console.error(error); + } + })(); + }, [chain_name, address]); + const columns: ColumnDef[] = [ { id: "token_details", diff --git a/src/components/Molecules/index.ts b/src/components/Molecules/index.ts index ce223eec..44ee0f43 100644 --- a/src/components/Molecules/index.ts +++ b/src/components/Molecules/index.ts @@ -8,7 +8,8 @@ export { LatestBlocks } from "./Block/LatestBlocks/LatestBlocks"; export { ChainSelector } from "./ChainSelector/ChainSelector"; export { GasCard } from "./GasCard/GasCard"; export { LatestPrice } from "./LatestPrice/LatestPrice"; -export { TokenApprovals } from "./Address/TokenApproval/TokenApprovals"; +export { TokenApprovalList } from "./Address/TokenApprovalList/TokenApprovalList"; +export { NFTApprovalList } from "./Address/NFTApprovalList/NFTApprovalList"; export { NFTCollectionDetails } from "./NFT/NFTCollectionDetails/NFTCollectionDetails"; export { NFTCollectionTokensList } from "./NFT/NFTCollectionTokensList/NFTCollectionTokensList"; export { NFTFloorPrice } from "./NFT/NFTFloorPrice/NFTFloorPrice"; diff --git a/src/utils/types/molecules.types.ts b/src/utils/types/molecules.types.ts index e6cb07d3..b789abc0 100644 --- a/src/utils/types/molecules.types.ts +++ b/src/utils/types/molecules.types.ts @@ -30,12 +30,12 @@ export interface AddressActivityListProps { errorMessage?: string | null; } -export interface NFTApprovalsProps { +export interface NFTApprovalListProps { chain_name: Chain; address: string; } -export interface TokenApprovalsProps { +export interface TokenApprovalListProps { chain_name: Chain; address: string; } From 242110f06fca0213109b7810fed63ac4a592f86d Mon Sep 17 00:00:00 2001 From: Karan Pargal Date: Thu, 16 May 2024 18:56:07 +0530 Subject: [PATCH 6/8] feat: add revoke button functionality --- .../NFTApprovalList.stories.tsx | 12 ++-- .../NFTApprovalList/NFTApprovalList.tsx | 45 ++++++++---- .../TokenApprovalList.stories.tsx | 12 ++-- .../TokenApprovalList/TokenApprovalList.tsx | 72 ++++++++++++------- src/utils/types/molecules.types.ts | 4 ++ 5 files changed, 97 insertions(+), 48 deletions(-) diff --git a/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.stories.tsx b/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.stories.tsx index 8a4a4091..ef9c48e9 100644 --- a/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.stories.tsx +++ b/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.stories.tsx @@ -1,16 +1,16 @@ import { type Meta, type StoryObj } from "@storybook/react"; -import { NFTApprovals as NFTApprovalsComponent } from "./NFTApprovalList"; +import { NFTApprovalList as NFTApprovalListComponent } from "./NFTApprovalList"; -type Story = StoryObj; +type Story = StoryObj; -const meta: Meta = { - title: "Molecules/Address/Approval", - component: NFTApprovalsComponent, +const meta: Meta = { + title: "Molecules/Address/Approval/NFT Approval List", + component: NFTApprovalListComponent, }; export default meta; -export const NFTApprovals: Story = { +export const NFTApprovalList: Story = { args: { chain_name: "eth-mainnet", address: "demo.eth", diff --git a/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.tsx b/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.tsx index 50c45fbf..51622f26 100644 --- a/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.tsx +++ b/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.tsx @@ -8,11 +8,12 @@ import { defaultErrorMessage } from "@/utils/constants/shared.constants"; import { TableHeaderSorting, TableList } from "@/components/Shared"; import { ColumnDef } from "@tanstack/react-table"; import { Address } from "@/components/Atoms"; +import { Button } from "@/components/ui/button"; export const NFTApprovalList: React.FC = ({ chain_name, address, - ...props + on_revoke_approval, }) => { const { covalentClient } = useGoldRush(); @@ -80,19 +81,24 @@ export const NFTApprovalList: React.FC = ({ column={column} /> ), + cell: ({ row }) => row.original.token_balances.length, + }, + { + id: "token_id", + accessorKey: "token_id", + header: ({ column }) => ( + + align="left" + header={"Token ID"} + column={column} + /> + ), cell: ({ row }) => { + const token_ids = row.original.token_balances.map( + (balance) => balance.token_id + ); return ( -
-

{row.original.token_balances.length}

-

- ID:{" "} - {row.original.token_balances.map((balance) => ( - - {balance.token_id?.toString()} - - )) || "N/A"} -

-
+

{token_ids.join(", ")}

); }, }, @@ -151,6 +157,21 @@ export const NFTApprovalList: React.FC = ({ }, ]; + if (on_revoke_approval) { + columns.push({ + id: "revoke", + accessorKey: "revoke", + header: () =>
, + cell: ({ row }) => { + return ( + + ); + }, + }); + } + return ( columns={columns} diff --git a/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.stories.tsx b/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.stories.tsx index cf231e3d..518a6d7d 100644 --- a/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.stories.tsx +++ b/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.stories.tsx @@ -1,16 +1,16 @@ import { type Meta, type StoryObj } from "@storybook/react"; -import { TokenApprovals as TokenApprovalsComponent } from "./TokenApprovalList"; +import { TokenApprovalList as TokenApprovalListComponent } from "./TokenApprovalList"; -type Story = StoryObj; +type Story = StoryObj; -const meta: Meta = { - title: "Molecules/Address/Approval", - component: TokenApprovalsComponent, +const meta: Meta = { + title: "Molecules/Address/Approval/Token Approval List", + component: TokenApprovalListComponent, }; export default meta; -export const TokenApprovals: Story = { +export const TokenApprovalList: Story = { args: { chain_name: "eth-mainnet", address: "demo.eth", diff --git a/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx b/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx index e9cdbc5b..5da8de14 100644 --- a/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx +++ b/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx @@ -5,14 +5,15 @@ import { type TokenApprovalListProps } from "@/utils/types/molecules.types"; import { useGoldRush } from "@/utils/store"; import { type CovalentAPIError } from "@/utils/types/shared.types"; import { defaultErrorMessage } from "@/utils/constants/shared.constants"; -import { TableHeaderSorting, TableList } from "@/components/Shared"; +import { CardDetail, TableHeaderSorting, TableList } from "@/components/Shared"; import { ColumnDef } from "@tanstack/react-table"; import { Address } from "@/components/Atoms"; +import { Button } from "@/components/ui/button"; export const TokenApprovalList: React.FC = ({ chain_name, address, - ...props + on_revoke_approval, }) => { const { covalentClient } = useGoldRush(); @@ -56,22 +57,30 @@ export const TokenApprovalList: React.FC = ({ cell: ({ row }) => { return (
-
- {row.original.ticker_symbol} - {row.original.ticker_symbol || ( + + {row.original.ticker_symbol} + {row.original.ticker_symbol || ( +
+ )} +
+ } + /> + - )} -
-

-

-

+ } + />
); }, @@ -89,13 +98,13 @@ export const TokenApprovalList: React.FC = ({ cell: ({ row }) => { return (
-

- {Number(row.original.balance) / - Math.pow(10, row.original.contract_decimals)} -

-

- {row.original.pretty_balance_quote} -

+ +
); }, @@ -178,6 +187,21 @@ export const TokenApprovalList: React.FC = ({ }, ]; + if (on_revoke_approval) { + columns.push({ + id: "revoke", + accessorKey: "revoke", + header: () =>
, + cell: ({ row }) => { + return ( + + ); + }, + }); + } + return ( columns={columns} diff --git a/src/utils/types/molecules.types.ts b/src/utils/types/molecules.types.ts index b789abc0..8ea678c7 100644 --- a/src/utils/types/molecules.types.ts +++ b/src/utils/types/molecules.types.ts @@ -1,5 +1,7 @@ import { type Option } from "@/utils/option"; import { + NftApprovalsItem, + TokensApprovalItem, type BalanceItem, type Block, type Chain, @@ -33,11 +35,13 @@ export interface AddressActivityListProps { export interface NFTApprovalListProps { chain_name: Chain; address: string; + on_revoke_approval?: (approval: NftApprovalsItem) => void; } export interface TokenApprovalListProps { chain_name: Chain; address: string; + on_revoke_approval?: (approval: TokensApprovalItem) => void; } export interface BlockDetailsProps { From 80887827fd4ed48d2e38a02e57d3b9311667954d Mon Sep 17 00:00:00 2001 From: Karan Pargal Date: Thu, 16 May 2024 19:24:34 +0530 Subject: [PATCH 7/8] fix: Use token avatar --- .../TokenApprovalList/TokenApprovalList.tsx | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx b/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx index 5da8de14..898e0492 100644 --- a/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx +++ b/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx @@ -4,10 +4,13 @@ import { useEffect, useState } from "react"; import { type TokenApprovalListProps } from "@/utils/types/molecules.types"; import { useGoldRush } from "@/utils/store"; import { type CovalentAPIError } from "@/utils/types/shared.types"; -import { defaultErrorMessage } from "@/utils/constants/shared.constants"; +import { + GRK_SIZES, + defaultErrorMessage, +} from "@/utils/constants/shared.constants"; import { CardDetail, TableHeaderSorting, TableList } from "@/components/Shared"; import { ColumnDef } from "@tanstack/react-table"; -import { Address } from "@/components/Atoms"; +import { Address, TokenAvatar } from "@/components/Atoms"; import { Button } from "@/components/ui/button"; export const TokenApprovalList: React.FC = ({ @@ -60,13 +63,9 @@ export const TokenApprovalList: React.FC = ({ - {row.original.ticker_symbol} {row.original.ticker_symbol || (
= ({ Math.pow(10, row.original.contract_decimals) } /> - +
); }, From 324adfc6ae3777f8d17bd5d4fe1462c102a492ad Mon Sep 17 00:00:00 2001 From: Karan Pargal Date: Thu, 16 May 2024 19:39:22 +0530 Subject: [PATCH 8/8] fix: lint --- .../Molecules/Address/NFTApprovalList/NFTApprovalList.tsx | 2 +- .../Molecules/Address/TokenApprovalList/TokenApprovalList.tsx | 2 +- src/utils/types/molecules.types.ts | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.tsx b/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.tsx index 51622f26..d2d3ead2 100644 --- a/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.tsx +++ b/src/components/Molecules/Address/NFTApprovalList/NFTApprovalList.tsx @@ -6,7 +6,7 @@ import { useGoldRush } from "@/utils/store"; import { type CovalentAPIError } from "@/utils/types/shared.types"; import { defaultErrorMessage } from "@/utils/constants/shared.constants"; import { TableHeaderSorting, TableList } from "@/components/Shared"; -import { ColumnDef } from "@tanstack/react-table"; +import type { ColumnDef } from "@tanstack/react-table"; import { Address } from "@/components/Atoms"; import { Button } from "@/components/ui/button"; diff --git a/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx b/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx index 898e0492..013baf99 100644 --- a/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx +++ b/src/components/Molecules/Address/TokenApprovalList/TokenApprovalList.tsx @@ -9,7 +9,7 @@ import { defaultErrorMessage, } from "@/utils/constants/shared.constants"; import { CardDetail, TableHeaderSorting, TableList } from "@/components/Shared"; -import { ColumnDef } from "@tanstack/react-table"; +import type { ColumnDef } from "@tanstack/react-table"; import { Address, TokenAvatar } from "@/components/Atoms"; import { Button } from "@/components/ui/button"; diff --git a/src/utils/types/molecules.types.ts b/src/utils/types/molecules.types.ts index 8ea678c7..ed3357d7 100644 --- a/src/utils/types/molecules.types.ts +++ b/src/utils/types/molecules.types.ts @@ -1,7 +1,9 @@ import { type Option } from "@/utils/option"; -import { +import type { NftApprovalsItem, TokensApprovalItem, +} from "@covalenthq/client-sdk"; +import { type BalanceItem, type Block, type Chain,