# Plan: Library UX Overhaul, Global Player, P2P Sharing

Started: 2026-09-03. Status legend: `[ ]` not started · `[~]` in progress · `[x]` done.

## Decisions made (2026-09-03)

- **Bottom sheets reuse the existing hand-rolled pattern** (the transparent
  `Modal` + slide-up sheet already used for the format picker in
  `index.tsx`/`browse.tsx`), not a new dependency like `@gorhom/bottom-sheet`.
  Matches the app's existing code and the user's stated preference for not
  over-complicating things. `PlaylistPickerModal.tsx` is the other existing
  sheet-shaped component to match visually.
- **Search is an inline animated expanding bar, not a bottom sheet.** Sort/
  filter are option-pickers (bottom sheet fits), free-text search is not —
  tapping a search icon expands an animated `TextInput` in place, no new
  screen either way.
- **Global mini-player reuses today's pause/resume plumbing.** `MediaContext`
  already exposes `isPlaying`/`videoPlaying`, `requestPause`/`requestResume`,
  and `playNext`/`playPrevious` (added earlier this session for the in-app
  YouTube browser's pause-on-navigate feature) — the mini-player's
  play/pause/next/prev buttons call straight into those, no new playback
  plumbing needed, just UI.
- **P2P sharing (item 13) is a separate, later phase gated on a research
  spike — not part of the same sequential UI task list.** It needs real
  native peer-to-peer transport (iOS MultipeerConnectivity, Android Nearby
  Connections/WiFi Direct), and — same as the on-device-YouTube-extraction
  investigation earlier today — there's no mature, actively-maintained
  Expo/RN library wrapping either. See Phase 5 below. Do not start
  implementation there until the spike produces a clear go/no-go.
- **Reveal-in-Files ships as a native share sheet first** (`expo-sharing`'s
  `shareAsync`), not a "make Documents visible in the Files app" native
  config change. The share sheet already offers "Save to Files" on iOS and
  the system share targets on Android — makes the feature cross-platform
  and config-free for v1. Making the Documents folder browsable directly in
  Files app (`UIFileSharingEnabled`/`LSSupportsOpeningDocumentsInPlace`) is
  a possible stretch add-on, not blocking.

## Task list

### Phase 1 — Global mini-player (foundational, do first) — DONE
- [x] New `GlobalMiniPlayer` component: shows `activeTrack`'s thumbnail,
      title, and play/pause/next/prev — reuses `requestPause`/`requestResume`/
      `playNext`/`playPrevious` from `MediaContext`, no new context plumbing.
- [x] Rendered once in root `_layout.tsx` (inside `MediaProvider`, above the
      `Stack`) so it's visible on every screen — tabs, playlist detail,
      browse, settings — not just the Player tab.
- [x] Hidden entirely when `activeTrack` is null (nothing playing).
- [x] Correct bottom offset on both tab screens (sits above the tab bar) and
      stack screens (playlist detail/browse/settings — no tab bar) — via
      `useSegments()`.
- [x] Tapping the bar (outside the transport buttons) navigates to the full
      Player tab.
- [x] Decided: the Player tab's own bottom bar stays as the "full" version;
      the mini-player hides itself specifically on the Player tab to avoid
      showing two playback bars stacked.
- Verified live: shows correctly-offset above Downloads/Playlists (tab bar)
  and Settings (no tab bar), pause/resume toggles the icon and actually
  pauses/resumes audio.

### Phase 2 — Downloads page overhaul — DONE
- [x] Total combined size of all downloads shown next to the page title
      (reflects the currently active type-filter/search, not just the
      grand total — matches what's visible below it).
- [x] Switched the flat list to a `SectionList` grouped by download date
      (derived from the existing `downloadedAt` field) when sorting by
      date; each section header shows the date label + that date's total
      size (sum of `fileSize`). Name/size sorts switch to a flat sorted
      list instead (date dividers don't make sense there).
- [x] Sort button (bottom sheet): date (newest/oldest), name (A–Z/Z–A), size
      (largest/smallest).
- [x] Grid view / list view toggle, persisted via a new reusable
      `useViewMode` hook (`src/hooks/useViewMode.ts`) so the choice survives
      app restarts. Grid uses its own thumbnail-forward card renderer.
- [x] Inline animated search bar, filtering by title/channel.
- [x] "Reveal in Files" / share action per item, via `expo-sharing`'s
      `shareAsync(resolveMediaUri(item.uri))` — confirmed live the iOS share
      sheet correctly offers "Save to Files" for a downloaded audio file.
- New shared `src/utils/format.ts` (`formatBytes`, `dateSectionLabel`,
  `dayStart`) — written to be reused by Phase 3's Playlists overhaul too.
- Bug found and fixed during testing: the search `TextInput`'s `autoFocus`
  prop only fires on mount, but this input stays permanently mounted (just
  animated between height 0/44) — so it never focused on the second+ open.
  Fixed with an explicit `ref.focus()`/`.blur()` call in the toggle handler.
- Verified live end to end: total size + per-day size both correct, search
  filters and updates the total live, grid/list toggle switches renderers
  correctly, sort's flat (non-date) render path confirmed correct via a
  live sort-order check (22.8 → 10.2 → 8.1 → 7.3 MB for "largest first"),
  share sheet opens with real file data and a working "Save to Files".

### Phase 3 — Playlists page overhaul — DONE
- [x] Sort button (bottom sheet): name (A–Z/Z–A), newest-created,
      **recently played** (new `lastPlayedAt`/`createdAt` fields on
      `Playlist`; `lastPlayedAt` is set inside `playMedia` whenever it's
      called with a `playlistId`, from both the Playlists list's play
      button and the playlist detail screen).
- [x] Playlist thumbnail = first track's thumbnail (`mediaIds[0]`).
- [x] Marquee-animated subtitle under the playlist name showing the first 3
      track titles — plain RN `Animated`, only actually scrolls if the text
      is wider than its row (short subtitles just sit still).
- [x] Grid view / list view toggle, same persisted-preference treatment as
      Phase 2 (reuses the same `useViewMode` hook).
- [x] Inline animated search bar, filtering by playlist name.
- [x] Share action added to playlist detail screen's track rows too
      (the Phase 4 leftover).
- Real bug found and fixed during testing (in **both** this screen and
  Phase 2's Downloads screen): `FlatList` doesn't support changing
  `numColumns` on an already-mounted instance ("Changing numColumns on the
  fly is not supported" — a real crash, confirmed live) — the grid and list
  `FlatList`s were conditionally rendered at the same JSX position without
  distinct `key`s, so toggling from list to grid updated the existing
  instance in place instead of mounting a fresh one. Fixed by giving each
  branch a stable `key` ("grid" / "list"). This didn't surface during
  Phase 2's own testing because that screen's default sort routes through
  `SectionList` (a different component type, unmounts automatically) rather
  than the flat-sort `FlatList` — worth remembering for any future
  grid/list toggle built the same way.
- Verified live: thumbnail-from-first-track and marquee subtitle both
  render correctly, grid/list toggles cleanly in both directions (including
  after the fix, re-tested from a fresh app launch with "grid" already
  persisted from a previous session), search filters correctly.

### Phase 4 — Reveal in Files / share downloaded file — DONE (merged into Phases 2 & 3)
Done: Downloads list item (Phase 2) and playlist detail track rows
(Phase 3) both have the share action now.
- [ ] Optional stretch, still open: iOS `Info.plist` flags to make the
      app's Documents folder browsable directly under Files ▸ On My iPhone.

### Phase 5 — P2P media/playlist sharing (RESEARCH SPIKE — findings in, implementation not started)
This is a different order of magnitude from Phases 1–4. Library survey is
done; a hands-on PoC and the user's go/no-go are still needed before any
implementation task gets written here.

- [x] Spike: survey iOS MultipeerConnectivity and Android Nearby
      Connections/WiFi Direct wrappers. **Result: neither path supports
      cross-platform (iPhone↔Android) sharing.** The most current maintained
      option, `expo-nearby-connections`, says so explicitly — it uses Apple
      Multipeer Connectivity on iOS and Google Nearby Connections on
      Android, two incompatible protocols, and also has no file-transfer
      API at all (text messages only). Same-platform-only libraries exist
      (`react-native-multipeer-connectivity` for iOS, `react-native-google-nearby-connection`
      for Android) but don't meet "share with another user" in general,
      since the two people could easily be on different platforms.
- [x] Spike: found a genuinely cross-platform alternative architecture —
      the same pattern apps like LocalSend/AirDrop-alternatives use: **mDNS
      discovery + a small embedded HTTP server**, both devices on the same
      WiFi network (not true offline P2P, but a reasonable bar — this is
      also literally what "share via WiFi" implies).
      - Discovery: `react-native-zeroconf` (mDNS/Bonjour/NSD) — iOS side
        looks solid; Android's own NSD implementation has well-documented
        OS-level reliability issues affecting *any* mDNS library there, not
        specific to this one (retry logic needed). New Architecture support
        isn't explicitly documented — needs a direct check against this
        app's setup.
      - Transfer: `@dr.pogodin/react-native-static-server` (embedded HTTP
        server, cross-platform, confirmed working with Expo + New
        Architecture per a public proof-of-concept) runs on the *sending*
        device; the *receiving* device does a plain HTTP GET — which means
        it can reuse this app's existing `File.downloadFileAsync` download
        path in `MediaContext.tsx` completely unchanged, rather than
        needing new transfer code.
      - This meaningfully de-risks the feature vs. a raw native P2P
        transport: both remaining libraries are ordinary JS/HTTP-shaped
        rather than exotic native protocol work, and metadata (thumbnail,
        title, channel, duration, originating playlist name) is just JSON
        served alongside the file from the same embedded server.
- [ ] Not yet done: a hands-on PoC actually running both libraries together
      in this app's New Architecture Expo setup (same spirit as today's
      on-device YouTube extraction PoC) — the research above is
      library-landscape confidence, not proof it works *here*. Needed
      before committing to this architecture for real.
- [ ] Rough scope estimate once the PoC above either confirms or rules out
      this architecture; decide go/no-go with the user before writing
      implementation tasks here.
- [ ] Open design question to resolve before implementation: on receipt,
      should a matching-named playlist be created automatically if the
      receiver doesn't already have one with that name, or only merged into
      an existing same-named playlist? (Ask the user — not assumed here.)
- [ ] Design: transfer payload shape (media file + thumbnail + metadata:
      title/channel/duration/webpageUrl + originating playlist name).
- [ ] Design: "share whole playlist" as a batch variant of the same
      transport, once the single-item path works.
- [ ] Known limitation to flag to the user up front: this architecture
      needs both devices on the same WiFi network — it is not a true
      offline/Bluetooth-only P2P transport like AirDrop. A same-platform-only
      Bluetooth fallback (MultipeerConnectivity or Nearby Connections) could
      be a later addition, not part of the MVP.

## Notes / running log (continued)

- 2026-09-03 (later): Phases 1–3 built, verified live, and committed
  (`daa81e8`, `0933d51`, `7e8ceb9`), plus the file-path-persistence bug fix
  (`3a5f277`) found while testing Phase 1 — user asked directly why
  downloads went missing after a rebuild and whether that would happen on
  a normal app update; answer: no, only reinstall-style events do, but the
  underlying absolute-path storage was a real latent fragility worth fixing
  regardless, not just a today's-dev-workflow artifact.
  Also did the Phase 5 library survey (see above) — cross-platform P2P
  needs an mDNS+embedded-HTTP-server architecture, not MultipeerConnectivity/
  Nearby Connections directly, since those two don't talk to each other.
  Stopped at the PoC/go-no-go gate as planned — no P2P implementation
  started.

## Notes / running log

- 2026-09-03: Plan created. User provided the full feature list in one
  message and asked for direct feedback before starting — flagged Phase 5
  (P2P sharing) as needing its own research spike given today's earlier
  finding (in the on-device YouTube extraction investigation) that
  Expo/RN's ecosystem often lacks maintained wrappers for this kind of
  native platform capability. User agreed to proceed phase by phase,
  starting with Phase 1.
