import { Image } from 'expo-image'; import { router, useFocusEffect } from 'expo-router'; import { useCallback } from 'react'; import { FlatList, Pressable, StyleSheet, Text, View } from 'react-native'; import { useSWRConfig } from 'swr'; import useSWRInfinite from 'swr/infinite'; import { notificationPath, notificationText, timeAgo, type NotificationDTO, type NotificationListDTO } from '@musicshare/shared'; import { Avatar } from '@/components/Avatar'; import { Body } from '@/components/ui'; import { api } from '@/lib/api'; import { colors, fonts, space } from '@/lib/theme'; /** Reactions, new followers and songs sent to you, newest first. Opening it marks them read. */ export default function NotificationsScreen() { const { mutate: mutateGlobal } = useSWRConfig(); const { data, size, setSize, isLoading, mutate } = useSWRInfinite( (i, prev: NotificationListDTO | null) => (i > 0 && !prev?.nextCursor ? null : ['notifications', prev?.nextCursor ?? null]), ([, cursor]: [string, string | null]) => api.notifications(cursor), ); const items = data?.flatMap((p) => p.notifications) ?? []; const more = Boolean(data?.at(-1)?.nextCursor); // Seen once you've looked; the bell clears (the list keeps showing which were new until you leave). const unread = data?.[0]?.unread ?? 0; useFocusEffect( useCallback(() => { if (!unread) return; api .markNotificationsRead() .then(() => mutateGlobal('/notifications')) .catch(() => {}); return () => { mutate(); }; }, [unread, mutate, mutateGlobal]), ); return ( n.id} contentContainerStyle={styles.list} style={{ backgroundColor: colors.bg }} onEndReached={() => more && setSize(size + 1)} ListEmptyComponent={ isLoading ? null : Nothing yet. Reactions, new followers and songs sent to you show up here. } renderItem={({ item }) => } /> ); } function Row({ n }: { n: NotificationDTO }) { const { title, body } = notificationText(n); return ( router.push(notificationPath(n) as never)} style={({ pressed }) => [styles.row, !n.read && styles.unread, pressed && { opacity: 0.7 }]} accessibilityRole="link"> {title} {body} {timeAgo(n.createdAt)} {n.track?.artworkUrl && } ); } const styles = StyleSheet.create({ list: { padding: space.lg, gap: space.sm }, row: { flexDirection: 'row', gap: space.md, alignItems: 'center', padding: space.md, borderRadius: 14 }, unread: { backgroundColor: colors.surface2 }, text: { flex: 1, minWidth: 0, gap: 2 }, title: { color: colors.text, fontFamily: fonts.display, fontSize: 15 }, body: { color: colors.text, opacity: 0.85, fontSize: 14, fontFamily: fonts.body }, time: { color: colors.muted, fontSize: 12 }, art: { width: 44, height: 44, borderRadius: 8 }, });