The best way to Construct a Winning NFT Market with React, Solidity, and CometChat

What you’ll be development, see a demo at the Rinkeby take a look at community and git repo right here…

Creation

Web3 is the web’s long run, and we will have to all be informed and embody this generation. Its use-cases proceed to emerge and feature a good have an effect on at the global.

One magnificent utility of web3 generation is the advent of non-fungible tokens (NFTs), that are a viable answer for digitalizing property whilst keeping possession rights.

This instructional will train you the way to create a ecocnomic and well-designed NFT market with chat capability.

Guide your personal categories with me if you want anyone that can assist you be informed web3 construction sooner.

With that stated, let’s get began…

Take a look at my Youtube channel for FREE web3 tutorials now.

Prerequisite

You are going to want the next gear put in to effectively overwhelm this construct:

  • Node
  • Ganache-Cli
  • Truffle
  • React
  • Infuria
  • Tailwind CSS
  • CometChat SDK
  • Metamask
  • Yarn

Putting in Dependencies

NodeJs Set up
Take a look at that NodeJs is already put in to your gadget, and if no longer, set up it from HERE. Run the code once more at the terminal to verify it’s put in.

Yarn, Ganache-cli and Truffle Set up
Run the instructions beneath to your terminal to put in those essential applications globally.

npm i -g yarn
npm i -g truffle
npm i -g ganache-cli

To verify set up, input the next code into the terminal.

yarn --version && ganache-cli --version && truffle edition

Cloning Web3 Starter Undertaking
Clone the internet 3.0 starter venture the use of the instructions beneath. This guarantees that we are all at the similar web page and the use of the similar instrument.

git clone https://github.com/Daltonic/timelessNFT

Very good, please change the **bundle.json** record with the next:

{
  "title": "TimelessNFT",
  "personal": true,
  "edition": "0.0.0",
  "scripts": {
    "delivery": "react-app-rewired delivery",
    "construct": "react-app-rewired construct",
    "take a look at": "react-app-rewired take a look at",
    "eject": "react-scripts eject"
  },
  "dependencies": {
    "@cometchat-pro/chat": "^3.0.9",
    "ipfs-http-client": "^56.0.0",
    "second": "^2.29.4",
    "moment-timezone": "^0.5.34",
    "react": "^17.0.2",
    "react-dom": "^17.0.2",
    "react-hooks-global-state": "^1.0.2",
    "react-icons": "^4.3.1",
    "react-identicons": "^1.2.5",
    "react-moment": "^1.1.2",
    "react-router-dom": "6",
    "react-scripts": "5.0.0",
    "web-vitals": "^2.1.4",
    "web3": "^1.7.1"
  },
  "devDependencies": {
    "@faker-js/faker": "^6.0.0-alpha.5",
    "@openzeppelin/contracts": "^4.5.0",
    "@tailwindcss/paperwork": "0.4.0",
    "@truffle/hdwallet-provider": "^2.0.4",
    "assert": "^2.0.0",
    "autoprefixer": "10.4.2",
    "babel-polyfill": "^6.26.0",
    "babel-preset-env": "^1.7.0",
    "babel-preset-es2015": "^6.24.1",
    "babel-preset-stage-2": "^6.24.1",
    "babel-preset-stage-3": "^6.24.1",
    "babel-register": "^6.26.0",
    "buffer": "^6.0.3",
    "chai": "^4.3.6",
    "chai-as-promised": "^7.1.1",
    "crypto-browserify": "^3.12.0",
    "dotenv": "^16.0.0",
    "https-browserify": "^1.0.0",
    "mnemonics": "^1.1.3",
    "os-browserify": "^0.3.0",
    "postcss": "8.4.5",
    "procedure": "^0.11.10",
    "react-app-rewired": "^2.1.11",
    "sharp": "^0.30.1",
    "stream-browserify": "^3.0.0",
    "stream-http": "^3.2.0",
    "tailwindcss": "3.0.18",
    "url": "^0.11.0"
  },
  "browserslist": {
    "manufacturing": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "construction": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

After changing the applications as directed, run **yarn set up** to your terminal to load the entire applications with the desired variations.

Configuring CometChat SDK

Practice the stairs beneath to configure the CometChat SDK; on the finish, you will have to save those keys as an atmosphere variable.

STEP 1:
Head to CometChat Dashboard and create an account.

STEP 2:
Log in to the CometChat dashboard, solely after registering.

STEP 3:
From the dashboard, upload a brand new app known as timelessNFT.

STEP 4:
Make a selection the app you simply constituted of the record.

STEP 5:
From the Fast Get started reproduction the APP_ID, REGION, and AUTH_KEY, in your .env record. See the picture and code snippet.

Substitute the REACT_COMET_CHAT placeholders keys with their suitable values.

REACT_APP_COMET_CHAT_REGION=**
REACT_APP_COMET_CHAT_APP_ID=**************
REACT_APP_COMET_CHAT_AUTH_KEY=******************************

Configuring Infuria App

STEP 1:
Head to Infuria, and create an account.

STEP 2:
From the dashboard create a brand new venture.

STEP 3:
Reproduction the Rinkeby take a look at community WebSocket endpoint URL in your .env record.

After that, input your Metamask secret word and most popular account’s personal key. Should you adopted the directions as it should be, your surroundings variables will have to now appear to be this.

ENDPOINT_URL=***************************
DEPLOYER_KEY=**********************

REACT_APP_COMET_CHAT_REGION=**
REACT_APP_COMET_CHAT_APP_ID=**************
REACT_APP_COMET_CHAT_AUTH_KEY=******************************

See the phase beneath if you do not know the way to get entry to your personal key.

Having access to Your Metamask Personal Key

STEP 1:
Ensure that Rinkeby is chosen because the take a look at community to your Metamask browser extension. Then, on the most well liked account, click on the vertical dotted line and make a choice account main points. Please see the picture beneath.

STEP 2:
Input your password at the box equipped and click on the ascertain button, this may enable you get entry to your account personal key.

STEP 3:
Click on on “export personal key” to peer your personal key. You should definitely by no means disclose your keys on a public web page equivalent to Github. This is the reason we’re appending it as an atmosphere variable.

STEP 4:
Reproduction your personal key in your .env record. See the picture and code snippet beneath.

ENDPOINT_URL=***************************
SECRET_KEY=******************
DEPLOYER_KEY=**********************

REACT_APP_COMET_CHAT_REGION=**
REACT_APP_COMET_CHAT_APP_ID=**************
REACT_APP_COMET_CHAT_AUTH_KEY=******************************

As on your SECRET_KEY, you might be required to stick your Metamask secret word within the area equipped within the surroundings record.

The TimlessNFT Sensible Contract

Here’s all the good contract code; Let’s pass over the entire purposes and variables one after the other.

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;

import "./ERC721.sol";
import "./ERC721Enumerable.sol";
import "@openzeppelin/contracts/get entry to/Ownable.sol";

contract TimelessNFT is ERC721Enumerable, Ownable {
    the use of Strings for uint256;

    mapping(string => uint8) existingURIs;
    mapping(uint256 => cope with) public holderOf;

    cope with public artist;
    uint256 public royalityFee;
    uint256 public provide = 0;
    uint256 public totalTx = 0;
    uint256 public value = 0.01 ether;

    match Sale(
        uint256 identification,
        cope with listed proprietor,
        uint256 value,
        string metadataURI,
        uint256 timestamp
    );

    struct TransactionStruct {
        uint256 identification;
        cope with proprietor;
        uint256 value;
        string identify;
        string description;
        string metadataURI;
        uint256 timestamp;
    }

    TransactionStruct[] transactions;
    TransactionStruct[] minted;

    constructor(
        string reminiscence _name,
        string reminiscence _symbol,
        uint256 _royalityFee,
        cope with _artist
    ) ERC721(_name, _symbol) {
        royalityFee = _royalityFee;
        artist = _artist;
    }

    serve as payToMint(
        string reminiscence identify,
        string reminiscence description,
        string reminiscence metadataURI,
        uint256 salesPrice
    ) exterior payable {
        require(msg.worth >= value, "Ether too low for minting!");
        require(existingURIs[metadataURI] == 0, "This NFT is already minted!");
        require(msg.sender != proprietor(), "Gross sales no longer allowed!");
        

        uint256 royality = (msg.worth * royalityFee) / 100;
        payTo(artist, royality);
        payTo(proprietor(), (msg.worth - royality));

        provide++;

        minted.push(
            TransactionStruct(
                provide,
                msg.sender,
                salesPrice,
                identify,
                description,
                metadataURI,
                block.timestamp
            )
        );

        emit Sale(
            provide,
            msg.sender,
            msg.worth,
            metadataURI,
            block.timestamp
        );

        _safeMint(msg.sender, provide);
        existingURIs[metadataURI] = 1;
        holderOf[supply] = msg.sender;
    }

    serve as payToBuy(uint256 identification) exterior payable {
        require(msg.worth >= minted[id - 1].value, "Ether too low for acquire!");
        require(msg.sender != minted[id - 1].proprietor, "Operation No longer Allowed!");

        uint256 royality = (msg.worth * royalityFee) / 100;
        payTo(artist, royality);
        payTo(minted[id - 1].proprietor, (msg.worth - royality));

        totalTx++;

        transactions.push(
            TransactionStruct(
                totalTx,
                msg.sender,
                msg.worth,
                minted[id - 1].identify,
                minted[id - 1].description,
                minted[id - 1].metadataURI,
                block.timestamp
            )
        );

        emit Sale(
            totalTx,
            msg.sender,
            msg.worth,
            minted[id - 1].metadataURI,
            block.timestamp
        );

        minted[id - 1].proprietor = msg.sender;
    }

    serve as changePrice(uint256 identification, uint256 newPrice) exterior returns (bool) {
        require(newPrice > 0 ether, "Ether too low!");
        require(msg.sender == minted[id - 1].proprietor, "Operation No longer Allowed!");

        minted[id - 1].value = newPrice;
        go back true;
    }

    serve as payTo(cope with to, uint256 quantity) inner {
        (bool good fortune, ) = payable(to).name{worth: quantity}("");
        require(good fortune);
    }

    serve as getAllNFTs() exterior view returns (TransactionStruct[] reminiscence) {
        go back minted;
    }

    serve as getNFT(uint256 identification) exterior view returns (TransactionStruct reminiscence) {
        go back minted[id - 1];
    }

    serve as getAllTransactions() exterior view returns (TransactionStruct[] reminiscence) {
        go back transactions;
    }
}

Code imports and contract data
Within the code beneath, we knowledgeable the solidity compiler of the license identifier and compiler variations certified to collect this code.

// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;

import "./ERC721.sol";
import "./ERC721Enumerable.sol";
import "@openzeppelin/contracts/get entry to/Ownable.sol";

contract TimelessNFT is ERC721Enumerable, Ownable {
  // codes is going in right here...
}

Additionally, this good contract makes use of a few openzepplin's ERC721 good contracts. You need to just be sure you put them in the similar listing as observed within the symbol beneath.

Seek advice from this hyperlink and obtain those good contracts as proven within the symbol above.

State variables declarations

the use of Strings for uint256;

mapping(string => uint8) existingURIs;
mapping(uint256 => cope with) public holderOf;

cope with public artist;
uint256 public royalityFee;
uint256 public provide = 0;
uint256 public totalTx = 0;
uint256 public value = 0.01 ether;

We specified that we’re the use of the string library for appearing uint to string operations. Subsequent, we additionally declared mappings for recording minted NFT works of art and likewise for figuring out the present proprietor of a token.

Then we specified the opposite crucial variables for shooting the artist account, the royalty charges, the present provide, general transactions which were made at the platform, and the mint value of an NFT.

Putting in occasions and constructions

match Sale(
    uint256 identification,
    cope with listed proprietor,
    uint256 value,
    string metadataURI,
    uint256 timestamp
);

struct TransactionStruct {
    uint256 identification;
    cope with proprietor;
    uint256 value;
    string identify;
    string description;
    string metadataURI;
    uint256 timestamp;
}

TransactionStruct[] transactions;
TransactionStruct[] minted;

Within the previous code, we’ve a gross sales match that emits knowledge from any transaction that happens at the good contract, if it is on minting or NFT switch.

We designed a transaction construction to assemble knowledge about minted or transferred NFTs. The use of the transaction construction we outlined, we created two variables known as transactions and minted.

Initializing the constructor

constructor(
    string reminiscence _name,
    string reminiscence _symbol,
    uint256 _royalityFee,
    cope with _artist
) ERC721(_name, _symbol) {
    royalityFee = _royalityFee;
    artist = _artist;
}

The constructor takes in 4 parameters for initializing the good contract. A token title, image, an artist account, and a royalty rate consistent with transaction. The token title and image are then handed into the ERC721 good contract all over deployment.

The mint serve as set of rules

serve as payToMint(
    string reminiscence identify,
    string reminiscence description,
    string reminiscence metadataURI,
    uint256 salesPrice
) exterior payable {
    require(msg.worth >= value, "Ether too low for minting!");
    require(existingURIs[metadataURI] == 0, "This NFT is already minted!");
    require(msg.sender != proprietor(), "Gross sales no longer allowed!");
    

    uint256 royality = (msg.worth * royalityFee) / 100;
    payTo(artist, royality);
    payTo(proprietor(), (msg.worth - royality));

    provide++;

    minted.push(
        TransactionStruct(
            provide,
            msg.sender,
            salesPrice,
            identify,
            description,
            metadataURI,
            block.timestamp
        )
    );

    emit Sale(
        provide,
        msg.sender,
        msg.worth,
        metadataURI,
        block.timestamp
    );

    _safeMint(msg.sender, provide);
    existingURIs[metadataURI] = 1;
    holderOf[supply] = msg.sender;
}

The above serve as is answerable for minting new tokens at the good contract. The caller of this system will have to supply 4 parameters which come with; an NFT identify, description, metadata URI, and the promoting value of the NFT after minting.

Validations are performed to be sure that the NFT is minting and are finished accordingly with bills made for every minting. Additionally, the validation guarantees that every paintings is uniquely connected with a token, and no different token bears the similar paintings. Finally for the validation, we made certain that the caller of this system isn’t the deployer of the good contract, that is to verify we don’t combine up issues too unhealthy.

Subsequent within the serve as is the fee sharing rule. The royalty proportion is going to the artist and the remainder of the ethers is going to the landlord.

In a while, we recorded that NFT within the minted array and emitted a gross sales match. Finally, we minted the NFT whilst recording the caller’s cope with as the landlord of the token.

The NFT switch serve as set of rules

serve as payToBuy(uint256 identification) exterior payable {
    require(msg.worth >= minted[id - 1].value, "Ether too low for acquire!");
    require(msg.sender != minted[id - 1].proprietor, "Operation No longer Allowed!");

    uint256 royality = (msg.worth * royalityFee) / 100;
    payTo(artist, royality);
    payTo(minted[id - 1].proprietor, (msg.worth - royality));

    totalTx++;

    transactions.push(
        TransactionStruct(
            totalTx,
            msg.sender,
            msg.worth,
            minted[id - 1].identify,
            minted[id - 1].description,
            minted[id - 1].metadataURI,
            block.timestamp
        )
    );

    emit Sale(
        totalTx,
        msg.sender,
        msg.worth,
        minted[id - 1].metadataURI,
        block.timestamp
    );

    minted[id - 1].proprietor = msg.sender;
}

The above serve as takes an NFT identification and makes a purchase order of the NFT consistent with the set value by means of the minter (proprietor).

Important validations are finished to impede homeowners from purchasing their NFTs and others from purchasing with 0 ethers.

Subsequent, a royalty rate is shipped to the artist account and the present proprietor of the NFT will get to stay the remaining.

Every token switch is recorded in a transactions array to stay monitor of all transactions finished at the platform.

In a while, a gross sales match is once more emitted for this acquire to complement the logged knowledge at the EVM.

Different Crucial purposes

// adjustments the cost of an NFT
serve as changePrice(uint256 identification, uint256 newPrice) exterior returns (bool) {
    require(newPrice > 0 ether, "Ether too low!");
    require(msg.sender == minted[id - 1].proprietor, "Operation No longer Allowed!");

    minted[id - 1].value = newPrice;
    go back true;
}

// sends ethers to a particular account
serve as payTo(cope with to, uint256 quantity) inner {
    (bool good fortune, ) = payable(to).name{worth: quantity}("");
    require(good fortune);
}

// returns all minted NFTs
serve as getAllNFTs() exterior view returns (TransactionStruct[] reminiscence) {
    go back minted;
}

// returns a particular NFT by means of token identification
serve as getNFT(uint256 identification) exterior view returns (TransactionStruct reminiscence) {
    go back minted[id - 1];
}

// returns all transactions
serve as getAllTransactions() exterior view returns (TransactionStruct[] reminiscence) {
    go back transactions;
}

And there you might have it for growing the good contract, we will be able to subsequent dive into development the UI elements with ReactJs.

Configuring the Deployment Script

Yet one more factor to do with the good contract is to configure the deployment script.

At the venture head to the migrations folder >> 2_deploy_contracts.js and replace it with the code snippet beneath.

const TimelessNFT = artifacts.require('TimelessNFT')

module.exports = async (deployer) => {
  const accounts = anticipate web3.eth.getAccounts()
  anticipate deployer.deploy(
    TimelessNFT, 
    'Undying NFTs', 
    'TNT', 10, accounts[1]
  )
}

Excellent, we simply completed the good contract for our utility; now it is time to get began at the DApp interface. If you want a personal tutor that can assist you be informed good contract construction, e-book your categories with me.

Growing the Frontend

The entrance finish is made up of a lot of elements and portions. The entire elements, perspectives, and peripherals might be created by means of us.

Header Part

This element was once created with tailwind CSS and makes use of the red Attach Pockets button to get entry to the Metamask pockets. The codes beneath exhibit the programming.

import { useGlobalState } from '../retailer'
import timelessLogo from '../property/undying.png'
import { connectWallet } from '../TimelessNFT'

const Header = () => {
  const [connectedAccount] = useGlobalState('connectedAccount')

  go back (
    <nav className="w-4/5 flex md:justify-center justify-between items-center py-4 mx-auto">
      <div className="md:flex-[0.5] flex-initial justify-center items-center">
        <img
          className="w-32 cursor-pointer"
          src={timelessLogo}
          alt="Undying Brand"
        />
      </div>

      <ul
        className="md:flex-[0.5] text-white md:flex
        hidden list-none flex-row justify-between 
        items-center flex-initial"
      >
        <li className="mx-4 cursor-pointer">Marketplace</li>
        <li className="mx-4 cursor-pointer">Artist</li>
        <li className="mx-4 cursor-pointer">Options</li>
        <li className="mx-4 cursor-pointer">Neighborhood</li>
      </ul>

      {!connectedAccount ? (
        <button
          className="shadow-xl shadow-black text-white
        bg-[#e32970] hover:bg-[#bd255f] md:text-xs p-2
          rounded-full cursor-pointer"
          onClick={connectWallet}
        >
          Attach Pockets
        </button>
      ) : (
        <></>
      )}
    </nav>
  )
}

export default Header

Hero Part

This element is answerable for showing the hooked up pockets and likewise for launching the modal used for growing a brand new NFT. Moreover, it’s answerable for signing in or up customers for one-on-one chats with a dealer of an NFT. Here’s the code answerable for those movements.

import Identicon from 'react-identicons'
import { setGlobalState, useGlobalState, truncate } from '../retailer'
import { getConversations, loginWithCometChat, signUpWithCometChat } from '../CometChat'
import ChatList from './ChatList'
import { useState } from 'react'

const Hero = () => {
  const [connectedAccount] = useGlobalState('connectedAccount')
  const [currentUser] = useGlobalState('currentUser')
  const [recentOpened] = useGlobalState('recentOpened')
  const [conversations, setConversations] = useState([])

  const onCreatedNFT = () => {
    if (currentUser?.uid.toLowerCase() != connectedAccount.toLowerCase())
      go back alert('Please login to obtain chats from patrons!')

    setGlobalState('modal', 'scale-100')
  }

  const onLunchRecent = () => {
    getConversations().then((convs) => {
      setConversations(convs)
      setGlobalState('recentOpened', true)
    })
  }

  go back (
    <div
      className="flex flex-col md:flex-row w-4/5 justify-between 
      items-center mx-auto py-10"
    >
      <div className="md:w-3/6 w-full">
        <div>
          <h1 className="text-white text-5xl font-bold">
            Purchase and Promote <br /> Virtual Arts, <br />
            <span className="text-gradient">NFTs</span> Collections
          </h1>
          <p className="text-gray-500 font-semibold text-sm mt-3">
            Mint and accumulate the most up to date NFTs round.
          </p>
        </div>

        <div className="flex flex-row mt-5">
          {connectedAccount ? (
            <>
              <button
                className="shadow-xl shadow-black text-white
                bg-[#e32970] hover:bg-[#bd255f]
                rounded-full cursor-pointer p-2"
                onClick={onCreatedNFT}
              >
                Create NFT
              </button>
              <>
                {currentUser?.uid.toLowerCase() ==
                connectedAccount.toLowerCase() ? (
                  <button
                    className="text-white border border-gray-500 
                    hover:border-[#e32970] hover:bg-[#bd255f] cursor-pointer 
                    rounded-full p-2 mx-3"
                    onClick={onLunchRecent}
                  >
                    Contemporary Chats
                  </button>
                ) : (
                  <>
                    <button
                      className="text-white border border-gray-500 
                    hover:border-[#e32970] hover:bg-[#bd255f] cursor-pointer 
                    rounded-full p-2 mx-3"
                      onClick={() => loginWithCometChat(connectedAccount)}
                    >
                      Login for Chat
                    </button>
                    <button
                      className="text-white border border-gray-500 
                    hover:border-[#e32970] hover:bg-[#bd255f] cursor-pointer 
                    rounded-full p-2 mx-3"
                      onClick={() => signUpWithCometChat(connectedAccount, connectedAccount)}
                    >
                      Signup for Chat
                    </button>
                  </>
                )}
              </>
            </>
          ) : null}
        </div>

        <div className="w-3/4 flex justify-between items-center mt-5">
          <div>
            <p className="text-white font-bold">1231k</p>
            <small className="text-gray-300">Person</small>
          </div>
          <div>
            <p className="text-white font-bold">152k</p>
            <small className="text-gray-300">Paintings</small>
          </div>
          <div>
            <p className="text-white font-bold">200k</p>
            <small className="text-gray-300">Artist</small>
          </div>
        </div>
      </div>

      <div
        className="shadow-xl shadow-black md:w-2/5 w-full 
      mt-10 md:mt-0 rounded-md overflow-hidden bg-gray-800"
      >
        <img
          src="https://pictures.cointelegraph.com/pictures/1434_aHR0cHM6Ly9zMy5jb2ludGVsZWdyYXBoLmNvbS91cGxvYWRzLzIwMjEtMDYvNGE4NmNmOWQtODM2Mi00YmVhLThiMzctZDEyODAxNjUxZTE1LmpwZWc=.jpg"
          alt="NFT Artwork"
          className="h-60 w-full object-cover"
        />
        <div className="flex justify-start items-center p-3">
          <Identicon
            string={
              connectedAccount
                ? connectedAccount.toLowerCase()
                : 'Attach Your Pockets'
            }
            dimension={50}
            className="h-10 w-10 object-contain rounded-full mr-3"
          />
          <div>
            <p className="text-white font-semibold">
              {connectedAccount
                ? truncate(connectedAccount, 4, 4, 11)
                : 'Attach Your Pockets'}
            </p>
            <small className="text-pink-800 font-bold">@you</small>
          </div>
        </div>
      </div>

      {recentOpened ? <ChatList customers={conversations} /> : null}
    </div>
  )
}

export default Hero

Artistic endeavors element

This element is answerable for rendering the record of NFTs minted at the platform the use of the superbly crafted tailwind CSS playing cards. Every card has an NFT symbol, identify, description, value, and proprietor. See the codes beneath for its implementation.

import { useEffect, useState } from 'react'
import { setGlobalState, useGlobalState, truncate } from '../retailer'

const Artistic endeavors = () => {
  const [nfts] = useGlobalState('nfts')
  const [end, setEnd] = useState(4)
  const [count] = useState(4)
  const [collection, setCollection] = useState([])

  const setNFT = (nft) => {
    setGlobalState('nft', nft)
    setGlobalState('showModal', 'scale-100')
  }

  const getCollection = () => {
    go back nfts.slice(0, finish)
  }

  useEffect(() => {
    setCollection(getCollection())
  }, [nfts, end])
  

  go back (
    <div className="bg-[#151c25] gradient-bg-artworks">
      <div className="w-4/5 py-10 mx-auto">
        <h4 className="text-white text-3xl font-bold uppercase text-gradient">
          {assortment.duration > 0 ? 'Newest Artistic endeavors' : 'No Artistic endeavors But'}
        </h4>

        <div className="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-6 md:gap-4 lg:gap-3 py-2.5">
          {assortment.map((nft) => (
            <div
              key={nft.identification}
              className="w-full shadow-xl shadow-black rounded-md overflow-hidden bg-gray-800 my-2 p-3"
            >
              <img
                src={nft.metadataURI}
                alt={truncate(nft.identify, 6)}
                className="h-60 w-full object-cover shadow-lg shadow-black rounded-lg mb-3"
              />
              <h4 className="text-white font-semibold">{nft.identify}</h4>
              <p className="text-gray-400 text-xs my-1">
                {truncate(nft.description)}
              </p>
              <div className="flex justify-between items-center mt-3 text-white">
                <div className="flex flex-col">
                  <small className="text-xs">Present Worth</small>
                  <p className="text-sm font-semibold">{nft.value} ETH</p>
                </div>

                <button
                  onClick={() => setNFT(nft)}
                  className="shadow-lg shadow-black text-white text-sm bg-[#e32970] hover:bg-[#bd255f] cursor-pointer rounded-full px-1.5 py-1"
                >
                  View Main points
                </button>
              </div>
            </div>
          ))}
        </div>

        {assortment.duration > 0 && nfts.duration > assortment.duration ? (
          <div className="text-center my-5">
            <button
              className="shadow-xl shadow-black text-white
            bg-[#e32970] hover:bg-[#bd255f]
            rounded-full cursor-pointer p-2"
            onClick={() => setEnd(finish + rely)}
            >
              Load Extra
            </button>
          </div>
        ) : null}
      </div>
    </div>
  )
}

export default Artistic endeavors

Transactions Part

This element is answerable for rendering the entire transactions that happened in our good contract. A transaction for instance could be Alison buying an NFT from Duke. This acquire might be captured on this element as a transaction. See the snippet beneath.

import { useEffect, useState } from 'react'
import { BiTransfer } from 'react-icons/bi'
import { MdOpenInNew } from 'react-icons/md'
import { useGlobalState, truncate } from '../retailer'

const Transactions = () => {
  const [transactions] = useGlobalState('transactions')
  const [end, setEnd] = useState(3)
  const [count] = useState(3)
  const [collection, setCollection] = useState([])

  const getCollection = () => {
    go back transactions.slice(0, finish)
  }

  useEffect(() => {
    setCollection(getCollection())
  }, [transactions, end])

  go back (
    <div className="bg-[#151c25]">
      <div className="w-4/5 py-10 mx-auto">
        <h4 className="text-white text-3xl font-bold uppercase text-gradient">
          {assortment.duration > 0 ? 'Newest Transactions' : 'No Transaction But'}
        </h4>

        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-4 lg:gap-2 py-2.5">
          {assortment
            .map((tx) => (
              <div
                key={tx.identification}
                className="flex justify-between items-center border border-pink-500 text-gray-400 w-full shadow-xl shadow-black rounded-md overflow-hidden bg-gray-800 my-2 p-3"
              >
                <div className="rounded-md shadow-sm shadow-pink-500 p-2">
                  <BiTransfer />
                </div>

                <div>
                  <h4 className="text-sm">{tx.identify} Transfered</h4>
                  <small className="flex flex-row justify-start items-center">
                    <span className="mr-1">Won by means of</span>
                    <a href="#" className="text-pink-500 mr-2">
                      {truncate(tx.proprietor, 4, 4, 11)}
                    </a>
                    <a href="#">
                      <MdOpenInNew />
                    </a>
                  </small>
                </div>

                <p className="text-sm font-medium">{tx.value}ETH</p>
              </div>
            ))}
        </div>

        {assortment.duration > 0 && transactions.duration > assortment.duration ? (
          <div className="text-center my-5">
            <button
              className="shadow-xl shadow-black text-white
            bg-[#e32970] hover:bg-[#bd255f]
            rounded-full cursor-pointer p-2"
            onClick={() => setEnd(finish + rely)}
            >
              Load Extra
            </button>
          </div>
        ) : null}
      </div>
    </div>
  )
}

export default Transactions

Footer Part

This element merely presentations some stunning hyperlinks on the backside of the web page, it doesn’t do a lot on the subject of functionalities however enhances the consumer interface. Its codes are written beneath.

import timelessLogo from '../property/undying.png'

const Footer = () => (
  <div className="w-full flex md:justify-center justify-between items-center flex-col p-4 gradient-bg-footer">
    <div className="w-full flex sm:flex-row flex-col justify-between items-center my-4">
      <div className="flex flex-[0.25] justify-center items-center">
        <img src={timelessLogo} alt="brand" className="w-32" />
      </div>

      <div className="flex flex-1 justify-evenly items-center flex-wrap sm:mt-0 mt-5 w-full">
        <p className="text-white text-base text-center mx-2 cursor-pointer">
          Marketplace
        </p>
        <p className="text-white text-base text-center mx-2 cursor-pointer">
          Artist
        </p>
        <p className="text-white text-base text-center mx-2 cursor-pointer">
          Options
        </p>
        <p className="text-white text-base text-center mx-2 cursor-pointer">
          Neighborhood
        </p>
      </div>

      <div className="flex flex-[0.25] justify-center items-center">
      <p className="text-white text-right text-xs">&reproduction;2022 All rights reserved</p>
      </div>
    </div>
  </div>
)

export default Footer

Implausible, this is it for the most obvious elements, let’s come with the hidden elements which might be solely invoked by the use of a modal interface.

CreateNFT Part

This element is saddled with the obligation of minting new NFTs by means of supplying a picture, identify, value, and outline. As soon as the Mint Now button is clicked, the picture is uploaded to IPFS (Inter Planetary Report Device) and a picture URL is returned.

The returned symbol URL at the side of the NFT knowledge equipped within the shape is shipped to our good contract for minting, in an instant after the consumer authorizes the transaction with their Metamask pockets.

On of completion of the transaction, the NFT is then indexed some of the works of art, and patrons can then acquire them or even trade their costs. See the code beneath for main points.

import {
  useGlobalState,
  setGlobalState,
  setLoadingMsg,
  setAlert,
} from '../retailer'
import { mintNFT } from '../TimelessNFT'
import { useState } from 'react'
import { FaTimes } from 'react-icons/fa'
import { create } from 'ipfs-http-client'

const Jstomer = create('https://ipfs.infura.io:5001/api/v0')

const CreateNFT = () => {
  const [modal] = useGlobalState('modal')
  const [title, setTitle] = useState('')
  const [price, setPrice] = useState('')
  const [description, setDescription] = useState('')
  const [fileUrl, setFileUrl] = useState('')
  const [imgBase64, setImgBase64] = useState(null)

  const onChange = async (e) => {
    const reader = new FileReader()
    if (e.goal.information[0]) reader.readAsDataURL(e.goal.information[0])

    reader.onload = (readerEvent) => {
      const record = readerEvent.goal.consequence
      setImgBase64(record)
      setFileUrl(e.goal.information[0])
    }
  }

  const handleSubmit = async (e) => {
    e.preventDefault()

    if (!identify || !value || !description) go back

    setGlobalState('modal', 'scale-0')
    setGlobalState('loading', { display: true, msg: 'Importing IPFS knowledge...' })

    take a look at {
      const created = anticipate Jstomer.upload(fileUrl)
      const metadataURI = `https://ipfs.infura.io/ipfs/${created.trail}`
      const nft = { identify, value, description, metadataURI }
      setLoadingMsg('Intializing transaction...')

      mintNFT(nft).then((res) => {
        if (res) {
          setFileUrl(metadataURI)
          resetForm()
          setAlert('Minting finished...', 'inexperienced')
          window.location.reload()
        }
      })
    } catch (error) {
      console.log('Error importing record: ', error)
      setAlert('Minting failed...', 'purple')
    }
  }

  const closeModal = () => {
    setGlobalState('modal', 'scale-0')
    resetForm()
  }

  const resetForm = () => {
    setFileUrl('')
    setImgBase64(null)
    setTitle('')
    setPrice('')
    setDescription('')
  }

  go back (
    <div
      className={`mounted top-0 left-0 w-screen h-screen flex items-center
      justify-center bg-black bg-opacity-50 rework
      transition-transform duration-300 ${modal}`}
    >
      <div className="bg-[#151c25] shadow-xl shadow-[#e32970] rounded-xl w-11/12 md:w-2/5 h-7/12 p-6">
        <shape className="flex flex-col">
          <div className="flex flex-row justify-between items-center">
            <p className="font-semibold text-gray-400">Upload NFT</p>
            <button
              kind="button"
              onClick={closeModal}
              className="border-0 bg-transparent center of attention:outline-none"
            >
              <FaTimes className="text-gray-400" />
            </button>
          </div>

          <div className="flex flex-row justify-center items-center rounded-xl mt-5">
            <div className="shrink-0 rounded-xl overflow-hidden h-20 w-20">
              <img
                alt="NFT"
                className="h-full w-full object-cover cursor-pointer"
                src=
                  'https://pictures.unsplash.com/photo-1580489944761-15a19d654956?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=structure&are compatible=crop&w=1361&q=80'
                
              />
            </div>
          </div>

          <div className="flex flex-row justify-between items-center bg-gray-800 rounded-xl mt-5">
            <label className="block">
              <span className="sr-only">Select profile photograph</span>
              <enter
                kind="record"
                settle for="symbol/png, symbol/gif, symbol/jpeg, symbol/webp"
                className="block w-full text-sm text-slate-500
                record:mr-4 record:py-2 record:px-4
                record:rounded-full record:border-0
                record:text-sm record:font-semibold
                record:bg-[#19212c] record:text-gray-400
                hover:record:bg-[#1d2631]
                cursor-pointer center of attention:ring-0 center of attention:outline-none"
                onChange={onChange}
                required
              />
            </label>
          </div>

          <div className="flex flex-row justify-between items-center bg-gray-800 rounded-xl mt-5">
            <enter
              className="block w-full text-sm
              text-slate-500 bg-transparent border-0
              center of attention:outline-none center of attention:ring-0"
              kind="textual content"
              title="identify"
              placeholder="Identify"
              onChange={(e) => setTitle(e.goal.worth)}
              worth={identify}
              required
            />
          </div>

          <div className="flex flex-row justify-between items-center bg-gray-800 rounded-xl mt-5">
            <enter
              className="block w-full text-sm
              text-slate-500 bg-transparent border-0
              center of attention:outline-none center of attention:ring-0"
              kind="quantity"
              step={0.01}
              min={0.01}
              title="value"
              placeholder="Worth (Eth)"
              onChange={(e) => setPrice(e.goal.worth)}
              worth={value}
              required
            />
          </div>

          <div className="flex flex-row justify-between items-center bg-gray-800 rounded-xl mt-5">
            <textarea
              className="block w-full text-sm resize-none
              text-slate-500 bg-transparent border-0
              center of attention:outline-none center of attention:ring-0 h-20"
              kind="textual content"
              title="description"
              placeholder="Description"
              onChange={(e) => setDescription(e.goal.worth)}
              worth={description}
              required
            ></textarea>
          </div>

          <button
            kind="publish"
            onClick={handleSubmit}
            className="flex flex-row justify-center items-center
            w-full text-white text-md bg-[#e32970]
            hover:bg-[#bd255f] py-2 px-5 rounded-full
            drop-shadow-xl border border-transparent
            hover:bg-transparent hover:text-[#e32970]
            hover:border hover:border-[#bd255f]
            center of attention:outline-none center of attention:ring mt-5"
          >
            Mint Now
          </button>
        </shape>
      </div>
    </div>
  )
}

export default CreateNFT

ShowNFT Part

This element presentations extra details about a particular NFT, providing the landlord a button to switch the fee, and the consumer a button to both acquire the NFT or chat with the vendor. See the code beneath for extra main points.

import Chat from './Chat'
import Identicon from 'react-identicons'
import { FaTimes } from 'react-icons/fa'
import { buyNFT } from '../TimelessNFT'
import { useGlobalState, setGlobalState, truncate, setAlert } from '../retailer'
import { useState } from 'react'
import { getMessages } from '../CometChat'

const ShowNFT = () => {
  const [showModal] = useGlobalState('showModal')
  const [chatOpened] = useGlobalState('chatOpened')
  const [currentUser] = useGlobalState('currentUser')
  const [connectedAccount] = useGlobalState('connectedAccount')
  const [nft] = useGlobalState('nft')
  const [messages, setMessages] = useState([])

  const onChangePrice = () => {
    setGlobalState('nft', nft)
    setGlobalState('showModal', 'scale-0')
    setGlobalState('updateModal', 'scale-100')
  }

  const onChatSeller = () => {
    if (currentUser?.uid.toLowerCase() != connectedAccount.toLowerCase())
      go back alert('Please login to obtain chats from patrons!')

    getMessages(nft.proprietor).then((msgs) => {
      setMessages(
        msgs.clear out((msg) => {
          go back (
            !!!msg?.deletedAt &&
            !!!msg?.motion &&
            (msg?.conversationId ==
              `${msg?.rawMessage.receiver}_user_${msg?.rawMessage.sender}` ||
              msg?.conversationId ==
                `${msg?.rawMessage.sender}_user_${msg?.rawMessage.receiver}`)
          )
        })
      )
      setGlobalState('nft', nft)
      setGlobalState('chatOpened', true)
    })
  }

  const handleNFTPurchase = () => {
    setGlobalState('showModal', 'scale-0')
    setGlobalState('loading', {
      display: true,
      msg: 'Initializing NFT switch...',
    })

    take a look at {
      buyNFT(nft).then((res) => {
        if (res) {
          setAlert('Switch finished...', 'inexperienced')
          window.location.reload()
        }
      })
    } catch (error) {
      console.log('Error transfering NFT: ', error)
      setAlert('Acquire failed...', 'purple')
    }
  }

  go back (
    <div>
      {chatOpened ? (
        <Chat receiver={nft.proprietor} chats={messages} />
      ) : (
        <div
          className={`mounted top-0 left-0 w-screen h-screen flex items-center
          justify-center bg-black bg-opacity-50 rework
          transition-transform duration-300 ${showModal}`}
        >
          <div className="bg-[#151c25] shadow-xl shadow-[#e32970] rounded-xl w-11/12 md:w-2/5 h-7/12 p-6">
            <div className="flex flex-col">
              <div className="flex flex-row justify-between items-center">
                <p className="font-semibold text-gray-400">Purchase NFT</p>
                <button
                  kind="button"
                  onClick={() => setGlobalState('showModal', 'scale-0')}
                  className="border-0 bg-transparent center of attention:outline-none"
                >
                  <FaTimes className="text-gray-400" />
                </button>
              </div>

              <div className="flex flex-row justify-center items-center rounded-xl mt-5">
                <div className="shrink-0 rounded-xl overflow-hidden h-40 w-40">
                  <img
                    className="h-full w-full object-cover cursor-pointer"
                    src={nft?.metadataURI}
                    alt={nft?.identify}
                  />
                </div>
              </div>

              <div className="flex flex-col justify-start rounded-xl mt-5">
                <h4 className="text-white font-semibold">{nft?.identify}</h4>
                <p className="text-gray-400 text-xs my-1">{nft?.description}</p>

                <div className="flex justify-between items-center mt-3 text-white">
                  <div className="flex justify-start items-center">
                    <Identicon
                      string={nft?.proprietor.toLowerCase()}
                      dimension={50}
                      className="h-10 w-10 object-contain rounded-full mr-3"
                    />
                    <div className="flex flex-col justify-center items-start">
                      <small className="text-white font-bold">@proprietor</small>
                      <small className="text-pink-800 font-semibold">
                        {nft?.proprietor ? truncate(nft.proprietor, 4, 4, 11) : '...'}
                      </small>
                    </div>
                  </div>

                  <div className="flex flex-col">
                    <small className="text-xs">Present Worth</small>
                    <p className="text-sm font-semibold">{nft?.value} ETH</p>
                  </div>
                </div>
              </div>
              {connectedAccount != nft?.proprietor ? (
                <div className="flex flex-row justify-between items-center">
                  <button
                    className="flex flex-row justify-center items-center
                w-full text-white text-md bg-[#e32970]
                hover:bg-[#bd255f] py-2 px-5 rounded-full
                drop-shadow-xl border border-transparent
                hover:bg-transparent hover:text-[#e32970]
                hover:border hover:border-[#bd255f]
                center of attention:outline-none center of attention:ring mt-5"
                    onClick={handleNFTPurchase}
                  >
                    Acquire Now
                  </button>
                  <button
                    className="flex flex-row justify-center items-center
                w-full text-white text-md bg-transparent 
                py-2 px-5 rounded-full drop-shadow-xl border
                border-transparent hover:bg-transparent
                hover:text-[#e32970] center of attention:outline-none
                center of attention:ring mt-5"
                    onClick={onChatSeller}
                  >
                    Chat with Dealer
                  </button>
                </div>
              ) : (
                <button
                  className="flex flex-row justify-center items-center
              w-full text-white text-md bg-[#e32970]
              hover:bg-[#bd255f] py-2 px-5 rounded-full
              drop-shadow-xl border border-transparent
              hover:bg-transparent hover:text-[#e32970]
              hover:border hover:border-[#bd255f]
              center of attention:outline-none center of attention:ring mt-5"
                  onClick={onChangePrice}
                >
                  Alternate Worth
                </button>
              )}
            </div>
          </div>
        </div>
      )}
    </div>
  )
}

export default ShowNFT

UpdateNFT Part

This element is tasked with converting the cost of the NFT. This motion can solely be carried out by means of the landlord of the NFT. Whilst this feature is to be had, it’s going to take some fuel rate to impact those adjustments. As soon as an NFT exchanges a hand with any other purchaser, the brand new proprietor may come to a decision to extend the fee, and that’s why this feature was once made to be had. See the code snippet beneath.

import {
  useGlobalState,
  setGlobalState,
  setLoadingMsg,
  setAlert,
} from '../retailer'
import { updateNFT } from '../TimelessNFT'
import { useState } from 'react'
import { FaTimes } from 'react-icons/fa'

const UpdateNFT = () => {
  const [modal] = useGlobalState('updateModal')
  const [nft] = useGlobalState('nft')
  const [price, setPrice] = useState('')
  

  const handleSubmit = async (e) => {
    e.preventDefault()
    if (!value || value <= 0) go back

    setGlobalState('modal', 'scale-0')
    setGlobalState('loading', { display: true, msg: 'Beginning value replace...' })

    take a look at {
      setLoadingMsg('Worth updating...')
      setGlobalState('updateModal', 'scale-0')

      updateNFT({...nft, value: value}).then((res) => {
        if (res) {
          setAlert('Worth up to date...', 'inexperienced')
          window.location.reload()
        }
      })
    } catch (error) {
      console.log('Error updating record: ', error)
      setAlert('Replace failed...', 'purple')
    }
    
  }

  go back (
    <div
      className={`mounted top-0 left-0 w-screen h-screen flex items-center
      justify-center bg-black bg-opacity-50 rework
      transition-transform duration-300 ${modal}`}
    >
      <div className="bg-[#151c25] shadow-xl shadow-[#e32970] rounded-xl w-11/12 md:w-2/5 h-7/12 p-6">
        <shape className="flex flex-col">
          <div className="flex flex-row justify-between items-center">
            <p className="font-semibold text-gray-400">{nft?.identify}</p>
            <button
              kind="button"
              onClick={() => setGlobalState('updateModal', 'scale-0')}
              className="border-0 bg-transparent center of attention:outline-none"
            >
              <FaTimes className="text-gray-400" />
            </button>
          </div>

          <div className="flex flex-row justify-center items-center rounded-xl mt-5">
            <div className="shrink-0 rounded-xl overflow-hidden h-20 w-20">
              <img
                alt="NFT"
                className="h-full w-full object-cover cursor-pointer"
                src={nft?.metadataURI}
              />
            </div>
          </div>

          <div className="flex flex-row justify-between items-center bg-gray-800 rounded-xl mt-5">
            <enter
              className="block w-full text-sm
              text-slate-500 bg-transparent border-0
              center of attention:outline-none center of attention:ring-0"
              kind="quantity"
              step={0.01}
              min={0.01}
              title="value"
              placeholder="Worth (Eth)"
              onChange={(e) => setPrice(e.goal.worth)}
              required
            />
          </div>

          <button
            kind="publish"
            onClick={handleSubmit}
            className="flex flex-row justify-center items-center
            w-full text-white text-md bg-[#e32970]
            hover:bg-[#bd255f] py-2 px-5 rounded-full
            drop-shadow-xl border border-transparent
            hover:bg-transparent hover:text-[#e32970]
            hover:border hover:border-[#bd255f]
            center of attention:outline-none center of attention:ring mt-5"
          >
            Replace Now
          </button>
        </shape>
      </div>
    </div>
  )
}

export default UpdateNFT

ChatList Part

This element unearths the hot chats a consumer has made with a dealer or purchaser at the platform. The element additionally captures the ultimate message that was once despatched of their dialog. A click on on every dialog will result in the chat interface. See the code beneath.

import Chat from './Chat'
import Second from 'react-moment'
import { useState } from 'react'
import { FaTimes } from 'react-icons/fa'
import { setGlobalState, truncate, useGlobalState } from '../retailer'
import { getMessages } from '../CometChat'

const ChatList = ({ customers }) => {
  const [messages, setMessages] = useState([])
  const [receiver, setReceiver] = useState('')
  const [recentChatOpened] = useGlobalState('recentChatOpened')
  const [connectedAccount] = useGlobalState('connectedAccount')

  const onEnterChat = (receiver) => {
    setReceiver(receiver)
    getMessages(receiver).then((msgs) => {
      setMessages(
        msgs.clear out((msg) => {
          go back (
            !!!msg?.deletedAt &&
            !!!msg?.motion &&
            msg?.conversationId ==
              `${msg?.rawMessage.receiver}_user_${msg?.rawMessage.sender}`
          )
        })
      )
      setGlobalState('recentChatOpened', true)
    })
  }

  go back (
    <div>
      {recentChatOpened ? (
        <Chat receiver={receiver} chats={messages} />
      ) : (
        <div
          className={`mounted top-0 left-0 w-screen h-screen flex items-center
        justify-center bg-black bg-opacity-50 rework
        transition-transform duration-300 scale-100`}
        >
          <div className="bg-[#151c25] shadow-xl shadow-[#e32970] rounded-xl w-11/12 md:w-2/5 h-7/12 p-6">
            <div className="flex flex-col text-gray-400">
              <div className="flex flex-row justify-between items-center">
                <p className="font-semibold text-gray-400">Conversations</p>
                <button
                  kind="button"
                  onClick={() => setGlobalState('recentOpened', false)}
                  className="border-0 bg-transparent center of attention:outline-none"
                >
                  <FaTimes />
                </button>
              </div>

              <div className="h-[calc(100vh_-_20rem)] overflow-y-auto sm:pr-4 my-3">
                {customers.map((consumer, i) => (
                  <button
                    key={i}
                    className="flex flex-row justify-between w-full
                    items-center bg-gray-800 hover:bg-gray-900 rounded-md 
                    px-4 py-3 my-1 cursor-pointer rework
                    transition-transform duration-300"
                    onClick={() => onEnterChat(consumer?.lastMessage.sender.uid)}
                  >
                    <div className="flex flex-col text-left">
                      <h4 className="text-sm text-[#e32970] font-semiBold">
                        @{truncate(consumer?.lastMessage.sender.uid, 4, 4, 11)}
                      </h4>
                      <p className="text-xs">
                        {consumer?.lastMessage.textual content}
                      </p>
                    </div>

                    <Second
                      className="text-xs font-bold"
                      unix
                      date={consumer?.lastMessage.sentAt}
                      structure="YYYY/MM/D hh:mm A"
                    />
                  </button>
                ))}
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  )
}

export default ChatList

Chat Part

This element is answerable for enticing two customers in a one-on-one chat. The picture above displays a dialog between a purchaser and a dealer at the platform, from two other browsers. See the code beneath for its implementation.

import Identicon from 'react-identicons'
import { useGlobalState, setGlobalState, truncate } from '../retailer'
import { sendMessage, CometChat } from '../CometChat'
import { useEffect, useState } from 'react'
import { FaTimes } from 'react-icons/fa'

const Chat = ({ receiver, chats }) => {
  const [connectedAccount] = useGlobalState('connectedAccount')
  const [message, setMessage] = useState('')
  const [messages, setMessages] = useState(chats)

  const handleSubmit = async (e) => {
    e.preventDefault()
    sendMessage(receiver, message).then((msg) => {
      setMessages((prevState) => [...prevState, msg])
      setMessage('')
      scrollToEnd()
    })
  }

  const listenForMessage = (listenerID) => {
    CometChat.addMessageListener(
      listenerID,
      new CometChat.MessageListener({
        onTextMessageReceived: (message) => {
          setMessages((prevState) => [...prevState, message])
          scrollToEnd()
        },
      })
    )
  }

  const onClose = () => {
    setGlobalState('chatOpened', false)
    setGlobalState('recentChatOpened', false)
  }

  const scrollToEnd = () => {
    const component = report.getElementById('messages-container')
    component.scrollTop = component.scrollHeight
  }

  useEffect(() => {
    listenForMessage(receiver)
  }, [receiver])

  go back (
    <div
      className={`mounted top-0 left-0 w-screen h-screen flex items-center
      justify-center bg-black bg-opacity-50 rework
      transition-transform duration-300 scale-100`}
    >
      <div className="bg-[#151c25] shadow-xl shadow-[#e32970] rounded-xl w-5/6 h-5/6 p-6">
        <div className="flex flex-col text-gray-400">
          <div className="flex flex-row justify-between items-center">
            <div className="flex flex-row justify-center items-center">
              <div className="shrink-0 rounded-full overflow-hidden h-10 w-10 mr-3">
                <Identicon
                  string={receiver.toLowerCase()}
                  dimension={50}
                  className="h-full w-full object-cover cursor-pointer rounded-full"
                />
              </div>
              <p className="font-bold">@{receiver ? truncate(receiver, 4, 4, 11) : '...'}</p>
            </div>
            <button
              kind="button"
              onClick={onClose}
              className="border-0 bg-transparent center of attention:outline-none"
            >
              <FaTimes />
            </button>
          </div>
          <div
            identification="messages-container"
            className="h-[calc(100vh_-_20rem)] overflow-y-auto sm:pr-4 my-3"
          >
            {messages.map((msg, i) =>
              msg?.receiverId?.toLowerCase() ==
              connectedAccount.toLowerCase() ? (
                <div
                  key={i}
                  className="flex flex-row justify-start items-center mt-5"
                >
                  <div className="flex flex-col justify-start items-start">
                    <h4 className="text-[#e32970]">
                      @{receiver ? truncate(receiver, 4, 4, 11) : '...'}
                    </h4>
                    <p className="text-xs">{msg.textual content}</p>
                  </div>
                </div>
              ) : (
                <div
                  key={i}
                  className="flex flex-row justify-end items-center mt-5"
                >
                  <div className="flex flex-col justify-start items-end">
                    <h4 className="text-[#e32970]">@you</h4>
                    <p className="text-xs">{msg.textual content}</p>
                  </div>
                </div>
              )
            )}
          </div>
          <shape
            onSubmit={handleSubmit}
            className="flex flex-row justify-between items-center bg-gray-800 rounded-xl mt-5"
          >
            <enter
              className="block w-full text-sm resize-none
              text-slate-500 bg-transparent border-0
              center of attention:outline-none center of attention:ring-0 h-20"
              kind="textual content"
              title="message"
              placeholder="Write message..."
              onChange={(e) => setMessage(e.goal.worth)}
              worth={message}
              required
            />
          </shape>
        </div>
      </div>
    </div>
  )
}

export default Chat

Great, now that we’ve integrated the ones incredible elements, let’s end it up with those ultimate two.

Loading Part

This element merely presentations the present job and standing when a transaction is in procedure. See the code beneath.

import { useGlobalState } from '../retailer'

const Loading = () => {
  const [loading] = useGlobalState('loading')

  go back (
    <div
      className={`mounted top-0 left-0 w-screen h-screen
      flex items-center justify-center bg-black 
      bg-opacity-50 rework transition-transform
      duration-300 ${loading.display ? 'scale-100' : 'scale-0'}`}
    >
      <div
        className="flex flex-col justify-center
        items-center bg-[#151c25] shadow-xl 
        shadow-[#e32970] rounded-xl 
        min-w-min px-10 pb-2"
      >
        <div className="flex flex-row justify-center items-center">
          <div className="lds-dual-ring scale-50"></div>
          <p className="text-lg text-white">Processing...</p>
        </div>
        <small className="text-white">{loading.msg}</small>
      </div>
    </div>
  )
}

export default Loading

The App Part
This record bundles up the above element mentioned on this paintings. That is simply how a ReactJs structure works. See the codes beneath.

import Alert from './elements/Alert'
import Artistic endeavors from './elements/Artistic endeavors'
import CreateNFT from './elements/CreateNFT'
import Footer from './elements/Footer'
import Header from './elements/Header'
import Hero from './elements/Hero'
import Loading from './elements/Loading'
import ShowNFT from './elements/ShowNFT'
import Transactions from './elements/Transactions'
import UpdateNFT from './elements/UpdateNFT'
import { isUserLoggedIn } from './CometChat'
import { loadWeb3 } from './TimelessNFT'
import { useEffect } from 'react'

const App = () => {
  useEffect(() => {
    loadWeb3()
    isUserLoggedIn()
  }, [])

  go back (
    <div className="min-h-screen">
      <div className="gradient-bg-hero">
        <Header />
        <Hero />
      </div>
      <Artistic endeavors />
      <Transactions />
      <CreateNFT />
      <UpdateNFT />
      <ShowNFT />
      <Footer />
      <Alert />
      <Loading />
    </div>
  )
}

export default App

Implausible, we’ve simply finished the combination of the more than a few elements, let’s seal it up with the opposite portions of this venture.

Different Crucial Information

This utility makes use of a state control retailer, a CometChat SDK, and a freelance carrier record. Let’s check out them one at a time.

The Retailer
This state control record makes use of the react-hooks-global-state npm bundle. It’s easy, rapid, and more straightforward than Redux. The entire world variables and purposes used on this app had been created on this retailer.

On the root of the venture, pass to the src listing and create a folder named retailer. Now, inside this retailer folder, create a record known as index.js and paste the codes beneath within it.

import { createGlobalState } from 'react-hooks-global-state'

const { setGlobalState, useGlobalState, getGlobalState } = createGlobalState({
  modal: 'scale-0',
  updateModal: 'scale-0',
  mintModal: '',
  alert: { display: false, msg: '', colour: '' },
  loading: { display: false, msg: '' },
  showModal: 'scale-0',
  chatOpened: false,
  recentChatOpened: false,
  recentOpened: false,
  chatList: 'scale-0',
  connectedAccount: '',
  currentUser: null,
  nft: null,
  nfts: [],
  transactions: [],
  contract: null,
})

const setAlert = (msg, colour = 'inexperienced') => {
  setGlobalState('loading', false)
  setGlobalState('alert', { display: true, msg, colour })
  setTimeout(() => {
    setGlobalState('alert', { display: false, msg: '', colour })
  }, 6000)
}

const setLoadingMsg = (msg) => {
  const loading = getGlobalState('loading')
  setGlobalState('loading', { ...loading, msg })
}

const truncate = (textual content, startChars, endChars, maxLength) => {
  if (textual content.duration > maxLength) {
    var delivery = textual content.substring(0, startChars)
    var finish = textual content.substring(textual content.duration - endChars, textual content.duration)
    whilst (delivery.duration + finish.duration < maxLength) {
      delivery = delivery + '.'
    }
    go back delivery + finish
  }
  go back textual content
}

export {
  useGlobalState,
  setGlobalState,
  getGlobalState,
  setAlert,
  setLoadingMsg,
  truncate,
}

The CometChat Carrier
This record comprises the entire crucial purposes to keep in touch with the CometChat SDK. See the codes beneath.

import { CometChat } from '@cometchat-pro/chat'
import { setGlobalState } from './retailer'

const CONSTANTS = {
  APP_ID: procedure.env.REACT_APP_COMET_CHAT_APP_ID,
  REGION: procedure.env.REACT_APP_COMET_CHAT_REGION,
  Auth_Key: procedure.env.REACT_APP_COMET_CHAT_AUTH_KEY,
}

const initCometChat = async () => {
  const appID = CONSTANTS.APP_ID
  const area = CONSTANTS.REGION

  const appSetting = new CometChat.AppSettingsBuilder()
    .subscribePresenceForAllUsers()
    .setRegion(area)
    .construct()

  anticipate CometChat.init(appID, appSetting)
    .then(() => console.log('Initialization finished effectively'))
    .catch((error) => error)
}

const loginWithCometChat = async (UID) => {
  const authKey = CONSTANTS.Auth_Key
  anticipate CometChat.login(UID, authKey)
    .then((consumer) => setGlobalState('currentUser', consumer))
    .catch((error) => {
      alert(error.message)
      console.log(error)
    })
  go back true
}

const signUpWithCometChat = async (UID, title) => {
  let authKey = CONSTANTS.Auth_Key
  const consumer = new CometChat.Person(UID)
  consumer.setName(title)

  anticipate CometChat.createUser(consumer, authKey)
    .then((consumer) => {
      alert('Signed up effectively, click on login now!')
      console.log('Logged In: ', consumer)
    })
    .catch((error) => {
      alert(error.message)
      console.log(error)
    })
  go back true
}

const logOutWithCometChat = async () => {
  go back anticipate CometChat.logout()
    .then(() => console.log('Logged Out Effectively'))
    .catch((error) => error)
}

const isUserLoggedIn = async () => {
  anticipate CometChat.getLoggedinUser()
    .then((consumer) => setGlobalState('currentUser', consumer))
    .catch((error) => console.log('error:', error))
}

const getMessages = async (UID) => {
  const prohibit = 30
  const messagesRequest = new CometChat.MessagesRequestBuilder()
    .setUID(UID)
    .setLimit(prohibit)
    .construct()

  go back anticipate messagesRequest
    .fetchPrevious()
    .then((messages) => messages)
    .catch((error) => error)
}

const sendMessage = async (receiverID, messageText) => {
  const receiverType = CometChat.RECEIVER_TYPE.USER
  const textMessage = new CometChat.TextMessage(
    receiverID,
    messageText,
    receiverType
  )

  go back anticipate CometChat.sendMessage(textMessage)
    .then((message) => message)
    .catch((error) => error)
}

const getConversations = async () => {
  const prohibit = 30
  const conversationsRequest = new CometChat.ConversationsRequestBuilder()
    .setLimit(prohibit)
    .construct()

  go back anticipate conversationsRequest
    .fetchNext()
    .then((conversationList) => conversationList)
}

export {
  initCometChat,
  loginWithCometChat,
  signUpWithCometChat,
  logOutWithCometChat,
  getMessages,
  sendMessage,
  getConversations,
  isUserLoggedIn,
  CometChat,
}

The Contract Carrier Report
This record comprises the entire purposes and procedures answerable for interacting with the good contract at the blockchain the use of the Web3 library. See the codes beneath.

import Web3 from 'web3'
import { setGlobalState, getGlobalState, setAlert } from './retailer'
import TimelessNFT from './abis/TimelessNFT.json'

const { ethereum } = window

const connectWallet = async () => {
  take a look at {
    if (!ethereum) go back alert('Please set up Metamask')
    const accounts = anticipate ethereum.request({ way: 'eth_requestAccounts' })
    setGlobalState('connectedAccount', accounts[0])
  } catch (error) {
    setAlert(JSON.stringify(error), 'purple')
  }
}

const structuredNfts = (nfts) => {
  go back nfts
    .map((nft) => ({
      identification: Quantity(nft.identification),
      proprietor: nft.proprietor,
      value: window.web3.utils.fromWei(nft.value),
      identify: nft.identify,
      description: nft.description,
      metadataURI: nft.metadataURI,
      timestamp: nft.timestamp,
    }))
    .opposite()
}

const loadWeb3 = async () => {
  take a look at {
    if (!ethereum) go back alert('Please set up Metamask')

    window.web3 = new Web3(ethereum)

    window.web3 = new Web3(window.web3.currentProvider)

    const web3 = window.web3
    const accounts = anticipate web3.eth.getAccounts()
    setGlobalState('connectedAccount', accounts[0])

    const networkId = anticipate web3.eth.web.getId()
    const networkData = TimelessNFT.networks[networkId]

    if (networkData) {
      const contract = new web3.eth.Contract(
        TimelessNFT.abi,
        networkData.cope with
      )
      const nfts = anticipate contract.strategies.getAllNFTs().name()
      const transactions = anticipate contract.strategies.getAllTransactions().name()

      setGlobalState('nfts', structuredNfts(nfts))
      setGlobalState('transactions', structuredNfts(transactions))
      setGlobalState('contract', contract)
    } else {
      window.alert('TimelessNFT contract no longer deployed to detected community.')
    }
  } catch (error) {
    alert('Please attach your metamask pockets!')
  }
}

const mintNFT = async ({ identify, description, metadataURI, value }) => {
  take a look at {
    value = window.web3.utils.toWei(value.toString(), 'ether')
    const contract = getGlobalState('contract')
    const account = getGlobalState('connectedAccount')
    const mintPrice = window.web3.utils.toWei('0.01', 'ether')

    anticipate contract.strategies
      .payToMint(identify, description, metadataURI, value)
      .ship({ from: account, worth: mintPrice })

    go back true
  } catch (error) {
    setAlert(error.message, 'purple')
  }
}

const buyNFT = async ({ identification, value }) => {
  take a look at {
    value = window.web3.utils.toWei(value.toString(), 'ether')
    const contract = getGlobalState('contract')
    const purchaser = getGlobalState('connectedAccount')

    anticipate contract.strategies.payToBuy(Quantity(identification)).ship({ from: purchaser, worth: value })

    go back true
  } catch (error) {
    setAlert(error.message, 'purple')
  }
}

const updateNFT = async ({ identification, value }) => {
  take a look at {
    value = window.web3.utils.toWei(value.toString(), 'ether')
    const contract = getGlobalState('contract')
    const purchaser = getGlobalState('connectedAccount')

    anticipate contract.strategies.changePrice(Quantity(identification), value).ship({ from: purchaser })

    go back true
  } catch (error) {
    setAlert(error.message, 'purple')
  }
}

export { loadWeb3, connectWallet, mintNFT, buyNFT, updateNFT }

Undertaking Property
Obtain this brand and come with it within the property folder to your root listing. And with that, you’ve effectively integrated all this is had to run this utility.

Beginning up the server

To continue with this step, migrate the good contract to the Internet so you’ll engage with it. Run the next code to your terminal.

truffle migrate --network rinkeby

The above code will send your good contract to the server the use of the infuria RPC.

You’ll additionally arrange an area blockchain the use of the ganache-cli server we arrange firstly of this instructional. Merely run the code beneath to send it in your native blockchain server when you desire that approach.

Open one terminal run **ganache-cli -d** and on a distinct terminal run **truffle migrate** or **truffle deploy**.

Word, in case you are the use of ganache-cli as your EVM, you will have to additionally upload the localhost server to Metamask, and import the personal keys generated by means of ganache. See Beginning Up the Construction Setting for steerage.

If you want my assist resolving problems to your venture, seek the advice of me in this web page.

Now, run yarn delivery besides up your react utility. And there you might have it with this construct at the NFT market.

Watch my FREE web3 tutorials on Youtube now.

Conclusion

We’ve come to the end line of this NFT construct, I do know you’ve gotten a ton of worth development at the side of me.

No matter stage you might be, if you wish to develop sooner to your web3 construction talents, get into my personal magnificence.

Until subsequent time, stay crushing it!

In regards to the Creator

Gospel Darlington is a full-stack blockchain developer with 6+ years of revel in within the instrument construction trade.

Via combining Device Construction, writing, and educating, he demonstrates the way to construct decentralized programs on EVM-compatible blockchain networks.

His stacks come with JavaScript, React, Vue, Angular, Node, React Local, NextJs, Solidity, and extra.

For more info about him, kindly discuss with and observe his web page on Twitter, Github, LinkedIn, or on his web site.

L O A D I N G
. . . feedback & extra!


https://hackernoon.com/how-to-build-a-profitable-nft-marketplace-with-react-solidity-and-cometchat