Commit 804f4936 by tungnq

IMPORTANT: Xử lý cử chỉ vuốt trong swipe list của màn hộp thư đến

parent 00653950
......@@ -110,6 +110,41 @@ const styles = StyleSheet.create({
fontWeight: '600',
fontFamily: R.fonts.fontMedium,
fontSize: R.fontsize.fontSizeContent,
},
swipeContainer: {
position: 'relative',
overflow: 'hidden',
},
deleteBackground: {
position: 'absolute',
right: 2.5,
top: 5,
bottom: 5,
width: 95,
backgroundColor: R.colors.red,
borderRadius:25,
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
},
deleteButton: {
width: 100,
height: '100%',
justifyContent: 'center',
alignItems: 'center',
flexDirection: 'column',
},
deleteIcon: {
width: 24,
height: 24,
tintColor: R.colors.white,
marginBottom: 4,
},
deleteText: {
color: R.colors.white,
fontSize: R.fontsize.fontSizeContent,
fontFamily: R.fonts.fontMedium,
fontWeight: '600',
}
})
......
import React from 'react';
import React, {useState, useRef} from 'react';
import {
Text,
View,
......@@ -7,6 +7,9 @@ import {
Image,
ActivityIndicator,
RefreshControl,
Animated,
PanResponder,
Dimensions,
} from 'react-native';
import styles from './style';
import FAB from '../../components/FAB/fab';
......@@ -19,6 +22,15 @@ const EmailHomeView = props => {
const {groupedEmails, loading, onEmailPress, onRefresh, formatEmailDate} = props;
const navigation = useNavigation();
const screenWidth = Dimensions.get('window').width;
const [localGroupedEmails, setLocalGroupedEmails] = useState(groupedEmails);
const [swipedEmailId, setSwipedEmailId] = useState(null);
// Update local state when props change
React.useEffect(() => {
setLocalGroupedEmails(groupedEmails);
}, [groupedEmails]);
if (loading) {
return (
......@@ -29,60 +41,173 @@ const EmailHomeView = props => {
);
}
const renderEmailItem = ({item}) => (
<TouchableOpacity
style={[styles.emailItem, !item.isRead && styles.unreadEmail]}
onPress={() => navigation.navigate(SCREENNAME.DETAILEMAIL)}>
<View style={styles.avatarContainer}>
<View style={styles.avatar}>
<Text style={styles.avatarText}>
{item.sender.charAt(0).toUpperCase()}
</Text>
</View>
</View>
const SwipeableEmailItem = ({item, onDelete}) => {
const translateX = useRef(new Animated.Value(0)).current;
const deleteThreshold = screenWidth * 0.3;
<View style={styles.emailContent}>
<View style={styles.emailHeader}>
<Text style={[styles.senderName, !item.isRead && styles.unreadText]}>
{item.sender}
</Text>
</View>
<Text
style={[styles.emailSubject, !item.isRead && styles.unreadText]}
numberOfLines={1}>
{item.subject}
</Text>
<Text
style={[styles.emailPreview, !item.isRead && styles.unreadEmail]}
numberOfLines={2}>
{item.preview}
</Text>
</View>
// Reset position when another email is swiped
React.useEffect(() => {
if (swipedEmailId !== null && swipedEmailId !== item.id) {
Animated.spring(translateX, {
toValue: 0,
useNativeDriver: true,
}).start();
}
}, [swipedEmailId]);
const panResponder = PanResponder.create({
onMoveShouldSetPanResponder: (evt, gestureState) => {
// console.log('gestureState', gestureState);
const moveX = Math.abs(gestureState.dx);
console.log('moveX', moveX);
const isSwipingLeft = moveX < deleteThreshold;
console.log('isSwipingLeft', isSwipingLeft);
return isSwipingLeft;
},
onPanResponderMove: (evt, gestureState) => {
console.log('gestureState', gestureState.dx);
if (gestureState.dx > -100 && gestureState.dx < 0 ) {
translateX.setValue(-100);
}else if(gestureState.dx >0){
translateX.setValue(0);
}
},
// onPanResponderRelease: (evt, gestureState) => {
// console.log('deleteThreshold', -deleteThreshold);
// if (gestureState.dx < -deleteThreshold) {
// // Show delete button
// Animated.timing(translateX, {
// toValue: -100,
// duration: 300,
// useNativeDriver: true,
// }).start();
// setSwipedEmailId(item.id);
// }
// else {
// Animated.spring(translateX, {
// toValue: 0,
// useNativeDriver: true,
// }).start(() => {
// setSwipedEmailId(null);
// });
// }
// },
});
const deleteOpacity = translateX.interpolate({
inputRange: [-deleteThreshold, 0],
outputRange: [1, 0],
extrapolate: 'clamp',
});
const handleDeletePress = () => {
// Delete immediately without animation
onDelete(item.id);
setSwipedEmailId(null);
};
const handleCancelPress = () => {
// Snap back to original position
Animated.spring(translateX, {
toValue: 0,
useNativeDriver: true,
}).start(() => {
setSwipedEmailId(null);
});
};
return (
<View style={styles.swipeContainer}>
{/* Delete background */}
<Animated.View style={[styles.deleteBackground, {opacity: deleteOpacity}]}>
<TouchableOpacity
style={styles.deleteButton}
onPress={handleDeletePress}
>
<Image source={R.images.icDelete} style={styles.deleteIcon} />
<Text style={styles.deleteText}>Xóa</Text>
</TouchableOpacity>
</Animated.View>
{/* Email item */}
<Animated.View
style={[{transform: [{translateX}]}]}
{...panResponder.panHandlers}>
<TouchableOpacity
style={[styles.emailItem, !item.isRead && styles.unreadEmail]}
onPress={() => navigation.navigate(SCREENNAME.DETAILEMAIL)}>
<View style={styles.avatarContainer}>
<View style={styles.avatar}>
<Text style={styles.avatarText}>
{item.sender.charAt(0).toUpperCase()}
</Text>
</View>
</View>
<View style={styles.emailContent}>
<View style={styles.emailHeader}>
<Text style={[styles.senderName, !item.isRead && styles.unreadText]}>
{item.sender}
</Text>
</View>
<Text
style={[styles.emailSubject, !item.isRead && styles.unreadText]}
numberOfLines={1}>
{item.subject}
</Text>
<Text
style={[styles.emailPreview, !item.isRead && styles.unreadEmail]}
numberOfLines={2}>
{item.preview}
</Text>
</View>
<View
style={{
justifyContent: 'center',
alignItems: 'flex-end',
position: 'absolute',
right: 15,
top: 10,
}}>
<Text style={styles.emailDate}>
{`${item.date.getDate().toString().padStart(2, '0')}/${(
item.date.getMonth() + 1
)
.toString()
.padStart(2, '0')}/${item.date.getFullYear()}`}
</Text>
{item.hasAttachment && (
<View style={styles.attachmentIndicator}>
<Text style={styles.attachmentCount}>2</Text>
</View>
)}
<View
style={{
justifyContent: 'center',
alignItems: 'flex-end',
position: 'absolute',
right: 15,
top: 10,
}}>
<Text style={styles.emailDate}>
{`${item.date.getDate().toString().padStart(2, '0')}/${(
item.date.getMonth() + 1
)
.toString()
.padStart(2, '0')}/${item.date.getFullYear()}`}
</Text>
{item.hasAttachment && (
<View style={styles.attachmentIndicator}>
<Text style={styles.attachmentCount}>2</Text>
</View>
)}
</View>
</TouchableOpacity>
</Animated.View>
</View>
</TouchableOpacity>
);
};
const handleDeleteEmail = (emailId) => {
// Remove email from local state temporarily
const updatedGroupedEmails = {};
Object.keys(localGroupedEmails).forEach(dateKey => {
const filteredEmails = localGroupedEmails[dateKey].filter(email => email.id !== emailId);
if (filteredEmails.length > 0) {
updatedGroupedEmails[dateKey] = filteredEmails;
}
});
setLocalGroupedEmails(updatedGroupedEmails);
};
const renderEmailItem = ({item}) => (
<SwipeableEmailItem item={item} onDelete={handleDeleteEmail} />
);
const renderDateSection = (dateLabel, emailsForDate) => (
......@@ -98,10 +223,10 @@ const EmailHomeView = props => {
<View style={styles.container}>
<View style={styles.body}>
<FlatList
data={Object.keys(groupedEmails)}
data={Object.keys(localGroupedEmails)}
keyExtractor={item => item}
renderItem={({item: dateLabel}) =>
renderDateSection(dateLabel, groupedEmails[dateLabel])
renderDateSection(dateLabel, localGroupedEmails[dateLabel])
}
refreshControl={
<RefreshControl
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment