Skip to content

Photo carousel with private Supabase Storage - #10

Merged
rastermanden merged 4 commits into
mainfrom
claude/happy-kapitsa-4d8bad
Apr 18, 2026
Merged

Photo carousel with private Supabase Storage#10
rastermanden merged 4 commits into
mainfrom
claude/happy-kapitsa-4d8bad

Conversation

@rastermanden

Copy link
Copy Markdown
Owner

Summary

  • Multi-photo carousel in gem detail view — arrows, dot navigation, smooth slide transition
  • Multiple photo upload in the add form — tap zone repeatedly to append photos; × to remove before saving
  • Photos stored in private Supabase Storage (gem-photos bucket) using signed URLs (1h expiry)
  • Signed URLs generated on-demand for both carousel and list thumbnails
  • Handles legacy public URLs stored before bucket was made private

Supabase setup required

Run in SQL Editor if not already done:

Storage RLS (gem-photos bucket must be private):

CREATE POLICY "Authenticated upload to gem-photos"
ON storage.objects FOR INSERT TO authenticated
WITH CHECK (bucket_id = 'gem-photos' AND auth.uid()::text = split_part(name, '/', 1));

CREATE POLICY "auth_read" ON storage.objects FOR SELECT TO authenticated
USING (bucket_id = 'gem-photos');

gem_photos table (optional — enables multi-photo per gem):

CREATE TABLE gem_photos (
  id uuid DEFAULT gen_random_uuid() PRIMARY KEY,
  gem_id uuid REFERENCES gems(id) ON DELETE CASCADE,
  photo_url text NOT NULL,
  sort_order int DEFAULT 0,
  created_at timestamptz DEFAULT now()
);
ALTER TABLE gem_photos ENABLE ROW LEVEL SECURITY;
CREATE POLICY "gem_photos_policy" ON gem_photos
  USING (gem_id IN (SELECT id FROM gems WHERE user_id = auth.uid()))
  WITH CHECK (gem_id IN (SELECT id FROM gems WHERE user_id = auth.uid()));

Test plan

  • Add gem with 3 photos — carousel shows all 3 with arrows and dots
  • Tap × on a thumbnail before saving — excluded from upload
  • Open existing gem with single photo_url — displays correctly in carousel
  • Open gem with no photos — emoji header shown
  • Thumbnails in list load correctly via signed URLs

🤖 Generated with Claude Code

rastermanden and others added 4 commits April 18, 2026 14:48
Photos were always saved as null in the gems table, so the carousel
had nothing to fall back to when gem_photos table doesn't exist yet.
Now uploads serially, saves first URL to gems.photo_url via UPDATE,
then inserts all URLs into gem_photos.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rlay

showDetail is async (fetches gem_photos) but was called without await,
so the overlay opened before the modal HTML was populated.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Store storage paths instead of public URLs in the database.
Generate signed URLs (1h) on-demand when showing carousel and thumbnails.
Handles legacy http:// URLs from previous saves.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Handles existing gem_photos rows that stored full public URLs instead of paths.
Extracts the storage path and creates a signed URL for private bucket access.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@rastermanden
rastermanden merged commit 3530b49 into main Apr 18, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant