
MailSift
Easily manage and unsubscribe from email subscriptions with intuitive swipes.
Created with prompt
[user]: Build an iOS-first React Native (Expo) app that is extremely similar to Unroll.Me and matches the following UI pixel-for-pixel. No extra screens, no extra buttons, no extra text, no creative layout changes. Only the 3 tabs: Home / Subs / History. Tech + integrations Framework: React Native + Expo, TypeScript. Animations/gestures: react-native-gesture-handler, react-native-reanimated. Rendering bubble chart: Skia (@shopify/react-native-skia) preferred; react-native-svg acceptable fallback. Persistence: react-native-mmkv (or SQLite) to cache subscription senders + actions. Gmail: use Wabi’s Gmail API/integration (OAuth handled by Wabi). You may call Gmail endpoints to: Read message metadata/headers (From, Subject, Date, List-Unsubscribe). Create/update filters/labels (for Block/Keep behavior). Optionally move/archive messages. If Gmail is not connected yet: show Wabi’s standard Gmail connect flow; after success, drop into Home (no custom new UI screens). App navigation (must match screenshots) Bottom floating pill tab bar, always visible, with 3 tabs: Home (house icon) Subs (bell icon) History (clock icon) Global design tokens (match the screenshots) Screen background: #F1F1F0 (warm light grey). Primary text: #1A1A1A Secondary text: #8A8A8A Divider line: #D9D9D9 Card white: #FFFFFF (slightly warm is fine) Font: iOS system (SF Pro). Use regular + bold weights only. Floating tab bar geometry (match proportions) Outer pill: centered horizontally, near bottom, with safe-area padding. Width ≈ 0.795 × screenWidth (about 80%). Height ≈ 0.073 × screenHeight (clamp to 72–84pt). Border radius: height/2 (very pill). Background: white. Shadow: soft and subtle (no harsh drop shadow). Inside: 3 equal segments. Selected tab has a raised inner capsule behind its icon+label (white-on-white “bump” with subtle shadow), unselected tabs have no capsule. Icon + label style: Outline icons, medium stroke, grey #6F6F6F Labels small (~12–13pt), grey #6F6F6F Selected state: same icon/label color, just the raised capsule behind it. DATA MODEL (local cache) Use this minimal structure: { "senders": [ { "id": "sender_domain_or_email_hash", "displayName": "Arcteryx", "fromEmail": "newsletter@arcteryx.com.au", "domain": "arcteryx.com.au", "category": "Fashion", "count30d": 57, "percent30d": 0.24, "listUnsubscribe": { "mailto": "…", "url": "…" }, "status": "pending|kept|unsubbed|blocked", "logo": { "type": "favicon|generated", "uri": "…" } } ], "actions": [ { "id": "action_uuid", "senderId": "…", "action": "kept|unsubbed|blocked", "timestamp": 1730000000, "affectedCount": 57, "notes": "unsub_url|mailto|fallback_filter|blocked_filter" } ] } Subscription detection + categorization (deterministic, not wordy) Identify “subscription senders” Primary signal: List-Unsubscribe header exists. Secondary signals: Gmail label/category = Promotions (if available) Repeated sender domain/email over time (newsletter-like patterns) Categorize sender into these buckets ONLY (used by Home bubble chart) Fashion, Gaming, Food, Travel, Streaming, Books, Misc Categorization logic (simple + robust): Use sender domain keywords + subject keywords + known patterns. If uncertain, category = Misc. Do not create new categories. Date window Default stats: last 30 days. If 30-day has zero, fall back to last 90 days. HOME TAB (1st screenshot) — copy layout exactly Top greeting text block Position: upper-left with generous whitespace. Left aligned. Rules: Greeting must start with only: Hi / Hello / Hey (no time/timezone logic). Keep it 1–2 sentences, not wordy. Must include: User first name (from profile or Gmail “given name”; if unknown, omit name) Total subscription email count (last 30d) Short high-level category summary (top 1–2 categories) Do NOT list all brands. Example output patterns: Hello Janina, you have **3 emails** **from your subscriptions**. Mostly **fashion** and **streaming**. If zero: Hey Janina, no subscription emails recently. Nice. Text styling: Mixed weights in the same paragraph: Bold: name, numbers, and the phrase “from your subscriptions” Regular: the rest Font size feels like ~22–26pt with comfortable line height. Bubble chart card (circle packing) Directly below greeting: a large white rounded rectangle card centered. Card geometry: Width ≈ 0.80 × screenWidth (match the screenshot) Height ≈ same as width (square-ish) Corner radius: very rounded (≈ 40–48pt) Background: white No heavy shadow; if any, extremely subtle. Inside the card: circle packing diagram where bubble area ∝ category percent. Exact bubble fill colors: Fashion: #FFD6D6 Gaming: #DDFFCF Food: #FFE7C2 Travel: #D1D0D0 Streaming: #C2E9FF Misc: #C2CDFF Books: #C7FAE9 Bubble label style: Category name centered inside each bubble Slight tilt/rotation (subtle) Bold-ish, dark text Some bubbles may clip at card edges (must be clipped by rounded corners) Under the card, centered caption: YOUR SUBSCRIPTIONS OVERVIEW (all caps) Small (~12–13pt), letterspaced feel Grey #6F6F6F Implementation: Use a circle packing algorithm (d3 pack or custom) and render via Skia. Animate bubble transitions subtly (200–300ms ease-out) when data changes. SUBS TAB (2nd screenshot) — swipe triage deck Top instruction text, centered with lots of whitespace: Line 1: SWIPE (all caps) Line 2: LEFT TO UNSUB • RIGHT TO KEEP • DOWN TO BLOCK (all caps) Keep it compact and clean. Card stack (center) A 3-card stacked deck: Back card: pale blue tint, slightly rotated + offset Middle card: pale beige tint, slightly rotated opposite + offset Front card: main pastel fill (based on category color) All are rounded-square with large radius. Front card geometry: Size ≈ 0.645 × screenWidth (match screenshot proportions) Corner radius: very round (≈ 56–64pt) Centered vertically and horizontally like the screenshot. Front card content (exact hierarchy): Top: black circle with brand mark/logo inside (white mark) Brand name large bold (e.g., Arcteryx) Smaller line: 57 emails Logo handling: Try sender favicon/brand image first. If none, generate a simple white monoline mark on transparent background using Nano Banana Pro (no text), then place it centered in the black circle. Generation prompt template (only used when needed): “Minimal white monoline icon representing the brand/domain: {domain}. No text. Centered. Transparent background. Crisp edges.” Swipe gestures (must feel exactly right) Use Reanimated + Gesture Handler. Swipe left → UNSUB Swipe right → KEEP Swipe down → BLOCK Motion rules: Card follows finger precisely. Left/right adds slight rotation (±6–10°). Down swipe minimal rotation. Commit thresholds: Left/right: ~25–30% of card width Down: ~18–22% of card height On commit: card flies off-screen in that direction with quick ease-out; next card rises to front. No confetti, no toast banners, no extra UI. Action semantics (Gmail effects) KEEP: Record action in local history. Optional: apply Gmail label WABI_KEEP to sender messages; do not unsubscribe. UNSUB: If List-Unsubscribe has URL: attempt unsubscribe call (server-side if needed). If mailto-only: create an unsubscribe request action; if sending is not possible, mark sender as unsubbed and apply a filter fallback. If no unsubscribe mechanism: fallback to Gmail filter that skips inbox + archives future messages from sender/domain. Record result in history notes. BLOCK: Create Gmail filter for sender/domain: skip inbox + move to trash (or delete) for future messages. Optional label WABI_BLOCKED. Record in history. Queue rules: The Subs deck shows senders in priority order: Highest count30d first Then newest activity Exclude already-blocked/unsubbed unless user resets cache (not a UI feature; internal only) Empty queue state: Keep the same layout; show a single front card reading: Title: All done Subtitle: No more senders No extra buttons. HISTORY TAB (3rd screenshot) — exact list + top filter At top: 3 text tabs: KEPT (left), UNSUBBED (center), BLOCKED (right) Selected state: thin underline directly under the word. Underneath: a full-width thin divider line. List rows (match spacing): Each row shows: Left: black circular avatar with white logo mark Middle: sender displayName bold (e.g., Arcteryx) Under it: sender email (e.g., newsletter@arcteryx.com.au) smaller, regular Right: 57 emails grey, right-aligned Row separators: Thin light grey line between rows. Filtering: Tapping KEPT/UNSUBBED/BLOCKED switches list instantly. Default selected filter when opening History: KEPT. Required screen behaviors App loads → if Gmail connected, fetch + compute stats (cached, incremental updates). Home greeting updates based on computed counts/categories (short, never wordy). Bubble chart reflects category percentages. Subs swipes perform the Gmail actions + write to local history immediately (optimistic UI), then confirm/adjust notes when Gmail operation completes. History always reflects actions taken. Layout fidelity requirements (non-negotiable) Keep the same whitespace “calm” composition as screenshots. Keep the same floating tab bar style and proportions. Keep the same soft shadows, rounded corners, and minimal typography. with Gmail integration [assistant]: Your subscription management app is being crafted to match the provided design and functionality specifications. Hang tight!