DEV Community

Cover image for Introducing Ready - Your Own PDF Reader & Library
Suman Sourabh
Suman Sourabh

Posted on

Introducing Ready - Your Own PDF Reader & Library

This is a submission for the The Pinata Challenge

What I Built

Ready is a full stack application where you can upload your PDFs, read them inside the application itself, and find other PDFs uploaded by other users just like you. All you need is a web browser and an 'okayish' network!

I have seen many people hesitate when they have to upload their stories online. I want to give them a platform where they can always be "Ready" to upload their books/stories, so that other users can read them too.

On the other hand, users can use this website as a cloud storage for their own documents. All they need to do is set the book as "Private" and their book or doc will not be visible to other users!

I have tried to make this site completely accessible by allowing keyboard navigation while uploading PDFs and editing them. Moreover, I have added tooltips and alt text for the images wherever possible.

Capabilities/Features

  • User authentication with email, password & Google
  • Upload PDF
  • Set PDF as private or public
  • Read PDF
  • Download PDF
  • Edit PDF details
  • Delete PDF and details
  • Browse the library of PDFs
  • Wishlist any PDF
  • See user statistics on dashboard (PDFs uploaded, wish listed)
  • Dark mode support with system option

Tech used

  • Next.js
  • Pinata
  • Appwrite
  • shadcn
  • Tailwind CSS

Demo

Here's the live version: Ready

My Code

alt text

Ready

Ready is a full stack application where you can upload your PDFs, read them inside the application itself, and find other PDFs uploaded by other users just like you. All you need is a web browser and an 'okayish' network!

Why?

I have seen many people hesitate when they have to upload their stories online. I want to give them a platform where they can always be "Ready" to upload their books/stories, so that other users can read them too.

On the other hand, users can use this website as a cloud storage for their own documents.

I have tried to make this site completely accessible by allowing keyboard navigation while uploading PDFs and editing them. Moreover, I have added tooltips and alt text for the images wherever possible.

Capabilities/Features

  • User authentication with email, password & Google
  • Upload PDF
  • Read PDF
  • Download PDF
  • Edit PDF details
  • Delete PDF and…

Screenshots

Upload PDF page

Image description

Edit PDF page

Image description

Login page

Image description

Library page - Grid view (User not logged in)

Image description

Library page - List view (User not logged in)

Image description

Library page - List view (User is logged in) on Light theme

Image description

Wishlist page - When there are no wish list items

Image description

Read PDF page

Image description

More Details

I used Pinata File API on a couple of occasions:

  • Upload PDF
  • Upload a thumbnail image

Let's look at them below:

1. Upload PDF Document with Pinata

const uploadDocFile = async () => {
    if (!file) {
      return;
    }

    try {
      setUploading(true);
      const res = await uploadFile(file);
      const url = await res.json();
      setUrl(url);
      setUploading(false);
      if (!!url) {
        toast({
          description: "Document uploaded successfully!",
        });
      }
    } catch (e) {
      setUploading(false);
    }
  };
Enter fullscreen mode Exit fullscreen mode

In the above code, when the user clicks on "Upload Document" button, uploadDocFile function gets called. This function is responsible for uploading the document to Pinata and get the file URL in return.

uploadFile function

This function works behind the scenes of the uploadDocFile function.

import { pinata } from "./config";

export const uploadFile = async (file) => {
  const keyRequest = await fetch("/api/key");
  const keyData = await keyRequest.json();
  const upload = await pinata.upload.file(file).key(keyData.JWT);
  const urlRequest = await fetch("/api/sign", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ cid: upload.cid }),
  });
  return urlRequest;
};
Enter fullscreen mode Exit fullscreen mode

In the above code, 3 things are happening:

  • Fetch the API key from Pinata
  • Upload the file with the help of the API key
  • Get the file URL, so that we can store it in database

Fetch the API key from Pinata

To upload files to Pinata, an API key is required. I used Next.js built-in API routes and created a GET request to fetch the API key from Pinata.

import { pinata } from "@/utils/config";
import { NextResponse } from "next/server";

export const dynamic = "force-dynamic";

export async function GET(req, res) {
  try {
    const uuid = crypto.randomUUID();
    const keyData = await pinata.keys.create({
      keyName: uuid.toString(),
      permissions: {
        endpoints: {
          pinning: {
            pinFileToIPFS: true,
          },
        },
      },
      maxUses: 1,
    });
    return NextResponse.json(keyData, { status: 200 });
  } catch (error) {
    return NextResponse.json(
      { text: "Error creating API Key:" },
      { status: 500 }
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Use the API key to upload file to Pinata

In the uploadFile function, I received the key from Pinata. Here, I used that key to upload the PDF to Pinata.

const upload = await pinata.upload.file(file).key(keyData.JWT);
Enter fullscreen mode Exit fullscreen mode

Get the file URL to store in the database

Using Next.js API routes, I created a POST request by sending the cid property to the Pinata server and get the signed URL in return. This URL will be stored in the database as well as used to download the PDF document.

import { pinata } from "@/utils/config";
import { NextResponse } from "next/server";

export const dynamic = "force-dynamic";

export async function POST(req, res) {
  try {
    const data = await req.json();
    const url = await pinata.gateways.createSignedURL({
      cid: data.cid,
      expires: 500000, // 138 hours
    });
    return NextResponse.json(url, { status: 200 });
  } catch (error) {
    return NextResponse.json(
      { text: "Error creating API Key:" },
      { status: 500 }
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Upload Image with Pinata

Similarly, I also used Pinata File API to upload thumbnail or cover images for the PDFs using the function below. This function uses the same underlying functions that were used to upload the PDF.

const uploadImageFile = async () => {
    if (!image) {
      return;
    }

    try {
      setThumbnailUploading(true);
      const res = await uploadFile(image);
      const url = await res.json();
      setThumbnailUrl(url);
      setThumbnailUploading(false);
    } catch (e) {
      setThumbnailUploading(false);
    }
  };
Enter fullscreen mode Exit fullscreen mode

Conclusion

Ready is a place where you can just sign up or "Continue with Google" to upload your PDF document or read other digital books uploaded by others.

Don't forget to try "Ready"! I need feedback!

If there's any issue with the project, please create an issue on the GitHub repository mentioned in this post or reach out to me or directly comment here.

Thank you!

Top comments (0)