Skip to content

Commit

Permalink
Paginate NFTs in more places and migrate to lazy NFT card (#1462)
Browse files Browse the repository at this point in the history
* Removed unused Stargaze NFT import modal.

* Convert all NFT displays to use the lazy NFT card to improve speed and usability. Also paginate NFTs in selection modal.

* Fixed selection string in actions and staked modal.
  • Loading branch information
NoahSaso authored Nov 10, 2023
1 parent 79c9815 commit bb98486
Show file tree
Hide file tree
Showing 38 changed files with 609 additions and 724 deletions.
1 change: 1 addition & 0 deletions packages/i18n/locales/dog/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@
"notificationsInInbox_other": "dere iz {{count}} open puppozalz in ur inbox",
"numNftsSelected_one": "{{count}} nft seleckted",
"numNftsSelected_other": "{{count}} nftz seleckted",
"numNftsSelected_zero": "nar nft seleckted",
"numProposals_one": "{{count}} puppozal",
"numProposals_other": "{{count}} puppozalz",
"numSelected_one": "{{count}} seleckted",
Expand Down
1 change: 1 addition & 0 deletions packages/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -916,6 +916,7 @@
"numContributors_other": "{{count}} contributors",
"numNftsSelected_one": "{{count}} NFT selected",
"numNftsSelected_other": "{{count}} NFTs selected",
"numNftsSelected_zero": "No NFT selected",
"numPayments_one": "{{count}} payment",
"numPayments_other": "{{count}} payments",
"numProposals_one": "{{count}} proposal",
Expand Down
1 change: 1 addition & 0 deletions packages/state/recoil/atoms/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './chain'
export * from './loading'
export * from './misc'
export * from './nft'
export * from './proposals'
export * from './refresh'
export * from './theme'
Expand Down
12 changes: 12 additions & 0 deletions packages/state/recoil/atoms/nft.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { atom } from 'recoil'

import { NftCardInfo } from '@dao-dao/types'

// Cache NFT card info per key once loaded so they can be accessed anywhere
// after lazy load.
export const nftCardInfosForKeyAtom = atom<
Record<string, NftCardInfo | undefined>
>({
key: 'nftCardInfosForKey',
default: {},
})
15 changes: 10 additions & 5 deletions packages/stateful/actions/core/nfts/BurnNft/Component.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { ComponentMeta, ComponentStory } from '@storybook/react'

import { makeProps as makeNftCardProps } from '@dao-dao/stateless/components/NftCard.stories'
import {
makeLazyInfo,
makeProps as makeNftCardProps,
} from '@dao-dao/stateless/components/NftCard.stories'
import {
ReactHookFormDecorator,
makeDaoInfo,
makeDaoProvidersDecorator,
} from '@dao-dao/storybook'

import { NftSelectionModal } from '../../../../components'
import { BurnNft } from './Component'

export default {
Expand Down Expand Up @@ -45,11 +49,12 @@ Default.args = {
errored: false,
data: [
selected,
makeNftCardProps(),
makeNftCardProps(),
makeNftCardProps(),
makeNftCardProps(),
makeLazyInfo(),
makeLazyInfo(),
makeLazyInfo(),
makeLazyInfo(),
],
},
NftSelectionModal,
},
}
34 changes: 18 additions & 16 deletions packages/stateful/actions/core/nfts/BurnNft/Component.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import clsx from 'clsx'
import { useEffect, useState } from 'react'
import { ComponentType, useEffect, useState } from 'react'
import { useFormContext } from 'react-hook-form'
import { useTranslation } from 'react-i18next'

Expand All @@ -8,13 +8,15 @@ import {
HorizontalNftCard,
InputErrorMessage,
Loader,
NftSelectionModal,
} from '@dao-dao/stateless'
import {
ActionComponent,
LazyNftCardInfo,
LoadingDataWithError,
NftCardInfo,
NftSelectionModalProps,
} from '@dao-dao/types'
import { getNftKey } from '@dao-dao/utils'

export type BurnNftData = {
chainId: string
Expand All @@ -24,16 +26,17 @@ export type BurnNftData = {

export interface BurnNftOptions {
// The set of NFTs that may be burned as part of this action.
options: LoadingDataWithError<NftCardInfo[]>
options: LoadingDataWithError<LazyNftCardInfo[]>
// Information about the NFT currently selected. If errored, it may be burnt.
nftInfo: LoadingDataWithError<NftCardInfo | undefined>
NftSelectionModal: ComponentType<NftSelectionModalProps>
}

export const BurnNft: ActionComponent<BurnNftOptions> = ({
fieldNamePrefix,
isCreating,
errors,
options: { options, nftInfo },
options: { options, nftInfo, NftSelectionModal },
}) => {
const { t } = useTranslation()
const { watch, setValue, setError, clearErrors } =
Expand All @@ -43,17 +46,15 @@ export const BurnNft: ActionComponent<BurnNftOptions> = ({
const tokenId = watch((fieldNamePrefix + 'tokenId') as 'tokenId')
const collection = watch((fieldNamePrefix + 'collection') as 'collection')

const selected = `${chainId}:${collection}:${tokenId}`
const getIdForNft = (nft: NftCardInfo) =>
`${nft.chainId}:${nft.collection.address}:${nft.tokenId}`
const selectedKey = getNftKey(chainId, collection, tokenId)

useEffect(() => {
if (
!selected ||
!selectedKey ||
// If selected, make sure it exists in options.
(!options.loading &&
!options.errored &&
!options.data.some((nft) => getIdForNft(nft) === selected))
!options.data.some((nft) => nft.key === selectedKey))
) {
if (!errors?.collection) {
setError((fieldNamePrefix + 'collection') as 'collection', {
Expand All @@ -67,7 +68,7 @@ export const BurnNft: ActionComponent<BurnNftOptions> = ({
}
}
}, [
selected,
selectedKey,
setError,
clearErrors,
t,
Expand All @@ -77,7 +78,9 @@ export const BurnNft: ActionComponent<BurnNftOptions> = ({
])

// Show modal initially if creating and no NFT already selected.
const [showModal, setShowModal] = useState<boolean>(isCreating && !selected)
const [showModal, setShowModal] = useState<boolean>(
isCreating && !selectedKey
)

return (
<>
Expand Down Expand Up @@ -119,27 +122,26 @@ export const BurnNft: ActionComponent<BurnNftOptions> = ({
label: t('button.save'),
onClick: () => setShowModal(false),
}}
getIdForNft={getIdForNft}
header={{
title: t('title.selectNftToBurn'),
}}
nfts={options}
onClose={() => setShowModal(false)}
onNftClick={(nft) => {
if (getIdForNft(nft) === selected) {
// No need to clear chain when deselecting.
if (nft.key === selectedKey) {
setValue((fieldNamePrefix + 'chainId') as 'chainId', '')
setValue((fieldNamePrefix + 'tokenId') as 'tokenId', '')
setValue((fieldNamePrefix + 'collection') as 'collection', '')
} else {
setValue((fieldNamePrefix + 'chainId') as 'chainId', nft.chainId)
setValue((fieldNamePrefix + 'tokenId') as 'tokenId', nft.tokenId)
setValue(
(fieldNamePrefix + 'collection') as 'collection',
nft.collection.address
nft.collectionAddress
)
}
}}
selectedIds={selected ? [selected] : []}
selectedKeys={selectedKey ? [selectedKey] : []}
visible={showModal}
/>
)}
Expand Down
31 changes: 15 additions & 16 deletions packages/stateful/actions/core/nfts/BurnNft/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {
ActionContextType,
ActionKey,
ActionMaker,
LazyNftCardInfo,
LoadingDataWithError,
NftCardInfo,
UseDecodedCosmosMsg,
UseDefaults,
UseTransformToCosmos,
Expand All @@ -22,15 +22,22 @@ import {
objectMatchesStructure,
} from '@dao-dao/utils'

import { NftSelectionModal } from '../../../../components'
import {
lazyNftCardInfosForDaoSelector,
nftCardInfoSelector,
nftCardInfosForDaoSelector,
walletNftCardInfos,
walletLazyNftCardInfosSelector,
} from '../../../../recoil/selectors/nft'
import { useCw721CommonGovernanceTokenInfoIfExists } from '../../../../voting-module-adapter'
import { useActionOptions } from '../../../react'
import { BurnNft, BurnNftData } from './Component'

const useDefaults: UseDefaults<BurnNftData> = () => ({
chainId: '',
collection: '',
tokenId: '',
})

const useTransformToCosmos: UseTransformToCosmos<BurnNftData> = () => {
const {
chain: { chain_id: currentChainId },
Expand Down Expand Up @@ -114,11 +121,11 @@ const Component: ActionComponent = (props) => {
const options = useCachedLoadingWithError(
props.isCreating
? context.type === ActionContextType.Wallet
? walletNftCardInfos({
? walletLazyNftCardInfosSelector({
walletAddress: address,
chainId: currentChainId,
})
: nftCardInfosForDaoSelector({
: lazyNftCardInfosForDaoSelector({
chainId: currentChainId,
coreAddress: address,
governanceCollectionAddress,
Expand All @@ -136,7 +143,7 @@ const Component: ActionComponent = (props) => {
? options
: combineLoadingDataWithErrors(
...Object.values(options.data).filter(
(data): data is LoadingDataWithError<NftCardInfo[]> => !!data
(data): data is LoadingDataWithError<LazyNftCardInfo[]> => !!data
)
)

Expand All @@ -146,21 +153,13 @@ const Component: ActionComponent = (props) => {
options={{
options: allChainOptions,
nftInfo,
NftSelectionModal,
}}
/>
)
}

export const makeBurnNftAction: ActionMaker<BurnNftData> = ({
t,
chain: { chain_id: chainId },
}) => {
const useDefaults: UseDefaults<BurnNftData> = () => ({
chainId,
collection: '',
tokenId: '',
})

export const makeBurnNftAction: ActionMaker<BurnNftData> = ({ t }) => {
return {
key: ActionKey.BurnNft,
Icon: FireEmoji,
Expand Down
6 changes: 2 additions & 4 deletions packages/stateful/actions/core/nfts/MintNft/MintNft.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,8 @@ export const MintNft: ActionComponent = (props) => {
? undefined
: {
key: chainId + collectionAddress + mintMsg.token_id,
collection: {
address: collectionAddress,
name: creatingCollectionInfoLoading.data?.name ?? '',
},
collectionAddress,
collectionName: creatingCollectionInfoLoading.data?.name ?? '',
tokenId: mintMsg.token_id,
imageUrl: creatingNftTokenUriDataLoading.data.imageUrl,
name: creatingNftTokenUriDataLoading.data.name ?? '',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import { ComponentMeta, ComponentStory } from '@storybook/react'

import { AddressInput } from '@dao-dao/stateless'
import { makeProps as makeNftCardProps } from '@dao-dao/stateless/components/NftCard.stories'
import {
makeLazyInfo as makeLazyNftInfo,
makeProps as makeNftCardProps,
} from '@dao-dao/stateless/components/NftCard.stories'
import {
ReactHookFormDecorator,
makeDaoInfo,
makeDaoProvidersDecorator,
} from '@dao-dao/storybook'

import { NftSelectionModal } from '../../../../components'
import { TransferNftComponent } from './Component'

export default {
Expand Down Expand Up @@ -42,12 +46,13 @@ Default.args = {
errored: false,
data: [
selected,
makeNftCardProps(),
makeNftCardProps(),
makeNftCardProps(),
makeNftCardProps(),
makeLazyNftInfo(),
makeLazyNftInfo(),
makeLazyNftInfo(),
makeLazyNftInfo(),
],
},
AddressInput,
NftSelectionModal,
},
}
Loading

0 comments on commit bb98486

Please sign in to comment.