Chào mừng bạn đến với bước cuối cùng trong quá trình tạo cổng sưu tầm của bạn! (phần 1 xem tại đây )
Trong phần này, chúng ta sẽ tập trung vào việc xây dựng giao diện người dùng—mảnh ghép cuối cùng.
Đây là những gì chúng ta sẽ đạt được:
Chúng tôi sẽ sử dụng Next.js để xây dựng giao diện người dùng.
Bắt đầu nào!
Mở thư mục flow-collectible-portal
dự án của bạn. Sau đó chạy
npx create-next-app@latest frontend
trong terminal và nhấn enter
.
Điều này sẽ cung cấp cho bạn một số tùy chọn. Trong hướng dẫn này, chúng tôi sẽ không sử dụng Typescript , ESLint hoặc TailwindCSS và chúng tôi sẽ sử dụng thư mục src
và bộ định tuyến Ứng dụng tại thời điểm viết bài này.
Bây giờ bạn đã có sẵn một ứng dụng web mới.
Thư mục lối vào của bạn trông như thế này:
Để tương tác với chuỗi khối Flow, chúng tôi sẽ sử dụng Thư viện ứng dụng khách Flow (FCL) để quản lý các kết nối ví, chạy tập lệnh và gửi giao dịch trong ứng dụng của chúng tôi. Nó sẽ cho phép chúng ta viết các hàm Cadence hoàn chỉnh và chạy chúng dưới dạng các hàm Javascript.
Để bắt đầu, hãy cài đặt FCL cho ứng dụng của chúng tôi bằng cách chạy lệnh sau:
npm install @onflow/fcl --save
Sau khi cài đặt FCL, chúng ta cần cấu hình nó. Đây là những gì bạn cần làm:
app
, tạo một thư mục mới có tên flow
và thêm tệp có tên config.js
.
Thêm mã sau vào tệp config.js
:
import { config } from "@onflow/fcl"; config({ "app.detail.title": "Flow Name Service", "app.detail.icon": "https://placekitten.com/g/200/200", "accessNode.api": "https://rest-testnet.onflow.org", "discovery.wallet": "https://fcl-discovery.onflow.org/testnet/authn", "0xCollectibles": "ADD YOUR CONTRACT ACCOUNT ADDRESS", "0xNonFungibleToken": "0x631e88ae7f1d7c20", });
Bây giờ bạn đã sẵn sàng sử dụng FCL trong ứng dụng của mình.
Để xác minh danh tính của người dùng trong ứng dụng, bạn có thể sử dụng một số chức năng:
fcl.logIn()
.fcl.signUp()
.fcl.unauthenticate()
.
Hãy tìm hiểu cách chúng tôi có thể triển khai các hàm fcl
này trong giao diện người dùng của bạn.
Đầu tiên, chúng ta sẽ thêm đoạn mã sau vào tệp page.js
bên trong thư mục ứng dụng. Điều này sẽ nhập một số phần phụ thuộc, thiết lập một số useState
ban đầu cho các phần của ứng dụng của chúng tôi và xây dựng giao diện người dùng cơ bản.
Để đảm bảo nó trông đẹp mắt, hãy xóa tệp page.module.css
bên trong thư mục ứng dụng và thay vào đó tạo một tệp có tên page.css. Sau đó dán nội dung của tập tin này vào bên trong nó. Bây giờ chúng ta có thể viết ra trang đầu tiên của mình.
"use client"; import React, { useState, useEffect, useRef } from "react"; import * as fcl from "@onflow/fcl"; import "./page.css"; import "./flow/config"; export default function Page() { const [currentUser, setCurrentUser] = useState({ loggedIn: false, addr: undefined, }); const urlInputRef = useRef(); const nameInputRef = useRef(); const idInputRef = useRef(); const [isInitialized, setIsInitialized] = useState(); const [collectiblesList, setCollectiblesList] = useState([]); const [loading, setLoading] = useState(false); const [ids, setIds] = useState([]); const [nft, setNFT] = useState({}); useEffect(() => fcl.currentUser.subscribe(setCurrentUser), []); function handleInputChange(event) { const inputValue = event.target.value; if (/^\d+$/.test(inputValue)) { idInputRef.current = +inputValue; } else { console.error("Invalid input. Please enter a valid integer."); } } return ( <div> <div className="navbar"> <h1>Flow Collectibles Portal</h1> <span>Address: {currentUser?.addr ?? "NO Address"}</span> <button onClick={currentUser.addr ? fcl.unauthenticate : fcl.logIn}> {currentUser.addr ? "Log Out" : "Connect Wallet"} </button> </div> {currentUser.loggedIn ? ( <div className="main"> <div className="mutate"> <h1>Mutate Flow Blockchain</h1> <form onSubmit={(event) => { event.preventDefault(); }} > <input type="text" placeholder="enter name of the NFT" ref={nameInputRef} /> <input type="text" placeholder="enter a url" ref={urlInputRef} /> <button type="submit">Mint</button> </form> <mark>Your Collection will be initialized while minting NFT.</mark> </div> <div className="query"> <h1>Query Flow Blockchain</h1> <mark>Click below button to check 👇</mark> <button>Check Collection</button> <p> Is your collection initialized: {isInitialized ? "Yes" : "No"} </p> <button onClick={viewIds}> View NFT IDs you hold in your collection </button> <p>NFT Id: </p> </div> <div className="view"> <h1>View Your NFT</h1> <input type="text" placeholder="enter your NFT ID" onChange={handleInputChange} /> <button>View NFT</button> <div className="nft-card"> <p>NFT id: </p> <p>NFT name: </p> <img src="" alt="" /> </div> </div> </div> ) : ( <div className="main-2"> <h1>Connect Wallet to mint NFT!!</h1> </div> )} </div> ); }
Sau khi thêm mã này, hãy chạy npm run dev
để đảm bảo mọi thứ tải chính xác.
Trước khi đi sâu vào cách chúng ta có thể sử dụng fcl
để truy vấn chuỗi khối Flow, hãy thêm các mã tập lệnh Cadence này sau hàm handleInput
trong tệp page.js
const CHECK_COLLECTION = ` import NonFungibleToken from 0xNonFungibleToken import Collectibles from 0xCollectibles pub fun main(address: Address): Bool? { return Collectibles.checkCollection(_addr: address) }` const GET_NFT_ID = ` import NonFungibleToken from 0xNonFungibleToken import Collectibles from 0xCollectibles pub fun main(user: Address): [UInt64] { let collectionCap = getAccount(user).capabilities.get <&{Collectibles.CollectionPublic}>(/public/NFTCollection) ?? panic("This public capability does not exist.") let collectionRef = collectionCap.borrow()! return collectionRef.getIDs() } ` const GET_NFT = ` import NonFungibleToken from 0xNonFungibleToken import Collectibles from 0xCollectibles pub fun main(user: Address, id: UInt64): &NonFungibleToken.NFT? { let collectionCap= getAccount(user).capabilities.get<&{Collectibles.CollectionPublic}>(/public/NFTCollection) ?? panic("This public capability does not exist.") let collectionRef = collectionCap.borrow()! return collectionRef.borrowNFT(id: id) }
Khi các tập lệnh Cadence của chúng tôi đã sẵn sàng hoạt động, giờ đây chúng tôi có thể khai báo một số hàm Javascript và chuyển các hằng số Cadence vào các truy vấn `fcl`
.
async function checkCollectionInit() { const isInit = await fcl.query({ cadence: CHECK_COLLECTION, args: (arg,t) => [arg(currentUser?.addr, t.Address)], }); console.log(isInit); } async function viewNFT() { console.log(idInputRef.current); const nfts = await fcl.query({ cadence: GET_NFT, args: (arg,t) => [arg(currentUser?.addr,t.Address), arg(idInputRef.current, t.UInt64)] }); setNFT(nfts); console.log(nfts); } async function viewIds() { const ids = await fcl.query({ cadence: GET_NFT_ID, args: (arg,t) => [arg(currentUser?.addr,t.Address)] }); setIds(ids); console.log(ids); }
Bây giờ chúng ta hãy xem tất cả các chức năng chúng tôi đã viết. Có hai điều cần chú ý:
fcl.query
args: (arg,t) => [arg(addr,t.Address)],
line.
Vì các tập lệnh tương tự như các chức năng view
trong Solidity và không yêu cầu bất kỳ khoản phí gas nào để chạy nên về cơ bản chúng tôi chỉ đang truy vấn chuỗi khối. Vì vậy chúng tôi sử dụng fcl.query
để chạy tập lệnh trên Flow.
Để truy vấn một cái gì đó, chúng ta cần truyền một đối số. Để làm được điều đó, chúng tôi sử dụng arg, là một hàm lấy giá trị chuỗi đại diện cho đối số và t
, là một đối tượng chứa tất cả các kiểu dữ liệu khác nhau mà Cadence có. Vì vậy, chúng ta có thể cho arg
biết cách mã hóa và giải mã đối số mà chúng ta đang truyền.
Mặc dù các chức năng trước đây của chúng tôi chỉ là “chỉ đọc”, nhưng các chức năng tiếp theo của chúng tôi sẽ có các hành động có thể thay đổi trạng thái blockchain và ghi vào nó; hay còn gọi là “đúc NFT.”
Để làm điều này, chúng ta sẽ viết một tập lệnh Cadence khác dưới dạng hằng số.
const MINT_NFT = ` import NonFungibleToken from 0xNonFungibleToken import Collectibles from 0xCollectibles transaction(name:String, image:String){ let receiverCollectionRef: &{NonFungibleToken.CollectionPublic} prepare(signer:AuthAccount){ // initialise account if signer.borrow<&Collectibles.Collection>(from: Collectibles.CollectionStoragePath) == nil { let collection <- Collectibles.createEmptyCollection() signer.save(<-collection, to: Collectibles.CollectionStoragePath) let cap = signer.capabilities.storage.issue<&{Collectibles.CollectionPublic}>(Collectibles.CollectionStoragePath) signer.capabilities.publish( cap, at: Collectibles.CollectionPublicPath) } //takes the receiver collection refrence self.receiverCollectionRef = signer.borrow<&Collectibles.Collection>(from: Collectibles.CollectionStoragePath) ?? panic("could not borrow Collection reference") } execute{ let nft <- Collectibles.mintNFT(name:name, image:image) self.receiverCollectionRef.deposit(token: <-nft) } }
Bây giờ hãy thêm hàm bên dưới sau mã giao dịch vào tệp page.js
async function mint() { try{ const txnId = await fcl.mutate({ cadence: MINT_NFT, args: (arg,t) => [arg(name,t.String), arg(image, t.String)], payer: fcl.authz, proposer: fcl.authz, authorizations: [fcl.authz], limit:999,}); } catch(error){ console.error('Minting failed:' error) } console.log(txnId); }
Về hàm, cú pháp fcl.mutate
giống với fcl.query
. Tuy nhiên, chúng tôi cung cấp một số thông số bổ sung như sau:
payer: fcl.authz, proposer: fcl.authz, authorizations: [fcl.authz], limit: 50,
fcl.authz
đề cập đến tài khoản hiện được kết nối.limit
giống như gasLimit trong thế giới Ethereum, đặt giới hạn trên cho số lượng tính toán tối đa. Nếu tính toán vượt quá giới hạn thì giao dịch sẽ thất bại.
Chúng ta sẽ cần thêm một hàm nữa để gọi và xử lý hàm mintNFT
mà chúng ta vừa tạo.
const saveCollectible = async () => { if (urlInputRef.current.value.length > 0 && nameInputRef.current.value.length > 0) { try { setLoading(true); const transaction = await mintNFT(nameInputRef.current.value, urlInputRef.current.value); console.log('transactionID:', transaction); // Handle minting success (if needed) } catch (error) { console.error('Minting failed:', error); // Handle minting failure (if needed) } finally { setLoading(false); } } else { console.log('Empty input. Try again.'); } };
Với các chức năng chính đã có, giờ đây chúng tôi có thể cắm chúng vào giao diện người dùng của mình.
Tuy nhiên, trước khi thực hiện điều đó, chúng tôi sẽ thêm một số lệnh gọi useEffect
để giúp tải trạng thái ban đầu. Bạn có thể thêm những thứ này ngay phía trên lệnh gọi useEffect
hiện có.
useEffect(() => { checkCollectionInit(); viewNFT(); }, [currentUser]); useEffect(() => { if (currentUser.loggedIn) { setCollectiblesList(collectiblesList); console.log('Setting collectibles...'); } }, [currentUser]);
Bây giờ quay lại phần return
với giao diện người dùng, chúng ta có thể thêm các chức năng của mình vào các phần thích hợp của ứng dụng.
return ( <div> <div className="navbar"> <h1>Flow Collectibles Portal</h1> <span>Address: {currentUser?.addr ?? "NO Address"}</span> <button onClick={currentUser.addr ? fcl.unauthenticate : fcl.logIn}> {currentUser.addr ? "Log Out" : "Connect Wallet"} </button> </div> {currentUser.loggedIn ? ( <div className="main"> <div className="mutate"> <h1>Mutate Flow Blockchain</h1> <form onSubmit={(event) => { event.preventDefault(); saveCollectible(); }} > <input type="text" placeholder="enter name of the NFT" ref={nameInputRef} /> <input type="text" placeholder="enter a url" ref={urlInputRef} /> <button type="submit">Mint</button> </form> <mark>Your Collection will be initialized while minting NFT.</mark> </div> <div className="query"> <h1>Query Flow Blockchain</h1> <mark>Click below button to check 👇</mark> <button onClick={checkCollectionInit}>Check Collection</button> <p> Is your collection initialized: {isInitialized ? "Yes" : "No"} </p> <button onClick={viewIds}> View NFT IDs you hold in your collection </button> <p>NFT Id: </p> {ids.map((id) => ( <p key={id}>{id}</p> ))} </div> <div className="view"> <h1>View Your NFT</h1> <input type="text" placeholder="enter your NFT ID" onChange={handleInputChange} /> <button onClick={viewNFT}>View NFT</button> <div className="nft-card"> <p>NFT id: {nft.id}</p> <p>NFT name: {nft.name}</p> <img src={nft.image} alt={nft.name} /> </div> </div> </div> ) : ( <div className="main-2"> <h1>Connect Wallet to mint NFT!!</h1> </div> )} </div> );
Kiểm tra mã cuối cùng ở đây .
Bây giờ khi ứng dụng đã hoàn tất, hãy cùng tìm hiểu cách sử dụng nó!
Trước tiên, hãy kết nối ví của bạn bằng cách nhấp vào nút “Kết nối ví” ở trên cùng bên phải.
Bây giờ bạn có thể đúc NFT! Nhập tên NFT của bạn và dán vào liên kết tới hình ảnh bạn muốn sử dụng. Sau khi bạn nhấp vào “mint”, nó sẽ nhắc bạn ký một giao dịch bằng ví của mình.
Có thể mất một chút thời gian để giao dịch hoàn tất. Sau khi hoàn tất, bạn có thể nhấp vào nút dưới cùng để xem ID NFT của mình. Nếu đây là lần đầu tiên của bạn thì ID sẽ chỉ là “1”.
Bây giờ bạn có thể sao chép ID NFT của mình, dán nó vào phần Xem và nhấp vào “Xem NFT”.
Làm tốt! Bạn đã hoàn thành phần 2 của dự án cổng thông tin sưu tầm. Tóm lại, chúng tôi tập trung vào việc xây dựng giao diện người dùng của cổng thông tin sưu tầm của mình.
Chúng tôi đã làm điều này bằng cách:
Chúc bạn có một ngày thật tuyệt vời!
Cũng được xuất bản ở đây.