Lädt...


🔧 Picshaw - An image sharing app


Nachrichtenbereich: 🔧 Programmierung
🔗 Quelle: dev.to

This is a submission for the The Pinata Challenge

What I Built

Imagine attending an exciting event — like a developer conference—where you meet new people, take tons of photos and vidoes with them, and make lasting memories. But once the event wraps up, you realize you have no easy way to get all those great pictures.

Or consider a wedding ceremony: You know your guests captured beautiful moments throughout the day, but gathering all those photos means individually reaching out to each person. It is definitely not the way to go.

Picshaw offers a simple solution. As an event organizer, you can create a dedicated event folder on the platform, generate a shareable link, and invite your guests to upload the pictures they’ve taken. This way, everyone gets to relive the event from the unique perspectives of all attendees. No more missing moments, just memories shared effortlessly.

Picshaw Features

  • Creating Public and Private events. Public events are showcased on the “Discover Events” page, allowing anyone to browse and explore them. Private events, on the other hand, remain accessible only via a shareable link provided by the event organizer.

Image description

  • Upload Files Effortlessly Guests can easily upload their event photos to the designated event folder, making sure all memorable moments are gathered in one place.

Image description

  • Browse Uploaded Photos with Two Viewing Modes.
    Users can explore photos either in list mode, which snaps images into a feed similar to Instagram, or in grid mode, offering a gallery-style layout.
    Image description
    Image description

  • Easily Share Event Links
    Organizers can generate and share a unique link to invite guests to upload their pictures, streamlining the process of gathering photos.
    Image description

  • Discover Public Events
    Explore and browse all public events from the “Discover Events” page, opening up new ways to experience moments shared by others.

Image description

  • Seamless Authentication Users can sign in quickly and securely using Google sign-in or magic links, making the experience smooth and hassle-free.

Image description

  • Support for dark mode and light mode Image description

Demo

Try out Picshaw live: https://picshaw.vercel.app

My Code

The full code is available on GitHub. Feel free to star the repo and follow me! 😊

This is a Next.js project bootstrapped with create-next-app.

Getting Started

First, run the development server:

npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev

Open http://localhost:3000 with your browser to see the result.

You can start editing the page by modifying app/page.tsx. The page auto-updates as you edit the file.

This project uses next/font to automatically optimize and load Geist, a new font family for Vercel.

Learn More

To learn more about Next.js, take a look at the following resources:

You can check out the Next.js GitHub repository - your feedback and contributions are welcome!

Deploy on Vercel

The easiest way to deploy your Next.js app is to use the Vercel Platform from the creators of Next.js.

Check out our Next.js deployment documentation for more…

Using Pinata

Integrating with Pinata was one of the smoothest parts of the project. Here’s a breakdown of how I implemented it:

1. Initialize the Pinata SDK

I set up a dedicated file, @/lib/pinata.ts, to manage the Pinata configuration:

import { PinataSDK } from "pinata"

export const pinata = new PinataSDK({
    pinataJwt: `${process.env.PINATA_JWT}`,
    pinataGateway: `${process.env.NEXT_PUBLIC_GATEWAY_URL}`
})

2. In my API Route:

  • Ensured my user was logged in: I ensured the user was logged in before uploading.
const session = await auth()

if (!session || !session.user) {
   return respondError(new Error("User not authenticated"), "Failed to authenticate user", 401)
    }
  • Validate the Request: I checked if the event exists and ensured the file upload is within limits.
const form = await request.formData()
const files = Array.from(form.values())
const eventSlug = params["event-slug"]

try {
    const event = await prisma.event.findUnique({ where: { slug: eventSlug } })

    if (!event) {
        return respondError(new Error("Event not found"), undefined, 404)
    }

    if (files.length > 50) {
        return respondError(new Error("Limit of 50 files per request"), "Too many files", 400)
    } else if (files.length === 0) {
        return respondError(new Error("At least one file is required"), "No files", 400)
    }
  • Upload files to Pinata The next step is uploading the files.
const res = await Promise.all(files.filter(f => typeof f !== 'string').map(f => pinata.upload.file(f)))
        const successfulUploads = res.filter(r => r.status === "fulfilled")

        const uploadsWithPublicURL = await Promise.all(successfulUploads.map(async r => {
            const publicURL = await pinata.gateways.createSignedURL({ cid: r.value.cid, expires: 60 * 60 * 24 * 365 })
            return { ...r.value, publicURL }
        }))

Note: A better implementation would include checks for failed uploads. This way, users are notified about failed files and can retry uploading them.

  • Save Upload Data to the Database Finally, I saved the upload information into the database using Prisma.
const dbUpload = await prisma.upload.create({
            data: {
                text: `${session.user.name} uploaded ${files.length} images for ${event?.name}`,
                ownerId: session.user?.id ?? "",
                eventId: event?.id ?? "",
                files: {
                    createMany: {
                        data: uploadsWithPublicURL.map(file => {
                            return {
                                ownerId: session.user?.id ?? "",
                                eventId: event?.id ?? "",
                                cid: file.id,
                                publicURL: file.publicURL,
                            }
                        })
                    }
                }
            }
        })

        return respondSuccess(dbUpload, "Uploaded successfully", 201)

This flow ensures the event photos are uploaded securely and efficiently to Pinata, with successful uploads tracked and stored in the database for easy access later.

Rendering the uploaded images in the browser

1. Updating next.config.js

I first had to update the images field in the next.config.js file to allow NextJS optimize images from remote URLs.

/** @type {import('next').NextConfig} */
const nextConfig = {
  images: {
    remotePatterns: [
      {
        hostname: "sapphire-obliged-canidae-626.mypinata.cloud",
        protocol: "https",
      },
    ],
  },
};

export default nextConfig;

2. Fetching Event Data

In my client, I use a custom hook useFetch to fetch the details about the selected event

 const params = useParams();
  const eventSlug = params["event-slug"];
  const [viewMode, setViewMode] = React.useState<"grid" | "list">("grid");
  const router = useRouter();

  const {
    loading,
    data,
    trigger: getEventDetails,
  } = useFetch<void, GetUploadsResponse>(`/api/e/${eventSlug}`, undefined, {
    fetchOnRender: true,
  });

3. Rendering the Images:

I render the retrieved images inside a responsive grrrrrrrrrrid

  <div className="grid grid-cols-3 md:grid-cols-4 gap-1 my-6">
        {photos.map((file) => (
          <Image
            key={file.id}
            src={file.publicURL}
            width={400}
            height={400}
            alt=""
            className="aspect-square object-cover"
          />
      ))}
 </div>

Ideas for improvement

To further improve the app, it would be necessary to add some content moderation features. This would ensure that users don't post NSFW content on public groups. I started integrating with Google Cloud Vision but I didn't have enough time to complete it before the deadline.

Conclusion

Integrating Pinata’s Files API into Picshaw has greatly enhanced how images are uploaded and managed. Pinata provided seamless performance, and its intuitive API made implementation straightforward, allowing me to focus on building the core features of the app while trusting Pinata to handle file storage and delivery efficiently.

Overall, Pinata has been an essential tool in building a reliable and smooth image management system for Picshaw.

Also, I really enjoyed building Picshaw.

Follow me on X @jeremiahlena13

...

🔧 Picshaw - An image sharing app


📈 51.78 Punkte
🔧 Programmierung

🔧 Picshaw - An image sharing app


📈 51.78 Punkte
🔧 Programmierung

🔧 MagPies - An Image sharing app built using Rasengan.js and Netlify CDN Image.


📈 23.28 Punkte
🔧 Programmierung

🕵️ Kajona CMS 4.7 Image Handler /kajona/image.php __construct image Directory Traversal


📈 18.6 Punkte
🕵️ Sicherheitslücken

🔧 Avoiding lock-in for your image pipeline with Nuxt Image and Netlify Image CDN


📈 18.6 Punkte
🔧 Programmierung

🐧 Image Roll - my new simple and fast GTK image viewer with basic image manipulation tools. Written in Rust.


📈 18.6 Punkte
🐧 Linux Tipps

🕵️ Plone up to 4.3.2 Image Tag Image.py OFS.Image Reflected cross site scriting


📈 18.6 Punkte
🕵️ Sicherheitslücken

🕵️ Free Lossless Image Format 0.3 LibPNG image/image-png.cpp flif File memory corruption


📈 18.6 Punkte
🕵️ Sicherheitslücken

🕵️ Free Lossless Image Format 0.3 LibPNG image/image-png.cpp flif File memory corruption


📈 18.6 Punkte
🕵️ Sicherheitslücken

🕵️ MiniMagick up to 4.9.3 lib/mini_magick/image.rb Image.open Image File privilege escalation


📈 18.6 Punkte
🕵️ Sicherheitslücken

🕵️ Free Lossless Image Format 0.3 LibPNG image/image-png.cpp memory corruption


📈 18.6 Punkte
🕵️ Sicherheitslücken

🕵️ Free Lossless Image Format 0.3 image/image-pnm.cpp image_load_pnm denial of service


📈 18.6 Punkte
🕵️ Sicherheitslücken

🕵️ Free Lossless Image Format 0.3 image/image-pnm.cpp image_load_pnm Denial of Service


📈 18.6 Punkte
🕵️ Sicherheitslücken

🕵️ Kajona CMS 4.7 Image Handler /kajona/image.php __construct image Directory Traversal


📈 18.6 Punkte
🕵️ Sicherheitslücken

🔧 How to implement Image Upload with Expo Image Picker in React Native for a professional app launched app.


📈 17.58 Punkte
🔧 Programmierung

🔧 How to implement Image Upload with Expo Image Picker in React Native for a professional app launched app.


📈 17.58 Punkte
🔧 Programmierung

🕵️ Medium CVE-2018-9059: Sharing-file Easy file sharing web server


📈 16.58 Punkte
🕵️ Sicherheitslücken

🕵️ Medium CVE-2017-17627: Readymade video sharing script project Readymade video sharing script


📈 16.58 Punkte
🕵️ Sicherheitslücken

📰 Disney Officially Launches Password-Sharing Crackdown With Paid Sharing Program


📈 16.58 Punkte
📰 IT Security Nachrichten

📰 Disney Officially Launches Password-Sharing Crackdown With Paid Sharing Program


📈 16.58 Punkte
📰 IT Security Nachrichten

📰 File-Sharing Giant Uloz Bans File-Sharing Citing EU's Digital Services Act


📈 16.58 Punkte
📰 IT Security Nachrichten

🔧 Collaboration Grows by Sharing: Project Sharing in Design Center [Video]


📈 16.58 Punkte
🔧 Programmierung

🔧 Challenges With Traditional Data Sharing and Emergence of Delta Sharing to the Rescue


📈 16.58 Punkte
🔧 Programmierung

📰 Netflix Account- und Passwort-Sharing: 100 Millionen „Sharing-Haushalte“ laut der Quartalszahlen


📈 16.58 Punkte
📰 IT Nachrichten

📰 Databricks Delta Sharing protocol secures real time data sharing between organizations


📈 16.58 Punkte
📰 IT Security Nachrichten

🕵️ Medium CVE-2018-18912: Sharing-file Easy file sharing web server


📈 16.58 Punkte
🕵️ Sicherheitslücken

📰 MISP - Malware Information Sharing Platform and Threat Sharing


📈 16.58 Punkte
📰 IT Security Nachrichten

🔧 Image search with Streamlit in Snowflake (SiS) Part 1 - Creating an image gallery app -


📈 14.99 Punkte
🔧 Programmierung

🪟 Image Creator from Designer’s swipe bug only shows one image in Copilot mobile app


📈 14.99 Punkte
🪟 Windows Tipps

⚠️ Image Sharing Script 4.13 Cross Site Scripting / SQL Injection


📈 14.49 Punkte
⚠️ PoC

matomo