diff --git a/bounty-6/DESIGN.md b/bounty-6/DESIGN.md new file mode 100644 index 00000000..abe150a7 --- /dev/null +++ b/bounty-6/DESIGN.md @@ -0,0 +1,75 @@ +# Warpspeed Bounty #6 — Enhanced Image Preview + +## Bounty Value +**$660** + +## Goal +Build a full-screen image preview modal (lightbox) for React Native + TypeScript that supports: +- Tap to open +- Pinch-to-zoom and pan +- Swipe navigation between multiple images +- Smooth open/close transitions +- Download / share / delete actions + +## Architecture + +``` +ImagePreview (Modal) +├── AnimatedBackdrop — fade-in/out overlay +├── GallerySwiper — horizontal swipe between items +│ └── ZoomableImage[] — pinch-to-zoom + pan per image +└── ActionOverlay — close, counter, download, share, delete +``` + +## State Management (MobX) +`PreviewStore` holds gallery items, current index, and visibility flag. Any screen can call `store.open(items, index)` to launch the preview. + +## Gesture Strategy + +| Gesture | When | Handler | +|---------|------|---------| +| Horizontal pan | scale == 1 | GallerySwiper → next/prev | +| Horizontal pan | scale > 1 | ZoomableImage → translate | +| Pinch (2 fingers) | always | ZoomableImage → scale | +| Double tap | always | ZoomableImage → toggle 2× / 1× | +| Single tap | always | ZoomableImage → toggle overlay | +| Vertical pan | scale == 1 | close modal (pull-down) | + +> **Zoom takes priority**: while an image is zoomed in, the swiper is locked so pans don't change pages. + +## Key Design Decisions + +1. **Pure React Native** — no `react-native-gesture-handler` or `react-native-reanimated` dependency. Uses `PanResponder` and `Animated` from RN core so the implementation works out of the box. +2. **FlatList for swiping** — `horizontal` + `pagingEnabled` + `scrollEnabled` toggle gives us swipe navigation with zero extra code. We toggle `scrollEnabled` off when the current image is zoomed in. +3. **Animated values, not state** — zoom & pan use `Animated.Value` for 60fps gesture feedback. +4. **Clamped transformations** — scale is clamped to `[1, 5]`, translation is bounded so image edges don't drift past screen edges. +5. **MobX** — lightweight observable store for preview state. + +## Data Flow + +``` +User taps thumbnail + → Screen calls previewStore.open(items, index) + → ImagePreview modal renders + → GallerySwiper renders items + → ZoomableImage handles scale/translate + → ActionOverlay shows actions +User taps close / swipes last item / pulls down + → previewStore.close() + → Modal unmounts +``` + +## File Structure + +``` +components/ + ImagePreview.tsx — Modal orchestrator + ZoomableImage.tsx — Pinch-to-zoom + pan + GallerySwiper.tsx — Swipe navigation + ActionOverlay.tsx — Action buttons overlay +state/ + previewStore.ts — MobX store +types.ts — Shared TypeScript types +README.md — Usage guide +DESIGN.md — This file +``` diff --git a/bounty-6/README.md b/bounty-6/README.md new file mode 100644 index 00000000..c4c01312 --- /dev/null +++ b/bounty-6/README.md @@ -0,0 +1,143 @@ +# Warpspeed Bounty #6 — Enhanced Image Preview + +**Value:** $660 + +## Overview + +A full-screen image preview modal (lightbox) for React Native + TypeScript. Supports pinch-to-zoom, pan, swipe navigation, and download/share/delete actions. Built with zero external gesture dependencies — uses only `PanResponder` and `Animated` from React Native core. + +## Features + +- ✅ Full-screen modal lightbox +- ✅ Tap thumbnail to open +- ✅ Pinch-to-zoom (2 fingers) +- ✅ Pan when zoomed in +- ✅ Double-tap to zoom in/out at focal point +- ✅ Horizontal swipe between multiple images +- ✅ Smooth open/close fade transitions +- ✅ Download / Share / Delete action buttons +- ✅ Image counter (e.g., "3 / 8") +- ✅ Zoomed images lock swiping (no accidental page turns) +- ✅ Lightweight — no external gesture libraries needed + +## File Structure + +``` +components/ + ImagePreview.tsx — Modal that orchestrates everything + ZoomableImage.tsx — Pinch-to-zoom + pan gesture handling + GallerySwiper.tsx — FlatList-based horizontal swipe + ActionOverlay.tsx — Close / counter / download / share / delete +state/ + previewStore.ts — MobX store for preview state +types.ts — GalleryItem, PreviewState, ZoomLevel +``` + +## Quick Start + +```tsx +import React from 'react' +import { Button } from 'react-native' +import ImagePreview from './components/ImagePreview' +import type { GalleryItem } from './types' + +const images: GalleryItem[] = [ + { id: '1', uri: 'https://example.com/photo1.jpg', filename: 'photo1.jpg' }, + { id: '2', uri: 'https://example.com/photo2.jpg', filename: 'photo2.jpg' }, +] + +const GalleryScreen = () => { + const [visible, setVisible] = React.useState(false) + + return ( + <> +