Skip to content
Open
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
15 changes: 11 additions & 4 deletions src/chat/ChatScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,11 @@ import InputToolbar, { IInputToolbar } from './components/InputToolbar';
import { CameraView, CameraViewRef } from '../chat_obs/components/CameraView';
import SelectedImageModal from './components/SelectedImage';
import { useCameraPermission } from 'react-native-vision-camera';
import { CustomBubble, CustomImageVideoBubbleProps } from './components/bubble';
import {
CustomBubble,
CustomImageBubbleProps,
CustomVideoBubbleProps,
} from './components/bubble';
import { clearConversation } from '../reducer';

interface ChatScreenProps extends GiftedChatProps {
Expand All @@ -49,7 +53,8 @@ interface ChatScreenProps extends GiftedChatProps {
hasGallery?: boolean;
onPressCamera?: () => void;
customConversationInfo?: CustomConversationInfo;
customImageVideoBubbleProps: CustomImageVideoBubbleProps;
customImageBubbleProps: CustomImageBubbleProps;
customVideoBubbleProps: CustomVideoBubbleProps;
}

export const ChatScreen: React.FC<ChatScreenProps> = ({
Expand All @@ -62,7 +67,8 @@ export const ChatScreen: React.FC<ChatScreenProps> = ({
renderComposer,
inputToolbarProps,
customConversationInfo,
customImageVideoBubbleProps,
customImageBubbleProps,
customVideoBubbleProps,
...props
}) => {
const { userInfo, chatDispatch } = useChatContext();
Expand Down Expand Up @@ -240,7 +246,8 @@ export const ChatScreen: React.FC<ChatScreenProps> = ({
onSelectedMessage={() => {
//TODO: handle image/video press
}}
customImageVideoBubbleProps={customImageVideoBubbleProps}
CustomImageBubbleProps={customImageBubbleProps}
customVideoBubbleProps={customVideoBubbleProps}
position={bubble.position}
/>
);
Expand Down
43 changes: 33 additions & 10 deletions src/chat/components/bubble/CustomBubble.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,72 @@ import { StyleSheet, View } from 'react-native';
import { MessageTypes, type MessageProps } from '../../../interfaces';
import { Bubble } from 'react-native-gifted-chat';
import {
CustomImageVideoBubble,
CustomImageVideoBubbleProps,
} from './CustomImageVideoBubble';
CustomImageBubble,
CustomImageBubbleProps,
CustomVideoBubbleProps,
CustomVideoBubble,
} from '.';

interface CustomBubbleProps {
bubbleMessage: Bubble<MessageProps>['props'];
position: 'left' | 'right';
customImageVideoBubbleProps: CustomImageVideoBubbleProps;
customImageBubbleProps: CustomImageBubbleProps;
customVideoBubbleProps: CustomVideoBubbleProps;
onSelectedMessage: (message: MessageProps) => void;
}

export const CustomBubble: React.FC<CustomBubbleProps> = ({
bubbleMessage,
position,
customImageVideoBubbleProps,
customImageBubbleProps,
customVideoBubbleProps,
onSelectedMessage,
}) => {
const styleBuble = {
const styleBubble = {
left: { backgroundColor: 'transparent' },
right: { backgroundColor: 'transparent' },
};

const renderBubble = (currentMessage: MessageProps) => {
switch (currentMessage?.type) {
case MessageTypes.image:
return (
<Bubble
{...bubbleMessage}
renderCustomView={() =>
currentMessage && (
<CustomImageBubble
{...customImageBubbleProps}
message={currentMessage}
onSelectImgVideoUrl={(message) => {
console.log('message: ', message);
//TODO: handle image press
}}
position={position}
/>
)
}
wrapperStyle={styleBubble}
/>
);
case MessageTypes.video:
return (
<Bubble
{...bubbleMessage}
renderCustomView={() =>
currentMessage && (
<CustomImageVideoBubble
{...customImageVideoBubbleProps}
<CustomVideoBubble
{...customVideoBubbleProps}
message={currentMessage}
onSelectImgVideoUrl={(message) => {
console.log('message: ', message);
//TODO: handle image/video press
//TODO: handle video press
}}
position={position}
/>
)
}
wrapperStyle={styleBuble}
wrapperStyle={styleBubble}
/>
);

Expand Down
82 changes: 82 additions & 0 deletions src/chat/components/bubble/CustomImageBubble.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React from 'react';
import {
StyleSheet,
View,
Pressable,
ViewStyle,
StyleProp,
} from 'react-native';
import FastImage, {
type ImageStyle as FastImageStyle,
} from 'react-native-fast-image';
import { MessageTypes, type MessageProps } from '../../../interfaces';

export interface CustomImageBubbleProps {
message: MessageProps;
position: 'left' | 'right';
onSelectImgVideoUrl: (url: string) => void;
bubbleContainerStyle?: StyleProp<ViewStyle>;
bubbleStyle?: StyleProp<ViewStyle>;
imageStyle?: StyleProp<FastImageStyle>;
}

export const CustomImageBubble: React.FC<CustomImageBubbleProps> = ({
position,
message,
onSelectImgVideoUrl,
bubbleContainerStyle,
bubbleStyle,
imageStyle,
}) => {
const handleImagePress = () => {
if (message.path && message.type === MessageTypes.image) {
onSelectImgVideoUrl(message.path);
}
};

return (
<View
style={[
styles.bubbleContainer,
StyleSheet.flatten(bubbleContainerStyle),
position === 'left' ? styles.flexStart : styles.flexEnd,
]}
>
<Pressable
onPress={handleImagePress}
style={[styles.bubble, StyleSheet.flatten(bubbleStyle)]}
>
<FastImage
source={{ uri: message.path, priority: FastImage.priority.high }}
style={[styles.image, StyleSheet.flatten(imageStyle)]}
resizeMode="cover"
/>
</Pressable>
</View>
);
};

const styles = StyleSheet.create({
bubble: {
maxWidth: '70%',
backgroundColor: '#e1ffc7',
borderRadius: 20,
overflow: 'hidden',
borderColor: '#d3d3d3',
borderWidth: 1,
},
bubbleContainer: {
flexDirection: 'row',
marginVertical: 5,
},
image: {
width: 200,
height: 200,
},
flexEnd: {
justifyContent: 'flex-end',
},
flexStart: {
justifyContent: 'flex-start',
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -2,55 +2,42 @@ import React, { useRef, useState } from 'react';
import {
StyleSheet,
View,
Pressable,
Image,
TouchableOpacity,
ViewStyle,
StyleProp,
ImageStyle,
} from 'react-native';
import Video, { VideoRef } from 'react-native-video';
import FastImage, {
type ImageStyle as FastImageStyle,
} from 'react-native-fast-image';
import { MessageTypes, type MessageProps } from '../../../interfaces';
import { type MessageProps } from '../../../interfaces';

export interface CustomImageVideoBubbleProps {
export interface CustomVideoBubbleProps {
message: MessageProps;
position: 'left' | 'right';
onSelectImgVideoUrl: (url: string) => void;
playIcon?: string;
bubbleContainerStyle?: StyleProp<ViewStyle>;
bubbleStyle?: StyleProp<ViewStyle>;
imageStyle?: StyleProp<FastImageStyle>;
videoContainerStyle?: StyleProp<ViewStyle>;
videoStyle?: StyleProp<ViewStyle>;
playIconStyle?: StyleProp<ImageStyle>;
}

export const CustomImageVideoBubble: React.FC<CustomImageVideoBubbleProps> = ({
export const CustomVideoBubble: React.FC<CustomVideoBubbleProps> = ({
position,
message,
onSelectImgVideoUrl,
playIcon = require('../../../images/play.png'),
bubbleContainerStyle,
bubbleStyle,
imageStyle,
videoContainerStyle,
videoStyle,
playIconStyle,
}) => {
const [isPauseVideo, setIsPauseVideo] = useState(true);
const videoRefs = useRef<VideoRef>(null);

const handleImagePress = () => {
if (message.path && message.type === MessageTypes.image) {
onSelectImgVideoUrl(message.path);
}
};

const handleVideoPress = () => {
setIsPauseVideo(false);
setIsPauseVideo((prev) => !prev);
};

const handleVideoEnd = () => {
Expand All @@ -60,57 +47,48 @@ export const CustomImageVideoBubble: React.FC<CustomImageVideoBubbleProps> = ({
}
};

const renderImage = () => (
<FastImage
source={{ uri: message.path, priority: FastImage.priority.high }}
style={[styles.image, imageStyle]}
resizeMode="cover"
/>
);

const renderVideo = () => (
<TouchableOpacity
style={[styles.videoContainer, videoContainerStyle]}
onPress={handleVideoPress}
>
<View>
<Video
source={{ uri: message.path }}
style={[styles.video, videoStyle]}
paused={isPauseVideo}
repeat={false}
onEnd={handleVideoEnd}
ref={videoRefs}
/>
{isPauseVideo && (
<Image style={[styles.playIcon, playIconStyle]} source={playIcon} />
)}
</View>
</TouchableOpacity>
);

return (
<View
style={[
styles.bubbleContainer,
bubbleContainerStyle,
StyleSheet.flatten(bubbleContainerStyle),
position === 'left' ? styles.flexStart : styles.flexEnd,
]}
>
<Pressable
onPress={handleImagePress}
style={[styles.bubble, bubbleStyle]}
>
{message.type === MessageTypes.image ? renderImage() : renderVideo()}
</Pressable>
<View style={[styles.bubble, StyleSheet.flatten(bubbleStyle)]}>
<TouchableOpacity
style={[
styles.videoContainer,
StyleSheet.flatten(videoContainerStyle),
]}
onPress={handleVideoPress}
activeOpacity={0.8}
>
<View>
<Video
source={{ uri: message.path }}
style={[styles.video, StyleSheet.flatten(videoStyle)]}
paused={isPauseVideo}
repeat={false}
onEnd={handleVideoEnd}
ref={videoRefs}
/>
{isPauseVideo && (
<Image
style={[styles.playIcon, StyleSheet.flatten(playIconStyle)]}
source={playIcon}
/>
)}
</View>
</TouchableOpacity>
</View>
</View>
);
};

const styles = StyleSheet.create({
bubble: {
maxWidth: '70%',
backgroundColor: '#e1ffc7',
borderRadius: 20,
overflow: 'hidden',
borderColor: '#d3d3d3',
Expand All @@ -120,10 +98,6 @@ const styles = StyleSheet.create({
flexDirection: 'row',
marginVertical: 5,
},
image: {
width: 200,
height: 200,
},
videoContainer: {
width: 200,
height: 200,
Expand All @@ -143,6 +117,7 @@ const styles = StyleSheet.create({
right: 105,
top: 75,
zIndex: 1,
tintColor: '#d3d3d3',
},
flexEnd: {
justifyContent: 'flex-end',
Expand Down
3 changes: 2 additions & 1 deletion src/chat/components/bubble/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './CustomBubble';
export * from './CustomImageVideoBubble';
export * from './CustomImageBubble';
export * from './CustomVideoBubble';