414 lines
15 KiB
TypeScript
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,
|
|
},
|
|
});
|