Completed code for Mango Heroes integration

This commit is contained in:
Yandre 2021-11-10 21:54:23 -05:00
parent 14147aab26
commit 9f66df9bf1
5 changed files with 114 additions and 55 deletions

View File

@ -1,4 +1,4 @@
import React, { useState } from 'react' import React, { useEffect, useState } from 'react'
import { FunctionComponent } from 'react' import { FunctionComponent } from 'react'
import Modal from './Modal' import Modal from './Modal'
import { ElementTitle } from './styles' import { ElementTitle } from './styles'
@ -6,9 +6,6 @@ import { Responsive, WidthProvider } from 'react-grid-layout'
import _ from 'lodash' import _ from 'lodash'
import Button from './Button' import Button from './Button'
import useMangoStore from '../stores/useMangoStore' import useMangoStore from '../stores/useMangoStore'
// import mango_hero from '../public/mango_heroes.jpg'
// import mango_hero1 from '../public/mango_heroes1.jpeg'
// import mango_hero2 from '../public/mango_heroes2.jpeg'
import { notify } from '../utils/notifications' import { notify } from '../utils/notifications'
import { NFT } from '../utils/metaplex/models' import { NFT } from '../utils/metaplex/models'
@ -25,30 +22,36 @@ const ChangeAvatarModal: FunctionComponent<ChangeAvatarModalProps> = ({
onClose, onClose,
currentAvatar, currentAvatar,
}) => { }) => {
const nfts = useMangoStore((s) => s.settings.nfts) const nfts = [...useMangoStore((s) => s.settings.nfts)].filter(
(nft) => nft.metadataUri
)
const currentIndex = nfts.indexOf(currentAvatar) const currentIndex = nfts.indexOf(currentAvatar)
if (currentIndex != -1) nfts.splice(currentIndex, 1) if (currentIndex != -1) nfts.splice(currentIndex, 1)
const testImageUrls = [nfts] const listOfNFTs = [currentAvatar, ...nfts]
const [selectedIndex, setSelectedIndex] = useState(0) const [selectedIndex, setSelectedIndex] = useState(0)
const set = useMangoStore((state) => state.set) const set = useMangoStore((state) => state.set)
const [layouts] = useState( const [layouts] = useState(
testImageUrls.map((url, key) => { listOfNFTs.map((nft, key) => {
return { return {
i: String(key), i: String(key),
x: key % 3, x: key % 3,
y: Math.floor(key / 3), y: Math.floor(key / 3),
w: 1, w: 1,
h: 1, h: 1,
url: url, nft: nft,
} }
}) })
) )
// Save selected profile picture
const saveSelection = () => { const saveSelection = () => {
const nft = listOfNFTs[selectedIndex]
set((state) => { set((state) => {
state.settings.avatar = testImageUrls[selectedIndex] state.settings.avatar = nft.mintAddress.toBase58()
}) })
localStorage.setItem('profilePic', nft.mintAddress.toBase58())
notify({ notify({
title: 'Avatar changed successfully', title: 'Avatar changed successfully',
@ -83,7 +86,7 @@ const ChangeAvatarModal: FunctionComponent<ChangeAvatarModalProps> = ({
onClick={() => setSelectedIndex(+layout.i)} onClick={() => setSelectedIndex(+layout.i)}
> >
<NFTDisplay <NFTDisplay
nft={layout.url} nft={layout.nft}
selected={selectedIndex === +layout.i} selected={selectedIndex === +layout.i}
/> />
</div> </div>
@ -105,8 +108,37 @@ const ChangeAvatarModal: FunctionComponent<ChangeAvatarModalProps> = ({
} }
const NFTDisplay = ({ nft, selected }) => { const NFTDisplay = ({ nft, selected }) => {
const [imageUri, setImageUri] = useState()
useEffect(() => {
if (nft.imageUri) {
setImageUri(nft.imageUri)
} else {
try {
fetch(nft.metadataUri).then(async (_) => {
try {
const data = await _.json()
nft.imageUri = data['image']
setImageUri(nft.imageUri)
console.log('imageUri is: ', nft.imageUri)
} catch (ex) {
console.error('Error trying to parse JSON: ' + ex)
}
})
} catch (ex) {
console.error('Error trying to fetch Arweave metadata: ' + ex)
}
}
}, [imageUri])
return ( return (
<div className="hover:scale-110"> <div
className={`hover:scale-110 ${
nft.imageUri == undefined
? 'bg-th-bkg-4 h-full w-full rounded-lg animate-pulse'
: ''
}`}
>
<img <img
className={`border ${ className={`border ${
selected ? 'border-th-primary' : 'border-th-bkg-4' selected ? 'border-th-primary' : 'border-th-bkg-4'

View File

@ -21,48 +21,61 @@ import { useEffect } from 'react'
import SettingsModal from './SettingsModal' import SettingsModal from './SettingsModal'
import { UserCircleIcon } from '@heroicons/react/solid' import { UserCircleIcon } from '@heroicons/react/solid'
import ChangeAvatarModal from './ChangeAvatarModal' import ChangeAvatarModal from './ChangeAvatarModal'
import { NFT } from '../utils/metaplex/models'
const ConnectWalletButton = () => { const ConnectWalletButton = () => {
const wallet = useMangoStore((s) => s.wallet.current) const wallet = useMangoStore((s) => s.wallet.current)
const connected = useMangoStore((s) => s.wallet.connected) const connected = useMangoStore((s) => s.wallet.connected)
const nfts = useMangoStore((s) => s.settings.nfts) const nfts = useMangoStore((s) => s.settings.nfts)
const imageUrl = useMangoStore((s) => s.settings.avatar) const profilePicMintAddress = useMangoStore((s) => s.settings.avatar)
const set = useMangoStore((s) => s.set) const set = useMangoStore((s) => s.set)
const [showAccountsModal, setShowAccountsModal] = useState(false) const [showAccountsModal, setShowAccountsModal] = useState(false)
const [showSettingsModal, setShowSettingsModal] = useState(false) const [showSettingsModal, setShowSettingsModal] = useState(false)
const [showChangeAvatarModal, setChangeAvatarModal] = useState(false) const [showChangeAvatarModal, setChangeAvatarModal] = useState(false)
const [selectedWallet, setSelectedWallet] = useState(DEFAULT_PROVIDER.url) const [selectedWallet, setSelectedWallet] = useState(DEFAULT_PROVIDER.url)
const [profilePicture, setProfilePicture] = useState(new NFT())
const [imageUrl, setImageUrl] = useState('')
const [savedProviderUrl] = useLocalStorageState( const [savedProviderUrl] = useLocalStorageState(
PROVIDER_LOCAL_STORAGE_KEY, PROVIDER_LOCAL_STORAGE_KEY,
DEFAULT_PROVIDER.url DEFAULT_PROVIDER.url
) )
// update in useEffect to prevent SRR error from next.js
useEffect(() => { useEffect(() => {
setSelectedWallet(savedProviderUrl) setSelectedWallet(savedProviderUrl)
}, [savedProviderUrl]) }, [savedProviderUrl])
if (nfts.length != 0 && imageUrl == '') { useEffect(() => {
const nft = nfts[0] if (!profilePicMintAddress && nfts.length == 0) {
return
}
const profilePics = nfts.filter(
(nft) => nft.mintAddress.toBase58() == profilePicMintAddress
)
const nft = profilePics.length == 1 ? profilePics[0] : nfts[0]
if (nft.imageUri) {
setImageUrl(nft.imageUri)
} else {
try { try {
fetch(nft).then(async (_) => { fetch(nft.metadataUri).then(async (_) => {
try { try {
const data = await _.json() const data = await _.json()
nft.imageUri = data['image']
set((state) => { setImageUrl(nft.imageUri)
state.settings.avatar = data['image']
})
} catch (ex) { } catch (ex) {
console.error('Error trying to parse JSON: ' + ex) console.error('Error trying to parse JSON: ' + ex)
} }
}) })
} catch (ex) { } catch (ex) {
console.error('Error trying to fetch Arweave metadata: ' + ex) console.error('Error trying to fetch metadata: ' + ex)
} }
} }
const handleWalletConect = () => { setProfilePicture(nft)
}, [nfts, profilePicMintAddress])
const handleWalletConnect = () => {
wallet.connect() wallet.connect()
set((state) => { set((state) => {
state.selectedMangoAccount.initialLoad = true state.selectedMangoAccount.initialLoad = true
@ -90,7 +103,6 @@ const ConnectWalletButton = () => {
} }
> >
{imageUrl == '' ? <ProfileIcon className="h-6 w-6" /> : null} {imageUrl == '' ? <ProfileIcon className="h-6 w-6" /> : null}
{/* <NewProfileIcon className="h-6 w-6" src={mango_hero.src} /> */}
</Menu.Button> </Menu.Button>
<Menu.Items className="bg-th-bkg-1 mt-2 p-1 absolute right-0 shadow-lg outline-none rounded-md w-48 z-20"> <Menu.Items className="bg-th-bkg-1 mt-2 p-1 absolute right-0 shadow-lg outline-none rounded-md w-48 z-20">
<Menu.Item> <Menu.Item>
@ -102,6 +114,7 @@ const ConnectWalletButton = () => {
<div className="pl-2 text-left">Accounts</div> <div className="pl-2 text-left">Accounts</div>
</button> </button>
</Menu.Item> </Menu.Item>
{imageUrl != '' ? (
<Menu.Item> <Menu.Item>
<button <button
className="flex flex-row font-normal items-center rounded-none w-full p-2 hover:bg-th-bkg-2 hover:cursor-pointer focus:outline-none" className="flex flex-row font-normal items-center rounded-none w-full p-2 hover:bg-th-bkg-2 hover:cursor-pointer focus:outline-none"
@ -111,6 +124,7 @@ const ConnectWalletButton = () => {
<div className="pl-2 text-left">Change Avatar</div> <div className="pl-2 text-left">Change Avatar</div>
</button> </button>
</Menu.Item> </Menu.Item>
) : null}
<Menu.Item> <Menu.Item>
<button <button
className="flex flex-row font-normal items-center rounded-none w-full p-2 hover:bg-th-bkg-2 hover:cursor-pointer focus:outline-none" className="flex flex-row font-normal items-center rounded-none w-full p-2 hover:bg-th-bkg-2 hover:cursor-pointer focus:outline-none"
@ -149,7 +163,7 @@ const ConnectWalletButton = () => {
) : ( ) : (
<div className="bg-th-bkg-1 h-14 flex divide-x divide-th-bkg-3 justify-between"> <div className="bg-th-bkg-1 h-14 flex divide-x divide-th-bkg-3 justify-between">
<button <button
onClick={handleWalletConect} onClick={handleWalletConnect}
disabled={!wallet} disabled={!wallet}
className="rounded-none text-th-primary hover:bg-th-bkg-4 focus:outline-none disabled:text-th-fgd-4 disabled:cursor-wait" className="rounded-none text-th-primary hover:bg-th-bkg-4 focus:outline-none disabled:text-th-fgd-4 disabled:cursor-wait"
> >
@ -184,7 +198,7 @@ const ConnectWalletButton = () => {
<ChangeAvatarModal <ChangeAvatarModal
onClose={() => setChangeAvatarModal(false)} onClose={() => setChangeAvatarModal(false)}
isOpen={showChangeAvatarModal} isOpen={showChangeAvatarModal}
url={imageUrl} currentAvatar={profilePicture}
/> />
) : null} ) : null}
</> </>

View File

@ -107,7 +107,7 @@ export default function useWallet() {
actions.reloadOrders() actions.reloadOrders()
actions.fetchTradeHistory() actions.fetchTradeHistory()
actions.fetchWalletTokens() actions.fetchWalletTokens()
actions.fetchProfilePicture() actions.fetchMangoHeroesNFTs()
notify({ notify({
title: 'Wallet connected', title: 'Wallet connected',
description: description:
@ -145,7 +145,7 @@ export default function useWallet() {
useInterval(() => { useInterval(() => {
if (connected && mangoAccount) { if (connected && mangoAccount) {
actions.fetchWalletTokens() actions.fetchWalletTokens()
actions.fetchProfilePicture() actions.fetchMangoHeroesNFTs()
actions.fetchTradeHistory() actions.fetchTradeHistory()
} }
}, 90 * SECONDS) }, 90 * SECONDS)

View File

@ -35,7 +35,11 @@ import { TOKEN_PROGRAM_ID } from '../utils/tokens'
import { findProgramAddress } from '../utils/metaplex/utils' import { findProgramAddress } from '../utils/metaplex/utils'
import * as borsh from 'borsh' import * as borsh from 'borsh'
import { Metadata, METADATA_SCHEMA, NFT } from '../utils/metaplex/models' import { Metadata, METADATA_SCHEMA, NFT } from '../utils/metaplex/models'
import { METADATA_KEY, METADATA_PREFIX } from '../utils/metaplex/types' import {
MANGO_HEROES_MINT_AUTHORITY,
METADATA_KEY,
METADATA_PREFIX,
} from '../utils/metaplex/types'
export const ENDPOINTS: EndpointInfo[] = [ export const ENDPOINTS: EndpointInfo[] = [
{ {
@ -283,7 +287,7 @@ const useMangoStore = create<MangoStore>((set, get) => {
}) })
} }
}, },
async fetchProfilePicture() { async fetchMangoHeroesNFTs() {
const wallet = get().wallet.current const wallet = get().wallet.current
const connected = get().wallet.connected const connected = get().wallet.connected
const connection = get().connection.current const connection = get().connection.current
@ -295,7 +299,7 @@ const useMangoStore = create<MangoStore>((set, get) => {
{ programId: TOKEN_PROGRAM_ID } { programId: TOKEN_PROGRAM_ID }
) )
const nfts = [] const walletNFTs = []
tokenAccounts.value.forEach((token) => { tokenAccounts.value.forEach((token) => {
const tokenAccount = token.account.data.parsed.info const tokenAccount = token.account.data.parsed.info
@ -306,15 +310,17 @@ const useMangoStore = create<MangoStore>((set, get) => {
) { ) {
const nft = new NFT() const nft = new NFT()
nft.mintAddress = new PublicKey(tokenAccount.mint) nft.mintAddress = new PublicKey(tokenAccount.mint)
nfts.push(nft) walletNFTs.push(nft)
} }
}) })
if (nfts.length == 0) return if (walletNFTs.length == 0) return
const metadataProgramId = new PublicKey(METADATA_KEY) const metadataProgramId = new PublicKey(METADATA_KEY)
for (const nft of nfts) { const mangoHeroesNFTs = []
for (const nft of walletNFTs) {
// The return value is [programDerivedAddress, bytes] but we only care about the address // The return value is [programDerivedAddress, bytes] but we only care about the address
const [pda] = await findProgramAddress( const [pda] = await findProgramAddress(
@ -335,12 +341,17 @@ const useMangoStore = create<MangoStore>((set, get) => {
Metadata, Metadata,
accountInfo!.data accountInfo!.data
) )
if (metadata.updateAuthority == MANGO_HEROES_MINT_AUTHORITY) {
const uri = metadata.data.uri.replace(/\0/g, '') const uri = metadata.data.uri.replace(/\0/g, '')
nft.metadataUri = uri nft.metadataUri = uri
mangoHeroesNFTs.push(nft)
}
} }
set((state) => { set((state) => {
state.settings.nfts = nfts ;(state.settings.nfts = mangoHeroesNFTs),
(state.settings.avatar = localStorage.getItem('profilePic'))
}) })
} }
}, },

View File

@ -7,6 +7,8 @@ export type StringPublicKey = string
export const EDITION = 'edition' export const EDITION = 'edition'
export const METADATA_PREFIX = 'metadata' export const METADATA_PREFIX = 'metadata'
export const METADATA_KEY = 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s' export const METADATA_KEY = 'metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s'
export const MANGO_HEROES_MINT_AUTHORITY =
'3RVBxNcF1LBAVbxPz1KsueWspASJhqz4ZJGDbxy2mKE3'
export const MAX_AUCTION_DATA_EXTENDED_SIZE = 8 + 9 + 2 + 200 export const MAX_AUCTION_DATA_EXTENDED_SIZE = 8 + 9 + 2 + 200