mirror of
https://github.com/zhigang1992/wallet.git
synced 2026-01-12 22:53:27 +08:00
refactor: nft hooks
This commit is contained in:
@@ -29,7 +29,7 @@ interface StacksBalanceViewerProps {
|
||||
account: StacksAccount;
|
||||
}
|
||||
|
||||
export function StacksBalanceViewer({ account }: StacksBalanceViewerProps) {
|
||||
function StacksBalanceViewer({ account }: StacksBalanceViewerProps) {
|
||||
const stacksFtAssetBalances = useStacksFungibleTokenAssetBalancesAnchoredWithMetadata(
|
||||
account.address
|
||||
);
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useQueryClient } from '@tanstack/react-query';
|
||||
import { RouteUrls } from '@shared/route-urls';
|
||||
|
||||
import { useWalletType } from '@app/common/use-wallet-type';
|
||||
import { CurrentStacksAccountLoader } from '@app/components/stacks-account-loader';
|
||||
import { useConfigNftMetadataEnabled } from '@app/query/common/remote-config/remote-config.query';
|
||||
|
||||
import { AddCollectible } from './components/add-collectible';
|
||||
@@ -44,7 +45,11 @@ export function Collectibles() {
|
||||
ledger: null,
|
||||
})}
|
||||
|
||||
{isNftMetadataEnabled ? <StacksCryptoAssets /> : null}
|
||||
{isNftMetadataEnabled && (
|
||||
<CurrentStacksAccountLoader>
|
||||
{account => <StacksCryptoAssets account={account} />}
|
||||
</CurrentStacksAccountLoader>
|
||||
)}
|
||||
|
||||
{whenWallet({
|
||||
software: (
|
||||
|
||||
@@ -4,16 +4,17 @@ import { useAnalytics } from '@app/common/hooks/analytics/use-analytics';
|
||||
import { parseIfValidPunycode } from '@app/common/utils';
|
||||
import { useCurrentAccountNames } from '@app/query/stacks/bns/bns.hooks';
|
||||
import { useStacksNonFungibleTokensMetadata } from '@app/query/stacks/tokens/non-fungible-tokens/non-fungible-token-metadata.hooks';
|
||||
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
|
||||
import { StacksBnsName } from './stacks-bns-name';
|
||||
import { StacksNonFungibleTokens } from './stacks-non-fungible-tokens';
|
||||
|
||||
export function StacksCryptoAssets() {
|
||||
const currentAccount = useCurrentStacksAccount();
|
||||
|
||||
interface StacksCryptoAssetsProps {
|
||||
account: StacksAccount;
|
||||
}
|
||||
export function StacksCryptoAssets({ account }: StacksCryptoAssetsProps) {
|
||||
const { data: names = [] } = useCurrentAccountNames();
|
||||
const stacksNftsMetadataResp = useStacksNonFungibleTokensMetadata();
|
||||
const stacksNftsMetadataResp = useStacksNonFungibleTokensMetadata(account);
|
||||
const analytics = useAnalytics();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -34,10 +34,10 @@ export function useSuggestedFirstSteps() {
|
||||
const stepsStatus = useSuggestedFirstStepsStatus();
|
||||
|
||||
const { data: nonFungibleTokenHoldings } = useGetNonFungibleTokenHoldingsQuery(
|
||||
currentAccount?.address
|
||||
currentAccount?.address!
|
||||
);
|
||||
|
||||
const firstFiveAccounts = accounts?.slice(0, 5);
|
||||
const firstFiveAccounts = accounts?.slice(0, 5) ?? [];
|
||||
const accountsAvailableStxBalance = useAllAccountsAvailableStxBalance(firstFiveAccounts);
|
||||
const accountsNonFungibleTokenHoldings =
|
||||
useAllAccountsNonFungibleTokenHoldingsTotal(firstFiveAccounts);
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { FullPageLoadingSpinner } from '@app/components/loading-spinner';
|
||||
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
|
||||
interface HomeLoaderProps {
|
||||
children(data?: StacksAccount): React.JSX.Element;
|
||||
}
|
||||
export function HomeLoader({ children }: HomeLoaderProps) {
|
||||
const currentAccount = useCurrentStacksAccount();
|
||||
// if (!currentAccount) return <FullPageLoadingSpinner />;
|
||||
return children(currentAccount);
|
||||
}
|
||||
@@ -1,4 +1,3 @@
|
||||
import { useDispatch } from 'react-redux';
|
||||
import { Outlet, useNavigate } from 'react-router-dom';
|
||||
|
||||
import { RouteUrls } from '@shared/route-urls';
|
||||
@@ -11,28 +10,19 @@ import { Header } from '@app/components/header';
|
||||
import { InAppMessages } from '@app/features/hiro-messages/in-app-messages';
|
||||
import { SuggestedFirstSteps } from '@app/features/suggested-first-steps/suggested-first-steps';
|
||||
import { HomeActions } from '@app/pages/home/components/home-actions';
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
import { keyActions } from '@app/store/keys/key.actions';
|
||||
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
|
||||
|
||||
import { CurrentAccount } from './components/account-area';
|
||||
import { HomeTabs } from './components/home-tabs';
|
||||
import { HomeLayout } from './components/home.layout';
|
||||
import { HomeLoader } from './home.loader';
|
||||
|
||||
export function Home() {
|
||||
return <HomeLoader>{account => <HomeContainer account={account} />}</HomeLoader>;
|
||||
}
|
||||
|
||||
interface HomeContainerProps {
|
||||
account: StacksAccount;
|
||||
}
|
||||
function HomeContainer({ account }: HomeContainerProps) {
|
||||
const { decodedAuthRequest } = useOnboardingState();
|
||||
|
||||
const stacksAccount = useCurrentStacksAccount();
|
||||
const navigate = useNavigate();
|
||||
useTrackFirstDeposit();
|
||||
|
||||
const dispatch = useDispatch();
|
||||
|
||||
useRouteHeader(
|
||||
<>
|
||||
<InAppMessages />
|
||||
@@ -51,8 +41,7 @@ function HomeContainer({ account }: HomeContainerProps) {
|
||||
actions={<HomeActions />}
|
||||
>
|
||||
<HomeTabs>
|
||||
<button onClick={() => dispatch(keyActions.debugKillStacks())}>kill stacks</button>
|
||||
<Outlet context={{ address: account?.address }} />
|
||||
<Outlet context={{ address: stacksAccount?.address }} />
|
||||
</HomeTabs>
|
||||
</HomeLayout>
|
||||
);
|
||||
|
||||
@@ -2,42 +2,19 @@ import { useMemo } from 'react';
|
||||
|
||||
import BigNumber from 'bignumber.js';
|
||||
|
||||
import { useCurrentStacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.hooks';
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
|
||||
import {
|
||||
useGetNonFungibleTokenHoldingsListQuery,
|
||||
useGetNonFungibleTokenHoldingsQuery,
|
||||
} from './non-fungible-token-holdings.query';
|
||||
import { useGetNonFungibleTokenHoldingsListQuery } from './non-fungible-token-holdings.query';
|
||||
|
||||
export function useAllAccountsNonFungibleTokenHoldingsTotal(accounts?: StacksAccount[]) {
|
||||
export function useAllAccountsNonFungibleTokenHoldingsTotal(accounts: StacksAccount[]) {
|
||||
const accountsNftHoldings = useGetNonFungibleTokenHoldingsListQuery(accounts);
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
accountsNftHoldings.reduce((acc, nftHoldings) => {
|
||||
return acc.plus(nftHoldings.data?.total || 0);
|
||||
}, new BigNumber(0)),
|
||||
accountsNftHoldings.reduce(
|
||||
(acc, nftHoldings) => acc.plus(nftHoldings.data?.total || 0),
|
||||
new BigNumber(0)
|
||||
),
|
||||
[accountsNftHoldings]
|
||||
);
|
||||
}
|
||||
|
||||
interface NonFungibleTokenHoldingListResult {
|
||||
asset_identifier: string;
|
||||
value: {
|
||||
hex: string;
|
||||
repr: string;
|
||||
};
|
||||
block_height: number;
|
||||
tx_id: string;
|
||||
}
|
||||
|
||||
// export function useAccountNonFungibleTokenHoldings() {
|
||||
// const currentAccount = useCurrentStacksAccount();
|
||||
|
||||
// const { data: nonFungibleTokenHoldings } = useGetNonFungibleTokenHoldingsQuery(
|
||||
// currentAccount?.address
|
||||
// );
|
||||
|
||||
// return (nonFungibleTokenHoldings?.results as NonFungibleTokenHoldingListResult[]) ?? [];
|
||||
// }
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { useQueries, useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { Paginated } from '@shared/models/api-types';
|
||||
|
||||
import { AppUseQueryConfig } from '@app/query/query-config';
|
||||
import { StacksClient } from '@app/query/stacks/stacks-client';
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
@@ -10,36 +12,42 @@ import { RateLimiter, useHiroApiRateLimiter } from '../../rate-limiter';
|
||||
|
||||
const staleTime = 15 * 60 * 1000; // 15 min
|
||||
|
||||
const queryOptions = {
|
||||
cacheTime: staleTime,
|
||||
staleTime,
|
||||
} as const;
|
||||
interface NonFungibleTokenHoldingListResult {
|
||||
asset_identifier: string;
|
||||
value: {
|
||||
hex: string;
|
||||
repr: string;
|
||||
};
|
||||
block_height: number;
|
||||
tx_id: string;
|
||||
}
|
||||
|
||||
const queryOptions = { cacheTime: staleTime, staleTime } as const;
|
||||
|
||||
type FetchNonFungibleTokenHoldingsResp = Paginated<NonFungibleTokenHoldingListResult[]>;
|
||||
|
||||
function fetchNonFungibleTokenHoldings(client: StacksClient, limiter: RateLimiter) {
|
||||
return async (address?: string) => {
|
||||
return async (address: string) => {
|
||||
if (!address) return;
|
||||
await limiter.removeTokens(1);
|
||||
return client.nonFungibleTokensApi.getNftHoldings({
|
||||
principal: address,
|
||||
limit: 50,
|
||||
});
|
||||
}) as unknown as Promise<FetchNonFungibleTokenHoldingsResp>;
|
||||
};
|
||||
}
|
||||
|
||||
type FetchNonFungibleTokenHoldingsResp = Awaited<
|
||||
ReturnType<ReturnType<typeof fetchNonFungibleTokenHoldings>>
|
||||
>;
|
||||
|
||||
export function useGetNonFungibleTokenHoldingsQuery<
|
||||
T extends unknown = FetchNonFungibleTokenHoldingsResp
|
||||
>(address?: string, options?: AppUseQueryConfig<FetchNonFungibleTokenHoldingsResp, T>) {
|
||||
>(address: string, options?: AppUseQueryConfig<FetchNonFungibleTokenHoldingsResp, T>) {
|
||||
const client = useStacksClientUnanchored();
|
||||
const network = useCurrentNetworkState();
|
||||
const limiter = useHiroApiRateLimiter();
|
||||
|
||||
return useQuery({
|
||||
enabled: !!address,
|
||||
queryKey: ['get-nft-holdings', address, network.chain.stacks.url],
|
||||
queryFn: () => fetchNonFungibleTokenHoldings(client, limiter)(address),
|
||||
queryFn: () => fetchNonFungibleTokenHoldings(client, limiter)(address) as any,
|
||||
...queryOptions,
|
||||
...options,
|
||||
});
|
||||
@@ -47,15 +55,15 @@ export function useGetNonFungibleTokenHoldingsQuery<
|
||||
|
||||
export function useGetNonFungibleTokenHoldingsListQuery<
|
||||
T extends unknown = FetchNonFungibleTokenHoldingsResp
|
||||
>(accounts?: StacksAccount[], options?: AppUseQueryConfig<FetchNonFungibleTokenHoldingsResp, T>) {
|
||||
>(accounts: StacksAccount[], options?: AppUseQueryConfig<FetchNonFungibleTokenHoldingsResp, T>) {
|
||||
const client = useStacksClientUnanchored();
|
||||
const network = useCurrentNetworkState();
|
||||
const limiter = useHiroApiRateLimiter();
|
||||
|
||||
return useQueries({
|
||||
queries: (accounts ?? []).map(account => ({
|
||||
queries: accounts.map(account => ({
|
||||
queryKey: ['get-nft-holdings', account.address, network.chain.stacks.url],
|
||||
queryFn: () => fetchNonFungibleTokenHoldings(client, limiter)(account.address),
|
||||
queryFn: () => fetchNonFungibleTokenHoldings(client, limiter)(account.address) as any,
|
||||
...queryOptions,
|
||||
...options,
|
||||
})),
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import { useMemo } from 'react';
|
||||
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
|
||||
import { isNftAsset } from '../token-metadata.utils';
|
||||
import { useGetNonFungibleTokenMetadataListQuery } from './non-fungible-token-metadata.query';
|
||||
|
||||
export function useStacksNonFungibleTokensMetadata() {
|
||||
const respList = useGetNonFungibleTokenMetadataListQuery();
|
||||
export function useStacksNonFungibleTokensMetadata(account: StacksAccount) {
|
||||
const respList = useGetNonFungibleTokenMetadataListQuery(account);
|
||||
|
||||
return useMemo(
|
||||
() =>
|
||||
|
||||
@@ -3,11 +3,13 @@ import { UseQueryResult, useQueries } from '@tanstack/react-query';
|
||||
|
||||
import { pullContractIdFromIdentity } from '@app/common/utils';
|
||||
import { QueryPrefixes } from '@app/query/query-prefixes';
|
||||
import { StacksAccount } from '@app/store/accounts/blockchain/stacks/stacks-account.models';
|
||||
import { useTokenMetadataClient } from '@app/store/common/api-clients.hooks';
|
||||
|
||||
import { RateLimiter, useHiroApiRateLimiter } from '../../rate-limiter';
|
||||
import { TokenMetadataClient } from '../../token-metadata-client';
|
||||
import { NftAssetResponse } from '../token-metadata.utils';
|
||||
import { useGetNonFungibleTokenHoldingsQuery } from './non-fungible-token-holdings.query';
|
||||
|
||||
const queryOptions = {
|
||||
refetchOnWindowFocus: true,
|
||||
@@ -27,13 +29,15 @@ function fetchNonFungibleTokenMetadata(client: TokenMetadataClient, limiter: Rat
|
||||
};
|
||||
}
|
||||
|
||||
export function useGetNonFungibleTokenMetadataListQuery(): UseQueryResult<NftAssetResponse>[] {
|
||||
export function useGetNonFungibleTokenMetadataListQuery(
|
||||
account: StacksAccount
|
||||
): UseQueryResult<NftAssetResponse>[] {
|
||||
const client = useTokenMetadataClient();
|
||||
const limiter = useHiroApiRateLimiter();
|
||||
const nftHoldings = useAccountNonFungibleTokenHoldings();
|
||||
const nftHoldings = useGetNonFungibleTokenHoldingsQuery(account.address);
|
||||
|
||||
return useQueries({
|
||||
queries: nftHoldings.map(nft => {
|
||||
queries: (nftHoldings.data?.results ?? []).map(nft => {
|
||||
const principal = pullContractIdFromIdentity(nft.asset_identifier);
|
||||
const tokenId = getTokenId(nft.value.hex);
|
||||
|
||||
|
||||
6
src/shared/models/api-types.ts
Normal file
6
src/shared/models/api-types.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export interface Paginated<T> {
|
||||
limit: number;
|
||||
offset: number;
|
||||
total: number;
|
||||
results: T;
|
||||
}
|
||||
Reference in New Issue
Block a user