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
43 changes: 25 additions & 18 deletions package/src/components/Message/Message.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import React, { useEffect, useMemo, useRef, useState } from 'react';
import {
GestureResponderEvent,
StyleProp,
StyleSheet,
useWindowDimensions,
View,
ViewStyle,
} from 'react-native';
import { GestureResponderEvent, StyleProp, StyleSheet, View, ViewStyle } from 'react-native';

import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Portal } from 'react-native-teleport';
Expand Down Expand Up @@ -54,6 +47,7 @@ import { isVideoPlayerAvailable, NativeHandlers } from '../../native';
import {
closeOverlay,
openOverlay,
Rect,
setOverlayBottomH,
setOverlayMessageH,
setOverlayTopH,
Expand Down Expand Up @@ -332,16 +326,18 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
const isMessageTypeDeleted = message.type === 'deleted';
const { client } = chatContext;

const [rect, setRect] = useState<{ w: number; h: number; x: number; y: number } | undefined>(
undefined,
);
const { width: screenW } = useWindowDimensions();
const rectRef = useRef<Rect>(undefined);
const bubbleRect = useRef<Rect>(undefined);
const contextMenuAnchorRef = useRef<View>(null);

const showMessageOverlay = useStableCallback(async () => {
dismissKeyboard();
try {
const layout = await measureInWindow(messageWrapperRef, insets);
setRect(layout);
const bubbleLayout = await measureInWindow(contextMenuAnchorRef, insets).catch(() => layout);

rectRef.current = layout;
bubbleRect.current = bubbleLayout;
setOverlayMessageH(layout);
openOverlay({ id: messageOverlayId, messageId: message.id });
} catch (e) {
Expand Down Expand Up @@ -698,6 +694,7 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
actionsEnabled,
alignment,
channel,
contextMenuAnchorRef,
deliveredToCount,
dismissOverlay,
files: attachments.files,
Expand Down Expand Up @@ -815,6 +812,8 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
const styles = useStyles({
highlightedMessage: (isTargetedMessage || message.pinned) && !isMessageTypeDeleted,
});
const rect = rectRef.current;
const overlayItemsAnchorRect = bubbleRect.current ?? rect;

if (!(isMessageTypeDeleted || messageContentOrder.length)) {
return null;
Expand All @@ -841,15 +840,18 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
) : null}
{/*TODO: V9: Find a way to separate these in a dedicated file*/}
<Portal hostName={overlayActive && rect ? 'top-item' : undefined}>
{overlayActive && rect ? (
{overlayActive && rect && overlayItemsAnchorRect ? (
<View
onLayout={(e) => {
const { width: w, height: h } = e.nativeEvent.layout;

setOverlayTopH({
h,
w,
x: isMyMessage ? screenW - rect.x - w : rect.x,
x:
alignment === 'right'
? overlayItemsAnchorRect.x + overlayItemsAnchorRect.w - w
: overlayItemsAnchorRect.x,
y: rect.y - h,
});
}}
Expand All @@ -865,7 +867,9 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
hostName={overlayActive ? 'message-overlay' : undefined}
style={overlayActive && rect ? { width: rect.w } : undefined}
>
<MessageItemView ref={messageWrapperRef} />
<View ref={messageWrapperRef}>
<MessageItemView />
</View>
</Portal>
{showMessageReactions ? (
<BottomSheetModal
Expand All @@ -883,14 +887,17 @@ const MessageWithContext = (props: MessagePropsWithContext) => {
</BottomSheetModal>
) : null}
<Portal hostName={overlayActive && rect ? 'bottom-item' : undefined}>
{overlayActive && rect ? (
{overlayActive && rect && overlayItemsAnchorRect ? (
<View
onLayout={(e) => {
const { width: w, height: h } = e.nativeEvent.layout;
setOverlayBottomH({
h,
w,
x: isMyMessage ? screenW - rect.x - w : rect.x,
x:
alignment === 'right'
? overlayItemsAnchorRect.x + overlayItemsAnchorRect.w - w
: overlayItemsAnchorRect.x,
y: rect.y + rect.h,
});
}}
Expand Down
103 changes: 5 additions & 98 deletions package/src/components/Message/MessageItemView/MessageBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,88 +10,24 @@ import Animated, {
withSpring,
} from 'react-native-reanimated';

import { MessageContentProps } from './MessageContent';
import { MessageItemViewPropsWithContext } from './MessageItemView';

import { MessagesContextValue, useTheme } from '../../../contexts';

import { NativeHandlers } from '../../../native';
import { MessageStatusTypes } from '../../../utils/utils';

export type MessageBubbleProps = Pick<
MessagesContextValue,
| 'reactionListPosition'
| 'MessageContent'
| 'ReactionListTop'
| 'MessageError'
| 'reactionListType'
> &
Pick<
MessageContentProps,
| 'isVeryLastMessage'
| 'backgroundColor'
| 'messageGroupedSingleOrBottom'
| 'noBorder'
| 'message'
> &
Pick<MessageItemViewPropsWithContext, 'alignment'>;

export const MessageBubble = React.memo(
({
alignment,
reactionListPosition,
reactionListType,
MessageContent,
ReactionListTop,
backgroundColor,
isVeryLastMessage,
messageGroupedSingleOrBottom,
noBorder,
MessageError,
message,
}: MessageBubbleProps) => {
const styles = useStyles({ alignment });
const isMessageErrorType =
message?.type === 'error' || message?.status === MessageStatusTypes.FAILED;

return (
<View style={styles.wrapper}>
{reactionListPosition === 'top' && ReactionListTop ? (
<View style={styles.reactionListTopContainer}>
<ReactionListTop type={reactionListType} />
</View>
) : null}
<View style={styles.contentContainer}>
<MessageContent
backgroundColor={backgroundColor}
isVeryLastMessage={isVeryLastMessage}
messageGroupedSingleOrBottom={messageGroupedSingleOrBottom}
noBorder={noBorder}
/>

{isMessageErrorType ? (
<View style={styles.errorContainer}>
<MessageError />
</View>
) : null}
</View>
</View>
);
},
);

const AnimatedWrapper = Animated.createAnimatedComponent(View);

type SwipableMessageWrapperProps = Pick<MessagesContextValue, 'MessageSwipeContent'> &
Pick<MessageItemViewPropsWithContext, 'alignment' | 'messageSwipeToReplyHitSlop'> & {
Pick<MessageItemViewPropsWithContext, 'messageSwipeToReplyHitSlop'> & {
children: ReactNode;
onSwipe: () => void;
};

export const SwipableMessageWrapper = React.memo((props: SwipableMessageWrapperProps) => {
const { MessageSwipeContent, children, messageSwipeToReplyHitSlop, onSwipe } = props;

const styles = useStyles({ alignment: props.alignment });
const styles = useStyles();

const translateX = useSharedValue(0);
const touchStart = useSharedValue<{ x: number; y: number } | null>(null);
Expand Down Expand Up @@ -187,14 +123,10 @@ export const SwipableMessageWrapper = React.memo((props: SwipableMessageWrapperP
);
});

const useStyles = ({ alignment }: { alignment?: 'left' | 'right' }) => {
const useStyles = () => {
const {
theme: {
messageItemView: {
bubble: { contentContainer, errorContainer, reactionListTopContainer, wrapper },
contentWrapper,
swipeContentContainer,
},
messageItemView: { contentWrapper, swipeContentContainer },
},
} = useTheme();
return useMemo(() => {
Expand All @@ -205,34 +137,9 @@ const useStyles = ({ alignment }: { alignment?: 'left' | 'right' }) => {
zIndex: 1, // To hide the stick inside the message content
...contentWrapper,
},
contentContainer: {
alignSelf: alignment === 'left' ? 'flex-start' : 'flex-end',
...contentContainer,
},
swipeContentContainer: {
...swipeContentContainer,
},
errorContainer: {
position: 'absolute',
top: 8,
right: -12,
...errorContainer,
},
reactionListTopContainer: {
alignSelf: alignment === 'left' ? 'flex-end' : 'flex-start',
...reactionListTopContainer,
},
wrapper: {
...wrapper,
},
});
}, [
alignment,
contentContainer,
contentWrapper,
errorContainer,
reactionListTopContainer,
swipeContentContainer,
wrapper,
]);
}, [contentWrapper, swipeContentContainer]);
};
Loading
Loading