mirror of
https://github.com/urosran/cally.git
synced 2025-07-15 17:47:08 +00:00
183 lines
6.0 KiB
TypeScript
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
|