import { toDMConversation } from '@/lib/link' import { useSecondaryPage } from '@/PageManager' import { useDM } from '@/providers/DMProvider' import { useFollowList } from '@/providers/FollowListProvider' import { useMuteList } from '@/providers/MuteListProvider' import storage from '@/services/local-storage.service' import { Check, Loader2, MessageSquare, MoreVertical, RefreshCw } from 'lucide-react' import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { Button } from '../ui/button' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../ui/dropdown-menu' import { ScrollArea } from '../ui/scroll-area' import ConversationItem from './ConversationItem' export default function ConversationList() { const { t } = useTranslation() const { push, pop } = useSecondaryPage() const { conversations, currentConversation, selectConversation, refreshConversations, loadMoreConversations, hasMoreConversations, isLoading } = useDM() const { followingSet } = useFollowList() const { mutePubkeySet } = useMuteList() const loadMoreRef = useRef(null) const [filterMode, setFilterMode] = useState<'all' | 'follows'>(() => storage.getDMConversationFilter() ) // Filter and sort conversations const sortedConversations = useMemo(() => { let filtered = [...conversations] if (filterMode === 'follows') { // Only show conversations from follows, and hide muted users filtered = filtered.filter( (c) => followingSet.has(c.partnerPubkey) && !mutePubkeySet.has(c.partnerPubkey) ) } return filtered.sort((a, b) => b.lastMessageAt - a.lastMessageAt) }, [conversations, filterMode, followingSet, mutePubkeySet]) const handleFilterChange = (mode: 'all' | 'follows') => { setFilterMode(mode) storage.setDMConversationFilter(mode) } // Infinite scroll: load more when sentinel is visible const handleIntersection = useCallback( (entries: IntersectionObserverEntry[]) => { const [entry] = entries if (entry.isIntersecting && hasMoreConversations && !isLoading) { loadMoreConversations() } }, [hasMoreConversations, isLoading, loadMoreConversations] ) useEffect(() => { const observer = new IntersectionObserver(handleIntersection, { root: null, rootMargin: '100px', threshold: 0 }) if (loadMoreRef.current) { observer.observe(loadMoreRef.current) } return () => observer.disconnect() }, [handleIntersection]) return (
{t('Conversations')}
handleFilterChange('follows')}> {filterMode === 'follows' && } {t('Only show follows')} handleFilterChange('all')}> {filterMode === 'all' && } {t('Show all')}
{sortedConversations.length === 0 && !isLoading ? (

{t('No conversations yet')}

{t('Start a conversation by visiting a profile')}

) : (
{sortedConversations.map((conversation, index) => ( { // If already viewing a different conversation, pop first to replace if (currentConversation && currentConversation !== conversation.partnerPubkey) { pop() } push(toDMConversation(conversation.partnerPubkey)) }} onClose={() => { selectConversation(null) pop() }} /> ))} {/* Sentinel element for infinite scroll */} {hasMoreConversations && (
)}
)}
) }