Skip to content
Merged
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
48 changes: 36 additions & 12 deletions docusaurus/docs/reactnative/core-components/chat.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,45 @@ We recommend using only one instance of `Chat` provider per application unless a

```tsx
import { StreamChat } from 'stream-chat';
import { ChannelList, Chat, OverlayProvider } from 'stream-chat-react-native';

const client = StreamChat.getInstance('api_key');

export const App = () => (
<OverlayProvider>
// highlight-next-line
<Chat client={client}>
<ChannelList />
import { ChannelList, Chat, OverlayProvider, useCreateChatClient } from 'stream-chat-react-native';

// highlight-start
const chatApiKey = 'REPLACE_WITH_API_KEY';
const chatUserId = 'REPLACE_WITH_USER_ID';
const chatUserName = 'REPLACE_WITH_USER_NAME';
const chatUserToken = 'REPLACE_WITH_USER_TOKEN';
// highlight-end

const user = {
id: chatUserId,
name: chatUserName,
};

export const App = () => {
// highlight-start
const chatClient = useCreateChatClient({
apiKey: chatApiKey,
userData: user,
tokenOrProvider: chatUserToken,
});
// highlight-end

return (
<OverlayProvider>
// highlight-next-line
</Chat>
</OverlayProvider>
);
<Chat client={chatClient}>
<ChannelList />
// highlight-next-line
</Chat>
</OverlayProvider>
);
};
```

:::tip
You can use the `useCreateChatClient` hook from `stream-chat-react-native`/`stream-chat-expo` to create a client instance and automatically connect/disconnect a user as per the example above, for simplicity.
:::

## Context Providers

`Chat` contains providers for the `ChatContext`, `ThemeContext`, and `TranslationContext`.
Expand Down
4 changes: 4 additions & 0 deletions docusaurus/docs/reactnative/ui-components/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ To disconnect a user you can call `disconnectUser` on the client.
await client.disconnectUser();
```

:::tip
Alternatively, you can also use the `useCreateChatClient` hook from `stream-chat-react-native`/`stream-chat-expo` to create a client instance and automatically connect/disconnect a user.
:::

## Creating a Channel

Channels are at the core of Stream Chat, they are where messages are contained, sent, and interacted with.
Expand Down
7 changes: 3 additions & 4 deletions examples/ExpoMessaging/components/ChatWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { PropsWithChildren } from 'react';
import { Chat, OverlayProvider, Streami18n } from 'stream-chat-expo';
import { useChatClient } from '../hooks/useChatClient';
import { Chat, OverlayProvider, Streami18n, useCreateChatClient } from 'stream-chat-expo';
import { AuthProgressLoader } from './AuthProgressLoader';
import { StreamChatGenerics } from '../types';
import { STREAM_API_KEY, user, userToken } from '../constants';
Expand All @@ -12,7 +11,7 @@ const streami18n = new Streami18n({

export const ChatWrapper = ({ children }: PropsWithChildren<{}>) => {
const { bottom } = useSafeAreaInsets();
const chatClient = useChatClient({
const chatClient = useCreateChatClient({
apiKey: STREAM_API_KEY,
userData: user,
tokenOrProvider: userToken,
Expand All @@ -24,7 +23,7 @@ export const ChatWrapper = ({ children }: PropsWithChildren<{}>) => {

return (
<OverlayProvider<StreamChatGenerics> bottomInset={bottom} i18nInstance={streami18n}>
<Chat client={chatClient} i18nInstance={streami18n} enableOfflineSupport={true}>
<Chat client={chatClient} i18nInstance={streami18n}>
{children}
</Chat>
</OverlayProvider>
Expand Down
46 changes: 0 additions & 46 deletions examples/ExpoMessaging/hooks/useChatClient.tsx

This file was deleted.

133 changes: 66 additions & 67 deletions examples/TypeScriptMessaging/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { DarkTheme, DefaultTheme, NavigationContainer, RouteProp } from '@react-
import { createStackNavigator, StackNavigationProp } from '@react-navigation/stack';
import { useHeaderHeight } from '@react-navigation/elements';
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
import { Channel as ChannelType, ChannelSort, StreamChat } from 'stream-chat';
import { Channel as ChannelType, ChannelSort } from 'stream-chat';
import {
Channel,
ChannelList,
Expand All @@ -18,12 +18,14 @@ import {
Thread,
ThreadContextValue,
useAttachmentPickerContext,
useCreateChatClient,
useOverlayContext,
} from 'stream-chat-react-native';

import { useStreamChatTheme } from './useStreamChatTheme';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { useFlipper } from 'stream-chat-react-native-devtools';
import { AuthProgressLoader } from './AuthProgressLoader';

LogBox.ignoreAllLogs(true);

Expand Down Expand Up @@ -62,7 +64,7 @@ QuickSqliteClient.logger = (level, message, extraData) => {
console.log(level, `QuickSqliteClient: ${message}`, extraData);
};

const chatClient = StreamChat.getInstance<StreamChatGenerics>('q95x9hkbyd6p');
const apiKey = 'q95x9hkbyd6p';
const userToken =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoicm9uIn0.eRVjxLvd4aqCEHY_JRa97g6k7WpHEhxL7Z4K4yTot1c';

Expand Down Expand Up @@ -219,85 +221,82 @@ type AppContextType = {
const AppContext = React.createContext({} as AppContextType);

const App = () => {
const colorScheme = useColorScheme();
const { bottom } = useSafeAreaInsets();
const theme = useStreamChatTheme();
const { channel } = useContext(AppContext);

const [channel, setChannel] = useState<ChannelType<StreamChatGenerics>>();
const [clientReady, setClientReady] = useState(false);
const [thread, setThread] = useState<ThreadContextValue<StreamChatGenerics>['thread']>();

useEffect(() => {
const setupClient = async () => {
const connectPromise = chatClient.connectUser(user, userToken);
setClientReady(true);
await connectPromise;
};
const chatClient = useCreateChatClient({
apiKey,
userData: user,
tokenOrProvider: userToken,
});

setupClient();
}, []);
if (!chatClient) {
return <AuthProgressLoader />;
}

return (
<DebugContextProvider useFlipper={useFlipper}>
<NavigationContainer
theme={{
colors: {
...(colorScheme === 'dark' ? DarkTheme : DefaultTheme).colors,
},
dark: colorScheme === 'dark',
}}
>
<AppContext.Provider value={{ channel, setChannel, setThread, thread }}>
<GestureHandlerRootView style={{ flex: 1 }}>
<OverlayProvider<StreamChatGenerics>
bottomInset={bottom}
i18nInstance={streami18n}
value={{ style: theme }}
>
<Chat client={chatClient} i18nInstance={streami18n} enableOfflineSupport>
{clientReady && (
<Stack.Navigator
initialRouteName='ChannelList'
screenOptions={{
headerTitleStyle: { alignSelf: 'center', fontWeight: 'bold' },
}}
>
<Stack.Screen
component={ChannelScreen}
name='Channel'
options={() => ({
headerBackTitle: 'Back',
headerRight: EmptyHeader,
headerTitle: channel?.data?.name,
})}
/>
<Stack.Screen
component={ChannelListScreen}
name='ChannelList'
options={{ headerTitle: 'Channel List' }}
/>
<Stack.Screen
component={ThreadScreen}
name='Thread'
options={() => ({ headerLeft: EmptyHeader })}
/>
</Stack.Navigator>
)}
</Chat>
</OverlayProvider>
</GestureHandlerRootView>
</AppContext.Provider>
</NavigationContainer>
</DebugContextProvider>
<OverlayProvider<StreamChatGenerics>
bottomInset={bottom}
i18nInstance={streami18n}
value={{ style: theme }}
>
<Chat client={chatClient} i18nInstance={streami18n} enableOfflineSupport>
<Stack.Navigator
initialRouteName='ChannelList'
screenOptions={{
headerTitleStyle: { alignSelf: 'center', fontWeight: 'bold' },
}}
>
<Stack.Screen
component={ChannelScreen}
name='Channel'
options={() => ({
headerBackTitle: 'Back',
headerRight: EmptyHeader,
headerTitle: channel?.data?.name,
})}
/>
<Stack.Screen
component={ChannelListScreen}
name='ChannelList'
options={{ headerTitle: 'Channel List' }}
/>
<Stack.Screen
component={ThreadScreen}
name='Thread'
options={() => ({ headerLeft: EmptyHeader })}
/>
</Stack.Navigator>
</Chat>
</OverlayProvider>
);
};

export default () => {
const [channel, setChannel] = useState<ChannelType<StreamChatGenerics>>();
const [thread, setThread] = useState<ThreadContextValue<StreamChatGenerics>['thread']>();
const theme = useStreamChatTheme();
const colorScheme = useColorScheme();

return (
<SafeAreaProvider style={{ backgroundColor: theme.colors?.white_snow || '#FCFCFC' }}>
<App />
<DebugContextProvider useFlipper={useFlipper}>
<NavigationContainer
theme={{
colors: {
...(colorScheme === 'dark' ? DarkTheme : DefaultTheme).colors,
},
dark: colorScheme === 'dark',
}}
>
<AppContext.Provider value={{ channel, setChannel, setThread, thread }}>
<GestureHandlerRootView style={{ flex: 1 }}>
<App />
</GestureHandlerRootView>
</AppContext.Provider>
</NavigationContainer>
</DebugContextProvider>
</SafeAreaProvider>
);
};
17 changes: 17 additions & 0 deletions examples/TypeScriptMessaging/AuthProgressLoader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { ActivityIndicator, StyleSheet } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';

export const AuthProgressLoader = () => {
return (
<SafeAreaView style={styles.container}>
<ActivityIndicator size={'large'} style={StyleSheet.absoluteFill} />
</SafeAreaView>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
},
});
57 changes: 57 additions & 0 deletions package/src/components/Chat/hooks/useCreateChatClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { useEffect, useState } from 'react';

import { StreamChat } from 'stream-chat';

import type {
DefaultGenerics,
ExtendableGenerics,
OwnUserResponse,
StreamChatOptions,
TokenOrProvider,
UserResponse,
} from 'stream-chat';

/**
* React hook to create, connect and return `StreamChat` client.
*/
export const useCreateChatClient = <SCG extends ExtendableGenerics = DefaultGenerics>({
apiKey,
options,
tokenOrProvider,
userData,
}: {
apiKey: string;
tokenOrProvider: TokenOrProvider;
userData: OwnUserResponse<SCG> | UserResponse<SCG>;
options?: StreamChatOptions;
}) => {
const [chatClient, setChatClient] = useState<StreamChat<SCG> | null>(null);
const [cachedUserData, setCachedUserData] = useState(userData);

if (userData.id !== cachedUserData.id) {
setCachedUserData(userData);
}

const [cachedOptions] = useState(options);

useEffect(() => {
const client = new StreamChat<SCG>(apiKey, undefined, cachedOptions);
let didUserConnectInterrupt = false;

const connectionPromise = client.connectUser(cachedUserData, tokenOrProvider).then(() => {
if (!didUserConnectInterrupt) setChatClient(client);
});

return () => {
didUserConnectInterrupt = true;
setChatClient(null);
connectionPromise
.then(() => client.disconnectUser())
.then(() => {
console.log(`Connection for user "${cachedUserData.id}" has been closed`);
});
};
}, [apiKey, cachedUserData, cachedOptions, tokenOrProvider]);

return chatClient;
};
Loading