From 4d31163d9bb3350eedd7eb9d019d0bcbacf256ef Mon Sep 17 00:00:00 2001 From: fbwoolf Date: Mon, 24 Apr 2023 16:53:33 -0500 Subject: [PATCH] feat: stamps collectibles, closes #3589 --- .../features/collectibles/collectibles.tsx | 2 ++ .../collectibles/components/bitcoin/stamp.tsx | 20 +++++++++++ .../components/bitcoin/stamps.tsx | 33 +++++++++++++++++++ .../bitcoin/stamps/stamp-collection.query.ts | 14 ++++---- .../bitcoin/stamps/stamps-by-address.query.ts | 31 +++++++++++++++++ src/app/query/query-prefixes.ts | 1 + 6 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 src/app/features/collectibles/components/bitcoin/stamp.tsx create mode 100644 src/app/features/collectibles/components/bitcoin/stamps.tsx create mode 100644 src/app/query/bitcoin/stamps/stamps-by-address.query.ts diff --git a/src/app/features/collectibles/collectibles.tsx b/src/app/features/collectibles/collectibles.tsx index 82728714..9d18a1f8 100644 --- a/src/app/features/collectibles/collectibles.tsx +++ b/src/app/features/collectibles/collectibles.tsx @@ -9,6 +9,7 @@ import { useConfigNftMetadataEnabled } from '@app/query/common/hiro-config/hiro- import { AddCollectible } from './components/add-collectible'; import { Ordinals } from './components/bitcoin/ordinals'; +import { Stamps } from './components/bitcoin/stamps'; import { CollectiblesLayout } from './components/collectibes.layout'; import { StacksCryptoAssets } from './components/stacks/stacks-crypto-assets'; import { TaprootBalanceDisplayer } from './components/taproot-balance-displayer'; @@ -40,6 +41,7 @@ export function Collectibles() { <> + ), ledger: null, diff --git a/src/app/features/collectibles/components/bitcoin/stamp.tsx b/src/app/features/collectibles/components/bitcoin/stamp.tsx new file mode 100644 index 00000000..4891e77c --- /dev/null +++ b/src/app/features/collectibles/components/bitcoin/stamp.tsx @@ -0,0 +1,20 @@ +import { openInNewTab } from '@app/common/utils/open-in-new-tab'; +import { Stamp as BitcoinStamp } from '@app/query/bitcoin/stamps/stamp-collection.query'; + +import { CollectibleImage } from '../_collectible-types/collectible-image'; + +const stampChainAssetUrl = 'https://stampchain.io/asset.html?stampNumber='; + +export function Stamp(props: { bitcoinStamp: BitcoinStamp }) { + const { bitcoinStamp } = props; + + return ( + openInNewTab(`${stampChainAssetUrl}${bitcoinStamp.stamp}`)} + src={bitcoinStamp.stamp_url} + subtitle="Bitcoin Stamp" + title={`# ${bitcoinStamp.stamp}`} + /> + ); +} diff --git a/src/app/features/collectibles/components/bitcoin/stamps.tsx b/src/app/features/collectibles/components/bitcoin/stamps.tsx new file mode 100644 index 00000000..005672e1 --- /dev/null +++ b/src/app/features/collectibles/components/bitcoin/stamps.tsx @@ -0,0 +1,33 @@ +import { useEffect } from 'react'; + +import { useAnalytics } from '@app/common/hooks/analytics/use-analytics'; +import { useStampsByAddressQuery } from '@app/query/bitcoin/stamps/stamps-by-address.query'; +import { useCurrentBtcNativeSegwitAccountAddressIndexZero } from '@app/store/accounts/blockchain/bitcoin/native-segwit-account.hooks'; + +import { Stamp } from './stamp'; + +export function Stamps() { + const currentAccountBtcAddress = useCurrentBtcNativeSegwitAccountAddressIndexZero(); + const { data: stamps } = useStampsByAddressQuery(currentAccountBtcAddress); + const analytics = useAnalytics(); + + useEffect(() => { + if (!stamps) return; + if (stamps.length > 0) { + void analytics.track('view_collectibles', { + stamps_count: stamps.length, + }); + void analytics.identify({ stamps_count: stamps.length }); + } + }, [analytics, stamps]); + + if (!stamps) return null; + + return ( + <> + {stamps.map(s => ( + + ))} + + ); +} diff --git a/src/app/query/bitcoin/stamps/stamp-collection.query.ts b/src/app/query/bitcoin/stamps/stamp-collection.query.ts index 0fcab5d4..c2106a52 100644 --- a/src/app/query/bitcoin/stamps/stamp-collection.query.ts +++ b/src/app/query/bitcoin/stamps/stamp-collection.query.ts @@ -3,16 +3,16 @@ import { useQuery } from '@tanstack/react-query'; import { AppUseQueryConfig } from '@app/query/query-config'; import { QueryPrefixes } from '@app/query/query-prefixes'; -interface Stamp { - message_index: number; - block_index: number; - timestamp: number; - tx_hash: string; +export interface Stamp { asset: string; - tx_index: number; + block_index: number; + message_index: number; + stamp: number; stamp_mimetype: string; stamp_url: string; - stamp: number; + timestamp: number; + tx_hash: string; + tx_index: number; } async function fetchStampCollection() { diff --git a/src/app/query/bitcoin/stamps/stamps-by-address.query.ts b/src/app/query/bitcoin/stamps/stamps-by-address.query.ts new file mode 100644 index 00000000..f9dae9ed --- /dev/null +++ b/src/app/query/bitcoin/stamps/stamps-by-address.query.ts @@ -0,0 +1,31 @@ +import { useQuery } from '@tanstack/react-query'; + +import { AppUseQueryConfig } from '@app/query/query-config'; +import { QueryPrefixes } from '@app/query/query-prefixes'; + +import { Stamp } from './stamp-collection.query'; + +const stampsByAddressQueryOptions = { + staleTime: Infinity, + cacheTime: Infinity, +} as const; + +async function fetchStampsByAddress(address: string): Promise { + return fetch(`https://stampchain.io/api/stamps?wallet_address=${address}`).then(res => + res.json() + ); +} + +type FetchStampsByAddressResp = Awaited>; + +export function useStampsByAddressQuery( + address: string, + options?: AppUseQueryConfig +) { + return useQuery({ + queryKey: [QueryPrefixes.StampsByAddress], + queryFn: () => fetchStampsByAddress(address), + ...stampsByAddressQueryOptions, + ...options, + }); +} diff --git a/src/app/query/query-prefixes.ts b/src/app/query/query-prefixes.ts index ecfb64f0..bcb04c8f 100644 --- a/src/app/query/query-prefixes.ts +++ b/src/app/query/query-prefixes.ts @@ -12,4 +12,5 @@ export enum QueryPrefixes { GetNftMetadata = 'get-nft-metadata', StampCollection = 'stamp-collection', + StampsByAddress = 'stamps-by-address', }