Files
bitvid/docs/capacitor-native-app-plan.md
T
thePR0M3TH3ANandClaude Opus 4.8 58047c2589 docs: mark Capacitor + NIP-46 plans on hold
Defer the Capacitor native-app plan and the NIP-46 roadmap enhancements
(current NIP-46 login works well) while the pre-launch backlog clears.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-06-23 14:53:11 -04:00

32 KiB

Capacitor Native App Plan

Status: ON HOLD (deferred 2026-06-23 — revisit after launch backlog clears) Status (original): Planning Platforms: Android, iOS, Windows, macOS, Linux Framework: Capacitor 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
  2. Architecture Overview
  3. Repository Structure
  4. Phase 1 — Foundation
  5. Phase 2 — Android & iOS
  6. Phase 3 — Desktop (Electron via Capacitor)
  7. Phase 4 — Platform-Specific Enhancements
  8. Phase 5 — CI/CD for Native Builds
  9. Phase 6 — Testing Strategy
  10. Platform Considerations & Risks
  11. App Store Strategy
  12. 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

# 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

    npm install @capacitor/core @capacitor/cli
    
  2. Create capacitor.config.ts

    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

    npx cap add android
    npx cap add ios
    
  4. Update build pipeline — extend build-dist.mjs or add a wrapper:

    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:

    npm install @nicepkg/capacitor-electron
    npx cap add electron
    
  6. Add npm scripts

    {
      "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:

// 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

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

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

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

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:

// 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:
    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:
    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 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