Documentation Index
Fetch the complete documentation index at: https://mintlify.com/kaleidal/raffi/llms.txt
Use this file to discover all available pages before exploring further.
The Raffi mobile app provides iOS and Android support using React Native and Expo, with cross-device synchronization and native video playback.
Tech Stack
Core Technologies
- React Native 0.81.5: Native mobile framework
- React 19.1.0: Latest React with concurrent features
- Expo SDK 54: Development framework and tooling
- TypeScript: Type-safe development
- Expo Router 6: File-based navigation system
Key Dependencies
From raffi-mobile/package.json:13-53:
{
"expo": "~54.0.29",
"expo-router": "~6.0.19", // File-based navigation
"expo-video": "^3.0.15", // Native video player
"react-native": "0.81.5",
"react": "19.1.0",
// Backend
"convex": "^1.31.7", // Real-time sync
"@ave-id/sdk": "^0.2.2", // Authentication
// State & Storage
"zustand": "^5.0.9", // State management
"@react-native-async-storage/async-storage": "^2.2.0",
// Media & UI
"expo-av": "^16.0.8", // Audio/video playback
"expo-image": "~3.0.11", // Optimized images
"expo-blur": "^15.0.8", // Native blur effects
"expo-linear-gradient": "^15.0.8", // Gradients
// Device Features
"expo-haptics": "~15.0.8", // Haptic feedback
"expo-brightness": "~14.0.8", // Screen brightness control
"expo-keep-awake": "^15.0.8", // Prevent sleep during playback
"expo-screen-orientation": "^9.0.8", // Orientation lock
"expo-pip": "^2.0.1", // Picture-in-picture
// Animation
"react-native-reanimated": "~4.1.1",
"react-native-gesture-handler": "~2.28.0"
}
Architecture Overview
┌──────────────────────────────────────────────────────┐
│ Mobile App Layer │
│ │
│ ┌────────────────────────────────────────────────┐ │
│ │ Expo Router (Navigation) │ │
│ │ app/ │ │
│ │ ├── (tabs)/ # Tab navigation │ │
│ │ │ ├── index.tsx # Home/Library │ │
│ │ │ ├── lists.tsx # Custom lists │ │
│ │ │ └── settings.tsx # Settings │ │
│ │ ├── player.tsx # Video player │ │
│ │ ├── addons.tsx # Addon management │ │
│ │ └── login.tsx # Authentication │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────┐ │
│ │ Component Layer │ │
│ │ components/ │ │
│ │ ├── player/ # Video player UI │ │
│ │ ├── library/ # Library components │ │
│ │ ├── meta/ # Metadata display │ │
│ │ └── ui/ # Generic UI components │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────┐ │
│ │ State & Data Layer │ │
│ │ lib/ │ │
│ │ ├── api.ts # API clients │ │
│ │ ├── convex.ts # Convex integration │ │
│ │ ├── db.ts # Local database │ │
│ │ ├── stores/ # Zustand stores │ │
│ │ ├── torrent/ # Torrent integration │ │
│ │ └── types.ts # TypeScript types │ │
│ └────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────┐ │
│ │ Native Layer │ │
│ │ modules/torrent-streamer/ │ │
│ │ - Native torrent streaming module │ │
│ │ - iOS & Android implementations │ │
│ └────────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────────┘
↕
┌──────────────────────────────────────────────────────┐
│ Backend Services │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │
│ │ Convex │ │ Supabase │ │ Decoder │ │
│ │ (Real-time) │ │ (Auth/DB) │ │ Server │ │
│ └──────────────┘ └──────────────┘ └───────────┘ │
└──────────────────────────────────────────────────────┘
App Configuration
Expo Config
Location: app.json
{
"expo": {
"name": "Raffi",
"slug": "raffi-mobile",
"version": "1.0.0",
"scheme": "raffi",
"userInterfaceStyle": "dark",
"newArchEnabled": true,
"ios": {
"bundleIdentifier": "al.kaleid.mobile",
"supportsTablet": true,
"requireFullScreen": false
},
"android": {
"package": "al.kaleid.raffimobile",
"edgeToEdgeEnabled": true,
"predictiveBackGestureEnabled": false
},
"experiments": {
"typedRoutes": true, // Type-safe navigation
"reactCompiler": true // React Compiler optimization
}
}
}
Key Features
- New Architecture: Enabled for better performance
- Dark Mode Only:
userInterfaceStyle: "dark"
- Edge-to-Edge: Full-screen immersive UI on Android
- Deep Linking:
raffi:// URL scheme
- Typed Routes: Compile-time route validation
Navigation System
File-Based Routing
Expo Router uses file structure for navigation:
app/
├── _layout.tsx # Root layout
├── (tabs)/ # Tab navigation group
│ ├── _layout.tsx # Tab bar configuration
│ ├── index.tsx # Home/Library tab
│ ├── lists.tsx # Lists tab
│ └── settings.tsx # Settings tab
├── player.tsx # Full-screen player (modal)
├── addons.tsx # Addon management
├── history.tsx # Watch history
├── login.tsx # Login screen
└── meta/ # Content details
└── [id].tsx # Dynamic route for content
Navigation Patterns
Tab Navigation (app/(tabs)/_layout.tsx):
- Bottom tabs for main sections
- Icons from
@expo/vector-icons
- Active/inactive states
Modal Navigation:
- Player opens as full-screen modal
- Swipe down to dismiss
- Orientation changes to landscape
Deep Linking:
// Open content
raffi://meta/tt1234567
// Open player
raffi://player?imdbId=tt1234567&season=1&episode=1
State Management
Zustand Stores
Location: lib/stores/
Raffi uses Zustand for global state:
// Example store structure
interface AppStore {
user: User | null;
setUser: (user: User | null) => void;
currentContent: Content | null;
setCurrentContent: (content: Content) => void;
playbackState: PlaybackState;
updatePlayback: (state: Partial<PlaybackState>) => void;
}
AsyncStorage
- Persisted user preferences
- Offline caching
- Auth tokens
- Download queue
Video Playback
Expo Video
Location: app/player.tsx
Native video player with hardware acceleration:
import { VideoView } from 'expo-video';
// Features:
// - HLS streaming support
// - PiP (Picture-in-Picture)
// - Background audio
// - Subtitle tracks
// - Audio track selection
// - Playback speed control
Player Features
Orientation Handling:
- Auto-rotate to landscape for video
- Lock orientation during playback
- Restore on exit
Brightness Control:
- System brightness adjustment
- Restore original brightness on exit
Keep Awake:
- Prevent screen sleep during playback
- Re-enable after pause/exit
Gesture Controls:
- Swipe for seek
- Pinch to zoom
- Double-tap to play/pause
Data Synchronization
Convex Integration
Location: lib/convex.ts
// Real-time features:
// - Watch progress sync
// - Custom lists
// - Watch parties
// - User presence
Sync Strategy:
- Local-first updates
- Background sync to Convex
- Optimistic UI updates
- Conflict resolution
Cross-Device Sync
Mobile App ←─────→ Convex ←─────→ Desktop App
↓ ↓
Local DB Local DB
Synced Data:
- Watch progress
- Custom lists
- Library items
- Addon configurations
- User preferences
Native Modules
Torrent Streamer Module
Location: modules/torrent-streamer/
Custom native module for torrent streaming:
modules/torrent-streamer/
├── package.json
├── android/ # Android implementation
│ └── (Java/Kotlin)
├── ios/ # iOS implementation
│ └── (Swift/Objective-C)
└── src/
└── index.ts # TypeScript bindings
Plugin Registration (app.json:56):
"plugins": [
"./plugins/withTorrentStreamer.js"
]
Functionality:
- Native torrent client integration
- On-device video streaming
- Download management
- Peer connection handling
Backend Integration
API Client
Location: lib/api.ts
// External APIs:
// - TMDB (metadata)
// - Stremio addons (streams)
// - Trakt (tracking)
// - Decoder server (transcoding)
Authentication
Ave ID SDK (@ave-id/sdk):
- OAuth authentication
- User profile management
- Session handling
Supabase:
- Database access
- Row-level security
- Real-time subscriptions
Decoder Server Connection
Mobile can connect to:
- Local decoder (on-device torrents)
- Remote decoder (desktop app server)
- Cloud decoder (hosted transcoding)
Configuration:
const STREAMING_SERVER = __DEV__
? 'http://192.168.1.100:6969' // Desktop on LAN
: 'https://decoder.example.com'; // Production
UI Components
Component Structure
components/
├── player/
│ ├── VideoPlayer.tsx # Main player component
│ ├── Controls.tsx # Playback controls
│ ├── SeekBar.tsx # Progress bar
│ └── SubtitleRenderer.tsx # Subtitle display
├── library/
│ ├── ContentCard.tsx # Media item card
│ ├── ContentList.tsx # Horizontal scroll list
│ └── ContinueWatching.tsx # Resume section
├── meta/
│ ├── ContentDetails.tsx # Details screen
│ ├── SeasonPicker.tsx # Season/episode selector
│ └── StreamPicker.tsx # Stream source picker
└── ui/
├── Button.tsx # Generic button
├── LoadingSpinner.tsx # Loading state
└── ErrorBoundary.tsx # Error handling
Styling
React Native StyleSheet:
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#090909',
},
});
Expo Linear Gradient:
import { LinearGradient } from 'expo-linear-gradient';
<LinearGradient
colors={['transparent', '#090909']}
style={styles.gradient}
/>
Expo Blur:
import { BlurView } from 'expo-blur';
<BlurView intensity={80} tint="dark">
{/* Content */}
</BlurView>
React Compiler
Enabled in app.json:61:
"experiments": {
"reactCompiler": true
}
Automatic memoization of components and hooks.
Image Optimization
Expo Image:
- Native image caching
- Progressive loading
- Blur placeholders
- WebP support
Reanimated
react-native-reanimated:
- 60 FPS animations
- Runs on UI thread
- Gesture-driven interactions
Code Splitting
Expo Router automatically splits routes:
- Lazy loading per route
- Reduced initial bundle size
- Faster app startup
Development Workflow
Commands
# Start Expo dev server
npm start
# Run on iOS
npm run ios
# Run on Android
npm run android
# Type checking
npx tsc --noEmit
# Linting
npm run lint
Expo Go vs. Development Build
Expo Go (Quick Testing):
- Scan QR code
- No native build required
- Limited to Expo SDK modules
Development Build (Full Features):
- Custom native modules
- Torrent streamer support
- Full feature testing
# Create development build
npx expo run:ios --device
npx expo run:android --device
Production Builds
EAS Build
# iOS
eas build --platform ios
# Android
eas build --platform android
# Both platforms
eas build --platform all
App Store Distribution
iOS:
- TestFlight beta testing
- App Store submission
- Bundle ID:
al.kaleid.mobile
Android:
- Google Play Console
- APK/AAB signing
- Package:
al.kaleid.raffimobile
Key Design Decisions
Why React Native + Expo?
- Cross-platform: Single codebase for iOS and Android
- Native performance: Native video playback and UI
- Developer experience: Fast refresh, great tooling
- Ecosystem: Access to native modules and APIs
Why Expo Router?
- File-based routing: Intuitive navigation structure
- Type safety: Compile-time route validation
- Deep linking: Built-in URL handling
- SEO ready: Meta tags for web export
Why Zustand Over Redux?
- Simplicity: Less boilerplate
- Performance: Minimal re-renders
- TypeScript: Excellent type inference
- Size: Smaller bundle footprint
Why Custom Torrent Module?
- Performance: Native torrent handling
- Reliability: Better than JavaScript implementations
- Features: Advanced peer management
- Offline: Works without remote server
iOS
Capabilities:
- Background audio playback
- Picture-in-Picture
- AirPlay support
- CarPlay ready
Permissions:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
Android
Features:
- Edge-to-edge display
- Adaptive icons
- Material You theming support
Permissions:
- Internet access (auto-included)
- Storage access for downloads
- Foreground service for downloads