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

Polytone NFT Support and miscellaneous UX improvements #1358

Merged
merged 8 commits into from
Sep 3, 2023
1 change: 1 addition & 0 deletions apps/dapp/pages/dao/[address]/[[...slug]].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ export const getStaticPaths: GetStaticPaths = () => ({
})

export const getStaticProps = makeGetDaoStaticProps({
appMode: DaoPageMode.Dapp,
getProps: async ({ coreAddress }) => ({
url: SITE_URL + getDaoPath(DaoPageMode.Dapp, coreAddress),
}),
Expand Down
1 change: 1 addition & 0 deletions apps/dapp/pages/dao/[address]/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ export const getStaticPaths: GetStaticPaths = () => ({
})

export const getStaticProps = makeGetDaoStaticProps({
appMode: DaoPageMode.Dapp,
getProps: async ({ t, coreAddress }) => ({
url: SITE_URL + getDaoPath(DaoPageMode.Dapp, coreAddress, 'create'),
followingTitle: t('title.createASubDao'),
Expand Down
1 change: 1 addition & 0 deletions apps/dapp/pages/dao/[address]/proposals/[proposalId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const getStaticPaths: GetStaticPaths = () => ({
})

export const getStaticProps = makeGetDaoProposalStaticProps({
appMode: DaoPageMode.Dapp,
getProposalUrlPrefix: ({ address }) =>
SITE_URL +
getDaoProposalPath(
Expand Down
1 change: 1 addition & 0 deletions apps/dapp/pages/dao/[address]/proposals/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export const getStaticPaths: GetStaticPaths = () => ({
})

export const getStaticProps = makeGetDaoStaticProps({
appMode: DaoPageMode.Dapp,
getProps: ({ t, coreAddress }) => ({
url: SITE_URL + getDaoProposalPath(DaoPageMode.Dapp, coreAddress, 'create'),
followingTitle: t('title.createAProposal'),
Expand Down
Binary file removed apps/dapp/public/stargaze.png
Binary file not shown.
1 change: 1 addition & 0 deletions apps/sda/pages/[address]/[[...slug]].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export const getStaticPaths: GetStaticPaths = () => ({
})

export const getStaticProps = makeGetDaoStaticProps({
appMode: DaoPageMode.Sda,
getProps: async ({ coreAddress }) => ({
url: SITE_URL + getDaoPath(DaoPageMode.Sda, coreAddress),
}),
Expand Down
1 change: 1 addition & 0 deletions apps/sda/pages/[address]/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const getStaticPaths: GetStaticPaths = () => ({
})

export const getStaticProps = makeGetDaoStaticProps({
appMode: DaoPageMode.Sda,
getProps: async ({ t, coreAddress }) => ({
url: SITE_URL + getDaoPath(DaoPageMode.Sda, coreAddress, 'create'),
followingTitle: t('title.createASubDao'),
Expand Down
1 change: 1 addition & 0 deletions apps/sda/pages/[address]/proposals/[proposalId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const getStaticPaths: GetStaticPaths = () => ({
})

export const getStaticProps = makeGetDaoProposalStaticProps({
appMode: DaoPageMode.Sda,
getProposalUrlPrefix: ({ address }) =>
SITE_URL +
getDaoProposalPath(
Expand Down
1 change: 1 addition & 0 deletions apps/sda/pages/[address]/proposals/create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export const getStaticPaths: GetStaticPaths = () => ({
})

export const getStaticProps = makeGetDaoStaticProps({
appMode: DaoPageMode.Sda,
getProps: ({ t, coreAddress }) => ({
url: SITE_URL + getDaoProposalPath(DaoPageMode.Sda, coreAddress, 'create'),
followingTitle: t('title.createAProposal'),
Expand Down
Binary file removed apps/sda/public/stargaze.png
Binary file not shown.
316 changes: 316 additions & 0 deletions packages/state/recoil/selectors/contracts/CommonNft.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,316 @@
import { selectorFamily } from 'recoil'

import { ChainId, WithChainId } from '@dao-dao/types'
import {
AllNftInfoResponse,
AllOperatorsResponse,
AllTokensResponse,
ApprovalResponse,
ApprovalsResponse,
ContractInfoResponse,
MinterResponse,
NftInfoResponse,
NumTokensResponse,
OwnerOfResponse,
TokensResponse,
} from '@dao-dao/types/contracts/Cw721Base'

import { Sg721BaseQueryClient } from '../../../contracts'
import { Cw721BaseQueryClient } from '../../../contracts/Cw721Base'
import { refreshWalletBalancesIdAtom } from '../../atoms'
import { queryClient as commonNftQueryClient } from './Cw721Base'
import { queryClient as sg721BaseQueryClient } from './Sg721Base'

type QueryClientParams = WithChainId<{
contractAddress: string
}>

export const queryClient = selectorFamily<
Cw721BaseQueryClient | Sg721BaseQueryClient,
QueryClientParams
>({
key: 'commonNftQueryClient',
get:
(params) =>
({ get }) =>
params.chainId === ChainId.StargazeMainnet ||
params.chainId === ChainId.StargazeTestnet
? get(sg721BaseQueryClient(params))
: get(commonNftQueryClient(params)),
dangerouslyAllowMutability: true,
})

export const ownerOfSelector = selectorFamily<
OwnerOfResponse,
QueryClientParams & {
params: Parameters<Cw721BaseQueryClient['ownerOf']>
}
>({
key: 'commonNftOwnerOf',
get:
({ params, ...queryClientParams }) =>
async ({ get }) => {
// Don't use the indexer for this since various NFT contracts have
// different methods of storing NFT info, and the indexer does not know
// about every different way.
const client = get(queryClient(queryClientParams))
return await client.ownerOf(...params)
},
})
export const approvalSelector = selectorFamily<
ApprovalResponse,
QueryClientParams & {
params: Parameters<Cw721BaseQueryClient['approval']>
}
>({
key: 'commonNftApproval',
get:
({ params, ...queryClientParams }) =>
async ({ get }) => {
// Don't use the indexer for this since various NFT contracts have
// different methods of storing NFT info, and the indexer does not know
// about every different way.
const client = get(queryClient(queryClientParams))
return await client.approval(...params)
},
})
export const approvalsSelector = selectorFamily<
ApprovalsResponse,
QueryClientParams & {
params: Parameters<Cw721BaseQueryClient['approvals']>
}
>({
key: 'commonNftApprovals',
get:
({ params, ...queryClientParams }) =>
async ({ get }) => {
// Don't use the indexer for this since various NFT contracts have
// different methods of storing NFT info, and the indexer does not know
// about every different way.
const client = get(queryClient(queryClientParams))
return await client.approvals(...params)
},
})
export const allOperatorsSelector = selectorFamily<
AllOperatorsResponse,
QueryClientParams & {
params: Parameters<Cw721BaseQueryClient['allOperators']>
}
>({
key: 'commonNftAllOperators',
get:
({ params, ...queryClientParams }) =>
async ({ get }) => {
// Don't use the indexer for this since various NFT contracts have
// different methods of storing NFT info, and the indexer does not know
// about every different way.
const client = get(queryClient(queryClientParams))
return await client.allOperators(...params)
},
})
export const numTokensSelector = selectorFamily<
NumTokensResponse,
QueryClientParams & {
params: Parameters<Cw721BaseQueryClient['numTokens']>
}
>({
key: 'commonNftNumTokens',
get:
({ params, ...queryClientParams }) =>
async ({ get }) => {
// Don't use the indexer for this since various NFT contracts have
// different methods of storing NFT info, and the indexer does not know
// about every different way.
const client = get(queryClient(queryClientParams))
return await client.numTokens(...params)
},
})
export const contractInfoSelector = selectorFamily<
ContractInfoResponse,
QueryClientParams & {
params: Parameters<Cw721BaseQueryClient['contractInfo']>
}
>({
key: 'commonNftContractInfo',
get:
({ params, ...queryClientParams }) =>
async ({ get }) => {
// Don't use the indexer for this since various NFT contracts have
// different methods of storing NFT info, and the indexer does not know
// about every different way.
const client = get(queryClient(queryClientParams))
return await client.contractInfo(...params)
},
})
export const nftInfoSelector = selectorFamily<
NftInfoResponse,
QueryClientParams & {
params: Parameters<Cw721BaseQueryClient['nftInfo']>
}
>({
key: 'commonNftNftInfo',
get:
({ params, ...queryClientParams }) =>
async ({ get }) => {
// Don't use the indexer for this since various NFT contracts have
// different methods of storing NFT info, and the indexer does not know
// about every different way.
const client = get(queryClient(queryClientParams))
return await client.nftInfo(...params)
},
})
export const allNftInfoSelector = selectorFamily<
AllNftInfoResponse,
QueryClientParams & {
params: Parameters<Cw721BaseQueryClient['allNftInfo']>
}
>({
key: 'commonNftAllNftInfo',
get:
({ params, ...queryClientParams }) =>
async ({ get }) => {
// Don't use the indexer for this since various NFT contracts have
// different methods of storing NFT info, and the indexer does not know
// about every different way.
const client = get(queryClient(queryClientParams))
return await client.allNftInfo(...params)
},
})

// Use allTokensForOwnerSelector as it implements pagination for chain queries.
export const _tokensSelector = selectorFamily<
TokensResponse,
QueryClientParams & {
params: Parameters<Cw721BaseQueryClient['tokens']>
}
>({
key: 'commonNft_Tokens',
get:
({ params, ...queryClientParams }) =>
async ({ get }) => {
const client = get(queryClient(queryClientParams))
get(refreshWalletBalancesIdAtom(params[0].owner))
return await client.tokens(...params)
},
})
export const _allTokensSelector = selectorFamily<
AllTokensResponse,
QueryClientParams & {
params: Parameters<Cw721BaseQueryClient['allTokens']>
}
>({
key: 'commonNft_AllTokens',
get:
({ params, ...queryClientParams }) =>
async ({ get }) => {
// Don't use the indexer for this since various NFT contracts have
// different methods of storing NFT info, and the indexer does not know
// about every different way.
const client = get(queryClient(queryClientParams))
return await client.allTokens(...params)
},
})
export const minterSelector = selectorFamily<
MinterResponse,
QueryClientParams & {
params: Parameters<Cw721BaseQueryClient['minter']>
}
>({
key: 'commonNftMinter',
get:
({ params, ...queryClientParams }) =>
async ({ get }) => {
// Don't use the indexer for this since various NFT contracts have
// different methods of storing NFT info, and the indexer does not know
// about every different way.
const client = get(queryClient(queryClientParams))
return await client.minter(...params)
},
})

const ALL_TOKENS_FOR_OWNER_LIMIT = 30
export const allTokensForOwnerSelector = selectorFamily<
TokensResponse['tokens'],
QueryClientParams & {
owner: string
}
>({
key: 'commonNftAllTokensForOwner',
get:
({ owner, ...queryClientParams }) =>
async ({ get }) => {
get(refreshWalletBalancesIdAtom(owner))

// Don't use the indexer for this since various NFT contracts have
// different methods of storing NFT info, and the indexer does not know
// about every different way.

const tokens: TokensResponse['tokens'] = []
while (true) {
const response = await get(
_tokensSelector({
...queryClientParams,
params: [
{
owner,
startAfter: tokens[tokens.length - 1],
limit: ALL_TOKENS_FOR_OWNER_LIMIT,
},
],
})
)?.tokens

if (!response?.length) {
break
}

tokens.push(...response)

// If we have less than the limit of items, we've exhausted them.
if (response.length < ALL_TOKENS_FOR_OWNER_LIMIT) {
break
}
}

return tokens
},
})

const ALL_TOKENS_LIMIT = 30
export const allTokensSelector = selectorFamily<
AllTokensResponse['tokens'],
QueryClientParams
>({
key: 'commonNftAllTokens',
get:
(queryClientParams) =>
async ({ get }) => {
const tokens: AllTokensResponse['tokens'] = []
while (true) {
const response = await get(
_allTokensSelector({
...queryClientParams,
params: [
{
startAfter: tokens[tokens.length - 1],
limit: ALL_TOKENS_LIMIT,
},
],
})
)?.tokens

if (!response?.length) {
break
}

tokens.push(...response)

// If we have less than the limit of items, we've exhausted them.
if (response.length < ALL_TOKENS_LIMIT) {
break
}
}

return tokens
},
})
Loading
Loading