Koleksiyon portalınızı oluşturmanın son adımına hoş geldiniz! (bölüm 1 için buraya bakın)
Bu bölümde bulmacanın son parçası olan ön ucu oluşturmaya odaklanacağız.
İşte başaracağımız şey:
Ön ucu oluşturmak için Next.js'yi kullanacağız.
Başlayalım!
Proje flow-collectible-portal
dizininizi açın. O zaman koş
Terminalde npx create-next-app@latest frontend
ve enter
tuşuna basın.
Bu size çeşitli seçenekler sunacaktır. Bu eğitimde Typescript , ESLint veya TailwindCSS kullanmayacağız ve bu makalenin yazıldığı sırada src
dizinini ve App router'ı kullanacağız.
Artık yeni bir web uygulamanız hazır.
Ön uç klasörünüz şu şekilde görünür:
Flow blockchain ile etkileşime geçmek için cüzdan bağlantılarını yönetmek, komut dosyalarını çalıştırmak ve uygulamamızdaki işlemleri göndermek için Flow Client Library'yi (FCL) kullanacağız. Tam Cadence fonksiyonlarını yazmamıza ve bunları Javascript fonksiyonları olarak çalıştırmamıza izin verecektir.
Başlamak için aşağıdaki komutu çalıştırarak uygulamamız için FCL'yi yükleyelim:
npm install @onflow/fcl --save
FCL'yi kurduktan sonra yapılandırmamız gerekiyor. İşte yapmanız gerekenler:
app
klasörünün içinde flow
adında yeni bir klasör oluşturun ve config.js
adlı bir dosya ekleyin.
Aşağıdaki kodu config.js
dosyasına ekleyin:
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", });
Artık uygulamanızda FCL'yi kullanmaya hazırsınız.
Bir uygulamada kullanıcının kimliğini doğrulamak için çeşitli işlevleri kullanabilirsiniz:
fcl.logIn()
öğesini çağırın.fcl.signUp()
öğesini arayın.fcl.unauthenticate()
öğesini çağırın.
Bu fcl
işlevlerini ön uçta nasıl uygulayabileceğimizi öğrenelim.
Öncelikle aşağıdaki kodu app dizini içerisindeki page.js
dosyamıza ekleyeceğiz. Bu, bazı bağımlılıkları içe aktaracak, uygulamamızın bazı bölümleri için bazı başlangıç useState
ayarlayacak ve temel bir kullanıcı arayüzü oluşturacaktır.
Güzel göründüğünden emin olmak için uygulama dizini içindeki page.module.css
dosyasını silin ve bunun yerine page.css adında bir dosya oluşturun. Daha sonra bu dosyanın içeriğini içine yapıştırın. Artık başlangıç sayfamızı yazabiliriz.
"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> ); }
Bu kodu ekledikten sonra her şeyin doğru şekilde yüklendiğinden emin olmak için npm run dev
çalıştırın.
Flow blok zincirini sorgulamak için fcl
nasıl kullanabileceğimizi derinlemesine incelemeden önce, bu Cadence komut dosyası kodlarını page.js
dosyasındaki handleInput
işlevinden sonra ekleyin.
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) }
Cadence betiklerimiz kullanıma hazır olduğundan, artık bazı Javascript fonksiyonlarını bildirebilir ve Cadence sabitlerini `fcl`
sorgularına aktarabiliriz.
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); }
Şimdi yazdığımız tüm fonksiyonlara bir göz atalım. Dikkat edilmesi gereken iki şey var:
fcl.query
args: (arg,t) => [arg(addr,t.Address)],
satır.
Komut dosyaları, Solidity'deki view
işlevlerine benzer olduğundan ve çalıştırılması için herhangi bir gas ücreti gerektirmediğinden, aslında sadece blok zincirini sorguluyoruz. Bu yüzden Flow'da komut dosyalarını çalıştırmak için fcl.query
kullanıyoruz.
Bir şeyi sorgulamak için bir argüman iletmeliyiz. Bunun için argümanı temsil eden bir dize değeri alan bir fonksiyon olan arg'yi ve Cadence'in sahip olduğu tüm farklı veri türlerini içeren bir nesne olan t
kullanıyoruz. Böylece arg
ilettiğimiz argümanın nasıl kodlanacağını ve kodunun nasıl çözüleceğini anlatabiliriz.
Önceki işlevlerimiz yalnızca "salt okunur" iken, sonraki işlevlerimizde blockchain durumunu değiştirebilecek ve ona yazabilecek eylemler olacak; diğer adıyla "bir NFT nane."
Bunu yapmak için sabit olarak başka bir Cadence betiği yazacağız.
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) } }
Şimdi page.js
dosyasına işlem kodundan sonra aşağıdaki fonksiyonu ekleyin.
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); }
İşleve gelince, fcl.mutate
sözdizimi fcl.query
ile aynıdır. Ancak aşağıdakiler gibi birkaç ekstra parametre sağlıyoruz:
payer: fcl.authz, proposer: fcl.authz, authorizations: [fcl.authz], limit: 50,
fcl.authz
şu anda bağlı olan hesabı ifade eder.limit
, Ethereum dünyasındaki maksimum hesaplama miktarına bir üst sınır koyan gasLimit gibidir. Hesaplama sınırı aşarsa işlem başarısız olur.
Az önce yaptığımız mintNFT
fonksiyonunu çağıracak ve işleyecek bir fonksiyon daha eklememiz gerekecek.
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.'); } };
Ana işlevlerimiz mevcut olduğundan artık bunları kullanıcı arayüzümüze ekleyebiliriz.
Ancak bunu yapmadan önce başlangıç durumunun yüklenmesine yardımcı olmak için bazı useEffect
çağrıları ekleyeceğiz. Bunları mevcut useEffect
çağrısının hemen üstüne ekleyebilirsiniz.
useEffect(() => { checkCollectionInit(); viewNFT(); }, [currentUser]); useEffect(() => { if (currentUser.loggedIn) { setCollectiblesList(collectiblesList); console.log('Setting collectibles...'); } }, [currentUser]);
Artık kullanıcı arayüzü ile return
bölümümüze döndüğümüzde, fonksiyonlarımızı uygulamanın uygun bölümlerine ekleyebiliriz.
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> );
Buradaki son kodu kontrol edin.
Artık uygulama tamamlandıktan sonra nasıl kullanılacağını inceleyelim!
Öncelikle sağ üstteki “Cüzdanı Bağla” butonuna tıklayarak cüzdanınızı bağlayın.
Artık bir NFT üretebilirsiniz! NFT'nizin adını girin ve kullanmak istediğiniz görselin bağlantısını yapıştırın. "Nane"ye tıkladıktan sonra cüzdanınızla bir işlem imzalamanız istenecektir.
İşlemin tamamlanması biraz zaman alabilir. Tamamlandıktan sonra, NFT'lerinizin kimliklerini görüntülemek için alt düğmeye tıklayabilmeniz gerekir. Bu sizin ilkinizse, kimlik yalnızca “1” olmalıdır.
Artık NFT'nizin kimliğini kopyalayıp Görünüm bölümüne yapıştırabilir ve "NFT'yi Görüntüle"ye tıklayabilirsiniz.
Tebrikler! Koleksiyon portalı projesinin 2. bölümünü tamamladınız. Özetle, Koleksiyon portalımızın ön yüzünü oluşturmaya odaklandık.
Bunu şu şekilde yaptık:
Gerçekten harika bir gün geçirin!