mango-ui-v3/components/NftProfilePicModal.tsx

263 lines
7.8 KiB
TypeScript

import { useState, useEffect } from 'react'
import { PublicKey } from '@solana/web3.js'
import { notify } from 'utils/notifications'
import useMangoStore from '../stores/useMangoStore'
import { useWallet } from '@solana/wallet-adapter-react'
import { PhotographIcon } from '@heroicons/react/outline'
import Modal from './Modal'
import { ElementTitle } from './styles'
import {
createRemoveProfilePictureTransaction,
createSetProfilePictureTransaction,
} from '@solflare-wallet/pfp'
import Button from './Button'
import { useTranslation } from 'next-i18next'
import { connectionSelector } from '../stores/selectors'
import { LinkButton } from 'components'
interface SelectedNft {
mint: PublicKey
tokenAddress: PublicKey
}
const ImgWithLoader = (props) => {
const [isLoading, setIsLoading] = useState(true)
return (
<div className="relative">
{isLoading && (
<PhotographIcon className="absolute left-1/2 top-1/2 z-10 h-1/4 w-1/4 -translate-x-1/2 -translate-y-1/2 transform animate-pulse text-th-fgd-4" />
)}
<img {...props} onLoad={() => setIsLoading(false)} />
</div>
)
}
const NftProfilePicModal = ({ isOpen, onClose }) => {
const { t } = useTranslation(['common', 'profile'])
const connection = useMangoStore(connectionSelector)
const { publicKey, wallet } = useWallet()
const pfp = useMangoStore((s) => s.wallet.pfp)
const nfts = useMangoStore((s) => s.wallet.nfts.data)
const nftAccounts = useMangoStore((s) => s.wallet.nfts.accounts)
const initialLoad = useMangoStore((s) => s.wallet.nfts.initialLoad)
const nftsLoading = useMangoStore((s) => s.wallet.nfts.loading)
const [selectedProfile, setSelectedProfile] = useState<SelectedNft | null>(
null
)
const actions = useMangoStore((s) => s.actions)
const mangoClient = useMangoStore.getState().connection.client
const [offset, setOffset] = useState(0)
const setMangoStore = useMangoStore((s) => s.set)
useEffect(() => {
if (publicKey) {
actions.fetchNftAccounts(connection, publicKey)
}
}, [publicKey])
useEffect(() => {
if (!initialLoad && publicKey && nftAccounts.length > 0) {
actions.fetchNfts(connection, publicKey)
}
}, [publicKey, initialLoad, nftAccounts])
useEffect(() => {
if (pfp?.isAvailable && pfp.mintAccount && pfp.tokenAccount) {
setSelectedProfile({
mint: pfp.mintAccount,
tokenAddress: pfp.tokenAccount,
})
}
}, [pfp])
const handleSaveProfilePic = async () => {
if (!publicKey || !selectedProfile || !wallet) {
return
}
try {
setMangoStore((state) => {
state.wallet.nfts.loadingTransaction = true
})
onClose()
const transaction = await createSetProfilePictureTransaction(
publicKey,
selectedProfile.mint,
selectedProfile.tokenAddress
)
if (transaction) {
const txid = await mangoClient.sendTransaction(
transaction,
wallet.adapter,
[]
)
if (txid) {
notify({
title: t('profile:profile-pic-success'),
description: '',
txid,
})
} else {
notify({
title: t('profile:profile-pic-failure'),
description: t('transaction-failed'),
})
}
} else {
notify({
title: t('profile:profile-pic-failure'),
description: t('transaction-failed'),
})
}
} catch (e) {
notify({
title: t('profile:profile-pic-failure'),
description: e.message,
txid: e.txid,
type: 'error',
})
} finally {
actions.fetchProfilePicture(wallet)
setMangoStore((state) => {
state.wallet.nfts.loadingTransaction = false
})
}
}
const handleRemoveProfilePic = async () => {
if (!publicKey || !wallet) {
return
}
try {
setMangoStore((state) => {
state.wallet.nfts.loadingTransaction = true
})
onClose()
const transaction = await createRemoveProfilePictureTransaction(publicKey)
if (transaction) {
const txid = await mangoClient.sendTransaction(
transaction,
wallet.adapter,
[]
)
if (txid) {
notify({
title: t('profile:profile-pic-remove-success'),
description: '',
txid,
})
} else {
notify({
title: t('profile:profile-pic-remove-failure'),
description: t('transaction-failed'),
})
}
} else {
notify({
title: t('profile:profile-pic-remove-failure'),
description: t('transaction-failed'),
})
}
} catch (e) {
notify({
title: t('profile:profile-pic-remove-failure'),
description: e.message,
txid: e.txid,
type: 'error',
})
} finally {
actions.fetchProfilePicture(wallet)
setMangoStore((state) => {
state.wallet.nfts.loadingTransaction = false
})
}
}
const handleLoadMore = async () => {
const offsetNfts = offset + 9
await actions.fetchNfts(connection, publicKey, offsetNfts)
setOffset(offsetNfts)
}
return (
<Modal isOpen={isOpen} onClose={onClose}>
<Modal.Header>
<div className="mb-3 flex w-full flex-col items-center sm:mt-3 sm:flex-row sm:justify-between">
<ElementTitle noMarginBottom>
{t('profile:choose-profile')}
</ElementTitle>
<div className="mt-3 flex items-center space-x-4 sm:mt-0">
<Button
disabled={!selectedProfile}
onClick={() => handleSaveProfilePic()}
>
{t('save')}
</Button>
{pfp?.isAvailable ? (
<LinkButton
className="text-xs"
onClick={() => handleRemoveProfilePic()}
>
{t('profile:remove')}
</LinkButton>
) : null}
</div>
</div>
</Modal.Header>
{nftAccounts.length > 0 ? (
<div className="flex flex-col items-center">
<div className="mb-4 grid w-full grid-flow-row grid-cols-3 gap-4">
{nfts.map((n) => {
return (
<button
className={`default-transitions col-span-1 flex items-center justify-center rounded-md border bg-th-bkg-3 py-3 sm:py-4 md:hover:bg-th-bkg-4 ${
selectedProfile?.tokenAddress.toString() === n.tokenAddress
? 'border-th-primary'
: 'border-th-bkg-3'
}`}
key={n.tokenAddress}
onClick={() =>
setSelectedProfile({
mint: new PublicKey(n.mint),
tokenAddress: new PublicKey(n.tokenAddress),
})
}
>
<ImgWithLoader
className="h-16 w-16 flex-shrink-0 rounded-full sm:h-20 sm:w-20"
src={n.val.image}
/>
</button>
)
})}
{nftsLoading
? [
...Array(
nftAccounts.length - nfts.length > 9
? 9
: nftAccounts.length - nfts.length
),
].map((i) => (
<div
className="col-span-1 h-[90px] animate-pulse rounded-md bg-th-bkg-3 sm:h-28"
key={i}
/>
))
: null}
</div>
{nftAccounts.length !== nfts.length ? (
<LinkButton onClick={() => handleLoadMore()}>
{t('show-more')}
</LinkButton>
) : null}
</div>
) : (
<p className="text-center">{t('profile:no-nfts')}</p>
)}
</Modal>
)
}
export default NftProfilePicModal