From bc1341d98990fb1696e7072c976717103d5a2f63 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 10 Feb 2026 02:07:24 +0000 Subject: [PATCH] Add Capacitor native app migration plan Comprehensive plan for wrapping bitvid as native apps using Capacitor for Android, iOS, and desktop (Windows/macOS/Linux via Electron). Covers architecture, phased implementation, CI/CD workflows, testing strategy, platform risks, and app store submission guidance. https://claude.ai/code/session_01RBdcFkjzXaawaLY68MMhxc --- docs/capacitor-native-app-plan.md | 849 ++++++++++++++++++++++++++++++ 1 file changed, 849 insertions(+) create mode 100644 docs/capacitor-native-app-plan.md diff --git a/docs/capacitor-native-app-plan.md b/docs/capacitor-native-app-plan.md new file mode 100644 index 00000000..2dd2f6a4 --- /dev/null +++ b/docs/capacitor-native-app-plan.md @@ -0,0 +1,849 @@ +# Capacitor Native App Plan + +> **Status**: Planning +> **Platforms**: Android, iOS, Windows, macOS, Linux +> **Framework**: [Capacitor](https://capacitorjs.com/) by Ionic +> **Goal**: Ship bitvid as a native app on all major platforms while keeping a single codebase with the existing web/PWA deployment. + +--- + +## Table of Contents + +1. [Why Capacitor](#why-capacitor) +2. [Architecture Overview](#architecture-overview) +3. [Repository Structure](#repository-structure) +4. [Phase 1 — Foundation](#phase-1--foundation) +5. [Phase 2 — Android & iOS](#phase-2--android--ios) +6. [Phase 3 — Desktop (Electron via Capacitor)](#phase-3--desktop-electron-via-capacitor) +7. [Phase 4 — Platform-Specific Enhancements](#phase-4--platform-specific-enhancements) +8. [Phase 5 — CI/CD for Native Builds](#phase-5--cicd-for-native-builds) +9. [Phase 6 — Testing Strategy](#phase-6--testing-strategy) +10. [Platform Considerations & Risks](#platform-considerations--risks) +11. [App Store Strategy](#app-store-strategy) +12. [Task Checklist](#task-checklist) + +--- + +## Why Capacitor + +### Fit for bitvid + +bitvid is a fully static, client-side web application. There is no backend server — all authentication, data, and state management happens in the browser via Nostr relays and WebTorrent. This makes it an ideal candidate for Capacitor, which wraps an existing web app in a native WebView shell. + +### Key reasons + +| Reason | Detail | +|--------|--------| +| **Zero rewrite** | The existing `dist/` build output drops directly into Capacitor as the web layer. No framework migration needed. | +| **Single codebase** | Web, PWA, Android, iOS, and desktop all share the same HTML/CSS/JS. Platform-specific behavior is handled via feature detection and Capacitor's plugin bridge. | +| **Progressive native access** | Start with a pure web wrapper, then incrementally add native plugins (deep links, push notifications, share targets, file system) only when needed. | +| **Mature mobile support** | Capacitor has first-class Android and iOS support with a large plugin ecosystem. | +| **Desktop via Electron** | The `@nicepkg/capacitor-electron` community plugin (or the `@nicepkg/cap-electron` plugin) lets Capacitor drive an Electron shell for Windows/macOS/Linux using the same web build. | +| **Existing PWA foundation** | bitvid already has `site.webmanifest`, a service worker (`sw.min.js`), and app icons — all of which Capacitor respects and builds on. | +| **Active ecosystem** | Large community, official Ionic backing, regular releases, good documentation. | + +### Alternatives considered + +| Framework | Why not primary | +|-----------|----------------| +| **Tauri** | Excellent for desktop (small binaries, Rust backend), but mobile support is still in beta. Could be revisited later as a desktop-only option if Electron bundle size becomes a concern. | +| **Electron standalone** | Bundles ~150MB of Chromium per platform. Capacitor+Electron gives us the same result but with a unified build pipeline across all platforms. | +| **React Native / Flutter** | Would require a complete rewrite. bitvid is vanilla JS with no component framework — porting to RN or Flutter would be a ground-up rebuild. | +| **PWA only** | Already partially implemented. Good for web-savvy users but lacks app store discoverability, has weaker iOS support (no push notifications, limited background execution), and misses users who expect a "real" app install. | + +--- + +## Architecture Overview + +``` +┌─────────────────────────────────────────────────────┐ +│ bitvid codebase │ +│ │ +│ ┌───────────┐ npm run build ┌───────────────┐ │ +│ │ Source │ ────────────────→ │ dist/ │ │ +│ │ (js/, │ │ (static web │ │ +│ │ css/, │ │ assets) │ │ +│ │ views/) │ └──────┬────────┘ │ +│ └───────────┘ │ │ +│ │ │ +│ ┌────────────────┼─────┐ │ +│ │ npx cap sync │ │ │ +│ ▼ ▼ ▼ │ +│ ┌─────────┐ ┌─────┐ ┌────────┐ │ +│ │ android/│ │ ios/│ │electron/│ │ +│ │ (native │ │ │ │ │ │ +│ │ project)│ │ │ │ │ │ +│ └─────────┘ └─────┘ └────────┘ │ +│ │ +│ Deploy targets: │ +│ • Web/PWA → dist/ served via Netlify/Vercel/etc │ +│ • Android → android/ → Play Store APK/AAB │ +│ • iOS → ios/ → App Store IPA │ +│ • Desktop → electron/ → Windows/macOS/Linux │ +└─────────────────────────────────────────────────────┘ +``` + +### Data flow (unchanged) + +The native shell is purely a container. All app logic remains in the web layer: + +1. User action triggers a UI controller event handler +2. Controller invokes callback provided by `bitvidApp` +3. `bitvidApp` executes logic, communicates with Nostr relays via WebSocket +4. WebTorrent handles P2P video streaming via WebRTC data channels +5. UI updates via DOM manipulation + +The only new data path is the **Capacitor bridge** — a thin JS-to-native channel used for: +- Deep link handling (Nostr URIs, `nostr:` protocol) +- Push notification tokens (optional, future) +- Native share sheet integration +- App lifecycle events (background/foreground) +- Status bar / navigation bar styling + +--- + +## Repository Structure + +The native project directories live at the repo root alongside the existing web source. This is the standard Capacitor monorepo layout. + +``` +bitvid/ +├── android/ # NEW — Native Android project (Android Studio) +│ ├── app/ +│ │ ├── src/main/ +│ │ │ ├── AndroidManifest.xml +│ │ │ ├── java/com/bitvid/app/ +│ │ │ └── res/ +│ │ └── build.gradle +│ ├── capacitor.settings.gradle +│ └── build.gradle +├── ios/ # NEW — Native iOS project (Xcode) +│ └── App/ +│ ├── App/ +│ │ ├── AppDelegate.swift +│ │ ├── Info.plist +│ │ └── capacitor.config.json (generated) +│ ├── App.xcodeproj +│ └── Podfile +├── electron/ # NEW — Electron desktop shell +│ ├── src/ +│ │ ├── index.ts +│ │ └── preload.ts +│ ├── capacitor.config.ts +│ └── package.json +├── capacitor.config.ts # NEW — Capacitor root config +├── dist/ # Existing build output (web assets) +├── js/ # Existing source +├── css/ # Existing source +├── config/ +│ └── instance-config.js # Extended with platform detection +├── scripts/ +│ ├── build-dist.mjs # Existing web build +│ ├── build-android.mjs # NEW — Android build helper +│ ├── build-ios.mjs # NEW — iOS build helper +│ └── build-desktop.mjs # NEW — Electron build helper +├── tests/ +│ ├── unit/ # Existing +│ ├── e2e/ # Existing (web) +│ ├── visual/ # Existing +│ ├── native/ # NEW — Native-specific tests +│ │ ├── android/ +│ │ ├── ios/ +│ │ └── desktop/ +│ └── platform/ # NEW — Cross-platform behavior tests +├── .github/workflows/ +│ ├── ci.yml # Existing (web CI) +│ ├── ci-android.yml # NEW — Android build + test +│ ├── ci-ios.yml # NEW — iOS build + test +│ └── ci-desktop.yml # NEW — Electron build + test +├── package.json # Extended with native scripts +├── capacitor.config.ts # Capacitor root configuration +└── site.webmanifest # Existing PWA manifest +``` + +### What gets gitignored + +```gitignore +# Native build artifacts +android/app/build/ +android/.gradle/ +android/capacitor-cordova-android-plugins/build/ +ios/App/Pods/ +ios/App/DerivedData/ +electron/dist/ +electron/node_modules/ + +# Capacitor copies of web assets (regenerated by cap sync) +android/app/src/main/assets/public/ +ios/App/App/public/ +``` + +### What gets committed + +The native project scaffolding (`android/`, `ios/`, `electron/`) **is committed** to the repo. This is the Capacitor convention — the native projects are source code, not generated artifacts. They contain platform-specific configuration (permissions, entitlements, splash screens, icons) that must be version-controlled. + +--- + +## Phase 1 — Foundation + +**Goal**: Initialize Capacitor, get the web app running inside native shells on Android and iOS simulators, and Electron on desktop. + +### Tasks + +1. **Install Capacitor core** + ```bash + npm install @capacitor/core @capacitor/cli + ``` + +2. **Create `capacitor.config.ts`** + ```typescript + import type { CapacitorConfig } from '@capacitor/cli'; + + const config: CapacitorConfig = { + appId: 'com.bitvid.app', + appName: 'bitvid', + webDir: 'dist', + server: { + // Allow WebSocket connections to Nostr relays + allowNavigation: ['*'], + }, + plugins: { + SplashScreen: { + launchShowDuration: 2000, + backgroundColor: '#0f172a', // matches theme + }, + }, + }; + + export default config; + ``` + +3. **Add native platforms** + ```bash + npx cap add android + npx cap add ios + ``` + +4. **Update build pipeline** — extend `build-dist.mjs` or add a wrapper: + ```bash + npm run build && npx cap sync + ``` + The `cap sync` command copies `dist/` into each native project and updates native dependencies. + +5. **Add Electron support** via community plugin: + ```bash + npm install @nicepkg/capacitor-electron + npx cap add electron + ``` + +6. **Add npm scripts** + ```json + { + "scripts": { + "cap:sync": "npx cap sync", + "cap:android": "npm run build && npx cap sync android && npx cap open android", + "cap:ios": "npm run build && npx cap sync ios && npx cap open ios", + "cap:electron": "npm run build && npx cap sync electron && npx cap open electron", + "build:all": "npm run build && npx cap sync" + } + } + ``` + +7. **Verify basic functionality** + - App loads in Android emulator + - App loads in iOS simulator + - App loads in Electron window + - Nostr relay WebSocket connections work + - Theme renders correctly + - Navigation / SPA routing works + +### Platform detection helper + +Add a lightweight platform detection utility that the rest of the app can use: + +```javascript +// js/utils/platform.js +import { Capacitor } from '@capacitor/core'; + +export const platform = { + isNative: Capacitor.isNativePlatform(), + isAndroid: Capacitor.getPlatform() === 'android', + isIos: Capacitor.getPlatform() === 'ios', + isElectron: Capacitor.getPlatform() === 'electron', + isWeb: Capacitor.getPlatform() === 'web', +}; +``` + +This module is safe to import on web — `@capacitor/core` detects it's running in a browser and returns `'web'` for the platform. + +--- + +## Phase 2 — Android & iOS + +**Goal**: Production-quality mobile builds ready for app store submission. + +### Android-specific work + +1. **Permissions** in `AndroidManifest.xml`: + - `INTERNET` (already default) + - `ACCESS_NETWORK_STATE` (connectivity detection) + - `CAMERA` / `MICROPHONE` — only if video recording is added later (not needed now) + +2. **WebView configuration**: + - Enable mixed content mode (for WebSocket `wss://` alongside `https://`) + - Enable JavaScript (default in Capacitor) + - Set `webSettings.mediaPlaybackRequiresUserGesture = false` for autoplay + +3. **App icons and splash screens**: + - Use `@capacitor/splash-screen` plugin + - Generate from existing `assets/png/android-chrome-512x512.png` + - Use `cordova-res` or `@capacitor/assets` to generate all density variants + +4. **Deep links**: + - Register `nostr:` URI scheme via Android intent filters + - Register `https://bitvid.tv` domain for App Links (needs `.well-known/assetlinks.json`) + +5. **Status bar and navigation bar**: + - `@capacitor/status-bar` plugin to match the dark theme (`#0f172a`) + - Handle safe areas / notch insets via CSS `env(safe-area-inset-*)` + +### iOS-specific work + +1. **Info.plist configuration**: + - `NSAppTransportSecurity` — allow WebSocket connections + - App description strings for any permission prompts + +2. **WKWebView settings**: + - `allowsInlineMediaPlayback: true` + - `mediaTypesRequiringUserActionForPlayback: []` (enables autoplay) + - These are configurable in `capacitor.config.ts` under `ios.webContentsConfiguration` + +3. **Deep links**: + - Universal Links (needs `apple-app-site-association` on the domain) + - Custom URL scheme `bitvid://` + +4. **Safe areas**: + - iOS notch, Dynamic Island, home indicator + - CSS `env(safe-area-inset-top)` etc. — update layout in `index.html` and key UI components + +5. **App icons**: + - Generate full icon set from existing 512x512 source + - Must include 1024x1024 for App Store + +### Shared mobile work + +1. **Hardware back button** (Android): + - Handle back navigation within the SPA instead of exiting the app + - Use `@capacitor/app` plugin's `backButton` listener + +2. **App lifecycle**: + - Pause/resume WebSocket connections on app background/foreground + - Use `@capacitor/app` `appStateChange` listener + +3. **Keyboard handling**: + - Adjust layout when virtual keyboard appears (search bar, comment inputs) + - Capacitor provides `Keyboard` plugin for this + +4. **Haptic feedback** (optional): + - Light haptics on zap actions using `@capacitor/haptics` + +--- + +## Phase 3 — Desktop (Electron via Capacitor) + +**Goal**: Ship bitvid as a standalone desktop app for Windows, macOS, and Linux. + +### Approach + +Use Capacitor's Electron integration so the desktop build uses the same `dist/` web assets and the same Capacitor plugin bridge as mobile. + +### Desktop-specific work + +1. **Window configuration**: + - Default size: 1280x800 (fits well for video content) + - Minimum size: 800x600 + - Remember window position/size across launches + - Frameless window option with custom title bar (matches bitvid header) + +2. **Menu bar**: + - Minimal native menu (File, Edit, View, Help) + - Or hide native menu and use the web app's own navigation + +3. **Tray icon** (optional): + - System tray icon for background seeding (WebTorrent) + - "Minimize to tray" behavior + +4. **Auto-updater**: + - Electron's `autoUpdater` for seamless desktop updates + - Or use GitHub Releases as the update feed + +5. **Protocol handler**: + - Register `nostr:` and `bitvid:` URI schemes at OS level + +6. **Packaging**: + - Windows: NSIS installer or MSIX + - macOS: DMG + notarization + - Linux: AppImage, .deb, .rpm + +--- + +## Phase 4 — Platform-Specific Enhancements + +These are optional features that improve the native experience. Implement after core functionality is solid. + +### Native share sheet + +```javascript +import { Share } from '@capacitor/share'; + +async function shareVideo(video) { + if (platform.isNative) { + await Share.share({ + title: video.title, + text: video.description, + url: `https://bitvid.tv/#video/${video.id}`, + dialogTitle: 'Share video', + }); + } +} +``` + +### Push notifications (future) + +Nostr-based push could be implemented via: +- A lightweight push relay that watches for mentions/zaps and sends FCM/APNs +- `@capacitor/push-notifications` plugin to receive them + +This is a significant feature and should be its own planning document. + +### Offline support + +- Cache the app shell and critical assets in the service worker +- Cache recently viewed video metadata for offline browsing +- Queue zaps and reactions for when connectivity returns + +### Picture-in-picture + +- Android: native PiP mode via custom Capacitor plugin +- iOS: `AVPictureInPictureController` via the WebView's native video player +- Desktop: Electron `BrowserWindow` PiP API + +--- + +## Phase 5 — CI/CD for Native Builds + +### New CI workflows + +#### `ci-android.yml` + +```yaml +name: CI — Android + +on: + push: + branches: [main] + paths: + - 'js/**' + - 'css/**' + - 'views/**' + - 'components/**' + - 'android/**' + - 'capacitor.config.ts' + - 'package.json' + pull_request: + paths: + - 'js/**' + - 'css/**' + - 'views/**' + - 'components/**' + - 'android/**' + - 'capacitor.config.ts' + +jobs: + build-android: + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: '17' + - name: Install dependencies + run: npm ci + - name: Build web assets + run: npm run build + - name: Sync Capacitor + run: npx cap sync android + - name: Build Android debug APK + working-directory: android + run: ./gradlew assembleDebug + - name: Upload APK artifact + uses: actions/upload-artifact@v4 + with: + name: bitvid-android-debug + path: android/app/build/outputs/apk/debug/app-debug.apk +``` + +#### `ci-ios.yml` + +```yaml +name: CI — iOS + +on: + push: + branches: [main] + paths: ['js/**', 'css/**', 'views/**', 'ios/**', 'capacitor.config.ts'] + pull_request: + paths: ['js/**', 'css/**', 'views/**', 'ios/**', 'capacitor.config.ts'] + +jobs: + build-ios: + runs-on: macos-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - name: Install dependencies + run: npm ci + - name: Build web assets + run: npm run build + - name: Sync Capacitor + run: npx cap sync ios + - name: Install CocoaPods + working-directory: ios/App + run: pod install + - name: Build iOS (no sign) + run: | + xcodebuild build \ + -workspace ios/App/App.xcworkspace \ + -scheme App \ + -destination 'generic/platform=iOS Simulator' \ + CODE_SIGN_IDENTITY="" \ + CODE_SIGNING_REQUIRED=NO +``` + +#### `ci-desktop.yml` + +```yaml +name: CI — Desktop + +on: + push: + branches: [main] + paths: ['js/**', 'css/**', 'views/**', 'electron/**', 'capacitor.config.ts'] + pull_request: + paths: ['js/**', 'css/**', 'views/**', 'electron/**', 'capacitor.config.ts'] + +jobs: + build-desktop: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 22 + - name: Install dependencies + run: npm ci + - name: Build web assets + run: npm run build + - name: Sync Capacitor + run: npx cap sync electron + - name: Build Electron + working-directory: electron + run: npm run build +``` + +### Path-filtered triggers + +Each native CI workflow uses `paths` filters so that: +- A CSS-only change triggers Android + iOS + Desktop builds (the web layer changed) +- A change to only `android/` triggers just the Android build +- Existing web CI (`ci.yml`) continues to run independently + +### CI cost management + +Native CI is more expensive than web CI: +- **iOS builds require `macos-latest` runners** (2-3x cost on GitHub Actions) +- **Android builds need JDK + Gradle** (slower startup) +- **Desktop builds run on 3 OS matrix** entries + +To manage costs: +- Only run native CI on PRs that touch relevant paths +- Cache Gradle/CocoaPods/node_modules aggressively +- Consider running iOS CI only on `main` merges (not every PR) initially +- Use GitHub Actions concurrency groups with `cancel-in-progress: true` + +--- + +## Phase 6 — Testing Strategy + +### Testing layers + +``` +┌─────────────────────────────────────────────┐ +│ Unit Tests (existing) │ ← Platform-agnostic +│ vitest — js logic, nostr, schemas, utils │ No changes needed +├─────────────────────────────────────────────┤ +│ Visual Regression (existing) │ ← Web viewport +│ Playwright — screenshot comparison │ Add mobile viewports +├─────────────────────────────────────────────┤ +│ E2E Web (existing) │ ← Browser-based +│ Playwright — user journeys in Chrome │ No changes needed +├─────────────────────────────────────────────┤ +│ E2E Mobile (NEW) │ ← Device/emulator +│ Appium or Detox — native app user flows │ +├─────────────────────────────────────────────┤ +│ Platform Smoke Tests (NEW) │ ← Quick validation +│ Per-platform build + launch + basic checks │ +├─────────────────────────────────────────────┤ +│ Cross-Platform Behavior Tests (NEW) │ ← Feature parity +│ Verify features work consistently across │ +│ web, Android, iOS, and desktop │ +└─────────────────────────────────────────────┘ +``` + +### New test categories + +#### Platform smoke tests (`tests/native/`) + +Quick checks that run after each native build: +- App launches without crash +- Main view renders +- WebSocket connection to a relay succeeds +- Video playback starts (URL source) +- Theme renders correctly (screenshot comparison) + +#### Mobile viewport visual tests + +Extend existing Playwright visual tests with mobile viewports: +```javascript +// playwright.config.ts addition +projects: [ + // existing desktop viewports... + { + name: 'Mobile Android', + use: { ...devices['Pixel 7'] }, + }, + { + name: 'Mobile iOS', + use: { ...devices['iPhone 14'] }, + }, +] +``` + +This runs in Playwright's browser emulation (not on real devices) but catches responsive layout issues early. + +#### WebView-specific E2E tests + +For testing inside the actual native WebView (not just a browser), use: +- **Android**: Espresso + WebView assertions, or Appium with WebView context switching +- **iOS**: XCUITest + WebView inspection, or Appium + +These are heavier to set up but catch WebView-specific issues (service worker behavior, WebRTC in WKWebView, etc). + +#### What to test per platform + +| Test | Web | Android | iOS | Desktop | +|------|-----|---------|-----|---------| +| Unit tests | yes | n/a | n/a | n/a | +| Visual regression (browser) | yes | mobile viewport | mobile viewport | yes | +| E2E web journeys | yes | — | — | — | +| App launches | — | yes | yes | yes | +| Relay WebSocket connects | yes | yes | yes | yes | +| Video playback (URL) | yes | yes | yes | yes | +| Video playback (WebTorrent) | yes | yes | **test carefully** | yes | +| Deep links (`nostr:`) | — | yes | yes | yes | +| Back button navigation | — | yes | — | — | +| Safe area rendering | — | — | yes | — | +| Keyboard interaction | — | yes | yes | — | +| Offline behavior | yes | yes | yes | yes | + +--- + +## Platform Considerations & Risks + +### WebTorrent on iOS (HIGH RISK) + +**Issue**: WKWebView's WebRTC support is more limited than desktop Chrome. WebTorrent relies on WebRTC data channels for peer-to-peer streaming. + +**Mitigation**: +- bitvid already has a URL-first playback fallback (`DEFAULT_PLAYBACK_SOURCE` in instance config) +- For iOS builds, default to URL-first playback: `DEFAULT_PLAYBACK_SOURCE: 'url'` +- Use platform detection to set this automatically: + ```javascript + if (platform.isIos) { + setUrlFirstEnabled(true); + } + ``` +- Test WebTorrent streaming on real iOS devices — it may work in modern iOS versions (16+), but should not be the primary path + +### Service worker in Capacitor WebView + +**Issue**: The `sw.min.js` service worker intercepts fetch requests to proxy WebTorrent stream data. Service worker support in native WebViews varies. + +**Mitigation**: +- Android: WKWebView supports service workers (API level 33+). For older Android, the URL fallback handles it. +- iOS: WKWebView supports service workers in iOS 14+, but with some caveats around scope and lifecycle. +- Test the WebTorrent streaming service worker flow specifically in native WebViews. +- If service worker is unreliable, consider a Capacitor plugin that bridges native HTTP interception as an alternative stream proxy. + +### WebSocket connectivity + +**Issue**: Native platforms may restrict background WebSocket connections. + +**Mitigation**: +- Use `@capacitor/app` lifecycle events to gracefully disconnect/reconnect: + ```javascript + App.addListener('appStateChange', ({ isActive }) => { + if (isActive) { + nostrClient.reconnect(); + } else { + nostrClient.pause(); + } + }); + ``` +- This also reduces battery drain from idle relay connections. + +### Content Security Policy + +**Issue**: Capacitor serves web assets from a local origin (`capacitor://localhost` on iOS, `https://localhost` on Android). CSP rules may need adjustment. + +**Mitigation**: +- Update `_headers` / CSP meta tags to allow the Capacitor local origin +- Allow `wss://*` for Nostr relay connections +- Allow `blob:` and `data:` for WebTorrent stream playback + +### App Store rejection risk + +See the [App Store Strategy](#app-store-strategy) section below. + +--- + +## App Store Strategy + +### Google Play Store + +**Risk level**: Low-Medium + +Google is generally more permissive. Key considerations: +- **Content rating**: Declare user-generated content (UGC) in the rating questionnaire +- **Moderation**: bitvid's existing moderation system (NIP-56 reports, NIP-51 mute lists, blur thresholds, autoplay blocking) demonstrates active content moderation +- **Crypto/payments**: Lightning/Zap functionality may trigger financial services review. Frame it as "tips" or "donations", not as a payment system. Emphasize that bitvid does not custody funds. + +### Apple App Store + +**Risk level**: Medium-High + +Apple has been unpredictable with Nostr/decentralized apps. Key considerations: +- **Guideline 4.2 (Minimum Functionality)**: The app must provide functionality beyond a "repackaged website". Native features (deep links, share sheet, haptics, PiP) help differentiate. +- **Guideline 1.2 (User Generated Content)**: Must have content reporting, blocking, and moderation. bitvid already has this. +- **Guideline 3.1.1 (In-App Purchase)**: If Lightning zaps are interpreted as digital purchases, Apple may require IAP. Mitigation: frame zaps as peer-to-peer tips external to the app (similar to how other Nostr apps handle this). +- **Review strategy**: Submit early with a clear App Review note explaining the decentralized architecture and content moderation approach. Engage with the review team proactively. + +### Desktop distribution + +**Risk level**: Low + +- **Windows**: Distribute via GitHub Releases initially. Microsoft Store submission optional. +- **macOS**: Notarize the DMG with an Apple Developer account. Distribute via GitHub Releases or the Mac App Store. +- **Linux**: AppImage on GitHub Releases. Snap Store / Flathub optional. + +--- + +## Task Checklist + +### Phase 1 — Foundation +- [ ] Install `@capacitor/core` and `@capacitor/cli` +- [ ] Create `capacitor.config.ts` with bitvid app ID and config +- [ ] Run `npx cap add android` and verify project structure +- [ ] Run `npx cap add ios` and verify project structure +- [ ] Set up Electron via community Capacitor plugin +- [ ] Add `js/utils/platform.js` platform detection utility +- [ ] Update `package.json` with Capacitor scripts (`cap:sync`, `cap:android`, `cap:ios`, `cap:electron`) +- [ ] Update `.gitignore` for native build artifacts +- [ ] Verify web app loads in Android emulator +- [ ] Verify web app loads in iOS simulator +- [ ] Verify web app loads in Electron window +- [ ] Verify Nostr relay connections work on all platforms +- [ ] Document local development setup for native builds + +### Phase 2 — Android & iOS +- [ ] Configure Android permissions in `AndroidManifest.xml` +- [ ] Configure iOS `Info.plist` settings +- [ ] Generate app icons for all densities (Android + iOS) +- [ ] Generate splash screens +- [ ] Implement safe area CSS for iOS notch / Dynamic Island +- [ ] Add `@capacitor/status-bar` and theme it to match bitvid +- [ ] Add `@capacitor/app` for lifecycle events (background/foreground) +- [ ] Implement hardware back button handling (Android) +- [ ] Set up deep link handling for `nostr:` URIs +- [ ] Configure WebView settings for media autoplay +- [ ] Test video playback (URL source) on both platforms +- [ ] Test video playback (WebTorrent) on both platforms +- [ ] Test service worker behavior in native WebViews +- [ ] Set iOS default to URL-first playback if WebTorrent is unreliable + +### Phase 3 — Desktop +- [ ] Configure Electron window defaults (size, min size, position memory) +- [ ] Set up native menu bar or hide it +- [ ] Register `nostr:` protocol handler at OS level +- [ ] Configure packaging (NSIS/DMG/AppImage) +- [ ] Test on Windows, macOS, and Linux +- [ ] Set up auto-updater with GitHub Releases feed + +### Phase 4 — Platform Enhancements +- [ ] Implement native share sheet integration +- [ ] Add `@capacitor/keyboard` for mobile input handling +- [ ] Explore picture-in-picture on each platform +- [ ] Add haptic feedback for zap actions (optional) +- [ ] Implement app shell caching in service worker for offline support + +### Phase 5 — CI/CD +- [ ] Create `ci-android.yml` workflow +- [ ] Create `ci-ios.yml` workflow +- [ ] Create `ci-desktop.yml` workflow (3-OS matrix) +- [ ] Add path filters to limit when native CI runs +- [ ] Set up artifact uploads for debug builds +- [ ] Configure caching (Gradle, CocoaPods, node_modules) +- [ ] Add concurrency groups to cancel stale runs +- [ ] Document release signing process (keystores, certificates, notarization) + +### Phase 6 — Testing +- [ ] Add mobile viewport projects to Playwright config +- [ ] Create platform smoke test suite (`tests/native/`) +- [ ] Set up Android emulator tests in CI +- [ ] Set up iOS simulator tests in CI +- [ ] Test WebTorrent streaming in native WebViews on real devices +- [ ] Test service worker behavior across platforms +- [ ] Test deep link handling on all platforms +- [ ] Create cross-platform feature parity test matrix +- [ ] Document known platform-specific limitations in `KNOWN_ISSUES.md` + +### App Store Submission +- [ ] Create Google Play developer account +- [ ] Prepare Play Store listing (screenshots, description, content rating) +- [ ] Submit Android app for review +- [ ] Create Apple Developer account (if not existing) +- [ ] Prepare App Store listing (screenshots, description, review notes) +- [ ] Submit iOS app for review +- [ ] Set up GitHub Releases for desktop distribution +- [ ] Notarize macOS DMG + +--- + +## Related Documentation + +- [Playback Fallback Architecture](playback-fallback.md) — URL-first strategy that mitigates WebTorrent issues on mobile +- [Instance Configuration](instance-config.md) — Platform-specific config overrides +- [WebTorrent Architecture](webtorrent-architecture.md) — Service worker streaming details +- [Moderation Service Overview](moderation-service-overview.md) — Content moderation (relevant for app store review)