[SOLVED] NFT Gated Page Redirect

this code checks if the user is logged in, you can add this before checking for nfts

change the destination to /signin

Thanks, yes that’s all already there from the tutorial.
Problem is as I say, the redirect to the videos page from the protected page doesn’t protect the videos page. So what I want to do is inject the videos page code into the projected page.

The code i sent is meant to be used on your /videos page inside getServerSideProps

Thanks, I have added this code as per your recommendation:

import Head from "next/head";
import Header from "../Components/Header";
import VideoContainer from "../Components/VideoContainer";
import { useEffect, useState } from "react";
import Data from "../Components/Data";
import RecommendedList from "../Components/RecommendedList";

export default function protectedPage() {
  const [isVideo, setVideo] = useState({
    name: Data[0].videoName,
    videoSrc: Data[0].videoSrc,
  });

  async function getServerSideProps(context) {
    const session = await getSession(context);

    if (!session) {
      return {
        redirect: {
          destination: "/",
          permanent: false,
        },
      };
    }
  }

  useEffect(() => {
    const scrollContainer = (Document =
      document.getElementById("scrollContainer"));
    scrollContainer.addEventListener("wheel", (evt) => {
      evt.preventDefault();
      scrollContainer.scrollLeft += evt.deltaY;
    });
  }, []);

  return (
    <div className="bg-fieldNight bg-cover w-screen h-screen overflow-hidden flex-col ">
      <Head>
        <title>Premium Content</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <Header />

      {/* Main Container */}
      <div className="w-full h-[calc(100%-80px)] flex">
        {/* Main Container */}
        <div className="ml-10 sm:w-[calc(100%-60px)] md:w-[90%] h-full justify-center">
          {/* Thank you */}
          <div className="w-full h-[20%] flex itmes-center justify-center">
            <div
              className=" flex overflow-x-auto items-center scrollbar-none py-2"
              id="scrollContainer"
            >
              <h1 className="text-textColor text-[12px] lg:text-[30px] md:text-[23px] sm:text-[16px]">
                Thank you for all your support! As you have one of my Official
                Proofreader NFTs, here you can find exclusive videos which log
                the writing process of my books from the very first day
              </h1>
            </div>
          </div>
          {/* ------------ */}

          {/* Video Section */}
          <div className="w-full h-[70%] grid grid-cols-3 gap-2 p-2">
            {/* Video Container */}
            <div
              className="bg-black
             col-span-6 sm:col-span-6 md:col-span-2 rounded-lg overflow-hidden flex items-center justify-center"
            >
              <VideoContainer data={isVideo} />
            </div>
            {/* Recommended List */}
            <div className="w-[100%] col-span-6 sm:col-span-6 md:col-span-1 bg-searchBg overflow-y-auto scrollbar-thin scrollbar-thumb-gray-800 rounded-lg">
              <p className="text-textColor text-[18px] font-bold my-2 px-2">
                Book 1 Production Videos
              </p>

              {Data &&
                Data.map((data) => (
                  <div
                    key={data.id}
                    onClick={() =>
                      setVideo({
                        name: data.videoName,
                        videoSrc: data.videoSrc,
                      })
                    }
                  >
                    <RecommendedList
                      imgSrc={data.imgSrc}
                      videoName={data.videoName}
                    />
                  </div>
                ))}
            </div>
          </div>

          {/* ------------ */}
        </div>
      </div>
    </div>
  );
}`

However still the issue remains that the videos page content can be viewed by anyone who has an active metamask session active regardless of whether they hold the NFT or not.

This is my protected page which redirects to the videos page. Ideally what I’d like is to make this the videos page and inject the content of the vdieos page into the if(nftList.raw.total >0) condition.

I have added a note to where I believe this should go, but please do correct me and advise on how to code this in:

import { getSession } from "next-auth/react";
import Moralis from "moralis";
import { EvmChain } from "@moralisweb3/common-evm-utils";

function Protected({ message, nftList }) {
  return (
    <div>
      <h3>Protected content</h3>
      <p>{message}</p>
      <pre>{JSON.stringify(nftList, null, 2)}</pre>
    </div>
  );
}

export async function getServerSideProps(context) {
  const session = await getSession(context);

  if (!session) {
    return {
      redirect: {
        destination: "/",
        permanent: false,
      },
    };
  }

  if (!Moralis.Core.isStarted) {
    await Moralis.start({ apiKey: process.env.MORALIS_API_KEY });
  }

  const nftList = await Moralis.EvmApi.nft.getWalletNFTs({
    chain: EvmChain.ETHEREUM,
    address: session.user.address,
    tokenAddresses: ["0x495f947276749ce646f68ac8c248420045cb7b5e"],
  });

  if (nftList.raw.total > 0) {
    return {
      // Can I add something here to show a videos component which will use the videos page code?
      props: {},
    };
  }

  if (nftList.raw.total < 1) {
    return {
      redirect: {
        permanent: false,
        destination: "/noNFT",
      },
      props: {},
    };
  }
}

export default Protected;

here you can add the check again on your page where you show the videos

  async function getServerSideProps(context) {
    const session = await getSession(context);

    if (!session) {
      return {
        redirect: {
          destination: "/",
          permanent: false,
        },
      };
    }

if (!Moralis.Core.isStarted) {
    await Moralis.start({ apiKey: process.env.MORALIS_API_KEY });
  }

  const nftList = await Moralis.EvmApi.nft.getWalletNFTs({
    chain: EvmChain.ETHEREUM,
    address: session.user.address,
    tokenAddresses: ["0x495f947276749ce646f68ac8c248420045cb7b5e"],
  });
 if (nftList.raw.total < 1) {
    return {
      redirect: {
        permanent: false,
        destination: "/noNFT",
      },
      props: {},
    };
  }

  return {
    props: {},
  };

}

Thanks.

Added this to the videos page and replaced the protected page code with what you recommended above. However issue remains can still access the videos page via url with no check running. It seems the issue may be that the getServerSideProps function in the videos page is showing as “declared but never read”.

I am sorry I know this must be becoming a pain, especially if I’m making obvious errors! As I say this is my first dabbling in coding so it must be frustrating for you. Hugely appreciate your ongoing support.

Video page code:

 import Head from "next/head";
import Header from "../Components/Header";
import VideoContainer from "../Components/VideoContainer";
import { useEffect, useState } from "react";
import Data from "../Components/Data";
import RecommendedList from "../Components/RecommendedList";

export default function protectedPage() {
  const [isVideo, setVideo] = useState({
    name: Data[0].videoName,
    videoSrc: Data[0].videoSrc,
  });

  async function getServerSideProps(context) {    //declared but never read
    const session = await getSession(context);

    if (!session) {
      return {
        redirect: {
          destination: "/",
          permanent: false,
        },
      };
    }

    if (!Moralis.Core.isStarted) {
      await Moralis.start({ apiKey: process.env.MORALIS_API_KEY });
    }

    const nftList = await Moralis.EvmApi.nft.getWalletNFTs({
      chain: EvmChain.ETHEREUM,
      address: session.user.address,
      tokenAddresses: ["0x495f947276749ce646f68ac8c248420045cb7b5e"],
    });
    if (nftList.raw.total < 1) {
      return {
        redirect: {
          permanent: false,
          destination: "/noNFT",
        },
        props: {},
      };
    }

    return {
      props: {},
    };
  }

  useEffect(() => {
    const scrollContainer = (Document =
      document.getElementById("scrollContainer"));
    scrollContainer.addEventListener("wheel", (evt) => {
      evt.preventDefault();
      scrollContainer.scrollLeft += evt.deltaY;
    });
  }, []);

  return (
    <div className="bg-fieldNight bg-cover w-screen h-screen overflow-hidden flex-col ">
      <Head>
        <title>Premium Content</title>
        <link rel="icon" href="/favicon.ico" />
      </Head>
      <Header />

      {/* Main Container */}
      <div className="w-full h-[calc(100%-80px)] flex">
        {/* Main Container */}
        <div className="ml-10 sm:w-[calc(100%-60px)] md:w-[90%] h-full justify-center">
          {/* Thank you */}
          <div className="w-full h-[20%] flex itmes-center justify-center">
            <div
              className=" flex overflow-x-auto items-center scrollbar-none py-2"
              id="scrollContainer"
            >
              <h1 className="text-textColor text-[12px] lg:text-[30px] md:text-[23px] sm:text-[16px]">
                Thank you for all your support! As you have one of my Official
                Proofreader NFTs, here you can find exclusive videos which log
                the writing process of my books from the very first day
              </h1>
            </div>
          </div>
          {/* ------------ */}

          {/* Video Section */}
          <div className="w-full h-[70%] grid grid-cols-3 gap-2 p-2">
            {/* Video Container */}
            <div
              className="bg-black
             col-span-6 sm:col-span-6 md:col-span-2 rounded-lg overflow-hidden flex items-center justify-center"
            >
              <VideoContainer data={isVideo} />
            </div>
            {/* Recommended List */}
            <div className="w-[100%] col-span-6 sm:col-span-6 md:col-span-1 bg-searchBg overflow-y-auto scrollbar-thin scrollbar-thumb-gray-800 rounded-lg">
              <p className="text-textColor text-[18px] font-bold my-2 px-2">
                Book 1 Production Videos
              </p>

              {Data &&
                Data.map((data) => (
                  <div
                    key={data.id}
                    onClick={() =>
                      setVideo({
                        name: data.videoName,
                        videoSrc: data.videoSrc,
                      })
                    }
                  >
                    <RecommendedList
                      imgSrc={data.imgSrc}
                      videoName={data.videoName}
                    />
                  </div>
                ))}
            </div>
          </div>

          {/* ------------ */}
        </div>
      </div>
    </div>
  );
}

the issues is that your have getServerSideProps inside the component, you can move the function out, and also add export

export async function getServerSideProps

That’s it!

Thank you so much for your ongoing support!

2 Likes

Perfect! :star_struck:

If you need any other help, feel free to create another thread

I’m closing this thread as it is solved, we’re available 24/7 to help you out solve your issue :grinning_face_with_smiling_eyes: