Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: init NFTs on v4 project page #4507

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions src/packages/v4/contexts/V4NftRewardsProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { useJBRulesetMetadata } from 'juice-sdk-react'
import { JB721GovernanceType, NftRewardTier } from 'models/nftRewards'
import { NftRewardsContext } from 'packages/v2v3/contexts/NftRewards/NftRewardsContext'
import { useEffect, useMemo, useState } from 'react'
import {
DEFAULT_NFT_FLAGS,
DEFAULT_NFT_PRICING,
EMPTY_NFT_COLLECTION_METADATA,
} from 'redux/slices/editingV2Project'
import { CIDsOfNftRewardTiersResponse } from 'utils/nftRewards'
import { useV4NftTiers } from './useV4NftTiers'

export const V4NftRewardsProvider: React.FC<
React.PropsWithChildren<unknown>
> = ({ children }) => {
const { data } = useJBRulesetMetadata()

const [firstLoad, setFirstLoad] = useState(true)

const dataHookAddress = data?.dataHook

// don't fetch stuff if there's no datasource in the first place.
const hasNftRewards = Boolean(dataHookAddress)

// fetch NFT tier data from the contract
const { data: nftRewardTiersResponse, isLoading: nftRewardsCIDsLoading } =
useV4NftTiers({
dataHookAddress,
shouldFetch: hasNftRewards,
})

// catchall to ensure nfts are never loaded if hasNftRewards is false (there's no datasource).
const tierData = hasNftRewards ? nftRewardTiersResponse ?? [] : []
const loadedCIDs = CIDsOfNftRewardTiersResponse(
tierData.map(t => ({ encodedIPFSUri: t.encodedIPFSUri })),
)

// // fetch NFT metadata (its image, name etc.) from ipfs
// const { data: loadedRewardTiers, isLoading: nftRewardTiersLoading } =
// useNftRewards(tierData, projectId, dataHookAddress)
const nftRewardTiersLoading = false // todo wip
// TODO very wip - townhall demo
const rewardTiers: NftRewardTier[] = tierData.map(t => {
return {
contributionFloor: Number(t.price), // ETH or USD amount
fileUrl: '',
name: 'demo - WIP',
id: t.id,
maxSupply: undefined,
remainingSupply: undefined,
reservedRate: undefined,
beneficiary: undefined,
votingWeight: undefined,
externalLink: undefined,
description: undefined,
}
})
const CIDs = loadedCIDs

useEffect(() => {
// First load is always true until either nftRewardTiersLoading or nftRewardsCIDsLoading is true
if (firstLoad && (nftRewardTiersLoading || nftRewardsCIDsLoading)) {
setFirstLoad(false)
}
}, [firstLoad, nftRewardTiersLoading, nftRewardsCIDsLoading])

const nftsLoading = useMemo(() => {
return Boolean(firstLoad || nftRewardTiersLoading || nftRewardsCIDsLoading)
}, [firstLoad, nftRewardTiersLoading, nftRewardsCIDsLoading])

// fetch some other related stuff
// const { data: collectionMetadataUri } =
// useNftCollectionMetadataUri(dataSourceAddress)
// const { data: flags } = useNftFlagsOf(dataSourceAddress)

const contextData = {
nftRewards: {
rewardTiers,
// pricing: pricing ?? DEFAULT_NFT_PRICING, // TODO wip
pricing: DEFAULT_NFT_PRICING,
// TODO: Load governance type
governanceType: JB721GovernanceType.NONE,
CIDs,
// TODO wip
collectionMetadata: {
...EMPTY_NFT_COLLECTION_METADATA, // only load the metadata CID in the context - other data not necessary
// uri: collectionMetadataUri,
uri: '', // TODO
},
postPayModal: undefined,
// postPayModal: projectMetadata?.nftPaymentSuccessModal,
// flags: flags ?? DEFAULT_NFT_FLAGS, TODO wip
flags: DEFAULT_NFT_FLAGS,
},
loading: nftsLoading,
}

return (
<NftRewardsContext.Provider value={contextData}>
{children}
</NftRewardsContext.Provider>
)
}
36 changes: 36 additions & 0 deletions src/packages/v4/contexts/useV4NftTiers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {
useReadJb721TiersHookStoreAddress,
useReadJb721TiersHookStoreTiersOf,
} from 'juice-sdk-react'
import { Address } from 'viem'

export function useV4NftTiers({
dataHookAddress,
limit,
shouldFetch,
}: {
dataHookAddress: Address | undefined
limit?: number
shouldFetch?: boolean
}) {
const { data: store } = useReadJb721TiersHookStoreAddress({
address: dataHookAddress,
})

// send null when project has no dataSource, so the fetch doesn't execute.
const args =
shouldFetch && dataHookAddress
? ([
dataHookAddress,
[], // _categories
false, // _includeResolvedUri, return in each tier a result from a tokenUriResolver if one is included in the delegate
0n, // _startingId
BigInt(limit ?? 69), // MAX_NFT_REWARD_TIERS
] as const)
: undefined

return useReadJb721TiersHookStoreTiersOf({
address: store,
args,
})
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { Trans } from '@lingui/macro'
import { NftRewardsContext } from 'packages/v2v3/contexts/NftRewards/NftRewardsContext'
import { useContext } from 'react'

export const V4NftRewardsPanel = () => {
const x = useContext(NftRewardsContext)
const rewardTiers = x.nftRewards.rewardTiers

return (
<div className="flex w-full flex-col gap-5">
<h2 className="font-heading text-2xl font-medium mb-2">
<Trans>NFTs</Trans>
</h2>

{rewardTiers?.map((tier, i) => (
<div key={tier.id} className="flex">
{tier.id}
</div>
))}

{/* {!nftsLoading && rewardTiers?.length ? (
<div className="grid grid-cols-2 gap-4 md:grid-cols-2 md:gap-6">
{rewardTiers?.map((tier, i) => (
<div key={i} className="flex">
<NftReward
className="min-w-0"
rewardTier={tier}
loading={nftsLoading}
onSelect={(quantity = 1) => handleTierSelect(tier.id, quantity)}
onDeselect={() => handleTierDeselect(tier.id)}
/>
</div>
))}
</div>
) : nftsLoading ? (
<div className="grid grid-cols-2 gap-4 md:grid-cols-2 md:gap-6">
{[...Array(6)].map((_, i) => (
<NftRewardSkeleton key={i} />
))}
</div>
) : (
<EmptyScreen subtitle={t`This project has no NFTs`} />
)} */}
</div>
)
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
import { Fragment, useEffect, useMemo, useRef, useState } from 'react'
import {
Fragment,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react'

import { Tab } from '@headlessui/react'
import { t } from '@lingui/macro'
import { ProjectTab } from 'components/Project/ProjectTabs/ProjectTab'
import { useOnScreen } from 'hooks/useOnScreen'
import { NftRewardsContext } from 'packages/v2v3/contexts/NftRewards/NftRewardsContext'
import { twMerge } from 'tailwind-merge'
import { useProjectPageQueries } from '../hooks/useProjectPageQueries'
import V4AboutPanel from './V4AboutPanel'
import { V4ActivityPanel } from './V4ActivityPanel/V4ActivityPanel'
import { V4CyclesPayoutsPanel } from './V4CyclesPayoutsPanel/V4CyclesPayoutsPanel'
import { V4NftRewardsPanel } from './V4NftRewardsPanel/V4NftRewardsPanel'
import { V4TokensPanel } from './V4TokensPanel/V4TokensPanel'

type ProjectTabConfig = {
Expand All @@ -20,7 +29,11 @@ type ProjectTabConfig = {

export const V4ProjectTabs = ({ className }: { className?: string }) => {
const { projectPageTab, setProjectPageTab } = useProjectPageQueries()

const {
nftRewards: { rewardTiers },
} = useContext(NftRewardsContext)
const hasNftRewards = (rewardTiers ?? []).length > 0
const showNftRewards = hasNftRewards
const containerRef = useRef<HTMLDivElement>(null)
const panelRef = useRef<HTMLDivElement>(null)
const isPanelVisible = useOnScreen(panelRef)
Expand Down Expand Up @@ -48,12 +61,12 @@ export const V4ProjectTabs = ({ className }: { className?: string }) => {
() => [
{ id: 'activity', name: t`Activity`, panel: <V4ActivityPanel /> },
{ id: 'about', name: t`About`, panel: <V4AboutPanel /> },
// {
// id: 'nft_rewards',
// name: t`NFTs`,
// panel: <NftRewardsPanel />,
// hideTab: !showNftRewards,
// },
{
id: 'nft_rewards',
name: t`NFTs`,
panel: <V4NftRewardsPanel />,
hideTab: !showNftRewards,
},
{
id: 'cycle_payouts',
name: t`Cycles & Payouts`,
Expand Down
9 changes: 6 additions & 3 deletions src/pages/v4/[chainName]/p/[projectId]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { JBChainId, JBProjectProvider } from 'juice-sdk-react'
import { useRouter } from 'next/router'
import { ReduxProjectCartProvider } from 'packages/v4/components/ProjectDashboard/ReduxProjectCartProvider'
import store from 'packages/v4/components/ProjectDashboard/redux/store'
import { V4NftRewardsProvider } from 'packages/v4/contexts/V4NftRewardsProvider'
import V4ProjectMetadataProvider from 'packages/v4/contexts/V4ProjectMetadataProvider'
import { useCurrentRouteChainId } from 'packages/v4/hooks/useCurrentRouteChainId'
import { V4ProjectDashboard } from 'packages/v4/views/V4ProjectDashboard/V4ProjectDashboard'
Expand Down Expand Up @@ -70,9 +71,11 @@ const Providers: React.FC<
}}
>
<V4ProjectMetadataProvider projectId={projectId}>
<Provider store={store}>
<ReduxProjectCartProvider>{children}</ReduxProjectCartProvider>
</Provider>
<V4NftRewardsProvider>
<Provider store={store}>
<ReduxProjectCartProvider>{children}</ReduxProjectCartProvider>
</Provider>
</V4NftRewardsProvider>
</V4ProjectMetadataProvider>
</JBProjectProvider>
</WagmiProvider>
Expand Down
9 changes: 5 additions & 4 deletions src/utils/nftRewards.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,18 @@ import {
JB721GovernanceType,
JB721PricingParams,
JB721TierParams,
JB721TierV3,
JBDeployTiered721DelegateData,
JBTiered721Flags,
JB_721_TIER_PARAMS_V3_1,
JB_721_TIER_PARAMS_V3_2,
JB_721_TIER_V3_2,
JB_DEPLOY_TIERED_721_DELEGATE_DATA_V3_1,
NftRewardTier,
NftRewardTier
} from 'models/nftRewards'
import { DEFAULT_NFT_MAX_SUPPLY } from 'packages/v2v3/constants/nftRewards'
import { JB721DelegateVersion } from 'packages/v2v3/models/contracts'
import { V2V3CurrencyOption } from 'packages/v2v3/models/currencyOption'
import { decodeEncodedIpfsUri, encodeIpfsUri, ipfsUri } from 'utils/ipfs'
import { Hash } from 'viem'

export function sortNftsByContributionFloor(
rewardTiers: NftRewardTier[],
Expand Down Expand Up @@ -67,7 +66,9 @@ export function getNftRewardOfFloor({

// returns an array of CIDs from a given array of RewardTier obj's
export function CIDsOfNftRewardTiersResponse(
nftRewardTiersResponse: JB721TierV3[] | JB_721_TIER_V3_2[] | undefined,
nftRewardTiersResponse: {
encodedIPFSUri: Hash
}[],
): string[] {
const cids =
nftRewardTiersResponse
Expand Down
Loading