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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,13 @@ npm install rn-firebase-chat
- Using [npm](https://www.npmjs.com/#getting-started):

```sh
npm install rn-firebase-chat @react-native-firebase/app @react-native-firebase/firestore @react-native-firebase/storage randomcolor react-native-aes-crypto react-native-gifted-chat react-native-keyboard-controller --save
npm install rn-firebase-chat @react-native-firebase/app @react-native-firebase/firestore @react-native-firebase/storage randomcolor react-native-aes-crypto react-native-gifted-chat react-native-gesture-handler react-native-keyboard-controller --save
```

- Using [Yarn](https://yarnpkg.com/):

```sh
yarn add rn-firebase-chat @react-native-firebase/app @react-native-firebase/firestore @react-native-firebase/storage randomcolor react-native-aes-crypto react-native-gifted-chat react-native-keyboard-controller
yarn add rn-firebase-chat @react-native-firebase/app @react-native-firebase/firestore @react-native-firebase/storage randomcolor react-native-aes-crypto react-native-gifted-chat react-native-gesture-handler react-native-keyboard-controller
```

If you're using Expo, please follow the dedicated setup guide to configure `plugins` in your `app.config.ts` and add Firebase files for Android and iOS:
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
"react-native": "0.80.0",
"react-native-aes-crypto": "^3.2.1",
"react-native-builder-bob": "^0.40.12",
"react-native-gesture-handler": "^2.28.0",
"react-native-gifted-chat": "^2.8.1",
"react-native-image-picker": "^8.2.1",
"react-native-keyboard-controller": "^1.18.6",
Expand All @@ -107,6 +108,7 @@
"react": "*",
"react-native": "*",
"react-native-aes-crypto": "*",
"react-native-gesture-handler": "*",
"react-native-gifted-chat": "*",
"react-native-image-picker": "*",
"react-native-keyboard-controller": "*",
Expand Down
1 change: 1 addition & 0 deletions src/asset/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const Images = {
placeHolder: require('../images/place_holder.png'),
playIcon: require('../images/play.png'),
pauseWhiteIcon: require('../images/pause_white.png'),
trash: require('../images/trash.png'),
};

export default Images;
162 changes: 154 additions & 8 deletions src/chat/ListConversationScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
import React, { useCallback, useMemo } from 'react';
import { FlatList, StyleSheet, View } from 'react-native';
import {
FlatList,
StyleSheet,
View,
Animated,
TouchableOpacity,
Image,
StyleProp,
ViewStyle,
} from 'react-native';
import { Swipeable } from 'react-native-gesture-handler';
import {
ConversationItem,
IConversationItemProps,
Expand All @@ -8,6 +18,7 @@ import type { ConversationProps } from '../interfaces';
import { useChatContext, useChatSelector } from '../hooks';
import { setConversation } from '../reducer';
import { getListConversation } from '../reducer/selectors';
import Images from '../asset';

type ListItem = {
item: ConversationProps;
Expand All @@ -17,15 +28,27 @@ type ListItem = {
export interface IListConversationProps {
hasSearchBar?: boolean;
onPress?: (conversation: ConversationProps) => void;
onDelete?: (conversation: ConversationProps) => void;
renderCustomItem?: ({ item, index }: ListItem) => React.JSX.Element | null;
conversationItemProps?: Omit<IConversationItemProps, 'data' | 'onPress'>; // remove default prop 'data' and 'onPress'
enableSwipeToDelete?: boolean;
deleteButtonColor?: string;
renderDeleteIcon?: () => React.ReactNode;
containerStyle?: StyleProp<ViewStyle>;
contentContainerStyle?: StyleProp<ViewStyle>;
}

export const ListConversationScreen: React.FC<IListConversationProps> = ({
// hasSearchBar,
onPress,
onDelete,
renderCustomItem,
conversationItemProps,
enableSwipeToDelete = true,
deleteButtonColor = '#FF3B30',
renderDeleteIcon,
containerStyle,
contentContainerStyle,
}) => {
const { chatDispatch, userInfo } = useChatContext();
const listConversation = useChatSelector(getListConversation);
Expand All @@ -43,33 +66,131 @@ export const ListConversationScreen: React.FC<IListConversationProps> = ({
[chatDispatch, onPress]
);

const handleDelete = useCallback(
(item: ConversationProps) => {
onDelete?.(item);
},
[onDelete]
);

const renderRightActions = useCallback(
(
item: ConversationProps,
progress: Animated.AnimatedInterpolation<number>,
dragX: Animated.AnimatedInterpolation<number>
) => {
const translateX = dragX.interpolate({
inputRange: [-100, 0],
outputRange: [0, 100],
extrapolate: 'clamp',
});

const opacity = progress.interpolate({
inputRange: [0, 1],
outputRange: [0, 1],
});

return (
<Animated.View
style={[
styles.deleteAction,
{
transform: [{ translateX }],
opacity,
},
]}
>
<TouchableOpacity
style={[
styles.deleteButton,
{ backgroundColor: deleteButtonColor },
]}
onPress={() => handleDelete(item)}
activeOpacity={0.7}
>
<Animated.View
style={[
styles.deleteContent,
{
transform: [
{
scale: progress.interpolate({
inputRange: [0, 1],
outputRange: [0.8, 1],
}),
},
],
},
]}
>
{renderDeleteIcon ? (
renderDeleteIcon()
) : (
<View style={styles.defaultDeleteIcon}>
<Image
source={Images.trash}
style={styles.trashIcon}
resizeMode="contain"
/>
</View>
)}
</Animated.View>
</TouchableOpacity>
</Animated.View>
);
},
[deleteButtonColor, handleDelete, renderDeleteIcon]
);

const renderItem = useCallback(
({ item, index }: ListItem) => {
if (renderCustomItem) return renderCustomItem({ item, index });
return (
const itemContent = renderCustomItem ? (
renderCustomItem({ item, index })
) : (
<ConversationItem
data={item}
onPress={handleConversationPressed}
{...(conversationItemProps || {})}
userInfo={userInfo}
/>
);

if (!enableSwipeToDelete) {
return itemContent;
}

return (
<Swipeable
key={item.id}
renderRightActions={(progress, dragX) =>
renderRightActions(item, progress, dragX)
}
overshootRight={false}
friction={2}
enableTrackpadTwoFingerGesture
>
{itemContent}
</Swipeable>
);
},
[
conversationItemProps,
handleConversationPressed,
renderCustomItem,
userInfo,
enableSwipeToDelete,
renderRightActions,
]
);

return (
<View style={styles.container}>
<View style={[styles.container, containerStyle]}>
<FlatList<ConversationProps>
contentContainerStyle={styles.contentContainer}
keyExtractor={(item, index) => index.toString()}
contentContainerStyle={[contentContainerStyle]}
keyExtractor={(item, index) => item.id || index.toString()}
data={data}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
/>
</View>
);
Expand All @@ -78,8 +199,33 @@ export const ListConversationScreen: React.FC<IListConversationProps> = ({
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'white',
},

deleteAction: {
justifyContent: 'center',
alignItems: 'flex-end',
minWidth: 100,
},
deleteButton: {
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 24,
height: '100%',
minWidth: 100,
},
deleteContent: {
justifyContent: 'center',
alignItems: 'center',
},
defaultDeleteIcon: {
width: 28,
height: 28,
justifyContent: 'center',
alignItems: 'center',
},
contentContainer: {
paddingTop: 15,
trashIcon: {
width: 28,
height: 28,
},
});
Loading