import React, { useCallback, useEffect, useState, useMemo } from "react"; import { View, ActivityIndicator, FlatList, RefreshControl, Pressable } from "react-native"; import { Text } from "@/components/ui/text"; import { ScreenWrapper } from "@/components/ScreenWrapper"; import { StandardHeader } from "@/components/StandardHeader"; import { api } from "@/lib/api"; import { EmptyState } from "@/components/EmptyState"; import { Bell, Clock } from "@/lib/icons"; type NotificationItem = { id: string; title?: string; body?: string; icon?: string; url?: string; sentAt?: string; createdAt?: string; isSent?: boolean; }; function formatRelativeTime(dateString: string): string { const now = new Date(); const date = new Date(dateString); const diffMs = now.getTime() - date.getTime(); const diffMins = Math.floor(diffMs / 60000); const diffHours = Math.floor(diffMins / 60); const diffDays = Math.floor(diffHours / 24); if (diffMins < 1) return "Just now"; if (diffMins < 60) return `${diffMins} min${diffMins > 1 ? "s" : ""} ago`; if (diffHours < 24) return `${diffHours} hr${diffHours > 1 ? "s" : ""} ago`; if (diffDays === 1) return "Yesterday"; if (diffDays < 7) return `${diffDays} days ago`; return date.toLocaleDateString("en-US", { month: "short", day: "numeric" }); } function getDateGroup(dateString: string): string { const now = new Date(); const date = new Date(dateString); const today = new Date(now.getFullYear(), now.getMonth(), now.getDate()); const yesterday = new Date(today); yesterday.setDate(yesterday.getDate() - 1); const itemDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()); if (itemDate.getTime() === today.getTime()) return "Today"; if (itemDate.getTime() === yesterday.getTime()) return "Yesterday"; if (now.getTime() - itemDate.getTime() < 7 * 86400000) return "This Week"; return date.toLocaleDateString("en-US", { month: "long", year: "numeric" }); } type SectionItem = { type: "header" | "item"; key: string; item?: NotificationItem; isLast?: boolean; }; export default function NotificationsScreen() { const [items, setItems] = useState([]); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); const [loadingMore, setLoadingMore] = useState(false); const fetchNotifications = useCallback( async (pageNum: number, mode: "initial" | "refresh" | "more") => { try { if (mode === "initial") setLoading(true); if (mode === "refresh") setRefreshing(true); if (mode === "more") setLoadingMore(true); const res = await (api as any).notifications.getAll({ query: { page: pageNum, limit: 20 }, }); const next = (res?.data ?? []) as NotificationItem[]; if (mode === "more") { setItems((prev) => [...prev, ...next]); } else { setItems(next); } setHasMore(Boolean(res?.meta?.hasNextPage)); setPage(pageNum); } catch (e) { setHasMore(false); } finally { setLoading(false); setRefreshing(false); setLoadingMore(false); } }, [], ); useEffect(() => { fetchNotifications(1, "initial"); }, [fetchNotifications]); const onRefresh = () => fetchNotifications(1, "refresh"); const onEndReached = () => { if (!loading && !loadingMore && hasMore) fetchNotifications(page + 1, "more"); }; const grouped = useMemo(() => { const groups: Record = {}; for (const item of items) { const dateStr = item.sentAt || item.createdAt; const group = dateStr ? getDateGroup(dateStr) : "Other"; if (!groups[group]) groups[group] = []; groups[group].push(item); } return Object.entries(groups); }, [items]); const sections = useMemo(() => { const data: SectionItem[] = []; for (const [title, groupItems] of grouped) { data.push({ type: "header", key: `header-${title}` }); groupItems.forEach((item, idx) => { data.push({ type: "item", key: item.id, item, isLast: idx === groupItems.length - 1, }); }); } return data; }, [grouped]); const renderSectionHeader = (title: string) => ( {title} ); const renderItem = ({ item, isLast }: { item: NotificationItem; isLast: boolean }) => { const time = item.sentAt || item.createdAt ? formatRelativeTime(item.sentAt || item.createdAt!) : ""; const iconName = item.icon || "bell"; return ( {/* Icon */} {/* Content */} {item.title || "Notification"} {item.body ? ( {item.body} ) : null} {/* Time + Unread dot */} {time ? ( {time} ) : null} ); }; return ( {loading ? ( ) : ( i.key} renderItem={({ item }) => { if (item.type === "header") { return renderSectionHeader(item.key.replace("header-", "")); } return renderItem({ item: item.item!, isLast: item.isLast! }); }} contentContainerStyle={{ paddingBottom: 32 }} onEndReached={onEndReached} onEndReachedThreshold={0.4} refreshControl={ } ListEmptyComponent={ } ListFooterComponent={ loadingMore ? ( ) : null } /> )} ); }