Wedding Memories: The Collaborative Wedding Album!

Femi Akinyemi - Oct 11 - - Dev Community

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 ${env.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: Vercel 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.


. . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player