Skip to content

Commit de5a167

Browse files
committed
feat: fix variety bugs and introduce actual action/reaction ui
1 parent 8996013 commit de5a167

File tree

7 files changed

+140
-93
lines changed

7 files changed

+140
-93
lines changed

examples/SampleApp/App.tsx

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ notifee.onBackgroundEvent(async ({ detail, type }) => {
9494
const Drawer = createDrawerNavigator();
9595
const Stack = createNativeStackNavigator<StackNavigatorParamList>();
9696
const UserSelectorStack = createNativeStackNavigator<UserSelectorParamList>();
97+
9798
const App = () => {
9899
const { chatClient, isConnecting, loginUser, logout, switchUser } = useChatClient();
99100
const [messageListImplementation, setMessageListImplementation] = useState<
@@ -107,6 +108,7 @@ const App = () => {
107108
>(undefined);
108109
const colorScheme = useColorScheme();
109110
const streamChatTheme = useStreamChatTheme();
111+
const streami18n = new Streami18n();
110112

111113
useEffect(() => {
112114
const messaging = getMessaging();
@@ -209,39 +211,41 @@ const App = () => {
209211
backgroundColor: streamChatTheme.colors?.white_snow || '#FCFCFC',
210212
}}
211213
>
212-
<ThemeProvider style={streamChatTheme}>
213-
<NavigationContainer
214-
ref={RootNavigationRef}
215-
theme={{
216-
colors: {
217-
...(colorScheme === 'dark' ? DarkTheme : DefaultTheme).colors,
218-
background: streamChatTheme.colors?.white_snow || '#FCFCFC',
219-
},
220-
fonts: (colorScheme === 'dark' ? DarkTheme : DefaultTheme).fonts,
221-
dark: colorScheme === 'dark',
222-
}}
223-
>
224-
<AppContext.Provider
225-
value={{
226-
chatClient,
227-
loginUser,
228-
logout,
229-
switchUser,
230-
messageListImplementation,
231-
messageListMode,
232-
messageListPruning,
214+
<OverlayProvider value={{ style: streamChatTheme }} i18nInstance={streami18n}>
215+
<ThemeProvider style={streamChatTheme}>
216+
<NavigationContainer
217+
ref={RootNavigationRef}
218+
theme={{
219+
colors: {
220+
...(colorScheme === 'dark' ? DarkTheme : DefaultTheme).colors,
221+
background: streamChatTheme.colors?.white_snow || '#FCFCFC',
222+
},
223+
fonts: (colorScheme === 'dark' ? DarkTheme : DefaultTheme).fonts,
224+
dark: colorScheme === 'dark',
233225
}}
234226
>
235-
{isConnecting && !chatClient ? (
236-
<LoadingScreen />
237-
) : chatClient ? (
238-
<DrawerNavigatorWrapper chatClient={chatClient} />
239-
) : (
240-
<UserSelector />
241-
)}
242-
</AppContext.Provider>
243-
</NavigationContainer>
244-
</ThemeProvider>
227+
<AppContext.Provider
228+
value={{
229+
chatClient,
230+
loginUser,
231+
logout,
232+
switchUser,
233+
messageListImplementation,
234+
messageListMode,
235+
messageListPruning,
236+
}}
237+
>
238+
{isConnecting && !chatClient ? (
239+
<LoadingScreen />
240+
) : chatClient ? (
241+
<DrawerNavigatorWrapper chatClient={chatClient} i18nInstance={streami18n} />
242+
) : (
243+
<UserSelector />
244+
)}
245+
</AppContext.Provider>
246+
</NavigationContainer>
247+
</ThemeProvider>
248+
</OverlayProvider>
245249
</SafeAreaProvider>
246250
);
247251
};
@@ -265,31 +269,27 @@ const isMessageAIGenerated = (message: LocalMessage) => !!message.ai_generated;
265269

266270
const DrawerNavigatorWrapper: React.FC<{
267271
chatClient: StreamChat;
268-
}> = ({ chatClient }) => {
269-
const streamChatTheme = useStreamChatTheme();
270-
const streami18n = new Streami18n();
271-
272+
i18nInstance: Streami18n;
273+
}> = ({ chatClient, i18nInstance }) => {
272274
return (
273275
<GestureHandlerRootView style={{ flex: 1 }}>
274-
<OverlayProvider value={{ style: streamChatTheme }} i18nInstance={streami18n}>
275-
<Chat
276-
client={chatClient}
277-
enableOfflineSupport
278-
// @ts-expect-error - the `ImageComponent` prop is generic, meaning we can expect an error
279-
ImageComponent={FastImage}
280-
isMessageAIGenerated={isMessageAIGenerated}
281-
i18nInstance={streami18n}
282-
>
283-
<StreamChatProvider>
284-
<AppOverlayProvider>
285-
<UserSearchProvider>
286-
<DrawerNavigator />
287-
<Toast />
288-
</UserSearchProvider>
289-
</AppOverlayProvider>
290-
</StreamChatProvider>
291-
</Chat>
292-
</OverlayProvider>
276+
<Chat
277+
client={chatClient}
278+
enableOfflineSupport
279+
// @ts-expect-error - the `ImageComponent` prop is generic, meaning we can expect an error
280+
ImageComponent={FastImage}
281+
isMessageAIGenerated={isMessageAIGenerated}
282+
i18nInstance={i18nInstance}
283+
>
284+
<StreamChatProvider>
285+
<AppOverlayProvider>
286+
<UserSearchProvider>
287+
<DrawerNavigator />
288+
<Toast />
289+
</UserSearchProvider>
290+
</AppOverlayProvider>
291+
</StreamChatProvider>
292+
</Chat>
293293
</GestureHandlerRootView>
294294
);
295295
};

package/src/components/Message/Message.tsx

Lines changed: 32 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
import React, { useEffect, useMemo, useRef, useState } from 'react';
22
import {
3-
Alert,
43
findNodeHandle,
54
GestureResponderEvent,
65
Keyboard,
7-
Pressable,
86
StyleProp,
97
UIManager,
108
View,
@@ -75,14 +73,15 @@ export type TouchableEmitter =
7573

7674
export type TextMentionTouchableHandlerAdditionalInfo = { user?: UserResponse };
7775

78-
function measureInWindow(node: any): Promise<{ x: number; y: number; w: number; h: number }> {
76+
// TODO: Take care of forwards compatibility with new measuring API (0.81+)
77+
const measureInWindow = (node: any): Promise<{ x: number; y: number; w: number; h: number }> => {
7978
return new Promise((resolve, reject) => {
8079
const handle = findNodeHandle(node);
8180
if (!handle) return reject(new Error('No native handle'));
8281

8382
UIManager.measureInWindow(handle, (x, y, w, h) => resolve({ h, w, x, y }));
8483
});
85-
}
84+
};
8685

8786
export type TextMentionTouchableHandlerPayload = {
8887
emitter: 'textMention';
@@ -216,6 +215,13 @@ export type MessagePropsWithContext = Pick<
216215
| 'supportedReactions'
217216
| 'updateMessage'
218217
| 'PollContent'
218+
// TODO: remove this comment later, using it as a pragma mark
219+
| 'MessageUserReactions'
220+
| 'MessageUserReactionsAvatar'
221+
| 'MessageUserReactionsItem'
222+
| 'MessageReactionPicker'
223+
| 'MessageActionList'
224+
| 'MessageActionListItem'
219225
> &
220226
Pick<ThreadContextValue, 'openThread'> &
221227
Pick<TranslationContextValue, 't'> & {
@@ -243,12 +249,6 @@ export type MessagePropsWithContext = Pick<
243249
* each individual Message component.
244250
*/
245251
const MessageWithContext = (props: MessagePropsWithContext) => {
246-
// const [messageOverlayVisible, setMessageOverlayVisible] = useState<{
247-
// x: number;
248-
// y: number;
249-
// w: number;
250-
// h: number;
251-
// } | null>(null);
252252
const [isErrorInMessage, setIsErrorInMessage] = useState(false);
253253
const [showMessageReactions, setShowMessageReactions] = useState(true);
254254
const [isBounceDialogOpen, setIsBounceDialogOpen] = useState(false);
@@ -288,7 +288,6 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
288288
MessageBlocked,
289289
MessageBounce,
290290
messageContentOrder: messageContentOrderProp,
291-
MessageMenu,
292291
messagesContext,
293292
MessageSimple,
294293
onLongPressMessage: onLongPressMessageProp,
@@ -312,6 +311,12 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
312311
updateMessage,
313312
readBy,
314313
setQuotedMessage,
314+
// MessageUserReactions,
315+
// MessageUserReactionsAvatar,
316+
// MessageUserReactionsItem,
317+
MessageReactionPicker,
318+
MessageActionList,
319+
MessageActionListItem,
315320
} = props;
316321
const isMessageAIGenerated = messagesContext.isMessageAIGenerated;
317322
const isAIGenerated = useMemo(
@@ -792,8 +797,6 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
792797
return <MessageBlocked message={message} />;
793798
}
794799

795-
console.log('ACTIVE: ', active);
796-
797800
return (
798801
<MessageProvider value={messageContext}>
799802
<View
@@ -817,7 +820,7 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
817820
]}
818821
testID='message-wrapper'
819822
>
820-
{active && state ? (
823+
{active && state?.rect ? (
821824
<View
822825
style={{
823826
height: state.rect.h,
@@ -827,34 +830,40 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
827830
) : null}
828831
<Portal hostName={active && !closing ? 'top-item' : undefined}>
829832
{active && !closing ? (
830-
<Pressable
833+
<View
831834
onLayout={(e) => {
832835
const { x, y, width: w, height: h } = e.nativeEvent.layout;
833836
topH.value = { h, w, x, y };
834837
}}
835-
onPress={() => Alert.alert('HIT TOP')}
836838
>
837-
<ReactionList />
838-
</Pressable>
839+
<MessageReactionPicker
840+
dismissOverlay={dismissOverlay}
841+
handleReaction={ownCapabilities.sendReaction ? handleReaction : undefined}
842+
ownReactionTypes={message?.own_reactions?.map((reaction) => reaction.type) || []}
843+
/>
844+
</View>
839845
) : null}
840846
</Portal>
841847
<Portal
842848
hostName={active ? 'message-overlay' : undefined}
843-
style={active && state ? { width: state.rect.w } : undefined}
849+
style={active && state?.rect ? { width: state.rect.w } : undefined}
844850
>
845851
<MessageSimple ref={messageWrapperRef} />
846852
</Portal>
847853
<Portal hostName={active && !closing ? 'bottom-item' : undefined}>
848854
{active && !closing ? (
849-
<Pressable
855+
<View
850856
onLayout={(e) => {
851857
const { x, y, width: w, height: h } = e.nativeEvent.layout;
852858
bottomH.value = { h, w, x, y };
853859
}}
854-
onPress={() => Alert.alert('HIT BOTTOM')}
855860
>
856-
<MessageActions />
857-
</Pressable>
861+
<MessageActionList
862+
dismissOverlay={dismissOverlay}
863+
MessageActionListItem={MessageActionListItem}
864+
messageActions={messageActions}
865+
/>
866+
</View>
858867
) : null}
859868
</Portal>
860869
{isBounceDialogOpen ? (
@@ -1084,8 +1093,3 @@ export const Message = (props: MessageProps) => {
10841093
/>
10851094
);
10861095
};
1087-
1088-
const ReactionList = () => <View style={{ backgroundColor: 'blue', height: 30, width: 150 }} />;
1089-
const MessageActions = () => (
1090-
<View style={{ backgroundColor: 'purple', height: 200, width: 150 }} />
1091-
);

package/src/components/Message/MessageSimple/MessageSimple.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { Dimensions, LayoutChangeEvent, StyleSheet, View } from 'react-native';
33

44
import { MessageBubble, SwipableMessageBubble } from './MessageBubble';
55

6+
import { updateOverlayState } from '../../../contexts';
67
import {
78
MessageContextValue,
89
useMessageContext,

package/src/components/MessageMenu/MessageActionList.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const MessageActionList = (props: MessageActionListProps) => {
2424
const { MessageActionListItem, messageActions } = props;
2525
const {
2626
theme: {
27+
colors: { white },
2728
messageMenu: {
2829
actionList: { container, contentContainer },
2930
},
@@ -37,8 +38,12 @@ export const MessageActionList = (props: MessageActionListProps) => {
3738
return (
3839
<ScrollView
3940
accessibilityLabel='Message action list'
40-
contentContainerStyle={[styles.contentContainer, contentContainer]}
41-
style={[styles.container, container]}
41+
contentContainerStyle={[
42+
styles.contentContainer,
43+
{ backgroundColor: white },
44+
contentContainer,
45+
]}
46+
style={[styles.container, { backgroundColor: white }, container]}
4247
>
4348
{messageActions?.map((messageAction, index) => (
4449
<MessageActionListItem
@@ -51,8 +56,10 @@ export const MessageActionList = (props: MessageActionListProps) => {
5156
};
5257

5358
const styles = StyleSheet.create({
54-
container: {},
59+
// TODO: Preliminary height fix, think about this more thoroughly
60+
container: { marginTop: 16, maxHeight: 200 },
5561
contentContainer: {
62+
flexGrow: 1,
5663
paddingHorizontal: 16,
5764
},
5865
});

package/src/components/MessageMenu/MessageActionListItem.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import React from 'react';
22
import { Pressable, StyleProp, StyleSheet, Text, TextStyle, View } from 'react-native';
33

4+
import { closeOverlay, scheduleActionOnClose } from '../../contexts';
45
import { useTheme } from '../../contexts/themeContext/ThemeContext';
6+
import { useStableCallback } from '../../hooks';
57

68
export type ActionType =
79
| 'banUser'
@@ -62,8 +64,13 @@ export const MessageActionListItem = (props: MessageActionListItemProps) => {
6264
},
6365
} = useTheme();
6466

67+
const onActionPress = useStableCallback(() => {
68+
closeOverlay();
69+
scheduleActionOnClose(() => action());
70+
});
71+
6572
return (
66-
<Pressable onPress={action} style={({ pressed }) => [{ opacity: pressed ? 0.5 : 1 }]}>
73+
<Pressable onPress={onActionPress} style={({ pressed }) => [{ opacity: pressed ? 0.5 : 1 }]}>
6774
<View
6875
accessibilityLabel={`${actionType} action list item`}
6976
style={[styles.container, container]}

package/src/components/MessageMenu/MessageReactionPicker.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { FlatList, StyleSheet, View } from 'react-native';
33

44
import { ReactionButton } from './ReactionButton';
55

6+
import { scheduleActionOnClose } from '../../contexts';
67
import { MessageContextValue } from '../../contexts/messageContext/MessageContext';
78
import {
89
MessagesContextValue,
@@ -62,10 +63,10 @@ export const MessageReactionPicker = (props: MessageReactionPickerProps) => {
6263

6364
const onSelectReaction = (type: string) => {
6465
NativeHandlers.triggerHaptic('impactLight');
66+
dismissOverlay();
6567
if (handleReaction) {
66-
handleReaction(type);
68+
scheduleActionOnClose(() => handleReaction(type));
6769
}
68-
dismissOverlay();
6970
};
7071

7172
if (!own_capabilities.sendReaction) {

0 commit comments

Comments
 (0)