Skip to content

Commit

Permalink
convert fetch to SWR and use axios instead of fetch on server side ro…
Browse files Browse the repository at this point in the history
…ute handler
  • Loading branch information
MattPereira committed Jan 18, 2024
1 parent 62f86ba commit 3433eaf
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 129 deletions.
4 changes: 0 additions & 4 deletions packages/nextjs/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { useCallback, useRef, useState } from "react";
// import Image from "next/image";
import Link from "next/link";
import { useDarkMode } from "usehooks-ts";
// import { useRouter } from "next/router";
import { Bars3Icon, BugAntIcon, BuildingLibraryIcon, PhotoIcon } from "@heroicons/react/24/outline";
import { SwitchTheme } from "~~/components/SwitchTheme";
Expand Down Expand Up @@ -62,15 +61,12 @@ export const HeaderMenuLinks = () => {
* Site header
*/
export const Header = () => {
const { isDarkMode } = useDarkMode();

const [isDrawerOpen, setIsDrawerOpen] = useState(false);
const burgerMenuRef = useRef<HTMLDivElement>(null);
useOutsideClick(
burgerMenuRef,
useCallback(() => setIsDrawerOpen(false), []),
);
console.log("isDarkMode", isDarkMode);

return (
<div
Expand Down
6 changes: 5 additions & 1 deletion packages/nextjs/components/only-buildors/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useDarkMode } from "usehooks-ts";

type ButtonProps = {
onClick?: () => void; // Make onClick optional
children: React.ReactNode; // children can be any JSX element
Expand All @@ -8,6 +10,8 @@ type ButtonProps = {
* https://devdojo.com/tailwindcss/buttons
*/
export const Button = ({ onClick, children, disabled }: ButtonProps) => {
const { isDarkMode } = useDarkMode();

return (
<button
disabled={disabled}
Expand All @@ -19,7 +23,7 @@ export const Button = ({ onClick, children, disabled }: ButtonProps) => {
<span className="absolute inset-0 w-full h-full transition-all duration-200 ease-in-out delay-100 bg-accent rounded-full opacity-0 group-hover:opacity-100 "></span>
<span
className={`${
disabled ? "text-neutral-500" : "text-accent"
disabled ? "text-neutral-500" : isDarkMode ? "text-accent" : "text-white"
} relative transition-colors duration-200 ease-in-out delay-100 group-hover:text-white`}
>
{children}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export const AddressInfoDropdown = ({
</summary>
<ul
tabIndex={0}
className="dropdown-content menu z-[2] p-2 mt-2 shadow-center shadow-accent bg-base-200 rounded-box gap-1"
className="dropdown-content menu z-[2] p-2 mt-2 bg-primary text-primary-content rounded-box gap-1"
>
<NetworkOptions hidden={!selectingNetwork} />
<li className={selectingNetwork ? "hidden" : ""}>
Expand Down Expand Up @@ -118,7 +118,7 @@ export const AddressInfoDropdown = ({
) : null}
<li className={selectingNetwork ? "hidden" : ""}>
<button
className="menu-item text-error btn-sm !rounded-xl flex gap-3 py-3"
className="menu-item text-red-600 btn-sm !rounded-xl flex gap-3 py-3"
type="button"
onClick={() => disconnect()}
>
Expand Down
31 changes: 19 additions & 12 deletions packages/nextjs/pages/api/get-nfts-for-contract.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,34 @@
import axios from "axios";
import type { NextApiRequest, NextApiResponse } from "next";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
// Base URL
const url = `https://eth-sepolia.g.alchemy.com/nft/v3/${process.env.ALCHEMY_API_KEY}/getNFTsForContract?contractAddress=0x6a7415A8d287b343417150E7a74069D6D121372d&withMetadata=true`;
const { contractAddress } = req.query;

// Fetch data
const response = await fetch(url);

// Check if the response was successful
if (!response.ok) {
throw new Error(`API responded with status code ${response.status}`);
// Check if contractAddress is provided
if (!contractAddress) {
return res.status(400).json({ error: "Contract address is required" });
}

const data = await response.json();
const nftsArray = data.nfts;
// Fetch data using axios
const response = await axios.get(
`https://eth-sepolia.g.alchemy.com/nft/v3/${process.env.ALCHEMY_API_KEY}/getNFTsForContract`,
{
params: {
contractAddress,
withMetadata: true,
},
},
);

// Extract data from response
const nftsArray = response.data.nfts;

res.status(200).json(nftsArray);
} catch (error) {
// Log the error for server-side debugging
console.error("Failed to fetch NFT data for contract", error);

// Send a generic error response to the client
// Send a response with the error message
res.status(500).json({ error: "Failed to fetch NFT data for contract" });
}
}
53 changes: 20 additions & 33 deletions packages/nextjs/pages/collection.tsx
Original file line number Diff line number Diff line change
@@ -1,55 +1,42 @@
import { useEffect, useState } from "react";
import Image from "next/image";
import type { NextPage } from "next";
import useSWR from "swr";
import { RarityTable } from "~~/components/only-buildors/";
import { useScaffoldContractRead } from "~~/hooks/scaffold-eth/";
import { useDeployedContractInfo } from "~~/hooks/scaffold-eth/";

// Define the fetcher function for SWR
const fetcher = (url: string) => fetch(url).then(res => res.json());

const Collection: NextPage = () => {
const [bgNft, setBgNft] = useState<any>(null);
const [nfts, setNfts] = useState<any>([]);

const { data: base64encodedTokenUri } = useScaffoldContractRead({
contractName: "OnlyBuidlorsNft",
functionName: "tokenURI",
args: [0n],
});
const { data: onlyBuildorsNftContract } = useDeployedContractInfo("OnlyBuidlorsNft");

const getNftsForContractUrl = onlyBuildorsNftContract?.address
? `/api/get-nfts-for-contract?contractAddress=${onlyBuildorsNftContract?.address}`
: null;

const { data: nftsData, error: nftsError } = useSWR(getNftsForContractUrl, fetcher);

if (nftsError) {
console.log("nftsError", nftsError);
}

useEffect(() => {
const fetchNftData = async () => {
if (nftsData) {
try {
const response = await fetch("/api/get-nfts-for-contract");
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
const decodedNfts = data.map((nft: any) => {
const decodedNfts = nftsData.map((nft: any) => {
const utf8String = Buffer.from(nft.raw.tokenUri, "base64").toString("utf-8");
return JSON.parse(utf8String);
});
setNfts(decodedNfts);
} catch (e) {
console.log("error", e);
}
};

fetchNftData();
}, []);

console.log("nfts", nfts);

useEffect(() => {
if (!base64encodedTokenUri) return;
// Decode the Base64 string
const decodedString = atob(base64encodedTokenUri);

// Parse the JSON metadata
const metadata = JSON.parse(decodedString);

// Set the NFT data
setBgNft(metadata);
}, [base64encodedTokenUri]);
}
}, [nftsData]);

console.log(bgNft);
return (
<>
<section className="p-5 md:p-10 xl:p-14">
Expand Down
155 changes: 78 additions & 77 deletions packages/nextjs/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@ import useSWR from "swr";
import { useAccount } from "wagmi";
import { CheckIcon } from "@heroicons/react/24/outline";
import { MetaHeader } from "~~/components/MetaHeader";
// import { BuidlGuidlLogo } from "~~/components/only-buildors";
import { Button } from "~~/components/only-buildors/";
import { useDeployedContractInfo, useScaffoldContractRead, useScaffoldContractWrite } from "~~/hooks/scaffold-eth/";

// Define the steps for the minting process outside component to save resources
const steps = [
{
number: 1,
text: <>Send a transaction to NFT contract to initiate a request to chainlink functions node</>,
text: <>Send a transaction to on chain NFT contract to send a request to chainlink functions node</>,
},
{
number: 2,
text: (
<>Wait for chainlink node to execute off chain API call to BuidlGuidl server and respond to the NFT contract</>
<>
Wait for chainlink node to execute off chain API call to BuidlGuidl server and relay the response to the NFT
contract
</>
),
},
{
Expand All @@ -28,7 +30,7 @@ const steps = [
},
];

// Define the subscription ID for the Chainlink functions
// Define the subscription ID for the Chainlink functions (specific to each network)
const SUBSCRIPTION_ID = 1905n;

// Define the fetcher function for SWR
Expand Down Expand Up @@ -155,90 +157,89 @@ const Home: NextPage = () => {
return (
<>
<MetaHeader />
{/* <section className="grow flex flex-col"> */}
<section className="">
<div className="p-5 md:p-10 lg:px-16 2xl:p-24">
<div className="grid grid-cols-1 2xl:grid-cols-2 gap-14 pb-20 items-end border-b border-primary">
<section className="p-5 md:p-10 lg:px-16 2xl:p-24 grow flex flex-col">
<div className="grid grid-cols-1 2xl:grid-cols-2 gap-14 pb-20 items-end border-b border-primary">
<div className="flex justify-center lg:justify-start">
<div>
<h1 className="text-5xl md:text-6xl lg:text-8xl font-lucky">
<h1 className="text-6xl md:text-7xl lg:text-8xl font-lucky">
<div>ONLY</div> BUIDLORS
</h1>
<div className="text-xl lg:text-2xl xl:text-3xl">
<div className="text-xl sm:text-2xl xl:text-3xl">
A dynamic SVG NFT collection for BuidlGuidl members only.
</div>
</div>
<div className="flex justify-center">
<Image
src={imgSrc}
width={800}
height={700}
alt="dynamic image of minting proccess and final NFT"
className="rounded-xl"
/>
</div>
</div>
<div className="flex justify-center">
<Image
src={imgSrc}
width={800}
height={700}
alt="dynamic image of minting proccess and final NFT"
className="rounded-xl"
/>
</div>
</div>

<div className="pt-20 pb-10">
{isBuilder ? (
<div className="grid grid-cols-1 2xl:grid-cols-2 gap-8 items-center">
<div>
{steps.map(step => (
<div key={step.number} className="text-2xl flex gap-4 mb-5 items-start">
<div
style={{ minWidth: "40px" }}
className={`${
stepsCompleted >= step.number ? "bg-green-600" : "bg-primary"
} font-bold w-10 h-10 flex items-center justify-center rounded-full text-primary-content`}
>
{stepsCompleted >= step.number ? <CheckIcon className="w-6 h-6 text-white" /> : step.number}
</div>
<div>{step.text}</div>
<div className="grow flex flex-col items-center justify-center py-10">
{isBuilder ? (
<div className="grid grid-cols-1 2xl:grid-cols-2 gap-20 items-center">
<div>
{steps.map(step => (
<div key={step.number} className="text-2xl flex gap-4 mb-8 items-start">
<div
style={{ minWidth: "40px" }}
className={`${
stepsCompleted >= step.number ? "bg-green-600" : "bg-primary"
} font-bold w-10 h-10 flex items-center justify-center rounded-full text-primary-content`}
>
{stepsCompleted >= step.number ? <CheckIcon className="w-6 h-6 text-white" /> : step.number}
</div>
))}
</div>

<div className="flex flex-col justify-center items-center">
{hasMinted ? (
<Link href="/collection">
<Button>View Collection</Button>
</Link>
) : buidlCount && buidlCount > 0n ? (
<Button onClick={() => mintNft()}>Mint NFT</Button>
) : (
<Button onClick={() => sendRequest()} disabled={stepsCompleted >= 1}>
{stepsCompleted >= 1 ? "Request proccessing..." : "Send Request"}
</Button>
)}
</div>
<div>{step.text}</div>
</div>
))}
</div>
) : (
<div className="text-xl lg:text-2xl xl:text-3xl text-center">
<div className="mb-10">
Please connect the wallet associated with your{" "}
<a
className="underline text-accent"
target="_blank"
rel="noopener noreferrer"
href="https://app.buidlguidl.com/builders/0x41f727fA294E50400aC27317832A9F78659476B9"
>
BuidlGuidl profile
</a>{" "}
in order to mint an NFT
</div>
<div>
If you are not a member yet, join us by the completing challenges at{" "}
<a
className="underline text-accent"
target="_blank"
rel="noopener noreferrer"
href="https://speedrunethereum.com/"
>
Speedrun Ethereum
</a>
</div>

<div className="flex flex-col justify-center items-center">
{hasMinted ? (
<Link href="/collection">
<Button>View Collection</Button>
</Link>
) : buidlCount && buidlCount > 0n ? (
<Button onClick={() => mintNft()}>Mint NFT</Button>
) : (
<Button onClick={() => sendRequest()} disabled={stepsCompleted >= 1}>
{stepsCompleted >= 1 ? "Request proccessing..." : "Send Request"}
</Button>
)}
</div>
)}
</div>
</div>
) : (
<div className="text-xl sm:text-2xl xl:text-3xl text-center">
<div className="mb-10">
Connect the wallet associated with your{" "}
<a
className="underline text-accent"
target="_blank"
rel="noopener noreferrer"
href="https://app.buidlguidl.com/builders/0x41f727fA294E50400aC27317832A9F78659476B9"
>
BuidlGuidl profile
</a>{" "}
to mint an NFT
</div>
<div>
If you are not a member yet, join us by the completing challenges at{" "}
<a
className="underline text-accent"
target="_blank"
rel="noopener noreferrer"
href="https://speedrunethereum.com/"
>
Speedrun Ethereum
</a>
</div>
</div>
)}
</div>
</section>
</>
Expand Down

0 comments on commit 3433eaf

Please sign in to comment.