Files
cally/components/pages/notifications/NotificationsPage.tsx
2024-11-23 16:39:56 +01:00

183 lines
6.0 KiB
TypeScript

import {ActivityIndicator, Animated, FlatList, StyleSheet} from "react-native";
import React, {useCallback, useState} from "react";
import {Card, Text, View} from "react-native-ui-lib";
import HeaderTemplate from "@/components/shared/HeaderTemplate";
import {Notification, useGetNotifications} from "@/hooks/firebase/useGetNotifications";
import {formatDistanceToNow} from "date-fns";
import {useRouter} from "expo-router";
import {useSetAtom} from "jotai";
import {modeAtom, selectedDateAtom} from "@/components/pages/calendar/atoms";
import {Swipeable} from 'react-native-gesture-handler';
import {useDeleteNotification} from "@/hooks/firebase/useDeleteNotification";
interface NotificationItemProps {
item: Notification;
onDelete: (id: string) => void;
onPress: () => void;
isDeleting: boolean;
}
const NotificationItem: React.FC<NotificationItemProps> = React.memo(({
item,
onDelete,
onPress,
isDeleting
}) => {
const renderRightActions = useCallback((
progress: Animated.AnimatedInterpolation<number>,
dragX: Animated.AnimatedInterpolation<number>
) => {
const trans = dragX.interpolate({
inputRange: [-100, 0],
outputRange: [0, 100],
extrapolate: 'clamp'
});
return (
<Animated.View
style={[
styles.deleteAction,
{
transform: [{translateX: trans}],
},
]}
>
<Text style={styles.deleteActionText}>Delete</Text>
</Animated.View>
);
}, []);
return (
<Swipeable
renderRightActions={renderRightActions}
onSwipeableRightOpen={() => onDelete(item.id)}
overshootRight={false}
enabled={!isDeleting}
>
<Card
padding-20
marginB-10
onPress={onPress}
activeOpacity={0.6}
enableShadow={false}
style={styles.card}
>
{isDeleting && (
<View style={styles.loadingOverlay}>
<ActivityIndicator color="#000" size="large"/>
</View>
)}
<Text text70>{item.content}</Text>
<View row spread marginT-10>
<Text text90>
{formatDistanceToNow(new Date(item.timestamp), {addSuffix: true})}
</Text>
<Text text90>
{item.timestamp.toLocaleDateString()}
</Text>
</View>
</Card>
</Swipeable>
);
});
const NotificationsPage: React.FC = () => {
const setSelectedDate = useSetAtom(selectedDateAtom);
const setMode = useSetAtom(modeAtom);
const {data: notifications} = useGetNotifications();
const deleteNotification = useDeleteNotification();
const {push} = useRouter();
const [deletingIds, setDeletingIds] = useState<Set<string>>(new Set());
const goToEventDay = useCallback((notification: Notification) => () => {
if (notification?.date) {
setSelectedDate(notification.date);
setMode("day")
}
push({pathname: "/calendar"});
}, [push, setSelectedDate]);
const handleDelete = useCallback((notificationId: string) => {
setDeletingIds(prev => new Set(prev).add(notificationId));
deleteNotification.mutate(notificationId, {
onSettled: () => {
setDeletingIds(prev => {
const newSet = new Set(prev);
newSet.delete(notificationId);
return newSet;
});
}
});
}, [deleteNotification]);
const renderNotificationItem = useCallback(({item}: { item: Notification }) => (
<NotificationItem
item={item}
onDelete={handleDelete}
onPress={goToEventDay(item)}
isDeleting={deletingIds.has(item.id)}
/>
), [handleDelete, goToEventDay, deletingIds]);
return (
<View flexG height={"100%"}>
<View flexG>
<View marginH-25>
<HeaderTemplate
message={"Welcome to your notifications!"}
isWelcome={false}
>
<Text style={styles.subtitle}>
See your notifications here.
</Text>
</HeaderTemplate>
</View>
<FlatList
contentContainerStyle={styles.listContainer}
data={notifications ?? []}
renderItem={renderNotificationItem}
keyExtractor={(item) => item.id}
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
listContainer: {
paddingBottom: 10,
paddingHorizontal: 25,
},
card: {
width: '100%',
backgroundColor: 'white',
},
subtitle: {
fontFamily: "Manrope_400Regular",
fontSize: 14,
},
deleteAction: {
backgroundColor: '#FF3B30',
justifyContent: 'center',
alignItems: 'flex-end',
paddingRight: 30,
marginBottom: 10,
width: 100,
borderRadius: 10,
},
deleteActionText: {
color: 'white',
fontWeight: '600',
},
loadingOverlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(255, 255, 255, 0.8)',
justifyContent: 'center',
alignItems: 'center',
zIndex: 1,
},
});
export default NotificationsPage