import { useState, useCallback } from 'react';
import {
View,
Text,
FlatList,
TouchableOpacity,
TextInput,
ActivityIndicator,
RefreshControl,
StyleSheet,
} from 'react-native';
import { useRouter } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
import { useQuery } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { apiFetch } from '../../lib/api';
import { AppHeader } from '../../components/AppHeader';
import { UserAvatar } from '../../components/UserAvatar';
import { useColors } from '../../lib/theme';
type DmConversation = {
partnerId: string;
partnerName: string;
partnerAvatar: string | null;
lastMessage: string;
lastMessageAt: string;
unreadCount: number;
isOwn: boolean;
};
function formatTime(ts: string, justNowLabel: string): string {
const diff = Date.now() - new Date(ts).getTime();
if (diff < 60_000) return justNowLabel;
if (diff < 3_600_000) return `${Math.floor(diff / 60_000)}m`;
if (diff < 86_400_000) return `${Math.floor(diff / 3_600_000)}h`;
return new Date(ts).toLocaleDateString('de-DE', { day: '2-digit', month: '2-digit' });
}
function DmItem({ conv, onPress }: { conv: DmConversation; onPress: () => void }) {
const { t } = useTranslation();
const colors = useColors();
const styles = makeStyles(colors);
const hasUnread = conv.unreadCount > 0;
return (
{conv.partnerName}
{formatTime(conv.lastMessageAt, t('chat.just_now'))}
{conv.isOwn ? `${t('chat.you')} ` : ''}
{conv.lastMessage}
{hasUnread && (
{conv.unreadCount > 99 ? '99+' : conv.unreadCount}
)}
);
}
export default function ChatScreen() {
const { t } = useTranslation();
const router = useRouter();
const colors = useColors();
const styles = makeStyles(colors);
const [search, setSearch] = useState('');
const [userRefreshing, setUserRefreshing] = useState(false);
const {
data: convs = [],
isLoading: loadingDms,
refetch: refetchDms,
} = useQuery({
queryKey: ['dm-conversations'],
queryFn: () => apiFetch('/api/chat/dm-conversations'),
staleTime: 30_000,
});
const handleRefresh = useCallback(async () => {
setUserRefreshing(true);
try {
await refetchDms();
} finally {
setUserRefreshing(false);
}
}, [refetchDms]);
const filtered = search.trim()
? convs.filter((c) =>
c.partnerName.toLowerCase().includes(search.toLowerCase()) ||
c.lastMessage.toLowerCase().includes(search.toLowerCase()),
)
: convs;
const openDm = useCallback(
(userId: string) => {
router.push(`/dm?userId=${userId}`);
},
[router],
);
return (
{search.length > 0 && (
setSearch('')} activeOpacity={0.7} hitSlop={8}>
)}
item.partnerId}
refreshControl={
}
ListEmptyComponent={
loadingDms ? (
) : (
{t('chat.no_chats')}
)
}
renderItem={({ item }) => openDm(item.partnerId)} />}
contentContainerStyle={{ paddingBottom: 100 }}
/>
);
}
function makeStyles(colors: ReturnType) {
return StyleSheet.create({
container: { flex: 1, backgroundColor: colors.bg },
headerSection: {
paddingHorizontal: 16,
paddingTop: 12,
paddingBottom: 10,
backgroundColor: colors.bg,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.border,
},
searchRow: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.surfaceElevated,
borderRadius: 999,
paddingHorizontal: 16,
paddingVertical: 8,
},
searchIcon: { marginRight: 8 },
searchInput: {
flex: 1,
fontSize: 14,
fontFamily: 'Nunito_500Medium',
color: colors.text,
paddingVertical: 0,
},
emptyBox: {
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 60,
paddingHorizontal: 32,
},
emptyText: {
fontSize: 13,
fontFamily: 'Nunito_600SemiBold',
color: colors.textMuted,
marginTop: 12,
},
dmRow: {
flexDirection: 'row',
alignItems: 'center',
gap: 12,
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: colors.bg,
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: colors.border,
minHeight: 68,
},
dmInfo: { flex: 1, minWidth: 0 },
dmHeaderRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
},
dmName: {
fontSize: 15,
fontFamily: 'Nunito_700Bold',
color: colors.text,
flexShrink: 1,
marginRight: 6,
},
dmTime: { fontSize: 11, fontFamily: 'Nunito_500Medium' },
dmBottomRow: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginTop: 2,
},
dmLast: { fontSize: 12, flex: 1 },
unreadBadge: {
minWidth: 20,
height: 20,
paddingHorizontal: 6,
borderRadius: 10,
backgroundColor: colors.brandOrange,
alignItems: 'center',
justifyContent: 'center',
marginLeft: 8,
},
unreadBadgeText: {
fontSize: 10,
fontFamily: 'Nunito_700Bold',
color: '#fff',
},
});
}