DEV Community

Cover image for Wedding Memories: The Collaborative Wedding Album!
Femi Akinyemi
Femi Akinyemi

Posted on • Edited on

Wedding Memories: The Collaborative Wedding Album!

Wedding Memories

This is a submission for the The Pinata Challenge

What I Built

Wedding Memories is an app designed to capture, share, and cherish every unforgettable moment from your special day! It allows wedding guests to easily upload photos, creating a collaborative album that captures the essence of the celebration. By using Pinata's Files API, the app provides a seamless experience for secure and efficient media uploads and storage.

Demo

Check out the live version of the app here: Wedding Memories

Screenshots:

  • Upload Section - A user-friendly interface for selecting and uploading media files:

Image description

  • Gallery View - Displays all uploaded memories in a responsive layout:

Image description

Image description

  • Mobile-Optimized Design - Ensures smooth user experience across all devices:

Image description

  • Download Options - Guests can download their favourite moments to keep memories forever.

Image description

My Code

You can explore the full code for this project on GitHub:

View on GitHub

More Details

Wedding Memories leverages Pinata’s Files API to build secure and efficient file uploads. Below is a brief breakdown of the integration:

  • File Uploads: Guests can easily upload images directly from their devices. This is accomplished using the pinata.upload.file, which uses JWT-based authentication to ensure secure file handling. Once uploaded, the files are securely stored, and their unique identifiers (CIDs) are generated for retrieval.

  • Local Previews Before Upload: To enhance the guest experience, wedding memories generate local previews of images before uploading them. This feature allows guests to confirm their selections and make adjustments, ensuring only their desired image is submitted.

After successful uploads, the files are rendered using the file API. This guarantees rapid access to the content, providing guests with a seamless experience when viewing shared memories.

  • Download Feature: Guests can now download their favourite photos directly from the app, making it easy to save cherished memories to their devices. The app includes an API route that facilitates the secure downloading of files.

API Integration Breakdown:

The building includes several key API routes, each designed to handle specific functionalities:

  • File Upload Endpoint

This accepts file uploads and stores them using Pinata’s upload function. It then generates a signed URL for easy access to the uploaded file.

import { NextResponse, NextRequest } from "next/server";
import { pinata } from "../../../../utils/config";


export async function POST(request: NextRequest) {
  try {
    const data = await request.formData();
    const file: File | null = data.get("file") as unknown as File;
    const uploadData = await pinata.upload.file(file)
    const url = await pinata.gateways.createSignedURL({
        cid: uploadData.cid,
        expires: 3600,
    });
    return NextResponse.json(url, { status: 200 });
  } catch (e) {
    console.log(e);
    return NextResponse.json(
      { error: "Internal Server Error" },
      { status: 500 }
    );
  }
}

Enter fullscreen mode Exit fullscreen mode
  • API Key Generation (/api/key/route.ts):

Creates a temporary API key with permissions to pin files to IPFS.


/* eslint-disable @typescript-eslint/no-unused-vars */
import { NextResponse } from "next/server";
import { pinata } from "../../../../utils/config";

export const dynamic = "force-dynamic";

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) {
        console.log(error);
        return NextResponse.json({ text: "Error creating API Key:" }, { status: 500 });
    }
}

Enter fullscreen mode Exit fullscreen mode
  • List Files (/api/listfiles/route.ts):

Retrieves a list of uploaded files from Pinata, allowing users to view all shared content in the gallery

import { NextResponse } from "next/server";
import { env } from "process";


export const dynamic = "force-dynamic";

export async function GET() {
  try {
    const options: RequestInit = {
      method: 'GET',
      headers: {
          Authorization: `Bearer ${process.env.NEXT_PUBLIC_PINATA_JWT}`,
      },
      cache: 'no-cache'
    };

    const response = await fetch('https://api.pinata.cloud/v3/files', options);

    if (!response.ok) {
      return NextResponse.json({ text: "Error listing files" }, { status: response.status });
    }

    const { data } = await response.json();
    return NextResponse.json(data, { status: 200 });
  } catch (error) {
    console.log(error, "Error listing files");
    return NextResponse.json({ text: "Error listing files" }, { status: 500 });
  }
}

Enter fullscreen mode Exit fullscreen mode
  • Image Proxy (/api/proxy/route.ts):

Acts as a proxy for fetching images from external sources, ensuring users can easily access and download their images.

import { NextResponse } from 'next/server';


export async function GET(request: Request) {
  const { searchParams } = new URL(request.url);
  const imageUrl = searchParams.get('url'); // Get the image URL from the query string

  if (!imageUrl) {
    return NextResponse.json({ error: 'Image URL is required' }, { status: 400 });
  }

  try {
    const response = await fetch(imageUrl);

    if (!response.ok) {
      return NextResponse.json({ error: 'Failed to fetch image' }, { status: response.status });
    }

    const contentType = response.headers.get('content-type') || 'application/octet-stream';
    const imageBuffer = await response.arrayBuffer();

    return new NextResponse(imageBuffer, {
      headers: {
        'Content-Type': contentType,
        'Content-Disposition': 'attachment; filename="downloaded_image"',
      },
    });
  } catch (error) {
    console.error('Error fetching image:', error);
    return NextResponse.json({ error: 'Error fetching image' }, { status: 500 });
  }
}

Enter fullscreen mode Exit fullscreen mode
  • Signed URL Creation (/api/sign/route.ts):

Generates signed URLs for uploaded files.


import { type NextRequest, NextResponse } from "next/server";
import { pinata } from "../../../../utils/config";

export const dynamic = "force-dynamic";

export async function POST(req: NextRequest) {
  try {
    const data = await req.json();
    const mimeType = data.mime_type;

    let url;

    if (mimeType === 'video/mp4') {
      url = await pinata.gateways.createSignedURL({
        cid: data.cid,
        expires: 7776000,
      })
    } else {
      url = await pinata.gateways.createSignedURL({
        cid: data.cid,
        expires: 7776000,
      }).optimizeImage({
        width: 300,
        height: 300,
        format: "webp",
        fit: "contain",
        quality: 90,
        dpr: 2,
        sharpen: 1,

      });
    }

    return NextResponse.json(url, { status: 200 });
  } catch (error) {
    console.log(error);
    return NextResponse.json({ text: "Error creating signed URL:" }, { status: 500 });
  }
}

Enter fullscreen mode Exit fullscreen mode

The app is built with:

  • Pinata: File API

  • Frontend: React, Next.js, Framer Motion

  • Styling: Tailwind CSS for responsive, beautiful layouts.

  • Hosting: Amplify for deployment.

Conclusion

Weddings are joyous occasions filled with love, laughter, and countless memorable moments. Capturing these precious memories is essential, and with the Wedding Memories app, it's now easier than ever!

This application allows every guest to take lovely pictures on the event day and share them seamlessly, ensuring that no moment is missed and everyone has access to the collective memories of the celebration.

The app harnesses the power of Pinata's Files API to provide a secure and efficient way for guests to upload and share their photos and videos, creating a collaborative album that beautifully encapsulates the essence of the day.

For more details on how the app integrates with Pinata and to explore its capabilities, please refer to the Pinata documentation.

Thank you.


Top comments (25)

Collapse
 
jaymeeu profile image
Abdulrasaq Jamiu Adewuyi

Wow... great project, am definitely pulling this, I hope you are open for contribution?

thanks for sharing

Collapse
 
femi_akinyemi profile image
Femi Akinyemi

Thank you for your kind words and interest in the project! Yes, contributions are welcome.

Collapse
 
jaymeeu profile image
Abdulrasaq Jamiu Adewuyi

Great stuff

Collapse
 
onlyoneerin profile image
The ERIN

Wow! This was well explained, and the project was properly built.

Collapse
 
femi_akinyemi profile image
Femi Akinyemi

Thank you, ERIN! Glad you liked it.

Collapse
 
nmaties profile image
Nicolae Maties

ievent.ro - found this one which is pretty impressive

Collapse
 
xiaowo profile image
harry li

How to ensure data security?

Collapse
 
femi_akinyemi profile image
Femi Akinyemi

Thank you @xiaowo. So, this first phase is for the users to use their personal access token. But, the next phase will be the integration of Authentication & Authorization, Secure and Personal Dashboard Access, and Data Encryption.

Collapse
 
westace10 profile image
Akeem Ashaolu

Awesome stuff… really thoughtful. I know a product that can leverage your app, the api precisely. Check Ahavaplan…

Collapse
 
femi_akinyemi profile image
Femi Akinyemi

Great ro hear! I'm going to check them out. Thanks @westace10

Collapse
 
ronke_akinyemi profile image
Ronke Akinyemi

This project is impressive, great job!

Collapse
 
femi_akinyemi profile image
Femi Akinyemi

Thank you, Ronke! Glad you liked it.

Collapse
 
andrewjack01 profile image
andrewjack01

This is a nice project. Well done!

Collapse
 
femi_akinyemi profile image
Femi Akinyemi

Thank you Andrew

Collapse
 
brolaz_enterprise_26f11ba profile image
Brolaz Enterprise

Weldone! This is a nice project, but how do you intend to manage NextJS Optimized Image Billing?

Collapse
 
femi_akinyemi profile image
Femi Akinyemi

Thanks for liking the project! I initially deployed it on Next.js, but after exhausting the free tier for Image Optimization, I switched to Amplify. The app is now live on Amplify, but I'll revisit Next.js later to investigate the usage issue.

Collapse
 
jumoke_akinyemi profile image
Jumoke Akinyemi

Great project!👍

Collapse
 
autoclips__ai profile image
Autoclips_ AI

I like what Pinata is doing