Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion sample-apps/react-native/dogfood/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { setPushConfig } from './src/utils/setPushConfig';
import { useSyncPermissions } from './src/hooks/useSyncPermissions';
import { NavigationHeader } from './src/components/NavigationHeader';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { Alert, LogBox } from 'react-native';
import { Alert, Appearance, LogBox } from 'react-native';
import { LiveStream } from './src/navigators/Livestream';
import PushNotificationIOS from '@react-native-community/push-notification-ios';
import {
Expand Down Expand Up @@ -72,6 +72,10 @@ const StackNavigator = () => {
? appTheme.colors.static_white
: defaultTheme.colors.sheetPrimary;

useEffect(() => {
Appearance.setColorScheme(themeMode);
}, [themeMode]);

useDeepLinkEffect();
useSyncPermissions();

Expand Down
166 changes: 166 additions & 0 deletions sample-apps/react-native/dogfood/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

This is the **React Native dogfood app** β€” an internal testing app for the `@stream-io/video-react-native-sdk`. It exercises all major SDK features: video calls, audio rooms, livestreaming, ringing/push notifications, and chat integration.

## Parent instructions

This app lives inside the `stream-video-js` monorepo. Always follow the root-level instructions:
@../../../AGENTS.md

## Commands

All commands should be run from this directory (`sample-apps/react-native/dogfood`).

| Action | Command |
| ------------------------------ | -------------------------------------------------------------------------- |
| First-time setup | `yarn setup` (installs deps, builds RN SDK dependencies, runs pod-install) |
| Start Metro (with cache reset) | `yarn start` |
| Run on iOS simulator | `yarn ios` |
| Run on iOS device | `yarn ios-device` |
| Run on Android emulator | `yarn android` |
| Type-check | `yarn build` (runs `tsc`) |
| Reinstall iOS pods | `cd ios && bundle exec pod install` |

Before running the app, the workspace SDK packages must be built first:

```bash
# From monorepo root
yarn build:all
# Or just the RN dependencies
yarn build:react-native:deps
```

If Metro has module resolution issues, always start with `yarn start` (which includes `--reset-cache`).

## Architecture

### App modes

The app has four distinct modes, selectable from the `ChooseAppModeScreen`:

- **Meeting** β€” standard video calls with lobby, join/create flows, guest mode, in-call chat
- **Call** β€” ringing calls (1:1 and group) with CallKit/ConnectionService integration
- **Audio-Room** β€” audio-only rooms with speaker request/grant flow
- **LiveStream** β€” host or viewer livestream with QR code joining and chat overlay

Mode selection is stored in `AppContext` and drives which navigator stack renders in `App.tsx`.

### Key wrappers (render order in App.tsx)

1. `SafeAreaProvider` + `AppGlobalContextProvider` (MMKV-backed global state)
2. `NavigationContainer` (React Navigation)
3. `VideoWrapper` β€” creates `StreamVideoClient` with token auth, provides `<StreamVideo>` context
4. `RingingWatcher` β€” monitors incoming ringing calls and switches to Call mode
5. `ChatWrapper` β€” creates Stream Chat client for in-call messaging
6. Mode-specific navigator (`Meeting`, `Call`, `LiveStream`, `AudioRoom`)

### Navigation flows

Each mode has its own navigator stack defined in `src/navigators/`:

```
Meeting: JoinMeetingScreen β†’ MeetingScreen β†’ ChatScreen (optional)
β†’ GuestModeScreen β†’ GuestMeetingScreen
Call: JoinCallScreen β†’ [RingingCallContent overlay for incoming calls]
AudioRoom: RoomList ↔ Room (state-based, not stack-based)
LiveStream: LiveStreamChooseScreen β†’ JoinLiveStream β†’ HostLiveStream | ViewerLiveStream
β†’ QRScanner
```

`StaticNavigationService` (`src/utils/staticNavigationUtils.ts`) provides navigation access outside React components β€” it polls until the navigation ref is ready and auth info is set. Used by push notification handlers and deep links.

### State management

Global app state uses a custom atomic store built on MMKV (`src/contexts/createStoreContext.tsx`). The store provides `useAppGlobalStoreValue(selector)` for reads and `useAppGlobalStoreSetState()` for writes. Persisted keys include: userId, userName, userImageUrl, appEnvironment, callId, appMode, themeMode, devMode.

A separate `LayoutContext` manages grid/spotlight layout selection within active calls.

### Authentication and token flow

1. User logs in via `LoginScreen` (sets userId/userName/userImageUrl in MMKV store)
2. `VideoWrapper` calls `createToken({ user_id }, appEnvironment)` against `https://pronto.getstream.io/api/auth/create-token`
3. Returns `{ apiKey, token }` β€” token expires in 4 hours, a `tokenProvider` callback handles refresh
4. `StreamVideoClient.getOrCreateInstance()` initializes the SDK client
5. `useChatClient` hook does the same for Stream Chat

Guest/anonymous users get a separate `StreamVideoClient` instance created in `GuestMeetingScreen`.

### Push notifications

Push config is set in `src/utils/setPushConfig.ts` and **must run at app startup** (before any React rendering) because the app can be opened from a dead state via push. Uses:

- iOS: APNs via `react-native-voip-push-notification` + `PushNotificationIOS` (provider: `rn-apn-video`)
- Android: FCM via `@react-native-firebase/messaging` + Notifee for notification display (provider: `rn-fcm-video`)

`createStreamVideoClient` in `setPushConfig.ts` creates a video client from persisted MMKV credentials when handling push in the background.

Firebase listeners are platform-split: `setFirebaseListeners.android.ts` (handles background/foreground FCM messages) vs `setFirebaseListeners.ts` (no-op on iOS).

### Deep links

`useDeepLinkEffect` hook parses URLs matching `/(join|video/demos/join)/<callId>/` and publishes the call ID via an RxJS `BehaviorSubject` (`deeplinkCallId$`). The `StackNavigator` subscribes and switches to Meeting mode on receipt.

### Call UI architecture

The active call screen (`MeetingUI` β†’ `ActiveCall`) is the most complex component tree:

- **MeetingUI** β€” state machine managing: lobby β†’ loading β†’ active-call β†’ error screens
- **ActiveCall** β€” composes the call layout with:
- `TopControls` β€” layout switcher, camera flip, call timer/recording badge, hang-up
- Video layout (grid or spotlight via `LayoutContext`)
- `BottomControls` β€” mic/camera/screenshare toggles, record button, participants/chat buttons, closed captions
- `MoreActionsButton` β€” drawer with: feedback, call stats, theme toggle, screenshot, audio route picker, noise cancellation, closed captions toggle, emoji reactions, raise hand, video filters

### Video effects

`src/components/VideoEffects/` provides a horizontal filter picker: grayscale (custom native module via `VideoEffectsModule`), background blur (SDK built-in), and background images. Effects are applied via `mediaStream.video.track._setVideoEffect()`.

### Call controls patterns

Custom call control buttons follow a consistent pattern:

- Use `useCallStateHooks()` for reactive state (participants, microphone, camera, calling state)
- Check `OwnCapability` for permission-gated actions (mute others, block, grant/revoke permissions)
- Use `useCall()` for direct call object access (recording, feedback, moderation)
- Platform-specific branches for iOS/Android (audio route picker, device selector)

### Audio rooms

Audio rooms use a state-based (not navigation-based) flow:

- `RoomList` queries `audio_room` calls with watch mode for real-time updates, supports pagination via cursor
- `Room` renders: description panel, participants grid (3-column), permission requests panel, and controls
- `ControlsPanel` handles the live/backstage lifecycle and audio permission request flow

### Chat integration

Two chat patterns:

- **Meeting mode**: navigates to a dedicated `ChatScreen` with `stream-chat-react-native` Channel/MessageList/MessageInput (channel type: `videocall`)
- **LiveStream mode**: `BottomSheetChatWrapper` using `@gorhom/bottom-sheet` with snap points (channel type: `livestream`)

Both use `useChatClient` hook for client lifecycle and `useUnreadCount` for badge indicators.

### Environment configuration

`AppEnvironment` type: `'pronto' | 'pronto-staging' | 'demo' | 'video-moderation' | 'stream-benchmark'`. Environment is selectable in dev mode (enabled by 3-tap secret on login screen). Local SFU support overrides SFU URLs via Axios response transformer in `VideoWrapper`.

### Constants and known users

- `src/constants/index.ts` β€” UI dimensions (BUTTON_HEIGHT, INPUT_HEIGHT, AVATAR_SIZE, Z_INDEX)
- `src/constants/KnownUsers.ts` β€” hardcoded test users (vishal, khushal, santhosh, oliver, zita, kristian) with Slack CDN avatar URLs
- `src/constants/TestIds.ts` β€” test identifier enums for buttons/components

### Translations

`src/translations/en.json` contains 72 English strings with `{{ placeholder }}` templating. Merged with SDK translations in `src/translations/index.ts` β€” app strings override SDK defaults.

### Metro configuration

`metro.config.js` uses `@rnx-kit/metro-config` and `@rnx-kit/metro-resolver-symlinks` to handle yarn workspace symlinks. Watch folders include the workspace SDK packages (`client`, `react-bindings`, `react-native-sdk`, `video-filters-react-native`, `noise-cancellation-react-native`).

### Babel

Uses React Compiler (`babel-plugin-react-compiler`), `react-native-dotenv` for environment variables, and `react-native-worklets/plugin` for vision camera worklets.
11 changes: 11 additions & 0 deletions sample-apps/react-native/dogfood/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,16 @@ target 'StreamReactNativeVideoSDKSample' do
:mac_catalyst_enabled => false,
# :ccache_enabled => true
)

# Fix fmt consteval compilation error with Xcode 26+
fmt_base = File.join(installer.sandbox.pod_dir('fmt'), 'include', 'fmt', 'base.h')
if File.exist?(fmt_base)
content = File.read(fmt_base)
patched = content.gsub(/^#\s*define FMT_USE_CONSTEVAL 1$/, '# define FMT_USE_CONSTEVAL 0')
if patched != content
File.chmod(0644, fmt_base)
File.write(fmt_base, patched)
end
end
end
end
85 changes: 71 additions & 14 deletions sample-apps/react-native/dogfood/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ PODS:
- React-RCTText (= 0.83.2)
- React-RCTVibration (= 0.83.2)
- React-callinvoker (0.83.2)
- React-Codegen (0.1.0)
- React-Core (0.83.2):
- boost
- DoubleConversion
Expand Down Expand Up @@ -3133,7 +3132,7 @@ PODS:
- SocketRocket
- Yoga
- SocketRocket (0.7.1)
- stream-chat-react-native (8.13.0):
- stream-chat-react-native (9.0.0-beta.11):
- boost
- DoubleConversion
- fast_float
Expand All @@ -3144,7 +3143,6 @@ PODS:
- RCT-Folly/Fabric
- RCTRequired
- RCTTypeSafety
- React-Codegen
- React-Core
- React-debug
- React-Fabric
Expand All @@ -3162,7 +3160,7 @@ PODS:
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- stream-io-noise-cancellation-react-native (0.5.0):
- stream-io-noise-cancellation-react-native (0.5.1):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -3192,7 +3190,7 @@ PODS:
- stream-react-native-webrtc
- StreamVideoNoiseCancellation
- Yoga
- stream-io-video-filters-react-native (0.10.0):
- stream-io-video-filters-react-native (0.10.1):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -3224,7 +3222,7 @@ PODS:
- stream-react-native-webrtc (137.1.0):
- React-Core
- StreamWebRTC (~> 137.0.54)
- stream-video-react-native (1.30.0):
- stream-video-react-native (1.30.4):
- boost
- DoubleConversion
- fast_float
Expand Down Expand Up @@ -3255,6 +3253,63 @@ PODS:
- Yoga
- StreamVideoNoiseCancellation (1.0.3)
- StreamWebRTC (137.0.54)
- Teleport (0.5.6):
- boost
- DoubleConversion
- fast_float
- fmt
- glog
- hermes-engine
- RCT-Folly
- RCT-Folly/Fabric
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- SocketRocket
- Teleport/common (= 0.5.6)
- Yoga
- Teleport/common (0.5.6):
- boost
- DoubleConversion
- fast_float
- fmt
- glog
- hermes-engine
- RCT-Folly
- RCT-Folly/Fabric
- RCTRequired
- RCTTypeSafety
- React-Core
- React-debug
- React-Fabric
- React-featureflags
- React-graphics
- React-ImageManager
- React-jsi
- React-NativeModulesApple
- React-RCTFabric
- React-renderercss
- React-rendererdebug
- React-utils
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- SocketRocket
- Yoga
- VisionCamera (4.7.2):
- VisionCamera/Core (= 4.7.2)
- VisionCamera/React (= 4.7.2)
Expand Down Expand Up @@ -3366,12 +3421,12 @@ DEPENDENCIES:
- "stream-io-video-filters-react-native (from `../node_modules/@stream-io/video-filters-react-native`)"
- "stream-react-native-webrtc (from `../node_modules/@stream-io/react-native-webrtc`)"
- "stream-video-react-native (from `../node_modules/@stream-io/video-react-native-sdk`)"
- Teleport (from `../node_modules/react-native-teleport`)
- VisionCamera (from `../node_modules/react-native-vision-camera`)
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)

SPEC REPOS:
trunk:
- React-Codegen
- SocketRocket
- StreamVideoNoiseCancellation
- StreamWebRTC
Expand Down Expand Up @@ -3578,6 +3633,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/@stream-io/react-native-webrtc"
stream-video-react-native:
:path: "../node_modules/@stream-io/video-react-native-sdk"
Teleport:
:path: "../node_modules/react-native-teleport"
VisionCamera:
:path: "../node_modules/react-native-vision-camera"
Yoga:
Expand All @@ -3590,7 +3647,7 @@ SPEC CHECKSUMS:
FBLazyVector: f1200e6ef6cf24885501668bdbb9eff4cf48843f
fmt: a40bb5bd0294ea969aaaba240a927bd33d878cdd
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
hermes-engine: 79258df51fb2de8c52574d7678c0aeb338e65c3b
hermes-engine: 439ec2137ac9376f08fd396565cb2a32323b781b
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
RCTDeprecation: 3b915a7b166f7d04eaeb4ae30aaf24236a016551
RCTRequired: fcfec6ba532cfe4e53c49e0a4061b5eff87f8c64
Expand All @@ -3599,7 +3656,6 @@ SPEC CHECKSUMS:
RCTTypeSafety: ef5deb31526e96bee85936b2f9fa9ccf8f009e46
React: f4edc7518ccb0b54a6f580d89dd91471844b4990
React-callinvoker: 55ce59d13846f45dcfb655f03160f54b26b7623e
React-Codegen: 4b8b4817cea7a54b83851d4c1f91f79aa73de30a
React-Core: d0a03d911ee2424dda52c8699f32ebba46b51de0
React-CoreModules: d7fa24a410f5e141720a0eb93584c2f99d49a908
React-cxxreact: ea34429da15d0cf04dd4489d59c0587af0ae51b4
Expand Down Expand Up @@ -3681,16 +3737,17 @@ SPEC CHECKSUMS:
RNVoipPushNotification: 4998fe6724d421da616dca765da7dc421ff54c4e
RNWorklets: 944dddd0eef13006b658e653abbb3ee8365c3809
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
stream-chat-react-native: 362e74c743dd34d750e1878f9e479707e9edc794
stream-io-noise-cancellation-react-native: 56787bb94ff912ee17661f4b24a3c4f9551f38ba
stream-io-video-filters-react-native: 8fdd1a1fcade0dcd699fd2e5b61b2152c0056219
stream-chat-react-native: 38b9780132c52c6a06ae5c0b3501778073cd51e8
stream-io-noise-cancellation-react-native: c175e436469ee525319cc909c76a8599958ac671
stream-io-video-filters-react-native: 48dcf138877985bd0b59fd15d8e22ea910340c53
stream-react-native-webrtc: dd4bc6e9717e6d90204008c22a44bc1c1f605e3b
stream-video-react-native: 68b9318fd73565de75ca25f35922b4c843d0227b
stream-video-react-native: ca80a0d5137f206ead47f8e8614c9337cd0cf2d9
StreamVideoNoiseCancellation: 41f5a712aba288f9636b64b17ebfbdff52c61490
StreamWebRTC: 57bd35729bcc46b008de4e741a5b23ac28b8854d
Teleport: 80f24aad22fab8c359e3db4504d9f302651856e5
VisionCamera: 891edb31806dd3a239c8a9d6090d6ec78e11ee80
Yoga: 89dfd64939cad2d0d2cc2dc48193100cef28bd98

PODFILE CHECKSUM: 40ce52e159cfdb7fd419e1127f09742de1db1cd5

COCOAPODS: 1.15.2
COCOAPODS: 1.16.2
Loading
Loading