diff --git a/components/account/AccountDetailsSection.tsx b/components/account/AccountDetailsSection.tsx index c10b24aa85e9658b2802228195e27b0b240f0822..8b95c003bbd3fec137363127af6bad9981b47176 100644 --- a/components/account/AccountDetailsSection.tsx +++ b/components/account/AccountDetailsSection.tsx @@ -1,8 +1,6 @@ import { useState } from "react"; -import { QueryObserverResult } from "@tanstack/react-query"; import { config } from "@/Config"; -import Hive from "@/types/Hive"; import useWitnessDetails from "@/hooks/api/common/useWitnessDetails"; import AccountMainCard from "./AccountMainCard"; import AccountDetailsCard from "./AccountDetailsCard"; @@ -16,6 +14,7 @@ import AccountRcDelegationsCard from "./AccountRcDelegationsCard"; import AccountBalanceCard from "./AccountBalanceCard"; import Explorer from "@/types/Explorer"; import AccountBalanceHistoryCard from "./AccountBalanceHistoryCard"; + interface AccountDetailsSectionProps { accountName: string; liveDataEnabled: boolean; @@ -101,6 +100,13 @@ const AccountDetailsSection: React.FC<AccountDetailsSectionProps> = ({ proxy={accountDetails.proxy} /> <AccountVestingDelegationsCard + direction="outgoing" + delegatorAccount={accountName} + liveDataEnabled={liveDataEnabled} + dynamicGlobalData={dynamicGlobalData} + /> + <AccountVestingDelegationsCard + direction="incoming" delegatorAccount={accountName} liveDataEnabled={liveDataEnabled} dynamicGlobalData={dynamicGlobalData} diff --git a/components/account/AccountVestingDelegationsCard.tsx b/components/account/AccountVestingDelegationsCard.tsx index 89282fe15d4ee772695d312b49cbfdcdb96c828a..9875099782b7e2925fcb9a18067950c24a088ecd 100644 --- a/components/account/AccountVestingDelegationsCard.tsx +++ b/components/account/AccountVestingDelegationsCard.tsx @@ -14,26 +14,40 @@ import { import Explorer from "@/types/Explorer"; import useConvertedVestingShares from "@/hooks/common/useConvertedVestingShares"; import { buildTableHead, handleSortDelegations } from "@/utils/DelegationsSort"; +import { capitalizeFirst } from "@/utils/StringUtils"; type AccountVestingDelegationsCardProps = { + direction: "incoming" | "outgoing"; delegatorAccount: string; liveDataEnabled: boolean; dynamicGlobalData?: Explorer.HeadBlockCardData; }; -const buildTableBody = (delegations: Explorer.VestingDelegation[]) => { +const buildTableBody = ( + direction: "outgoing" | "incoming", + delegations: Explorer.VestingDelegation[] +) => { return delegations.map((delegation, index: number) => { return ( <Fragment key={index}> <TableRow className={"border-b border-gray-700 hover:bg-inherit"}> <TableCell>{index + 1}</TableCell> <TableCell className="text-right"> - <Link - className="text-blue-400" - href={`/@${delegation.delegatee}`} - > - {delegation.delegatee} - </Link> + {direction === "outgoing" ? ( + <Link + className="text-link" + href={`/@${delegation.delegatee}`} + > + {delegation.delegatee} + </Link> + ) : ( + <Link + className="text-link" + href={`/@${delegation.delegator}`} + > + {delegation.delegator} + </Link> + )} </TableCell> <TableCell className="text-right"> {delegation.vesting_shares} @@ -45,10 +59,11 @@ const buildTableBody = (delegations: Explorer.VestingDelegation[]) => { }; const AccountVestingDelegationsCard: React.FC< AccountVestingDelegationsCardProps -> = ({ delegatorAccount, liveDataEnabled, dynamicGlobalData }) => { +> = ({ direction, delegatorAccount, liveDataEnabled, dynamicGlobalData }) => { const [isPropertiesHidden, setIsPropertiesHidden] = useState(true); const { hiveChain } = useHiveChainContext(); const delegations = useConvertedVestingShares( + direction, delegatorAccount, liveDataEnabled, dynamicGlobalData @@ -57,7 +72,7 @@ const AccountVestingDelegationsCard: React.FC< key: string; isAscending: boolean; }>({ - key: "recipient", + key: direction === "outgoing" ? "recipient" : "delegator", isAscending: true, }); @@ -65,7 +80,6 @@ const AccountVestingDelegationsCard: React.FC< if (!hiveChain || !dynamicGlobalData || !delegations || !delegations.length) return; - const handlePropertiesVisibility = () => { setIsPropertiesHidden(!isPropertiesHidden); }; @@ -78,10 +92,14 @@ const AccountVestingDelegationsCard: React.FC< delegations, key, isAscending, - recipient: "delegatee", + recipient: direction === "outgoing" ? "delegatee" : "delegator", amount: "vesting_shares", }) as Explorer.VestingDelegation[]; + const headerText = `${capitalizeFirst(direction)} HP Delegations (${ + delegations.length + })`; + return ( <Card data-testid="vesting-delegations-dropdown" @@ -92,16 +110,18 @@ const AccountVestingDelegationsCard: React.FC< onClick={handlePropertiesVisibility} className="h-full flex justify-between align-center p-2 hover:bg-rowHover cursor-pointer px-4" > - <div className="text-lg">HP Delegations ({delegations.length})</div> + <div className="text-lg">{headerText}</div> {isPropertiesHidden ? <ArrowDown /> : <ArrowUp />} </div> </CardHeader> <CardContent hidden={isPropertiesHidden}> <Table> <TableHeader> - <TableRow>{buildTableHead(sortBy, key, isAscending)}</TableRow> + <TableRow> + {buildTableHead(sortBy, key, isAscending, direction)} + </TableRow> </TableHeader> - <TableBody>{buildTableBody(sortedDelegations)}</TableBody> + <TableBody>{buildTableBody(direction, sortedDelegations)}</TableBody> </Table> </CardContent> </Card> diff --git a/hooks/api/common/useVestingDelegations.ts b/hooks/api/common/useVestingDelegations.ts index 8122acb6a2f8bfd8eee406548200537454e85a3d..4238d52b9659a8aa1e2c8df505471a07acd860b6 100644 --- a/hooks/api/common/useVestingDelegations.ts +++ b/hooks/api/common/useVestingDelegations.ts @@ -13,23 +13,16 @@ const useVestingDelegations = ( data: vestingDelegationsData, isLoading: isVestingDelegationsLoading, isError: isVestingDelegationsError, - }: UseQueryResult<Hive.VestingDelegations[]> = useQuery({ + }: UseQueryResult<Hive.TwoDirectionDelegations> = useQuery({ queryKey: ["vestingDelegations", delegatorAccount, liveDataEnabled], queryFn: () => fetchingService.getVestingDelegations(delegatorAccount), enabled: !!delegatorAccount, - select: (data) => { - const sortedData = data.sort( - (a: Hive.VestingDelegations, b: Hive.VestingDelegations) => - a.delegatee.toLowerCase().localeCompare(b.delegatee.toLowerCase()) - ); - return sortedData; - }, - refetchOnWindowFocus: false, }); return { - vestingDelegationsData, + outgoingDelegations: vestingDelegationsData?.outgoing_delegations, + incomingDelegations: vestingDelegationsData?.incoming_delegations, isVestingDelegationsLoading, isVestingDelegationsError, }; diff --git a/hooks/common/useConvertedVestingShares.tsx b/hooks/common/useConvertedVestingShares.tsx index 2fb736f14e6cb8f028a88307dab5009e1da0a133..98ed4797b478fb6b42702203e30a6f93caef5516 100644 --- a/hooks/common/useConvertedVestingShares.tsx +++ b/hooks/common/useConvertedVestingShares.tsx @@ -1,28 +1,57 @@ import { useHiveChainContext } from "@/contexts/HiveChainContext"; -import useAccountDetails from "../api/accountPage/useAccountDetails"; -import useDynamicGlobal from "../api/homePage/useDynamicGlobal"; -import { formatAndDelocalizeTime } from "@/utils/TimeUtils"; import Explorer from "@/types/Explorer"; -import {convertVestsToHP } from "@/utils/Calculations"; +import { convertVestsToHP } from "@/utils/Calculations"; import { config } from "@/Config"; import useVestingDelegations from "../api/common/useVestingDelegations"; -const useConvertedVestingShares = (accountName: string, liveDataEnabled: boolean, dynamicGlobalData?: Explorer.HeadBlockCardData) => { +const useConvertedVestingShares = ( + direction: "outgoing" | "incoming", + accountName: string, + liveDataEnabled: boolean, + dynamicGlobalData?: Explorer.HeadBlockCardData +) => { const { hiveChain } = useHiveChainContext(); - const { vestingDelegationsData: delegations } = useVestingDelegations( + const { incomingDelegations, outgoingDelegations } = useVestingDelegations( accountName, null, config.maxDelegatorsCount, liveDataEnabled ); - if (!dynamicGlobalData || !hiveChain || !delegations) return []; + if (!dynamicGlobalData || !hiveChain) return []; + const { - headBlockDetails: { rawFeedPrice, rawQuote, rawTotalVestingFundHive, rawTotalVestingShares }, + headBlockDetails: { rawTotalVestingFundHive, rawTotalVestingShares }, } = dynamicGlobalData; - const convertedDelgations = delegations?.map( - (delegation) => ({...delegation, vesting_shares: convertVestsToHP(hiveChain, delegation.vesting_shares, rawTotalVestingFundHive, rawTotalVestingShares)}) - ) as Explorer.VestingDelegation[]; - return convertedDelgations; -} -export default useConvertedVestingShares; \ No newline at end of file + if (direction === "outgoing") { + const convertedOutgoingDelegations = outgoingDelegations?.map( + (delegation) => ({ + ...delegation, + vesting_shares: convertVestsToHP( + hiveChain, + delegation.amount, + rawTotalVestingFundHive, + rawTotalVestingShares + ), + }) + ) as Explorer.VestingDelegation[]; + return convertedOutgoingDelegations; + } + if (direction === "incoming") { + const convertedIncomingDelegations = incomingDelegations?.map( + (delegation) => ({ + ...delegation, + vesting_shares: convertVestsToHP( + hiveChain, + delegation.amount, + rawTotalVestingFundHive, + rawTotalVestingShares + ), + }) + ) as Explorer.VestingDelegation[]; + + return convertedIncomingDelegations; + } +}; + +export default useConvertedVestingShares; diff --git a/services/FetchingService.ts b/services/FetchingService.ts index 930719fd2bfb182b38965b07ade861a318390f31..74756016299fa4fdd4e068c00aa547b328de6c8c 100644 --- a/services/FetchingService.ts +++ b/services/FetchingService.ts @@ -262,12 +262,10 @@ class FetchingService { async getVestingDelegations( delegatorAccount: string - ): Promise<Hive.VestingDelegations[]> { - return await this.extendedHiveChain!.api.database_api.find_vesting_delegations( - { - account: delegatorAccount, - } - ).then((response) => response.delegations); + ): Promise<Hive.VestingDelegationsResponse> { + return await this.extendedHiveChain!.restApi["balance-api"].delegations({ + accountName: delegatorAccount, + }); } async getRcDelegations( diff --git a/types/Hive.ts b/types/Hive.ts index 1fcf677b1c30dde71ad08f29d0b7459b7ff15afd..7253bf9afc4181d6ee70a137b8dabbc7c0d676b9 100644 --- a/types/Hive.ts +++ b/types/Hive.ts @@ -653,8 +653,9 @@ namespace Hive { export class VestingDelegations { delegatee!: string; delegator!: string; - id!: number; - min_delegation_time!: string; + amount!: string; + operation_id!: string; + block_num!: number; vesting_shares!: Supply; } @@ -664,24 +665,6 @@ namespace Hive { to!: string; } - export interface VestingDelegations { - delegatee: string; - delegator: string; - id: number; - min_delegation_time: string; - vesting_shares: { - amount: string; - precision: number; - nai: string; - }; - } - - export interface RCDelegations { - delegated_rc: number; - from: string; - to: string; - } - export interface BlockByOpResponse { block_num: number; op_type_id: number[]; @@ -793,6 +776,26 @@ namespace Hive { total_pages!: number; operations_result!: AccountBalanceHistory[]; } + + export class Delegation { + delegator!: string; + amount!: string; + operation_id!: string; + block_num!: number; + } + export class TwoDirectionDelegations { + outgoing_delegations!: Delegation[]; + incoming_delegations!: Delegation[]; + } + + export class GetVestingDelegationsParams { + accountName!: string; + } + export class VestingDelegationsResponse { + total_operations!: number; + total_pages!: number; + operations_result!: TwoDirectionDelegations[]; + } } export default Hive; diff --git a/types/Rest.ts b/types/Rest.ts index 131e7f6de52843a8d9c37d768c25b022875e2d09..f9f8d8298e9fb2081c45ba64ce47ccb011db0154 100644 --- a/types/Rest.ts +++ b/types/Rest.ts @@ -140,12 +140,16 @@ export const extendedRest = { urlPath: "block-number-by-date/{date}", }, }, - "balance-api" : { - balanceHistory: { - params: Hive.GetAccountBalanceHistoryParams, - result: Hive.AccountBalanceHistoryResponse, - urlPath: "accounts/{accountName}/balance-history", - }, - + "balance-api": { + balanceHistory: { + params: Hive.GetAccountBalanceHistoryParams, + result: Hive.AccountBalanceHistoryResponse, + urlPath: "accounts/{accountName}/balance-history", + }, + delegations: { + params: Hive.GetVestingDelegationsParams, + result: Hive.VestingDelegationsResponse, + urlPath: "accounts/{accountName}/delegations", + }, }, }; diff --git a/utils/DelegationsSort.tsx b/utils/DelegationsSort.tsx index e3453149b98b43cc83c334e6f8bfc9355b4f5a08..e4e042596780cf24b8cdbcdda2e66ec5c78cfdd7 100644 --- a/utils/DelegationsSort.tsx +++ b/utils/DelegationsSort.tsx @@ -20,7 +20,8 @@ type SortedDelegations = { amount: string | number; }; -export const TABLE_HEADER_CELLS = ["", "Recipient", "Amount"]; +export const TABLE_HEADER_CELLS_OUTGOING = ["", "Recipient", "Amount"]; +export const TABLE_HEADER_CELLS_INCOMING = ["", "Delegator", "Amount"]; export const handleSortDelegations = ({ delegations, @@ -30,7 +31,7 @@ export const handleSortDelegations = ({ amount, }: SortedDelegations) => { return [...delegations].sort((a: any, b: any) => { - if (key === "recipient") { + if (key === "recipient" || key === "delegator") { const delegateeA = a[recipient].toLowerCase(); const delegateeB = b[recipient].toLowerCase(); return isAscending @@ -86,9 +87,15 @@ const renderChevron = ( export const buildTableHead = ( handleSort: (key: string) => void, sortKey: string, - isOrderAscending: boolean + isOrderAscending: boolean, + direction?: "incoming" | "outgoing" ) => { - return TABLE_HEADER_CELLS.map((cellName: string) => { + const tableHeaderCells = + direction === "incoming" + ? TABLE_HEADER_CELLS_INCOMING + : TABLE_HEADER_CELLS_OUTGOING; + + return tableHeaderCells.map((cellName: string) => { return ( <Fragment key={cellName}> <TableHead