gotangible / components / NftGrid.jsx
NftGrid.jsx
Raw
import React, { useState, useEffect, Fragment } from "react";
import { useMoralis } from "react-moralis";
import { Listbox } from "@headlessui/react";
import NftGridItem from "./NftGridItem";
import Account from "./Account/Account";
import { ToastContainer } from "react-toastify";

const sortOptions = [
  { id: 0, name: "Recently Received", sortCode: "recently-received" },
  { id: 1, name: "Name: A-Z", sortCode: "name-a-z" },
  { id: 2, name: "Name: Z-A", sortCode: "name-z-a" },
  { id: 3, name: "Collection: A-Z", sortCode: "collection-a-z" },
  { id: 4, name: "Collection: Z-A", sortCode: "collection-z-a" },
  { id: 5, name: "Oldest", sortCode: "oldest" },
];

const NftGrid = () => {
  const { isAuthenticated, account } = useMoralis();

  const [fetchLoading, setFetchLoading] = useState(false);
  const [assets, setAssets] = useState([]);
  const [cursorNext, setCursorNext] = useState(null);
  const [cursorPrev, setCursorPrev] = useState(null);
  const [selectedSortOption, setSelectedSortOption] = useState(sortOptions[0]);

  const fetchAssets = async () => {
    let assetsUrl = `/api/assets?owner=${account}`;
    if (cursorNext) assetsUrl += `&cursor=${cursorNext}`;
    setFetchLoading(true);
    const response = await fetch(assetsUrl);
    const data = await response.json();
    setAssets([...assets, ...data.data.assets]);
    setCursorNext(data?.data?.next);
    setCursorPrev(data?.data?.previous);
    setFetchLoading(false);
  };

  const handleLoadMoreAssets = () => {
    fetchAssets();
  };

  useEffect(() => {
    if (account && assets?.length == 0) {
      fetchAssets();
    }
  }, [account]);

  // TODO: Implement sorting for grid items
  const handleSortAZ = () => {
    const sorted = [...assets.sort((a, b) => a.name.localeCompare(b.name))];
    // setNumbers((num) => [...num.sort()]);
    console.log(sorted);
    setAssets(sorted);
  };

  const toastMarkup = (
    <div className="absolute bg-zinc-800">
      <ToastContainer
        position="bottom-left"
        autoClose={5000}
        hideProgressBar={false}
        newestOnTop={false}
        closeOnClick={true}
        rtl={false}
        pauseOnFocusLoss={false}
        draggable={false}
        pauseOnHover={true}
        theme="dark"
      />
    </div>
  );

  const sortByMarkup = (
    <div className="mb-4 inline-block">
      <Listbox value={selectedSortOption} onChange={setSelectedSortOption}>
        <Listbox.Button className="inline-flex items-center rounded-lg border-2 border-transparent bg-zinc-700 bg-opacity-30 px-4 py-2.5 text-center text-sm font-medium text-white backdrop-blur-lg backdrop-filter hover:border-[#5cd4ac]">
          Sort by
          <svg
            xmlns="http://www.w3.org/2000/svg"
            className="ml-2 h-4 w-4"
            fill="none"
            viewBox="0 0 24 24"
            stroke="currentColor"
            strokeWidth={2}
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              d="M19 9l-7 7-7-7"
            />
          </svg>
        </Listbox.Button>
        <Listbox.Options className="absolute z-20 mt-2 rounded-lg bg-zinc-800 bg-opacity-80 py-2 text-sm text-white shadow backdrop-blur-lg backdrop-filter">
          {sortOptions.map((sortOption) => (
            /* Use the `active` state to conditionally style the active option. */
            /* Use the `selected` state to conditionally style the selected option. */
            <Listbox.Option
              key={sortOption.id}
              value={sortOption}
              as={Fragment}
              className="block cursor-pointer px-4 py-2 hover:bg-zinc-600"
            >
              {({ active, selected }) => (
                <li
                  className={`${
                    active ? "bg-blue-500 text-white" : "bg-white text-black"
                  }`}
                >
                  <div className="flex flex-row items-center justify-start">
                    {selected ? (
                      <>
                        <svg
                          xmlns="http://www.w3.org/2000/svg"
                          className="mr-2 h-6 w-6"
                          fill="none"
                          viewBox="0 0 24 24"
                          stroke="#5cd4ac"
                          strokeWidth={2}
                        >
                          <path
                            strokeLinecap="round"
                            strokeLinejoin="round"
                            d="M5 13l4 4L19 7"
                          />
                        </svg>
                        {sortOption.name}
                      </>
                    ) : (
                      <div className="ml-8">{sortOption.name}</div>
                    )}
                  </div>
                </li>
              )}
            </Listbox.Option>
          ))}
        </Listbox.Options>
      </Listbox>
    </div>
  );

  return (
    <div className="container mx-auto py-4">
      {toastMarkup}
      {!isAuthenticated || !account ? (
        <div className="mt-24 flex flex-col items-center justify-start">
          <h3 className="mt-12 mb-6 text-base font-semibold text-white lg:text-xl">
            Connect your wallet to continue
          </h3>
          <Account />
        </div>
      ) : (
        <>
          {/* TODO: Implement sort and filter */}
          {/* {sortByMarkup} */}
          <div className="mb-4 flex flex-row">
            <div className="grid grid-cols-1 gap-6 md:grid-cols-3 lg:grid-cols-4 xl:grid-cols-5">
              {assets.length > 0 &&
                assets.map((asset, index) => {
                  return <NftGridItem asset={asset} key={index} />;
                })}
            </div>
          </div>
          {cursorNext ? (
            <div className="flex flex-row items-center justify-center">
              <button
                type="button"
                className="inline-flex items-center rounded-lg border-2 border-transparent bg-zinc-700 bg-opacity-30 px-5 py-2.5 text-center text-sm font-medium text-white backdrop-blur-lg backdrop-filter hover:border-[#5cd4ac]"
                onClick={handleLoadMoreAssets}
                disabled={fetchLoading}
              >
                {fetchLoading ? (
                  <svg
                    width="20"
                    height="20"
                    viewBox="0 0 38 38"
                    xmlns="http://www.w3.org/2000/svg"
                    stroke="#fff"
                  >
                    <g fill="none" fillRule="evenodd">
                      <g transform="translate(1 1)" strokeWidth="2">
                        <circle strokeOpacity=".5" cx="18" cy="18" r="18" />
                        <path d="M36 18c0-9.94-8.06-18-18-18">
                          <animateTransform
                            attributeName="transform"
                            type="rotate"
                            from="0 18 18"
                            to="360 18 18"
                            dur="1s"
                            repeatCount="indefinite"
                          />
                        </path>
                      </g>
                    </g>
                  </svg>
                ) : (
                  <p>Load More</p>
                )}
              </button>
            </div>
          ) : null}
        </>
      )}
    </div>
  );
};

export default NftGrid;