diff --git a/sample-apps/react-native/dogfood/App.tsx b/sample-apps/react-native/dogfood/App.tsx index 3d0d46c9ab..f13dd65095 100755 --- a/sample-apps/react-native/dogfood/App.tsx +++ b/sample-apps/react-native/dogfood/App.tsx @@ -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 { @@ -72,6 +72,10 @@ const StackNavigator = () => { ? appTheme.colors.static_white : defaultTheme.colors.sheetPrimary; + useEffect(() => { + Appearance.setColorScheme(themeMode); + }, [themeMode]); + useDeepLinkEffect(); useSyncPermissions(); diff --git a/sample-apps/react-native/dogfood/CLAUDE.md b/sample-apps/react-native/dogfood/CLAUDE.md new file mode 100644 index 0000000000..a540de7714 --- /dev/null +++ b/sample-apps/react-native/dogfood/CLAUDE.md @@ -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 `` 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)//` 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. diff --git a/sample-apps/react-native/dogfood/ios/Podfile b/sample-apps/react-native/dogfood/ios/Podfile index 88fb0ea1dc..b5c26649cc 100644 --- a/sample-apps/react-native/dogfood/ios/Podfile +++ b/sample-apps/react-native/dogfood/ios/Podfile @@ -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 diff --git a/sample-apps/react-native/dogfood/ios/Podfile.lock b/sample-apps/react-native/dogfood/ios/Podfile.lock index a34e812d44..c3e3be6138 100644 --- a/sample-apps/react-native/dogfood/ios/Podfile.lock +++ b/sample-apps/react-native/dogfood/ios/Podfile.lock @@ -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 @@ -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 @@ -3144,7 +3143,6 @@ PODS: - RCT-Folly/Fabric - RCTRequired - RCTTypeSafety - - React-Codegen - React-Core - React-debug - React-Fabric @@ -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 @@ -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 @@ -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 @@ -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) @@ -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 @@ -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: @@ -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 @@ -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 @@ -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 diff --git a/sample-apps/react-native/dogfood/package.json b/sample-apps/react-native/dogfood/package.json index eb3acc2671..8f6572ec38 100644 --- a/sample-apps/react-native/dogfood/package.json +++ b/sample-apps/react-native/dogfood/package.json @@ -11,7 +11,7 @@ "setup": "yarn && yarn build:react-native:deps && npx pod-install" }, "dependencies": { - "@gorhom/bottom-sheet": "5.1.6", + "@gorhom/bottom-sheet": "5.1.8", "@notifee/react-native": "9.1.8", "@react-native-clipboard/clipboard": "^1.16.3", "@react-native-community/netinfo": "^11.4.1", @@ -40,14 +40,15 @@ "react-native-safe-area-context": "^5.6.1", "react-native-screens": "^4.16.0", "react-native-svg": "^15.14.0", + "react-native-teleport": "^0.5.4", "react-native-toast-message": "^2.3.3", "react-native-video": "^6.17.0", "react-native-vision-camera": "^4.7.2", "react-native-voip-push-notification": "~3.3.3", "react-native-worklets": "^0.7.3", "rxjs": "~7.8.2", - "stream-chat": "^9.33.0", - "stream-chat-react-native": "^8.13.0" + "stream-chat": "^9.38.0", + "stream-chat-react-native": "9.0.0-beta.11" }, "devDependencies": { "@babel/core": "^7.28.4", diff --git a/sample-apps/react-native/dogfood/src/hooks/useTheme.ts b/sample-apps/react-native/dogfood/src/hooks/useTheme.ts index 43a1e9450d..4bbf2997fe 100644 --- a/sample-apps/react-native/dogfood/src/hooks/useTheme.ts +++ b/sample-apps/react-native/dogfood/src/hooks/useTheme.ts @@ -1,76 +1,14 @@ -import { ColorSchemeName } from 'react-native'; import type { DeepPartial, Theme } from 'stream-chat-react-native'; -const getChatStyle = (colorScheme: ColorSchemeName): DeepPartial => ({ +const getChatStyle = (): DeepPartial => ({ avatar: { image: { height: 32, width: 32, }, }, - colors: - colorScheme === 'dark' - ? { - accent_blue: '#005FFF', - accent_green: '#20E070', - accent_red: '#FF3742', - bg_gradient_end: '#101214', - bg_gradient_start: '#070A0D', - black: '#FFFFFF', - blue_alice: '#00193D', - border: '#141924', - grey: '#7A7A7A', - grey_gainsboro: '#2D2F2F', - grey_whisper: '#1C1E22', - grey_dark: '#F7F7F8', - icon_background: '#FFFFFF', - light_blue: '#005FFF', - modal_shadow: '#000000', - overlay: '#FFFFFFCC', // CC = 80% opacity - shadow_icon: '#00000080', // 80 = 50% opacity - targetedMessageBackground: '#302D22', - transparent: 'transparent', - white: '#101418', - white_smoke: '#13151B', - white_snow: '#070A0D', - } - : { - accent_blue: '#005FFF', - accent_green: '#20E070', - accent_red: '#FF3742', - bg_gradient_end: '#F7F7F7', - bg_gradient_start: '#FCFCFC', - black: '#000000', - blue_alice: '#E9F2FF', - border: '#00000014', // 14 = 8% opacity; top: x=0, y=-1; bottom: x=0, y=1 - grey: '#7A7A7A', - grey_gainsboro: '#DBDBDB', - grey_whisper: '#ECEBEB', - grey_dark: '#17191C', - icon_background: '#FFFFFF', - modal_shadow: '#00000099', // 99 = 60% opacity; x=0, y= 1, radius=4 - overlay: '#00000099', // 99 = 60% opacity - shadow_icon: '#00000040', // 40 = 25% opacity; x=0, y=0, radius=4 - targetedMessageBackground: '#FBF4DD', // dark mode = #302D22 - transparent: 'transparent', - white: '#FFFFFF', - white_smoke: '#F2F2F2', - white_snow: '#FCFCFC', - }, - spinner: { - height: 15, - width: 15, - }, - messageSimple: { - content: { - receiverMessageBackgroundColor: '#005FFF', - senderMessageBackgroundColor: '#005FFF', - }, - }, }); export const useStreamChatTheme = () => { - // Enable this to use the device color scheme when light mode support is added - // const colorScheme = useColorScheme(); - return getChatStyle('dark'); + return getChatStyle(); }; diff --git a/sample-apps/react-native/dogfood/src/screens/LiveStream/BottomSheetChatWrapper.tsx b/sample-apps/react-native/dogfood/src/screens/LiveStream/BottomSheetChatWrapper.tsx index 0d4640157b..11ac47fac5 100644 --- a/sample-apps/react-native/dogfood/src/screens/LiveStream/BottomSheetChatWrapper.tsx +++ b/sample-apps/react-native/dogfood/src/screens/LiveStream/BottomSheetChatWrapper.tsx @@ -32,7 +32,7 @@ import Animated, { } from 'react-native-reanimated'; import { Channel, - MessageInput, + MessageComposer, MessageList, useChatContext, } from 'stream-chat-react-native'; @@ -259,7 +259,7 @@ const LivestreamChat = ({ */} {/* @ts-expect-error typing error is expected and can be ignored */} - + ); diff --git a/sample-apps/react-native/dogfood/src/screens/Meeting/ChatScreen.tsx b/sample-apps/react-native/dogfood/src/screens/Meeting/ChatScreen.tsx index 0ffc48fff0..58592ad71c 100644 --- a/sample-apps/react-native/dogfood/src/screens/Meeting/ChatScreen.tsx +++ b/sample-apps/react-native/dogfood/src/screens/Meeting/ChatScreen.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { Pressable, StatusBar, StyleSheet, Text, View } from 'react-native'; import { Channel, - MessageInput, + MessageComposer, MessageList, useChatContext, } from 'stream-chat-react-native'; @@ -76,7 +76,7 @@ export const ChatScreen = ({ route }: ChatScreenProps) => { - + ); diff --git a/sample-apps/react/messenger-clone/data/users.json b/sample-apps/react/messenger-clone/data/users.json index 041e914cf3..ba88804fc4 100644 --- a/sample-apps/react/messenger-clone/data/users.json +++ b/sample-apps/react/messenger-clone/data/users.json @@ -2,37 +2,31 @@ { "id": "alice", "name": "Alice", - "image": "https://randomuser.me/api/portraits/women/47.jpg", - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYWxpY2UiLCJleHAiOjE2OTI1MzAyNzh9.ccWd_Y3TEwtQDMpt77jZO5OA9_8wSptOrwNkd-peA-U" + "image": "https://randomuser.me/api/portraits/women/47.jpg" }, { "id": "mark", "name": "Mark", - "image": "https://randomuser.me/api/portraits/men/38.jpg", - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoibWFyayIsImV4cCI6MTY5MjUzMDI3OH0.gK2TJVML1n_QJh6PxEZtCX1YNz4dXUWCHfB0cYUR9jk" + "image": "https://randomuser.me/api/portraits/men/38.jpg" }, { "id": "bob", "name": "Bob", - "image": "https://randomuser.me/api/portraits/men/42.jpg", - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiYm9iIiwiZXhwIjoxNjkyNTMwMjc4fQ.0pOs785Jxv_kQtKe-giTCxRILFSVlITT7_C3-qaI_3g" + "image": "https://randomuser.me/api/portraits/men/42.jpg" }, { "id": "jane", "name": "Jane", - "image": "https://randomuser.me/api/portraits/women/60.jpg", - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiamFuZSIsImV4cCI6MTY5MjUzMDI3OH0.bGU2_YelYTlM1tVEnfpfoBq4BnxMaeP8eZlgay0MTe4" + "image": "https://randomuser.me/api/portraits/women/60.jpg" }, { "id": "tamara", "name": "Tamara", - "image": "https://randomuser.me/api/portraits/women/40.jpg", - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoidGFtYXJhIiwiZXhwIjoxNjkyNTMwMjc4fQ.3YQ46yFxY1rlvWt6eKyrlh3XQwiteK4Sgs1pGab5Ro0" + "image": "https://randomuser.me/api/portraits/women/40.jpg" }, { "id": "john", "name": "John", - "image": "https://randomuser.me/api/portraits/men/54.jpg", - "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiam9obiIsImV4cCI6MTY5MjUzMDI3OH0.YvtbiWYfoX8efSQRTY6gkunJA4JvbDBrrNxFHw9VVHw" + "image": "https://randomuser.me/api/portraits/men/54.jpg" } -] \ No newline at end of file +] diff --git a/sample-apps/react/messenger-clone/package.json b/sample-apps/react/messenger-clone/package.json index 54e451ce39..f590b9d722 100644 --- a/sample-apps/react/messenger-clone/package.json +++ b/sample-apps/react/messenger-clone/package.json @@ -22,8 +22,8 @@ "react": "19.1.0", "react-dom": "19.1.0", "react-router-dom": "^6.30.1", - "stream-chat": "^9.33.0", - "stream-chat-react": "^13.14.0" + "stream-chat": "^9.38.0", + "stream-chat-react": "14.0.0-beta.3" }, "devDependencies": { "@types/react": "~19.1.17", diff --git a/sample-apps/react/messenger-clone/src/App.tsx b/sample-apps/react/messenger-clone/src/App.tsx index c635fdde73..5771b70d73 100644 --- a/sample-apps/react/messenger-clone/src/App.tsx +++ b/sample-apps/react/messenger-clone/src/App.tsx @@ -9,7 +9,7 @@ import { UserList } from './components/UserList'; import { UserContextProvider } from './contexts/UserContext'; import { getSelectedUser } from './utils/user'; -import 'stream-chat-react/dist/css/v2/index.css'; +import 'stream-chat-react/dist/css/index.css'; import '@stream-io/video-react-sdk/dist/css/styles.css'; import './styles/index.scss'; diff --git a/sample-apps/react/messenger-clone/src/components/Channel/Channel.tsx b/sample-apps/react/messenger-clone/src/components/Channel/Channel.tsx index 08bf1f0f51..9265f845ef 100644 --- a/sample-apps/react/messenger-clone/src/components/Channel/Channel.tsx +++ b/sample-apps/react/messenger-clone/src/components/Channel/Channel.tsx @@ -1,20 +1,23 @@ import { Channel as StreamChatChannel, - MessageInput, + MessageComposer, MessageList, Thread, Window, + WithComponents, } from 'stream-chat-react'; import { CustomEventComponent } from '../CustomEventComponent'; import { ChannelHeader } from '../ChannelHeader'; export const Channel = () => ( - - - - - - - - + + + + + + + + + + ); diff --git a/sample-apps/react/messenger-clone/src/components/ChannelHeader/ChannelHeader.tsx b/sample-apps/react/messenger-clone/src/components/ChannelHeader/ChannelHeader.tsx index 84d471fc72..b6ab9b887d 100644 --- a/sample-apps/react/messenger-clone/src/components/ChannelHeader/ChannelHeader.tsx +++ b/sample-apps/react/messenger-clone/src/components/ChannelHeader/ChannelHeader.tsx @@ -42,7 +42,7 @@ const UnMemoizedChannelHeader = (props: ChannelHeaderProps) => { > - +

{displayTitle}{' '} diff --git a/sample-apps/react/messenger-clone/src/components/ChannelPreview/ChannelPreview.tsx b/sample-apps/react/messenger-clone/src/components/ChannelPreview/ChannelPreview.tsx index ed0de79520..eba6fc1580 100644 --- a/sample-apps/react/messenger-clone/src/components/ChannelPreview/ChannelPreview.tsx +++ b/sample-apps/react/messenger-clone/src/components/ChannelPreview/ChannelPreview.tsx @@ -1,16 +1,12 @@ import React, { useMemo, useRef } from 'react'; import clsx from 'clsx'; -import { - Avatar as DefaultAvatar, - ChannelPreviewUIComponentProps, -} from 'stream-chat-react'; +import { Avatar, ChannelListItemUIProps } from 'stream-chat-react'; import { StreamCall, useCalls } from '@stream-io/video-react-sdk'; import { ChannelPreviewCallControls } from './ChannelPreviewCallControls'; -const UnMemoizedChannelPreview = (props: ChannelPreviewUIComponentProps) => { +const UnMemoizedChannelPreview = (props: ChannelListItemUIProps) => { const { active, - Avatar = DefaultAvatar, channel, className: customClassName = '', displayImage, @@ -58,7 +54,7 @@ const UnMemoizedChannelPreview = (props: ChannelPreviewUIComponentProps) => { role="option" >

- +
diff --git a/sample-apps/react/messenger-clone/src/components/CustomChannelSearch/CustomChannelSearch.tsx b/sample-apps/react/messenger-clone/src/components/CustomChannelSearch/CustomChannelSearch.tsx index ab8e4fd27b..43cee4c7dc 100644 --- a/sample-apps/react/messenger-clone/src/components/CustomChannelSearch/CustomChannelSearch.tsx +++ b/sample-apps/react/messenger-clone/src/components/CustomChannelSearch/CustomChannelSearch.tsx @@ -1,10 +1,10 @@ -import { ChannelSearchProps, ChannelSearch } from 'stream-chat-react'; +import { Search } from 'stream-chat-react'; import { QuickDial } from '../QuickDial'; -export const CustomChannelSearch = (props: ChannelSearchProps) => { +export const CustomChannelSearch = () => { return ( <> - + {/* TODO: add call stuff */} diff --git a/sample-apps/react/messenger-clone/src/components/QuickDial/QuickDial.tsx b/sample-apps/react/messenger-clone/src/components/QuickDial/QuickDial.tsx index bb70da422d..ca251ed258 100644 --- a/sample-apps/react/messenger-clone/src/components/QuickDial/QuickDial.tsx +++ b/sample-apps/react/messenger-clone/src/components/QuickDial/QuickDial.tsx @@ -115,7 +115,7 @@ const QuickDialButton = ({ user }: QuickDialButtonProps) => { away: !user.online, })} > - + ); }; diff --git a/sample-apps/react/messenger-clone/src/components/Sidebar/Sidebar.tsx b/sample-apps/react/messenger-clone/src/components/Sidebar/Sidebar.tsx index 8b4726e48a..53e0a7a985 100644 --- a/sample-apps/react/messenger-clone/src/components/Sidebar/Sidebar.tsx +++ b/sample-apps/react/messenger-clone/src/components/Sidebar/Sidebar.tsx @@ -1,6 +1,6 @@ import { LogoutButton } from './LogoutButton'; import { CustomChannelSearch } from '../CustomChannelSearch'; -import { ChannelList } from 'stream-chat-react'; +import { ChannelList, WithComponents } from 'stream-chat-react'; import { ChannelFilters, ChannelOptions, @@ -26,14 +26,19 @@ export const Sidebar = ({ user }: SidebarProps) => { return (