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
31 changes: 25 additions & 6 deletions examples/ExpoMessaging/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,26 @@
"ios": {
"supportsTablet": true,
"usesIcloudStorage": true,
"bundleIdentifier": "io.stream.expomessagingapp",
"appleTeamId": "EHV7XZLAHA"
"bundleIdentifier": "io.getstream.iOS.expomessagingapp",
"appleTeamId": "EHV7XZLAHA",
"googleServicesFile": "./firebase/GoogleService-Info.plist"
},
"android": {
"config": {
"googleMaps": {
"apiKey": "AIzaSyDVh35biMyXbOjt74CQyO1dlqSMlrdHOOA"
}
},
"package": "io.stream.expomessagingapp",
"package": "io.getstream.android.expomessagingapp",
"adaptiveIcon": {
"foregroundImage": "./assets/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"permissions": ["android.permission.RECORD_AUDIO", "android.permission.MODIFY_AUDIO_SETTINGS"]
"permissions": [
"android.permission.RECORD_AUDIO",
"android.permission.MODIFY_AUDIO_SETTINGS"
],
"googleServicesFile": "./firebase/google-services.json"
},
"web": {
"favicon": "./assets/favicon.png",
Expand Down Expand Up @@ -69,8 +74,22 @@
"microphonePermission": "$(PRODUCT_NAME) would like to use your microphone for voice recording."
}
],

"./plugins/keyboardInsetMainActivityListener.js"
"@react-native-firebase/app",
"@react-native-firebase/messaging",
[
"expo-build-properties",
{
"android": {
"extraMavenRepos": ["$rootDir/../../../node_modules/@notifee/react-native/android/libs"]
},
"ios": {
"useFrameworks": "static",
"forceStaticLinking": ["RNFBApp", "RNFBMessaging"]
}
}
],
"./plugins/keyboardInsetMainActivityListener.js",
"./plugins/opSqliteSwiftPlugin.js"
]
}
}
18 changes: 17 additions & 1 deletion examples/ExpoMessaging/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ import { StyleSheet } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { LiveLocationManagerProvider } from 'stream-chat-expo';
import { watchLocation } from '../utils/watchLocation';
import { UserProvider, useUserContext } from '@/context/UserContext';
import UserLogin from '@/components/UserLogin';

function Layout() {
const { user } = useUserContext();

if (!user) {
return <UserLogin />;
}

export default function Layout() {
return (
<SafeAreaProvider>
<GestureHandlerRootView style={styles.container}>
Expand All @@ -24,6 +32,14 @@ export default function Layout() {
);
}

export default function App() {
return (
<UserProvider>
<Layout />
</UserProvider>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
Expand Down
85 changes: 58 additions & 27 deletions examples/ExpoMessaging/app/channel/[cid]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,42 @@
import React, { useContext } from 'react';
import { SafeAreaView, View } from 'react-native';
import { Channel, MessageInput, MessageFlashList } from 'stream-chat-expo';
import { Stack, useRouter } from 'expo-router';
import React, { useContext, useEffect, useState } from 'react';
import type { Channel as StreamChatChannel } from 'stream-chat';
import { Channel, MessageInput, MessageFlashList, useChatContext } from 'stream-chat-expo';
import { Stack, useLocalSearchParams, useRouter } from 'expo-router';
import { AuthProgressLoader } from '../../../components/AuthProgressLoader';
import { AppContext } from '../../../context/AppContext';
import { useHeaderHeight } from '@react-navigation/elements';
import InputButtons from '../../../components/InputButtons';
import { MessageLocation } from '../../../components/LocationSharing/MessageLocation';
import { SafeAreaView } from 'react-native-safe-area-context';
import { StyleSheet } from 'react-native';

export default function ChannelScreen() {
const { client } = useChatContext();
const router = useRouter();
const { setThread, channel, thread } = useContext(AppContext);
const params = useLocalSearchParams();
const navigateThroughPushNotification = params.push_notification as string;
const channelId = params.cid as string;
const [channelFromParams, setChannelFromParams] = useState<StreamChatChannel | undefined>(
undefined,
);

// Effect to fetch channel from params if channel is navigated through push notification
useEffect(() => {
const initChannel = async () => {
if (navigateThroughPushNotification) {
const channel = client?.channel('messaging', channelId);
await channel?.watch();
setChannelFromParams(channel);
}
};
initChannel();
}, [navigateThroughPushNotification, channelId, client]);

const { setThread, channel: channelContext, thread } = useContext(AppContext);
const headerHeight = useHeaderHeight();

const channel = channelFromParams || channelContext;

if (!channel) {
return <AuthProgressLoader />;
}
Expand All @@ -32,29 +56,36 @@ export default function ChannelScreen() {
defaultHandler?.();
};

if (!channel) {
return null;
}

return (
<SafeAreaView>
<Channel
audioRecordingEnabled={true}
channel={channel}
onPressMessage={onPressMessage}
keyboardVerticalOffset={headerHeight}
MessageLocation={MessageLocation}
thread={thread}
>
<Stack.Screen options={{ title: 'Channel Screen' }} />
{channel && (
<Channel
audioRecordingEnabled={true}
channel={channel}
onPressMessage={onPressMessage}
keyboardVerticalOffset={headerHeight}
MessageLocation={MessageLocation}
thread={thread}
>
<View style={{ flex: 1 }}>
<MessageFlashList
onThreadSelect={(thread) => {
setThread(thread);
router.push(`/channel/${channel.cid}/thread/${thread.cid}`);
}}
/>
<MessageInput InputButtons={InputButtons} />
</View>
</Channel>
)}
</SafeAreaView>

<SafeAreaView edges={['bottom']} style={styles.container}>
<MessageFlashList
onThreadSelect={(thread) => {
setThread(thread);
router.push(`/channel/${channel.cid}/thread/${thread.cid}`);
}}
/>
<MessageInput InputButtons={InputButtons} />
</SafeAreaView>
</Channel>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
},
});
47 changes: 24 additions & 23 deletions examples/ExpoMessaging/app/channel/[cid]/thread/[cid]/index.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,43 @@
import React, { useContext } from 'react';
import { SafeAreaView, View } from 'react-native';
import { StyleSheet, View } from 'react-native';
import { Channel, Thread } from 'stream-chat-expo';
import { Stack } from 'expo-router';
import { AppContext } from '../../../../../context/AppContext';
import { useHeaderHeight } from '@react-navigation/elements';
import { SafeAreaView } from 'react-native-safe-area-context';

export default function ThreadScreen() {
const { channel, thread, setThread } = useContext(AppContext);
const headerHeight = useHeaderHeight();

if (channel === undefined) {
if (!channel) {
return null;
}

return (
<SafeAreaView>
<Channel
audioRecordingEnabled={true}
channel={channel}
keyboardVerticalOffset={headerHeight}
thread={thread}
threadList
>
<Stack.Screen options={{ title: 'Thread Screen' }} />

<Channel
audioRecordingEnabled={true}
channel={channel}
keyboardVerticalOffset={headerHeight}
thread={thread}
threadList
>
<View
style={{
flex: 1,
justifyContent: 'flex-start',
<SafeAreaView edges={['bottom']} style={styles.container}>
<Thread
onThreadDismount={() => {
setThread(undefined);
}}
>
<Thread
onThreadDismount={() => {
setThread(undefined);
}}
/>
</View>
</Channel>
</SafeAreaView>
/>
</SafeAreaView>
</Channel>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'flex-start',
},
});
56 changes: 46 additions & 10 deletions examples/ExpoMessaging/app/index.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,62 @@
import { StyleSheet, View } from 'react-native';
import { Alert, Image, Pressable, StyleSheet, View } from 'react-native';
import { ChannelList } from 'stream-chat-expo';
import { useContext, useMemo } from 'react';
import { useCallback, useContext, useMemo } from 'react';
import { Stack, useRouter } from 'expo-router';
import { ChannelSort } from 'stream-chat';
import { AppContext } from '../context/AppContext';
import { user } from '../constants';
import { useUserContext } from '@/context/UserContext';
import { getInitialsOfName } from '@/utils/getInitialsOfName';

const filters = {
members: { $in: [user.id] },
type: 'messaging',
};
const sort: ChannelSort = { last_updated: -1 };
const options = {
state: true,
watch: true,
};

const LogoutButton = () => {
const { logOut, user } = useUserContext();

const onLogoutHandler = useCallback(() => {
Alert.alert('Logout', 'Are you sure you want to logout?', [
{ text: 'Cancel', style: 'cancel' },
{ text: 'Logout', onPress: logOut },
]);
}, [logOut]);

return (
<Pressable onPress={onLogoutHandler}>
<Image
source={{
uri:
user?.image ||
`https://getstream.imgix.net/images/random_svg/${getInitialsOfName(user?.name || '')}.png`,
}}
style={styles.avatar}
/>
</Pressable>
);
};

export default function ChannelListScreen() {
const memoizedFilters = useMemo(() => filters, []);
const { user } = useUserContext();
const filters = useMemo(
() => ({
members: { $in: [user?.id as string] },
type: 'messaging',
}),
[user?.id],
);
const router = useRouter();
const { setChannel } = useContext(AppContext);

return (
<View style={styles.container}>
<Stack.Screen options={{ title: 'Channel List Screen' }} />
<Stack.Screen
options={{ title: 'Channel List Screen', headerLeft: () => <LogoutButton /> }}
/>

<ChannelList
filters={memoizedFilters}
filters={filters}
onSelect={(channel) => {
setChannel(channel);
router.push(`/channel/${channel.cid}`);
Expand All @@ -41,4 +72,9 @@ const styles = StyleSheet.create({
container: {
flex: 1,
},
avatar: {
borderRadius: 18,
height: 36,
width: 36,
},
});
7 changes: 0 additions & 7 deletions examples/ExpoMessaging/babel.config.js

This file was deleted.

15 changes: 11 additions & 4 deletions examples/ExpoMessaging/components/ChatWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ import {
Streami18n,
useCreateChatClient,
} from 'stream-chat-expo';
import { UserResponse } from 'stream-chat';
import { AuthProgressLoader } from './AuthProgressLoader';
import { STREAM_API_KEY, user, userToken } from '../constants';
import { useStreamChatTheme } from '../useStreamChatTheme';
import { useStreamChatTheme } from '../hooks/useStreamChatTheme';
import { useUserContext } from '@/context/UserContext';
import { STREAM_API_KEY, USER_TOKENS } from '@/constants/ChatUsers';
import { usePushNotifications } from '@/hooks/usePushNotifications';
import '../utils/backgroundMessageHandler';

const streami18n = new Streami18n({
language: 'en',
Expand All @@ -20,12 +24,15 @@ SqliteClient.logger = (level, message, extraData) => {
};

export const ChatWrapper = ({ children }: PropsWithChildren<{}>) => {
const { user } = useUserContext();
const chatClient = useCreateChatClient({
apiKey: STREAM_API_KEY,
userData: user,
tokenOrProvider: userToken,
userData: user as UserResponse,
tokenOrProvider: USER_TOKENS[user?.id as string],
});

usePushNotifications({ chatClient });

streami18n.registerTranslation('en', {
...enTranslations,
'timestamp/Location end at': '{{ milliseconds | durationFormatter(withSuffix: false) }}',
Expand Down
Loading