nextjs前端dapp实现可以合成的NFT(下)

介绍

一个开源solidity合集仓库
https://github.com/qdwds/smart-contracts
以太坊ERC721全栈开发开NFT合集从入门到项目实战项目
https://learnblockchain.cn/course/31
https://edu.51cto.com/course/33566.html

一起学习吧

我们一起沟通、交流、学习吧!

可合成的NFT

这一小节是实现NFT的页面展示和功能交互
在这里插入图片描述

项目目录

├── README.md
├── abi
│   └── ComposeNFT.json
├── components
│   ├── Botton
│   │   └── index.tsx
│   ├── Header
│   │   └── index.tsx
│   └── NFT
│       └── index.tsx
├── deploy.sh
├── next-env.d.ts
├── next.config.js
├── package-lock.json
├── package.json
├── pages
│   ├── _app.tsx
│   ├── _document.tsx
│   ├── api
│   │   └── hello.ts
│   └── index.tsx
├── pnpm-lock.yaml
├── public
│   ├── favicon.ico
│   ├── next.svg
│   └── vercel.svg
├── src
│   └── rainbowkit.ts
├── styles
│   └── globals.css
└── tsconfig.json

Index.tsx

当前文件为主页面,功能主要实现了NFT页面展示和交互

import React, { useState } from 'react'
import { Stack, Box, Snackbar } from '@mui/material';
import NFT from "../components/NFT";
import { Breed, Mint } from "../components/Botton";
import { useAccount, useContractRead } from 'wagmi';
import compose from "../abi/ComposeNFT.json";

const compsoeContract: any = {
    address: compose.address,
    abi: compose.abi,
}
export default function Index() {
    const [compose, setCompose] = useState<string[]>([])
    const [donkey, setDonkey] = useState<any[]>([]);
    const { address } = useAccount();
    const [open, setOpen] = useState(false);

    const handleClose = (event: React.SyntheticEvent | Event, reason?: string) => {
        if (reason === 'clickaway') {
            return;
        }
        setOpen(false);
    };

    const { refetch } = useContractRead({
        ...compsoeContract,
        functionName: 'getDonkeys',
        args: [address],
        onSuccess(data: any) {
            if(data.length > 0)
                setDonkey(data.map((d: any) => ({
                    ...d,
                    check: false
                })));
        }
    })

    const handleAddCompose = (data: any) => {
        console.log(data);
        if (compose.length >= 2) return setOpen(true);;
        const newDonkey = donkey.map((d: any) => {
            if (d.tokenId == data.tokenId) {
                return {
                    ...d,
                    check: true
                }
            } else {
                return { ...d }
            }
        })
        setDonkey(newDonkey);
        setCompose([...compose, data.tokenId]);
    }

    const handleRemoveCompose = (data: any) => {
        const newDonkey = donkey.map((d: any) => {
            if (d.tokenId == data.tokenId) {
                return {
                    ...d,
                    check: false
                }
            } else {
                return { ...d }
            }
        })
        setDonkey(newDonkey);
        setCompose(compose.filter((c: string) => data.tokenId != c));
    }
    return (
        <> 
            <Snackbar open={open} anchorOrigin={{ vertical:"top", horizontal:"center" }} autoHideDuration={3000} onClose={handleClose} message="最多只能选择两个NFT进行合成!"/>
            <Stack direction="row" spacing={2} sx={{ justifyContent: 'center', marginTop: 2 }}>
                <Breed refetch={refetch} compose={compose} setCompose={setCompose}></Breed>
                <Mint refetch={refetch}></Mint>
            </Stack>
            <Box sx={{ padding: 4 }}>
                {
                    donkey.map((itme: any) => {
                        return <NFT
                            key={itme.tokenId}
                            data={itme}
                            handleAddCompose={handleAddCompose}
                            handleRemoveCompose={handleRemoveCompose}
                        ></NFT>
                    })
                }
            </Box>
        </>
    )
}

NFT展示组件

这个组件主要功能是展示当前用户的NFT。

import { Box, Button, Card, CardActionArea, CardActions, CardContent, CardMedia, Typography } from '@mui/material'
import React from 'react'

const resetAddress = (owner: string) => `${owner.slice(0, 4)}...${owner.slice(38)}`;



export default function NFT({ data, handleAddCompose, handleRemoveCompose }: any) {
    return (
        <>
            {
                <Card sx={{ maxWidth: 260, float: "left", marginLeft: 2, marginBottom: 2 }} key={data.tokenId}>
                    <CardActionArea>
                        <CardMedia
                            component="img"
                            height="240"
                            image={data.uri}
                            alt="green iguana"
                        />
                        <CardContent>
                            <Typography gutterBottom variant="h5" component="div">
                                {String(data.tokenId)} #
                            </Typography>
                            <Typography variant="body2" color="text.secondary" component="div">
                                <Box>
                                    <div>等级: {data.level}</div>
                                    <div>dadId: {String(data.dadId)}</div>
                                    <div>mumId: {String(data.mumId)}</div>
                                    <div>拥有者:{resetAddress(data.owner)}</div>
                                </Box>
                            </Typography>
                        </CardContent>
                    </CardActionArea>
                    <CardActions>
                        {
                            data.check ?
                                <Button size="small" color="error" onClick={() => handleRemoveCompose(data)}>移除合成</Button> :
                                <Button size="small" color="primary" onClick={() => handleAddCompose(data)}>添加合成</Button>
                        }
                    </CardActions>
                </Card>
            }
        </>
    )
}

mint和合成按钮组件

import { useAccount, useContractWrite, usePrepareContractWrite } from 'wagmi';
import compose from "../../abi/ComposeNFT.json"
import { Alert, Button, Snackbar } from '@mui/material';
import { LoadingButton } from '@mui/lab';
import { useState } from 'react';
const compsoeContract: any = {
    address: compose.address,
    abi: compose.abi,
}


export const Breed = ({ refetch, compose, setCompose }: any) => {
    const [msg, setMsg] = useState("");
    const [open, setOpen] = useState(false);

    const handleClose = (event: React.SyntheticEvent | Event, reason?: string) => {
        if (reason === 'clickaway') {
            return;
        }
        setOpen(false);
    };

    const { config }: any = usePrepareContractWrite({
        ...compsoeContract,
        functionName: 'breed',
        args:compose,
        onError(error:any){
            const message = error?.reason || error.data?.message || error.message;
            if(message == "missing argument: passed to contract") return;
            setOpen(true);
            setMsg(message);
        }
    })

    const { isLoading, writeAsync } = useContractWrite({
        ...config,
        async onSuccess(data:any){
            await data.wait();
            refetch();
            setCompose([]);
        },
    })
 
    return (
        <>
            <Snackbar open={open} anchorOrigin={{ vertical:"top", horizontal:"center" }} autoHideDuration={3000} onClose={handleClose}>
                <Alert onClose={handleClose} severity="error" sx={{ width: '100%' }}>{msg}</Alert>
            </Snackbar>
            <LoadingButton 
                loading={isLoading} 
                variant="contained" 
                color="success" 
                disabled={!writeAsync} 
                onClick={()=>writeAsync?.()}
            >合成</LoadingButton>
        </>
    )
}


export const Mint = ({ refetch }: any) => {
    const { address } = useAccount();

    const { isLoading, writeAsync } = useContractWrite({
        mode: 'recklesslyUnprepared',
        ...compsoeContract,
        functionName: 'safeMint',
        args: [address],
        async onSuccess(data){
            await data.wait();
            refetch();
        }
    })

    return (
        <LoadingButton 
            loading={isLoading} 
            variant="outlined" 
            color="error" 
            disabled={!writeAsync} 
            onClick={()=>writeAsync?.()}
        >mint</LoadingButton>
    )
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值