Completed code for Mango Heroes integration
This commit is contained in:
parent
14147aab26
commit
9f66df9bf1
|
@ -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'
|
||||||
|
|
|
@ -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}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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'))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue