Feature: Mobile Share-Sheet for Reel Imports
ID: 0010Status: In progressOwner: @satyaCreated: 2026-05-14Updated: 2026-05-14Related ADRs: 0001 (Flutter mobile)Depends on: 0008 (reel video imports), 0009 (reel extraction cache)1. Why
Section titled “1. Why”Instagram / TikTok / YouTube share-sheets pass a URL (not the video
bytes) to the host app. Today users have to copy the URL out, switch
apps, and paste it into Treeper. With this slice they tap
Share → Treeper, pick a trip, and the global reel cache (spec 0009)
serves the draft in <1s on cache hits or queues a worker run on
misses.
2. Who it is for
Section titled “2. Who it is for”P1 — Solo planner. Reels and TikToks are how they collect “must visit” ideas during commutes.
3. Scope
Section titled “3. Scope”In scope
Section titled “In scope”F0010.1Accept shared text URLs from the OS share-sheet on both iOS and Android.F0010.2Filter incoming URLs to the reel allowlist (instagram, tiktok, youtube, youtu.be); silently ignore other shares.F0010.3Trip-picker bottom sheet — pick from the user’s existing trips, or “Create new trip” pre-filled with the URL.F0010.4Single API call to existingPOST /trips/:id/importswithsource_type='video'and the shared URL assource_uri.F0010.5Show progress / cache-hit feedback inline — when the imports row comes backstatus='ready'immediately (cache hit), jump straight to the existing review screen.F0010.6ImportSourceType.videoin the Flutter domain model + repository.
Out of scope (this slice)
Section titled “Out of scope (this slice)”- Sharing files (mp4/mov) — only URLs from share-sheet for now.
- Background reel pre-fetch — share opens app foreground.
- Authenticated share when user isn’t signed in — gate behind a “log in to continue” screen.
- iOS Share Extension full setup (requires Xcode UI; documented in
apps/mobile/ios/README.mdbut not scripted). - “Imported by N others” badge — comes after global cache UI lands.
4. User stories
Section titled “4. User stories”- As P1, when I’m watching a travel reel on Instagram, I can tap Share → Treeper, pick the trip, and see the draft populate in the review screen without re-typing anything.
5. UX notes
Section titled “5. UX notes”[User on Instagram] tap Share → grid → Treeper ↓[Treeper opens → cold or warm start] ┌─────────────────────────────┐ │ Save reel to which trip? │ │ ─────────────────────────── │ │ ▢ Bali Aug 2026 │ │ ▢ Vietnam – planning │ │ + Create new trip… │ └─────────────────────────────┘ ↓ (pick)[POST /trips/:id/imports] - cache HIT → review screen instantly - cache MISS → review screen with "parsing…" → realtime updates6. Acceptance criteria
Section titled “6. Acceptance criteria”AC-1 F0010.1 On Android, sharing https://www.instagram.com/p/X/ from Instagram launches Treeper with the URL.AC-2 F0010.2 Sharing https://example.com/foo (not on allowlist) is silently ignored — Treeper opens but no sheet.AC-3 F0010.3 Trip-picker shows all user's non-archived trips + "Create new trip" affordance.AC-4 F0010.4 Confirming a trip POSTs to /trips/:id/imports with source_type='video', source_uri=<shared URL>.AC-5 F0010.5 If the returned import is status='ready' on first read, navigate directly to /imports/:id/review. Otherwise navigate and the existing realtime cubit handles progress.AC-6 F0010.6 ImportSourceType.video round-trips through TripImport.fromJson without loss.7. Data flow
Section titled “7. Data flow”No new endpoints. Reuses:
POST /trips/:id/imports { source_type: "video", source_uri: <url> }GET /imports/:id8. Implementation surface
Section titled “8. Implementation surface”pubspec.yaml:receive_sharing_intent: ^1.8.0lib/features/trips/domain/trip_import.dart: addvideotoImportSourceTypeenum +PendingImport.videofactory.lib/core/share_intake/share_intake_service.dart(new):- subscribes to both cold + warm shared-text streams,
- validates the host allowlist (same list as backend / worker),
- emits a
SharedReelevent with the canonical URL.
lib/features/share_intake/view/pick_trip_sheet.dart(new):- bottom sheet listing user’s trips + “new trip” CTA,
- on submit calls
ImportsRepository.create(sourceType: video, …), - on
status='ready'navigates to review screen.
lib/app/app.dart: wireShareIntakeService.start(router)next toDeepLinkHandler.start(router).android/app/src/main/AndroidManifest.xml: intent filter forandroid.intent.action.SEND+text/plain.ios/Runner/Info.plist+ newShareExtension/target: documented inios/README.md. Xcode UI is the canonical path.
9. Non-functional requirements
Section titled “9. Non-functional requirements”- No iOS Share Extension boilerplate change visible in IDE diff: reviewer should be able to read this PR without touching Xcode.
- Cold-start latency: app launches and shows the trip picker in < 1.5s on warm cache (Flutter engine already loaded).
10. Risks & open questions
Section titled “10. Risks & open questions”- R1 — iOS Share Extension setup is mostly Xcode UI clicks; if a contributor skips them, share from iOS won’t work. Mitigation: CI smoke job for iOS later, plus a README.
- R2 —
receive_sharing_intentv1.x had a major redesign; pinned to ^1.8.0. Watch for v2 API changes. - Q1 — Should a share into a new trip pre-fill the trip title
with “Inspired by
”? Deferred — keep first slice minimal, just open the editor with the URL pending.
11. Rollout plan
Section titled “11. Rollout plan”Plain feature ship. No flag — when the OS share-sheet shows Treeper, sharing into it Just Works. Old build still ignores share intents until updated.
12. References
Section titled “12. References”- Parent: 0009 Reel Extraction Cache
- Parent: 0008 Reel Video Imports
- Mobile ADR: 0001 Flutter mobile