Willkommen beim letzten Schritt bei der Erstellung Ihres Sammlerportals! (Teil 1 finden Sie hier )
In diesem Teil konzentrieren wir uns auf den Aufbau des Frontends – dem letzten Teil des Puzzles.
Folgendes werden wir erreichen:
Wir werden Next.js verwenden, um das Frontend zu erstellen.
Lass uns anfangen!
Öffnen Sie Ihr Projekt flow-collectible-portal
Verzeichnis. Dann renne
npx create-next-app@latest frontend
im Terminal und drücken Sie enter
.
Dadurch stehen Ihnen mehrere Optionen zur Verfügung. In diesem Tutorial verwenden wir nicht Typescript , ESLint oder TailwindCSS und verwenden das src
Verzeichnis und den App-Router zum Zeitpunkt dieses Artikels.
Jetzt haben Sie eine neue Web-App parat.
So sieht Ihr Frontend-Ordner aus:
Um mit der Flow-Blockchain zu interagieren, verwenden wir die Flow Client Library (FCL), um Wallet-Verbindungen zu verwalten, Skripte auszuführen und Transaktionen in unserer Anwendung zu senden. Damit können wir komplette Cadence-Funktionen schreiben und sie als Javascript-Funktionen ausführen.
Um zu beginnen, installieren wir FCL für unsere App, indem wir den folgenden Befehl ausführen:
npm install @onflow/fcl --save
Nach der Installation von FCL müssen wir es konfigurieren. Folgendes müssen Sie tun:
app
Ordner einen neuen Ordner mit dem Namen flow
und fügen Sie eine Datei mit dem Namen config.js
hinzu.
Fügen Sie der Datei config.js
den folgenden Code hinzu:
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", });
Jetzt sind Sie bereit, die FCL in Ihrer App zu verwenden.
Um die Identität eines Benutzers in einer App zu überprüfen, können Sie mehrere Funktionen verwenden:
fcl.logIn()
auf.fcl.signUp()
auf.fcl.unauthenticate()
auf.
Erfahren Sie, wie wir diese fcl
Funktionen in Ihrem Frontend implementieren können.
Zuerst fügen wir den folgenden Code zu unserer page.js
Datei im App-Verzeichnis hinzu. Dadurch werden einige Abhängigkeiten importiert, ein anfänglicher useState
für Teile unserer App eingerichtet und eine grundlegende Benutzeroberfläche erstellt.
Um sicherzustellen, dass es gut aussieht, löschen Sie die Datei page.module.css
im App-Verzeichnis und erstellen Sie stattdessen eine Datei mit dem Namen page.css. Fügen Sie dann den Inhalt dieser Datei ein. Jetzt können wir unsere erste Seite schreiben.
"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> ); }
Nachdem Sie diesen Code hinzugefügt haben, führen Sie npm run dev
aus, um sicherzustellen, dass alles korrekt geladen wird.
Bevor wir uns eingehend damit befassen, wie wir fcl
zum Abfragen der Flow-Blockchain verwenden können, fügen Sie diese Cadence-Skriptcodes nach der handleInput
Funktion in der Datei page.js
hinzu.
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) }
Da unsere Cadence-Skripte einsatzbereit sind, können wir nun einige Javascript-Funktionen deklarieren und die Cadence-Konstanten in die `fcl`
-Abfragen übergeben.
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); }
Schauen wir uns nun alle Funktionen an, die wir geschrieben haben. Dabei sind zwei Dinge zu beachten:
fcl.query
args: (arg,t) => [arg(addr,t.Address)],
Zeile.
Da Skripte den view
in Solidity ähneln und für die Ausführung keine Gasgebühren anfallen, fragen wir im Wesentlichen nur die Blockchain ab. Deshalb verwenden wir fcl.query
um Skripte auf Flow auszuführen.
Um etwas abzufragen, müssen wir ein Argument übergeben. Dazu verwenden wir arg, eine Funktion, die einen String-Wert annimmt, der das Argument darstellt, und t
, ein Objekt, das alle verschiedenen Datentypen enthält, die Cadence hat. So können wir arg
mitteilen, wie das von uns übergebene Argument kodiert und dekodiert werden soll.
Während unsere vorherigen Funktionen nur „schreibgeschützt“ waren, werden unsere nächsten Aktionen haben, die den Blockchain-Status ändern und in ihn schreiben können; auch bekannt als „Mint an NFT“.
Dazu schreiben wir ein weiteres Cadence-Skript als Konstante.
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) } }
Fügen Sie nun die folgende Funktion nach dem Transaktionscode zur Datei page.js
hinzu.
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); }
Was die Funktion betrifft, ist die Syntax fcl.mutate
dieselbe wie die fcl.query
. Wir stellen jedoch mehrere zusätzliche Parameter zur Verfügung, wie zum Beispiel die folgenden:
payer: fcl.authz, proposer: fcl.authz, authorizations: [fcl.authz], limit: 50,
fcl.authz
bezieht sich auf das aktuell verbundene Konto.limit
ist wie gasLimit in der Ethereum-Welt, das eine Obergrenze für den maximalen Rechenaufwand festlegt. Wenn die Berechnung den Grenzwert überschreitet, schlägt die Transaktion fehl.
Wir müssen eine weitere Funktion hinzufügen, die die gerade erstellte mintNFT
-Funktion aufruft und verarbeitet.
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.'); } };
Nachdem wir unsere Hauptfunktionen eingerichtet haben, können wir sie nun in unsere Benutzeroberfläche integrieren.
Bevor wir das tun, fügen wir jedoch einige useEffect
Aufrufe hinzu, um das Laden des Anfangszustands zu erleichtern. Sie können diese direkt über dem bereits vorhandenen useEffect
Aufruf hinzufügen.
useEffect(() => { checkCollectionInit(); viewNFT(); }, [currentUser]); useEffect(() => { if (currentUser.loggedIn) { setCollectiblesList(collectiblesList); console.log('Setting collectibles...'); } }, [currentUser]);
Zurück in unserem return
mit der Benutzeroberfläche können wir unsere Funktionen den entsprechenden Teilen der App hinzufügen.
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> );
Überprüfen Sie den endgültigen Code hier .
Jetzt, da die App fertig ist, gehen wir durch die Bedienung!
Verbinden Sie zunächst Ihr Wallet, indem Sie oben rechts auf die Schaltfläche „Wallet verbinden“ klicken.
Jetzt können Sie einen NFT prägen! Geben Sie den Namen Ihres NFT ein und fügen Sie einen Link zu dem Bild ein, das Sie verwenden möchten. Nachdem Sie auf „Mint“ geklickt haben, werden Sie aufgefordert, eine Transaktion mit Ihrem Wallet zu unterzeichnen.
Es kann eine Weile dauern, bis die Transaktion abgeschlossen ist. Nach Abschluss des Vorgangs sollten Sie auf die untere Schaltfläche klicken können, um die IDs Ihrer NFTs anzuzeigen. Wenn dies Ihr erster ist, sollte die ID nur „1“ sein.
Jetzt können Sie die ID Ihres NFT kopieren, in den Abschnitt „Ansicht“ einfügen und auf „NFT anzeigen“ klicken.
Gut gemacht! Sie haben Teil 2 des Collectibles-Portalprojekts abgeschlossen. Zusammenfassend haben wir uns auf den Aufbau des Frontends unseres Sammlerportals konzentriert.
Wir haben dies getan, indem wir:
Ich wünsche Ihnen einen wirklich tollen Tag!
Auch hier veröffentlicht.