DEV Community

Cover image for Break-Time: Digital Time Capsules with Pinata
Ansell Maximilian
Ansell Maximilian

Posted on

Break-Time: Digital Time Capsules with Pinata

This is a submission for the The Pinata Challenge

Quick Links

What I Built

A lot of our footprint we leave in this world are increasingly becoming digital. I won't argue whether that's a bad or good thing, but I will say that our footprint, our memories, that we leave is important regardless if it's digital or physical.

That's why I decided to build Break Time. Break Time is a web app where you can make digital time capsules, store your digital memories, and then open them at a later date to look back on those fond memories so they aren't lost.

I call the time capsules Pinatas, named after the service used to make this app happen (Pinata). But I also decided with this term because it's apt for what it represents, a container with valuables and goodies (memories) inside which you can access by opening said container.

Example Usage

Here I'll give a quick overview of how you would use this app (I'll go into detail in a later section).

Let's say your best friend, John, and your other best friend, Jill, is about to get married in a few weeks. You get the great idea to start storing memories of the upcoming marriage ceremony up to their 5th anniversary (you're confident they love each other deeply and will last forever).

You start thinking of creating a time capsule to store those memories and then let them open it at the start of their 5th anniversary to look back on their fond memories.

However, you realize that even now most of your memories with them are digital (videos, photos, PDFs, SVGs, etc.). And you suspect it will be the same case with their marriage, considering it's 2024.

So you decide to use Break Time. You create an account, encourage your circle of friends, including Jill and John themselves to create their own accounts. You invite them to be your friends in the app.

Then, you create a Pinata (Time Capsule). You name it "John and Jill's 5th Anniversary", you create some description, such as "Hi friends! 🌸 Let’s celebrate the love John and Jill will have built over the next five years by capturing memories, well-wishes, and hopes for their future. When we unlock this capsule later, we’ll look back, laugh, and cherish how far they’ve come. Leave your messages, photos, or videos and help make this anniversary truly unforgettable!". You might even add a little thumbnail of their first date.

Next you decide who should be able to contribute to this Pinata (Time Capsule). You include all your friends to be contributors. And you set John and Jill as the sole openers of the Pinata, so only they can decide to open it. You set the minimum time to the next five years, so you force yourselves, including John and Jill, to wait and not spoil the memories before the 5th anniversary.

And then finally, after five years, after you've built up a mountain of memories, you can finally ask John and Jill to open the Pinata. Then you'll be able to download and look back on all those memories with a smile.

Other Usages

Of course the timeline doesn't necessarily have to span 5 years. You can even create Pinatas to store memories of a 1-week camping trip. It's up to you!

Demo

Video


Demo Video on YouTube

Application Flow

Home Page

Image description

Authentication Pages

Image description

Image description

Dashboard

Image description

This is where most of your data will be listed. On the top you'll see the Pinatas you've created. On the bottom right, you'll see a list of users you've added as friends. In the same place, there's also a form where you can add users as your friends by their usernames.

On the bottom left, you'll see a list of Pinatas in which others have created but involves you somehow, either you've been added as a contributor, an opener, or both.

Create Pinata

Image description

Here's where you create your Pinata. You can set its title, description, and thumbnail picture.

You can also set who can contribute to the Pinata once it has been created, as well as limit when invited users can contribute to the Pinata. You can set when allowed contributors can start contributing as well as when the Pinata will stop accepting contributions.

Then, you can set attributes related to opening the Pinata. You can set a minimum open time, which will mark the earliest date and time the Pinata can be opened. You can also set who will be required to open this Pinata. You can also omit one of these values. For example, you can set a minimum open time and omit the "openers", this way, Pinatas will open automatically when the time comes. Or you can only include allowed "openers"; this way, the Pinata will open once all the openers have opened the Pinata. Or you can combine include both these values so "openers" can only open the Pinata after the set date time.

You can exclude yourself from contributing or opening.

Viewing Pinata

Unopened Pinata

Image description

This is what you'll see when you click on an unopened Pinata. You'll see it's details. This is also where you'll contribute your digital memories/files to the Pinata (if you're part of the contributors).

You won't be able to view the files you've contributed in this state (you have to open it).

You'll also open the Pinata in this view. It's in real time so when all users open the Pinata, or the it's just time for the Pinata to open, you'll see the Pinata in the open state. See below.

Open Pinata

Image description

This is the Pinata in the open state. You can no longer contribute files, but now you'll be able to see all those memories you've created and uploaded.

Profile Page

Image description

My Code

The full code can be found on my Github

Quick Links

What I Built

Break Time is a web app for creating digital time capsules (called Pinatas) to store and cherish memories. Pinatas are opened on a future date to relive those memories, making sure your digital footprints remain meaningful.

Example Usage

You can create a Pinata named "John and Jill’s 5th Anniversary", where friends store messages, photos, or videos leading up to their 5th anniversary. Only John and Jill can open it after five years, unlocking all the memories inside.

The app allows contributors to add memories, with customizable settings for contribution timelines and who can open the Pinata.

Demo

Demo Video on YouTube

Screenshots

Home Page

Home

Dashboard

Dashboard

Create Pinata

Create Pinata

Tech Stack

  • Next.js for frontend and secure route handlers
  • Appwrite for authentication and metadata storage
  • Pinata for file storage on IPFS

Example Code for Route Handlers

export async function
Enter fullscreen mode Exit fullscreen mode

More Details

Tech Stack

Next.js

I'm using Ol' Reliable Next.js as my front end as well as to create a few secure route handlers for handling file upload on the client side, as recommended by Pinata.

// getting a one time use key to use in the front end
 export async function GET() {
  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

I would call this each time I want to upload a file, because Next.js limits on how big a file you can send on the server.

Similarly, I would call another route handler to get a secure URL for a certain file:

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

Appwrite

I needed authentication as well as a place to store some metadata for the files, so I used Appwrite's authentication and database in conjunction with Pinata, so I can store extra details to files uploaded to Pinata, such as file names, cid, etc.

So I would fetch a file metadata in Appwrite (in this case, it would be a contribution for a Pinata), then I would use the resulting metadata to retrieve a file from Pinata by its CID.

const openFile = async (contribution: Contribution) => {
    // getFileURL calls the route handler that returns a signed URL for a particular file by its CID
    const fileURL = (await getFileUrl(contribution.cid)) as string;

    window.open(fileURL, "_blank");
  };
Enter fullscreen mode Exit fullscreen mode

How Pinata was Used

Pinata (the service) was used for this app's main file storage. It stores a Pinata's (the time capsule) thumbnail, a user's profile picture, and, most importantly, digital contributions for the Pinata.

Here's a simplified version of how I implemented Pinata to upload/replace a user's profile picture:

// gets set on the onChange prop of an input element of type "file"
const handlePictureChange: React.ChangeEventHandler<
    HTMLInputElement
  > = async (e) => {
    if (file) {
      // get the one time key
      const keyRequest = await fetch("/api/key");
      const keyData = await keyRequest.json();

      // upload to pinata
      const upload = await pinata.upload.file(file).key(keyData.JWT);

      // delete the original profile picture (replace) by its file id
      if (user.profile.profilePictureFileId) {
        const delRes = await fetch("/api/delete-files", {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
          },
          body: JSON.stringify({
            fileIds: currentUser.profile.profilePictureFileId,
          }),
        });
      }

      // update metadata on appwrite
      await databases.updateDocument(
        config.dbId,
        config.userProfileCollectionId,
        currentUser.profile.$id,
        {
          profilePictureFileId: upload.id,
          profilePictureCid: upload.cid,
        }
      );
    }
  };
Enter fullscreen mode Exit fullscreen mode

Similar flow was used to upload file contributions as well as uploading Pinata thumbnails.

Top comments (2)

Collapse
 
jagroop2001 profile image
Jagroop Singh

@ansellmaximilian ,this project deserves to be the challenge winner.

Collapse
 
ansellmaximilian profile image
Ansell Maximilian

haha thanks. I liked your submission. great idea!