Skip to content

Commit 5daf7f2

Browse files
committed
feat: add draftscreen to sample app
1 parent 94c766a commit 5daf7f2

File tree

9 files changed

+448
-9
lines changed

9 files changed

+448
-9
lines changed

examples/SampleApp/App.tsx

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@ import { DevSettings, LogBox, Platform, useColorScheme } from 'react-native';
33
import { createDrawerNavigator } from '@react-navigation/drawer';
44
import { DarkTheme, DefaultTheme, NavigationContainer } from '@react-navigation/native';
55
import { createStackNavigator } from '@react-navigation/stack';
6-
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context';
6+
import { SafeAreaProvider } from 'react-native-safe-area-context';
77
import {
88
Chat,
99
OverlayProvider,
1010
SqliteClient,
1111
ThemeProvider,
12+
useChatContext,
1213
useOverlayContext,
1314
} from 'stream-chat-react-native';
1415
import { getMessaging } from '@react-native-firebase/messaging';
@@ -171,14 +172,6 @@ const DrawerNavigatorWrapper: React.FC<{
171172
}> = ({ chatClient }) => {
172173
const streamChatTheme = useStreamChatTheme();
173174

174-
chatClient.setMessageComposerSetupFunction(({ composer }) => {
175-
composer.updateConfig({
176-
drafts: {
177-
enabled: true,
178-
},
179-
});
180-
});
181-
182175
return (
183176
<GestureHandlerRootView style={{ flex: 1 }}>
184177
<OverlayProvider value={{ style: streamChatTheme }}>
@@ -219,6 +212,19 @@ const UserSelector = () => (
219212
const HomeScreen = () => {
220213
const { overlay } = useOverlayContext();
221214

215+
const { client: chatClient } = useChatContext();
216+
217+
useEffect(() => {
218+
chatClient.setMessageComposerSetupFunction(({ composer }) => {
219+
console.log('Setting up message composer:', composer.tag);
220+
composer.updateConfig({
221+
drafts: {
222+
enabled: true,
223+
},
224+
});
225+
});
226+
}, [chatClient]);
227+
222228
return (
223229
<Stack.Navigator
224230
initialRouteName={initialChannelIdGlobalRef.current ? 'ChannelScreen' : 'MessagingScreen'}

examples/SampleApp/src/components/BottomTabs.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { MentionsTab } from '../icons/MentionsTab';
1111

1212
import type { BottomTabBarProps } from '@react-navigation/bottom-tabs';
1313
import type { Route } from '@react-navigation/native';
14+
import { DraftsTab } from '../icons/DraftsTab';
1415

1516
const styles = StyleSheet.create({
1617
notification: {
@@ -44,6 +45,13 @@ const getTab = (key: string) => {
4445
notification: <ChannelsUnreadCountBadge />,
4546
title: 'Chats',
4647
};
48+
case 'DraftsScreen':
49+
return {
50+
icon: <DraftsTab />,
51+
iconActive: <DraftsTab active />,
52+
title: 'Drafts',
53+
notification: <ChannelsUnreadCountBadge />,
54+
};
4755
case 'ThreadsScreen':
4856
return {
4957
icon: <ThreadsTab />,
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { FlatList, Pressable, StyleSheet, Text, View } from 'react-native';
2+
import { DraftsIcon } from '../icons/DraftIcon';
3+
import { useChatContext, useStateStore, useTheme } from 'stream-chat-react-native';
4+
import { DraftManagerState, DraftsManager } from '../utils/DraftsManager';
5+
import { useEffect, useMemo } from 'react';
6+
import dayjs from 'dayjs';
7+
import { useNavigation } from '@react-navigation/native';
8+
import { ChannelResponse, MessageResponseBase } from 'stream-chat';
9+
10+
export type DraftItemProps = {
11+
type?: 'channel' | 'thread';
12+
channel?: ChannelResponse;
13+
date?: string;
14+
content?: string;
15+
// TODO: Fix the type for thread
16+
thread?: MessageResponseBase;
17+
parentId?: string;
18+
};
19+
20+
export const DraftItem = ({ type, channel, date, content, parentId, thread }: DraftItemProps) => {
21+
const {
22+
theme: {
23+
colors: { grey },
24+
},
25+
} = useTheme();
26+
const navigation = useNavigation();
27+
const { client } = useChatContext();
28+
29+
const onNavigationHandler = async () => {
30+
if (channel?.type && channel.id) {
31+
console.log(channel);
32+
const resultChannel = client.channel(channel?.type, channel?.id);
33+
await resultChannel?.watch();
34+
if (type === 'thread' && parentId) {
35+
navigation.navigate('ThreadScreen', {
36+
thread: thread,
37+
channel: resultChannel,
38+
});
39+
} else if (type === 'channel') {
40+
navigation.navigate('ChannelScreen', { channel: resultChannel });
41+
}
42+
}
43+
};
44+
45+
return (
46+
<Pressable
47+
style={({ pressed }) => [styles.itemContainer, { opacity: pressed ? 0.8 : 1 }]}
48+
onPress={onNavigationHandler}
49+
>
50+
<View style={styles.header}>
51+
<Text style={styles.name}>
52+
{type === 'channel' ? `# ${channel?.name}` : `Thread in # ${channel?.name}`}
53+
</Text>
54+
<Text style={[styles.date, { color: grey }]}>{dayjs(date).fromNow()}</Text>
55+
</View>
56+
<View style={styles.content}>
57+
<View style={styles.icon}>
58+
<DraftsIcon />
59+
</View>
60+
<Text style={[styles.text, { color: grey }]} numberOfLines={1}>
61+
{content}
62+
</Text>
63+
</View>
64+
</Pressable>
65+
);
66+
};
67+
68+
const selector = (nextValue: DraftManagerState) =>
69+
({
70+
isLoading: nextValue.pagination.isLoading,
71+
isLoadingNext: nextValue.pagination.isLoadingNext,
72+
drafts: nextValue.drafts,
73+
}) as const;
74+
75+
export const DraftsList = () => {
76+
const { client } = useChatContext();
77+
const draftsManager = useMemo(() => new DraftsManager({ client }), [client]);
78+
79+
useEffect(() => {
80+
draftsManager.reload({ force: true });
81+
}, [draftsManager]);
82+
83+
const { isLoading, drafts, isLoadingNext } = useStateStore(draftsManager.state, selector);
84+
85+
return (
86+
<View>
87+
<FlatList
88+
data={drafts}
89+
refreshing={isLoading}
90+
keyExtractor={(item) => item.message.id}
91+
renderItem={({ item }) => (
92+
<DraftItem
93+
channel={item.channel}
94+
type={item.parent_id ? 'thread' : 'channel'}
95+
date={item.created_at}
96+
content={item.message.text}
97+
thread={item.parent_message}
98+
parentId={item.parent_id}
99+
/>
100+
)}
101+
onRefresh={() => draftsManager.reload({ force: true })}
102+
ListEmptyComponent={
103+
!isLoading && drafts.length === 0 ? (
104+
<Text style={{ textAlign: 'center', padding: 20 }}>No drafts available</Text>
105+
) : null
106+
}
107+
onEndReached={() => {
108+
if (!isLoadingNext) {
109+
draftsManager.loadNextPage();
110+
}
111+
}}
112+
/>
113+
</View>
114+
);
115+
};
116+
117+
const styles = StyleSheet.create({
118+
itemContainer: {
119+
paddingVertical: 8,
120+
marginHorizontal: 8,
121+
borderBottomWidth: 1,
122+
borderBottomColor: '#ccc',
123+
},
124+
header: {
125+
flexDirection: 'row',
126+
alignItems: 'center',
127+
justifyContent: 'space-between',
128+
},
129+
name: {
130+
fontSize: 16,
131+
fontWeight: 'bold',
132+
},
133+
date: {},
134+
content: {
135+
flexDirection: 'row',
136+
alignItems: 'center',
137+
marginTop: 4,
138+
},
139+
icon: {},
140+
text: {
141+
marginLeft: 8,
142+
flexShrink: 1,
143+
},
144+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from 'react';
2+
import Svg, { Path } from 'react-native-svg';
3+
import { useTheme } from 'stream-chat-react-native';
4+
5+
import { IconProps } from '../utils/base';
6+
7+
export const DraftsIcon: React.FC<IconProps> = ({ height = 29, width = 30 }) => {
8+
const {
9+
theme: {
10+
colors: { grey },
11+
},
12+
} = useTheme();
13+
return (
14+
<Svg fill='none' height={height} viewBox={`0 0 ${height} ${width}`} width={width}>
15+
<Path
16+
clipRule='evenodd'
17+
d='M7.93652 28.729C7.53808 28.729 7.22754 28.6001 7.00488 28.3423C6.78222 28.0845 6.67089 27.7329 6.67089 27.2876V24.4048H5.98535C4.93066 24.4048 4.02539 24.2026 3.26953 23.7983C2.51367 23.3999 1.93359 22.8198 1.52929 22.0581C1.125 21.2905 0.922848 20.3706 0.922848 19.2983V10.3071C0.922848 9.23486 1.12207 8.31787 1.5205 7.55615C1.9248 6.78857 2.50781 6.20264 3.26953 5.79834C4.03125 5.39404 4.95703 5.19189 6.04687 5.19189H20.6103L18.668 7.12549H6.09961C5.3789 7.12549 4.77832 7.25146 4.29785 7.50342C3.82324 7.74951 3.46582 8.11279 3.22558 8.59326C2.98535 9.07373 2.86523 9.6626 2.86523 10.3599V19.228C2.86523 19.9312 2.98535 20.5229 3.22558 21.0034C3.46582 21.4839 3.82324 21.8501 4.29785 22.1021C4.77832 22.3481 5.3789 22.4712 6.09961 22.4712H7.59375C7.88086 22.4712 8.08886 22.5327 8.21777 22.6558C8.35254 22.7788 8.41992 22.9897 8.41992 23.2886V26.5493L12.0674 23.0161C12.29 22.7935 12.4951 22.647 12.6826 22.5767C12.8701 22.5063 13.1221 22.4712 13.4385 22.4712H20.8916C21.6064 22.4712 22.2012 22.3481 22.6758 22.1021C23.1562 21.8501 23.5166 21.4839 23.7568 21.0034C24.0029 20.5229 24.126 19.9312 24.126 19.228V9.99072L26.0684 8.04834V19.2983C26.0684 20.3647 25.8662 21.2817 25.4619 22.0493C25.0635 22.811 24.4834 23.394 23.7217 23.7983C22.9658 24.2026 22.04 24.4048 20.9443 24.4048H13.5088L9.58007 27.8589C9.25195 28.1519 8.96484 28.3687 8.71875 28.5093C8.47851 28.6558 8.21777 28.729 7.93652 28.729ZM15.1611 15.1587C15.0439 15.2056 14.9326 15.1792 14.8271 15.0796C14.7275 14.98 14.707 14.8687 14.7656 14.7456L15.8379 12.4956L25.541 2.79248L27.1758 4.41846L17.4639 14.1216L15.1611 15.1587ZM28.0107 3.5835L26.3848 1.93994L27.1846 1.15771C27.3896 0.946777 27.6269 0.83252 27.8965 0.814941C28.166 0.797363 28.3916 0.882324 28.5732 1.06982L28.8457 1.32471C29.0566 1.53564 29.1592 1.77588 29.1533 2.04541C29.1475 2.30908 29.0332 2.55225 28.8105 2.7749L28.0107 3.5835Z'
18+
fill={grey}
19+
fillRule='evenodd'
20+
/>
21+
</Svg>
22+
);
23+
};
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from 'react';
2+
import Svg, { Path } from 'react-native-svg';
3+
import { useTheme } from 'stream-chat-react-native';
4+
5+
import { IconProps } from '../utils/base';
6+
7+
export const DraftsTab: React.FC<IconProps> = ({ active, height = 29, width = 30 }) => {
8+
const {
9+
theme: {
10+
colors: { black, grey },
11+
},
12+
} = useTheme();
13+
return (
14+
<Svg fill='none' height={height} viewBox={`0 0 ${height} ${width}`} width={width}>
15+
<Path
16+
clipRule='evenodd'
17+
d='M7.93652 28.729C7.53808 28.729 7.22754 28.6001 7.00488 28.3423C6.78222 28.0845 6.67089 27.7329 6.67089 27.2876V24.4048H5.98535C4.93066 24.4048 4.02539 24.2026 3.26953 23.7983C2.51367 23.3999 1.93359 22.8198 1.52929 22.0581C1.125 21.2905 0.922848 20.3706 0.922848 19.2983V10.3071C0.922848 9.23486 1.12207 8.31787 1.5205 7.55615C1.9248 6.78857 2.50781 6.20264 3.26953 5.79834C4.03125 5.39404 4.95703 5.19189 6.04687 5.19189H20.6103L18.668 7.12549H6.09961C5.3789 7.12549 4.77832 7.25146 4.29785 7.50342C3.82324 7.74951 3.46582 8.11279 3.22558 8.59326C2.98535 9.07373 2.86523 9.6626 2.86523 10.3599V19.228C2.86523 19.9312 2.98535 20.5229 3.22558 21.0034C3.46582 21.4839 3.82324 21.8501 4.29785 22.1021C4.77832 22.3481 5.3789 22.4712 6.09961 22.4712H7.59375C7.88086 22.4712 8.08886 22.5327 8.21777 22.6558C8.35254 22.7788 8.41992 22.9897 8.41992 23.2886V26.5493L12.0674 23.0161C12.29 22.7935 12.4951 22.647 12.6826 22.5767C12.8701 22.5063 13.1221 22.4712 13.4385 22.4712H20.8916C21.6064 22.4712 22.2012 22.3481 22.6758 22.1021C23.1562 21.8501 23.5166 21.4839 23.7568 21.0034C24.0029 20.5229 24.126 19.9312 24.126 19.228V9.99072L26.0684 8.04834V19.2983C26.0684 20.3647 25.8662 21.2817 25.4619 22.0493C25.0635 22.811 24.4834 23.394 23.7217 23.7983C22.9658 24.2026 22.04 24.4048 20.9443 24.4048H13.5088L9.58007 27.8589C9.25195 28.1519 8.96484 28.3687 8.71875 28.5093C8.47851 28.6558 8.21777 28.729 7.93652 28.729ZM15.1611 15.1587C15.0439 15.2056 14.9326 15.1792 14.8271 15.0796C14.7275 14.98 14.707 14.8687 14.7656 14.7456L15.8379 12.4956L25.541 2.79248L27.1758 4.41846L17.4639 14.1216L15.1611 15.1587ZM28.0107 3.5835L26.3848 1.93994L27.1846 1.15771C27.3896 0.946777 27.6269 0.83252 27.8965 0.814941C28.166 0.797363 28.3916 0.882324 28.5732 1.06982L28.8457 1.32471C29.0566 1.53564 29.1592 1.77588 29.1533 2.04541C29.1475 2.30908 29.0332 2.55225 28.8105 2.7749L28.0107 3.5835Z'
18+
fill={active ? black : grey}
19+
fillRule='evenodd'
20+
/>
21+
</Svg>
22+
);
23+
};

examples/SampleApp/src/screens/ChatScreen.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { RouteProp } from '@react-navigation/native';
1111
import type { StackNavigationProp } from '@react-navigation/stack';
1212

1313
import type { BottomTabNavigatorParamList, StackNavigatorParamList } from '../types';
14+
import { DraftsScreen } from './DraftScreen';
1415

1516
const Tab = createBottomTabNavigator<BottomTabNavigatorParamList>();
1617

@@ -32,5 +33,6 @@ export const ChatScreen: React.FC<Props> = () => (
3233
name='ThreadsScreen'
3334
options={{ headerShown: false }}
3435
/>
36+
<Tab.Screen component={DraftsScreen} name='DraftsScreen' options={{ headerShown: false }} />
3537
</Tab.Navigator>
3638
);
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React from 'react';
2+
import { StackNavigationProp } from '@react-navigation/stack';
3+
import { BottomTabNavigatorParamList } from '../types';
4+
import { StyleSheet, View } from 'react-native';
5+
import { useTheme } from 'stream-chat-react-native';
6+
import { ChatScreenHeader } from '../components/ChatScreenHeader';
7+
import { DraftsList } from '../components/DraftsList';
8+
9+
export type DraftsScreenProps = {
10+
navigation: StackNavigationProp<BottomTabNavigatorParamList, 'DraftsScreen'>;
11+
};
12+
13+
export const DraftsScreen: React.FC<DraftsScreenProps> = () => {
14+
const {
15+
theme: {
16+
colors: { white_snow },
17+
},
18+
} = useTheme();
19+
20+
return (
21+
<View
22+
style={[
23+
styles.container,
24+
{
25+
backgroundColor: white_snow,
26+
},
27+
]}
28+
>
29+
<ChatScreenHeader />
30+
<DraftsList />
31+
</View>
32+
);
33+
};
34+
35+
const styles = StyleSheet.create({
36+
container: {
37+
flex: 1,
38+
},
39+
emptyIndicatorContainer: {
40+
alignItems: 'center',
41+
flex: 1,
42+
justifyContent: 'center',
43+
},
44+
emptyIndicatorText: {
45+
fontSize: 14,
46+
paddingTop: 28,
47+
},
48+
});

examples/SampleApp/src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export type UserSelectorParamList = {
4949

5050
export type BottomTabNavigatorParamList = {
5151
ChatScreen: undefined;
52+
DraftsScreen: undefined;
5253
MentionsScreen: undefined;
5354
ThreadsScreen: undefined;
5455
};

0 commit comments

Comments
 (0)