@@ -11,7 +11,6 @@ import {
1111 View ,
1212 Text ,
1313 Alert ,
14- Dimensions ,
1514} from 'react-native' ;
1615import {
1716 SafeAreaProvider ,
@@ -22,7 +21,6 @@ import { GestureHandlerRootView } from 'react-native-gesture-handler';
2221import React , { useCallback , useMemo } from 'react' ;
2322import {
2423 AIStates ,
25- AITypingIndicatorView ,
2624 Channel ,
2725 ChannelList ,
2826 ChannelPreviewMessengerProps ,
@@ -37,7 +35,6 @@ import {
3735 MessageList ,
3836 MessageProps ,
3937 OverlayProvider ,
40- ThemeProvider ,
4138 useAIState ,
4239 useChannelContext ,
4340 useChannelsContext ,
@@ -47,6 +44,8 @@ import {
4744 useMessageInputContext ,
4845 useStableCallback ,
4946 useTheme ,
47+ ThemeProvider ,
48+ isLocalUrl ,
5049} from 'stream-chat-react-native' ;
5150import { AppProvider , useAppContext } from './contexts/AppContext.tsx' ;
5251import {
@@ -62,9 +61,11 @@ import {
6261} from 'stream-chat' ;
6362import { startAI } from './http/requests.ts' ;
6463import {
65- MarkdownRichText ,
66- AIMessageComposer ,
67- AIMessageComposerProps ,
64+ StreamingMessageView ,
65+ ComposerView ,
66+ StreamTheme ,
67+ AITypingIndicatorView ,
68+ type ComposerViewProps ,
6869} from '@stream-io/ai-components-react-native' ;
6970import Animated , { FadeIn , FadeOut } from 'react-native-reanimated' ;
7071
@@ -88,19 +89,21 @@ function App() {
8889 return (
8990 < SafeAreaProvider >
9091 < AppProvider client = { chatClient } >
91- < GestureHandlerRootView style = { { flex : 1 } } >
92- < OverlayProvider value = { { style : chatTheme } } >
93- < Chat
94- client = { chatClient }
95- isMessageAIGenerated = { isMessageAIGenerated }
96- enableOfflineSupport = { false }
97- >
98- < NavigationContainer >
99- < DrawerNavigator />
100- </ NavigationContainer >
101- </ Chat >
102- </ OverlayProvider >
103- </ GestureHandlerRootView >
92+ < StreamTheme >
93+ < GestureHandlerRootView style = { { flex : 1 } } >
94+ < OverlayProvider value = { { style : chatTheme } } >
95+ < Chat
96+ client = { chatClient }
97+ isMessageAIGenerated = { isMessageAIGenerated }
98+ enableOfflineSupport = { false }
99+ >
100+ < NavigationContainer >
101+ < DrawerNavigator />
102+ </ NavigationContainer >
103+ </ Chat >
104+ </ OverlayProvider >
105+ </ GestureHandlerRootView >
106+ </ StreamTheme >
104107 </ AppProvider >
105108 </ SafeAreaProvider >
106109 ) ;
@@ -177,21 +180,18 @@ const DrawerNavigator = () => (
177180 </ Drawer . Navigator >
178181) ;
179182
183+ const additionalFlatListProps = {
184+ maintainVisibleContentPosition : {
185+ minIndexForVisible : 0 ,
186+ autoscrollToTopThreshold : 0 ,
187+ } ,
188+ ListHeaderComponent : null ,
189+ } ;
190+
180191const AppContent = ( ) => {
181192 const { channel } = useAppContext ( ) ;
182193 const { bottom } = useSafeAreaInsets ( ) ;
183194
184- const safeAreaInsets = useSafeAreaInsets ( ) ;
185- const insets = useMemo (
186- ( ) => ( {
187- ...safeAreaInsets ,
188- bottom :
189- safeAreaInsets . bottom +
190- ( Platform . OS === 'android' ? ( StatusBar . currentHeight ?? 0 ) * 2 : 0 ) ,
191- } ) ,
192- [ safeAreaInsets ] ,
193- ) ;
194-
195195 const preSendMessageRequest = useStableCallback ( async ( { localMessage } ) => {
196196 if ( ! channel ) {
197197 return ;
@@ -201,6 +201,7 @@ const AppContent = () => {
201201 await channel . watch ( {
202202 created_by_id : localMessage . user_id ,
203203 } ) ;
204+ await channel . update ( { name : localMessage . text } ) ;
204205 }
205206
206207 if (
@@ -229,7 +230,7 @@ const AppContent = () => {
229230 initializeOnMount = { false }
230231 // @ts -expect-error This will be fixed upstream, the type is wrong
231232 preSendMessageRequest = { preSendMessageRequest }
232- StreamingMessageView = { StreamingMessageView }
233+ StreamingMessageView = { CustomStreamingMessageView }
233234 Message = { CustomMessage }
234235 enableSwipeToReply = { false }
235236 EmptyStateIndicator = { EmptyStateIndicator }
@@ -238,15 +239,8 @@ const AppContent = () => {
238239 MessageAvatar = { RenderNull }
239240 MessageFooter = { RenderNull }
240241 >
241- < MessageList
242- additionalFlatListProps = { {
243- maintainVisibleContentPosition : {
244- minIndexForVisible : 0 ,
245- autoscrollToTopThreshold : 0 ,
246- } ,
247- } }
248- />
249- < AITypingIndicatorView />
242+ < MessageList additionalFlatListProps = { additionalFlatListProps } />
243+ < AIThinkingIndicatorView />
250244 < MessageComposerAI bottomSheetOptions = { bottomSheetOptions } />
251245 </ Channel >
252246 </ Animated . View >
@@ -286,13 +280,58 @@ const bottomSheetOptions = [
286280 } ,
287281] ;
288282
283+ const AIThinkingIndicatorView = ( ) => {
284+ const { channel } = useChannelContext ( ) ;
285+ const { aiState } = useAIState ( channel ) ;
286+
287+ const allowedStates = {
288+ [ AIStates . Thinking ] : 'Thinking about the question...' ,
289+ [ AIStates . Generating ] : 'Generating a response...' ,
290+ [ AIStates . ExternalSources ] : 'Checking external sources...' ,
291+ } ;
292+
293+ if ( aiState === AIStates . Idle || aiState === AIStates . Error ) {
294+ return null ;
295+ }
296+
297+ return (
298+ < View
299+ style = { {
300+ paddingHorizontal : 24 ,
301+ paddingVertical : 12 ,
302+ } }
303+ >
304+ < AITypingIndicatorView text = { allowedStates [ aiState ] } />
305+ </ View >
306+ ) ;
307+ } ;
308+
289309const CustomMessage = ( props : MessageProps ) => {
290310 const { theme } = useTheme ( ) ;
291- const isFromBot = props . message . ai_generated ;
311+ const { message } = props ;
312+ const isFromBot = message . ai_generated ;
313+ const hasPendingAttachments = useMemo (
314+ ( ) =>
315+ ( message . attachments ?? [ ] ) . some (
316+ ( attachment ) =>
317+ ( attachment . image_url && isLocalUrl ( attachment . image_url ) ) ||
318+ ( attachment . asset_url && isLocalUrl ( attachment . asset_url ) ) ,
319+ ) ,
320+ [ message . attachments ] ,
321+ ) ;
292322
293323 const modifiedTheme = useMemo ( ( ) => {
294324 if ( ! isFromBot ) {
295- return theme ;
325+ return mergeThemes ( {
326+ theme,
327+ style : {
328+ messageSimple : {
329+ wrapper : {
330+ opacity : hasPendingAttachments ? 0.5 : 1 ,
331+ } ,
332+ } ,
333+ } ,
334+ } ) ;
296335 }
297336
298337 return mergeThemes ( {
@@ -310,27 +349,26 @@ const CustomMessage = (props: MessageProps) => {
310349 } ,
311350 } ,
312351 } ) ;
313- } , [ theme , isFromBot ] ) ;
352+ } , [ theme , isFromBot , hasPendingAttachments ] ) ;
353+
314354 return (
315355 < ThemeProvider mergedStyle = { modifiedTheme } >
316356 < Message { ...props } />
317357 </ ThemeProvider >
318358 ) ;
319359} ;
320360
321- const w = Dimensions . get ( 'window' ) . width - 32 ;
322-
323- const StreamingMessageView = ( ) => {
361+ const CustomStreamingMessageView = ( ) => {
324362 const { message } = useMessageContext ( ) ;
325363 return (
326- < View style = { { width : w , paddingLeft : 16 } } >
327- < MarkdownRichText text = { message . text ?? '' } />
364+ < View style = { { width : '100%' , paddingHorizontal : 16 } } >
365+ < StreamingMessageView text = { message . text ?? '' } />
328366 </ View >
329367 ) ;
330368} ;
331369
332370const MessageComposerAI = (
333- props : Pick < AIMessageComposerProps , 'bottomSheetOptions' > ,
371+ props : Pick < ComposerViewProps , 'bottomSheetOptions' > ,
334372) => {
335373 const messageComposer = useMessageComposer ( ) ;
336374 const { sendMessage } = useMessageInputContext ( ) ;
@@ -342,10 +380,22 @@ const MessageComposerAI = (
342380 ( ) => channel ?. stopAIResponse ( ) ,
343381 [ channel ] ,
344382 ) ;
383+
345384 const isGenerating = [ AIStates . Thinking , AIStates . Generating ] . includes (
346385 aiState ,
347386 ) ;
348387
388+ const safeAreaInsets = useSafeAreaInsets ( ) ;
389+ const insets = useMemo (
390+ ( ) => ( {
391+ ...safeAreaInsets ,
392+ bottom :
393+ safeAreaInsets . bottom +
394+ ( Platform . OS === 'android' ? ( StatusBar . currentHeight ?? 0 ) * 2 : 0 ) ,
395+ } ) ,
396+ [ safeAreaInsets ] ,
397+ ) ;
398+
349399 const serializeToMessage = useStableCallback (
350400 async ( { text, attachments } : { text : string ; attachments ?: any [ ] } ) => {
351401 messageComposer . textComposer . setText ( text ) ;
@@ -363,8 +413,9 @@ const MessageComposerAI = (
363413 ) ;
364414
365415 return (
366- < AIMessageComposer
416+ < ComposerView
367417 { ...props }
418+ bottomSheetInsets = { insets }
368419 onSendMessage = { serializeToMessage }
369420 isGenerating = { isGenerating }
370421 stopGenerating = { stopGenerating }
0 commit comments