Files
circle-collect/src/screens/notification/Notification.tsx
T

414 lines
15 KiB
TypeScript

// import React from 'react';
import {
View,
Text,
TouchableOpacity,
FlatList,
StyleSheet,
} from 'react-native';
import { useDispatch, useSelector } from 'react-redux';
import {
removeNotification,
} from '../../redux/notificationSlice';
import LinearGradient from 'react-native-linear-gradient';
import LucideIcon from '../../components/common/LucideIcon';
import AppView from '../../components/AppView';
import Header from '../../components/common/Header';
import { useNotificationHandler } from '../hook/useNotificationHandler';
import colors from '../../components/colors';
type FilterType = 'all' | 'JOIN_REQUEST' | 'CONTRIBUTION' | 'CONTRIBUTION_ACK' |
'CYCLE_COMPLETED' | 'WITHDRAWAL' | 'WITHDRAWAL_NOTIFY' |
'CYCLE_WAIVER' | 'WAIVER_RECEIVED' | 'USER_SELECTED' |
'GROUP_STARTED' | 'JOIN_APPROVED' | 'JOIN_VIA_INVITE';
const Notification = () => {
const dispatch = useDispatch();
const { handleMarkAsReadAndNavigate } = useNotificationHandler();
const notifications = useSelector(
(state: any) => state.notifications.notifications
);
console.log("notifications ==>", notifications);
const handleDelete = (messageId: string) => {
dispatch(removeNotification(messageId));
};
const handleMarkAsRead = (messageId: string, screen: string, groupId: string, cycleNumber: string, contributionId: string) => {
console.log("screen type : ", screen);
handleMarkAsReadAndNavigate(
messageId,
screen,
{
groupId: groupId,
cycleNumber,
contributionId,
actionTab3: 'Your Requests',
actionTab1: 'Joined Groups',
GroupDetails: {
item: {}
},
}
);
};
const formatTime = (timestamp: number) => {
const date = new Date(timestamp);
const now = new Date();
const diffInHours = (now.getTime() - date.getTime()) / (1000 * 60 * 60);
if (diffInHours < 1) {
return 'Just now';
} else if (diffInHours < 24) {
return `${Math.floor(diffInHours)}h ago`;
} else {
return date.toLocaleDateString();
}
};
const getNotificationIcon = (msgType: string) => {
switch (msgType) {
case 'JOIN_REQUEST':
return 'person-add-outline';
case 'CONTRIBUTION':
return 'cash-outline';
case 'CONTRIBUTION_ACK':
return 'checkmark-done-outline';
case 'CYCLE_COMPLETED':
return 'trophy-outline';
case 'WITHDRAWAL':
return 'card-outline';
case 'WITHDRAWAL_NOTIFY':
return 'notifications-outline';
case 'CYCLE_WAIVER':
return 'document-text-outline';
case 'WAIVER_RECEIVED':
return 'download-outline';
case 'USER_SELECTED':
return 'people-outline';
case 'GROUP_STARTED':
return 'rocket-outline';
case 'JOIN_APPROVED':
return 'checkmark-circle-outline';
case 'JOIN_VIA_INVITE':
return 'gift-outline';
default:
return 'notifications-outline';
}
};
const getNotificationColor = (msgType: string) => {
switch (msgType) {
case 'JOIN_REQUEST':
return '#FFB6C1'; // Pastel Pink
case 'CONTRIBUTION':
return '#98FB98'; // Pastel Green
case 'CONTRIBUTION_ACK':
return '#ADD8E6'; // Pastel Blue
case 'CYCLE_COMPLETED':
return '#D8BFD8'; // Pastel Purple
case 'WITHDRAWAL':
return '#FFD700'; // Pastel Yellow
case 'WITHDRAWAL_NOTIFY':
return '#FFA07A'; // Pastel Orange
case 'CYCLE_WAIVER':
return '#B0C4DE'; // Pastel Steel Blue
case 'WAIVER_RECEIVED':
return '#AFEEEE'; // Pastel Turquoise
case 'USER_SELECTED':
return '#DDA0DD'; // Pastel Plum
case 'GROUP_STARTED':
return '#87CEFA'; // Pastel Sky Blue
case 'JOIN_APPROVED':
return '#98FB98'; // Pastel Green
case 'JOIN_VIA_INVITE':
return '#FFFACD'; // Pastel Lemon
default:
return '#D3D3D3'; // Pastel Gray
}
};
const getFilterDisplayName = (filter: FilterType) => {
switch (filter) {
case 'JOIN_REQUEST':
return 'Requests';
case 'CONTRIBUTION':
return 'Contributions';
case 'CONTRIBUTION_ACK':
return 'Contributions Ack';
case 'CYCLE_COMPLETED':
return 'Cycle Completed';
case 'WITHDRAWAL':
return 'Withdrawals';
case 'WITHDRAWAL_NOTIFY':
return 'Withdrawal Notify';
case 'CYCLE_WAIVER':
return 'Waivers';
case 'WAIVER_RECEIVED':
return 'Waiver Received';
case 'USER_SELECTED':
return 'User Selected';
case 'GROUP_STARTED':
return 'Group Started';
case 'JOIN_APPROVED':
return 'Join Approved';
case 'JOIN_VIA_INVITE':
return 'Invites';
default:
return 'All';
}
};
const unreadCount = notifications.filter((item: any) => !item.read).length;
return (
<AppView>
{/* Header with actions */}
<Header
title={`Notifications ${unreadCount > 0 ? `(${unreadCount})` : ''}`}
/>
{/* <View style={styles.headerActions}>
{unreadCount > 0 && (
<LinearGradient
colors={[colors.primary, colors.primary]} // Pastel Green gradient
style={[styles.headerButton, styles.markReadButton]}>
<TouchableOpacity onPress={handleMarkAllAsRead}>
<Text style={styles.headerButtonText}>Mark All Read</Text>
</TouchableOpacity>
</LinearGradient>
)}
{notifications.length > 0 && (
<LinearGradient
colors={[colors.pastelPink, colors.primary]} // Pastel Pink to Pastel Orange gradient
style={styles.headerButton}>
<TouchableOpacity onPress={handleClearAll}>
<Text style={styles.headerButtonText}>Clear All</Text>
</TouchableOpacity>
</LinearGradient>
)}
</View> */}
{/* Notification List */}
<FlatList
data={notifications}
keyExtractor={(item) => item.messageId}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.listContainer}
renderItem={({ item }) => {
console.log("noti items : ", item)
return (
<TouchableOpacity
onPress={() => handleMarkAsRead(item.messageId, item?.data?.msgType, item?.data?.groupId, item?.data?.cycleNumber, item?.data?.contributionId)}
activeOpacity={0.7}
>
<LinearGradient
colors={item.read ? ['#F5F5F5', '#E8E8E8'] : ['#FFFFFF', '#F8F8FF']} // Pastel grays and whites
style={[
styles.notificationCard,
!item.read && styles.unreadNotification,
{ borderLeftColor: getNotificationColor(item.data.msgType) }
]}>
<View style={styles.notificationContent}>
{/* Icon */}
<View style={styles.iconContainer}>
<LucideIcon
name={getNotificationIcon(item.data.msgType)}
size={24}
color={getNotificationColor(item.data.msgType)}
/>
</View>
{/* Content */}
<View style={styles.textContent}>
<View style={styles.headerRow}>
<Text style={[
styles.title,
!item.read && styles.unreadTitle
]}>
{item.data.title}
</Text>
<Text style={styles.time}>
{formatTime(item.sentTime)}
</Text>
</View>
<Text style={styles.message}>
{item.data.body}
</Text>
{item.data.msgType && (
<View style={styles.tagContainer}>
<Text style={[
styles.tag,
{ backgroundColor: getNotificationColor(item.data.msgType) }
]}>
{getFilterDisplayName(item.data.msgType as FilterType)}
</Text>
</View>
)}
</View>
{/* Actions */}
<View style={styles.actionsContainer}>
{!item.read && (
<View style={styles.unreadIndicator} />
)}
<TouchableOpacity
onPress={() => handleDelete(item.messageId)}
style={styles.deleteButton}
>
<LucideIcon name="trash-outline" size={20} color={colors.pastelPink} /> {/* Pastel Pink */}
</TouchableOpacity>
</View>
</View>
</LinearGradient>
</TouchableOpacity>
)
}}
ListEmptyComponent={
<View style={styles.emptyContainer}>
<LucideIcon name="notifications-off-outline" size={64} color="#D3D3D3" /> {/* Pastel Gray */}
<Text style={styles.emptyText}>No notifications yet</Text>
<Text style={styles.emptySubtext}>
When you get notifications, they'll appear here
</Text>
</View>
}
/>
</AppView>
);
};
export default Notification;
const styles = StyleSheet.create({
headerActions: {
flexDirection: 'row',
gap: 8,
justifyContent: 'flex-end',
paddingHorizontal: 16,
paddingVertical: 10,
},
headerButton: {
borderRadius: 8,
paddingVertical: 6,
paddingHorizontal: 12,
minWidth: 80,
alignItems: 'center',
},
markReadButton: {
minWidth: 100,
},
headerButtonText: {
color: '#FFFFFF', // White text for better contrast on pastel backgrounds
fontWeight: '600',
fontSize: 12,
},
listContainer: {
paddingHorizontal: 12,
paddingBottom: 30,
paddingTop: 8,
},
notificationCard: {
borderRadius: 12,
padding: 16,
marginBottom: 10,
shadowColor: '#000',
shadowOpacity: 0.05, // Reduced opacity for softer shadow
shadowRadius: 4,
elevation: 2,
borderLeftWidth: 4,
borderLeftColor: 'transparent',
},
unreadNotification: {
backgroundColor: '#F8F8FF', // Pastel Lavender
},
notificationContent: {
flexDirection: 'row',
alignItems: 'flex-start',
gap: 12,
},
iconContainer: {
paddingTop: 2,
},
textContent: {
flex: 1,
gap: 4,
},
headerRow: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-start',
marginBottom: 4,
},
title: {
fontWeight: '600',
fontSize: 16,
color: '#696969', // Dim Gray
flex: 1,
marginRight: 8,
},
unreadTitle: {
fontWeight: '700',
color: '#2F4F4F', // Dark Slate Gray
},
message: {
fontSize: 14,
color: '#808080', // Gray
lineHeight: 20,
},
time: {
fontSize: 12,
color: '#A9A9A9', // Dark Gray
fontWeight: '500',
},
tagContainer: {
marginTop: 6,
},
tag: {
fontSize: 10,
color: '#FFFFFF', // White text for better contrast
paddingHorizontal: 8,
paddingVertical: 2,
borderRadius: 4,
overflow: 'hidden',
alignSelf: 'flex-start',
fontWeight: '600',
},
actionsContainer: {
alignItems: 'center',
gap: 8,
},
unreadIndicator: {
width: 8,
height: 8,
borderRadius: 4,
backgroundColor: '#87CEEB', // Pastel Sky Blue
},
deleteButton: {
padding: 4,
},
emptyContainer: {
alignItems: 'center',
justifyContent: 'center',
marginTop: 100,
paddingHorizontal: 40,
},
emptyText: {
fontSize: 18,
fontWeight: '600',
color: '#A9A9A9', // Dark Gray
marginTop: 16,
marginBottom: 8,
},
emptySubtext: {
fontSize: 14,
color: '#C0C0C0', // Silver
textAlign: 'center',
lineHeight: 20,
},
});