Files
ChatApp/apps/mobile/app/(app)/chats.tsx
T

116 lines
3.8 KiB
TypeScript

import { chat } from '@chat-app/shared';
import type { ConversationSummary } from '@chat-app/shared/chat';
import { Stack, useRouter } from 'expo-router';
import { useCallback, useEffect, useState } from 'react';
import {
ActivityIndicator,
FlatList,
Pressable,
RefreshControl,
StyleSheet,
Text,
View,
} from 'react-native';
import { ConversationRow } from '../../components/ConversationRow';
import { supabase } from '../../lib/supabase';
import { colors } from '../../theme/colors';
// Phase 1 conversation list. The last-message preview is the static
// fallback ('…') — fetching + decrypting last-messages per conversation
// is a Phase 1.5 follow-up. Tap → conversation detail.
export default function Chats() {
const router = useRouter();
const [list, setList] = useState<ConversationSummary[] | null>(null);
const [refreshing, setRefreshing] = useState(false);
const [error, setError] = useState<string | null>(null);
const load = useCallback(async () => {
setError(null);
try {
const result = await chat.listConversations(supabase);
setList(result.filter((c) => !c.archived));
} catch (err: unknown) {
setError(err instanceof Error ? err.message : 'Konversationen konnten nicht geladen werden');
}
}, []);
useEffect(() => {
void load();
}, [load]);
const onRefresh = useCallback(async () => {
setRefreshing(true);
await load();
setRefreshing(false);
}, [load]);
return (
<View style={styles.container}>
<Stack.Screen
options={{
title: 'Chats',
headerStyle: { backgroundColor: colors.bg },
headerTitleStyle: { color: colors.text },
headerRight: () => (
<Pressable onPress={() => router.push('/(app)/settings')} hitSlop={10}>
<Text style={styles.logoutLink}>Einstellungen</Text>
</Pressable>
),
}}
/>
{list === null && !error && (
<View style={styles.loading}>
<ActivityIndicator color={colors.accent} />
</View>
)}
{error && <Text style={styles.error}>{error}</Text>}
{list && list.length === 0 && !error && (
<View style={styles.empty}>
<Text style={styles.emptyText}>Noch keine Konversationen.</Text>
<Text style={styles.emptyHint}>
Lege dir auf dem Desktop einen Chat an er taucht hier auf, sobald du
herunterziehst, um zu aktualisieren.
</Text>
</View>
)}
{list && list.length > 0 && (
<FlatList
data={list}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<ConversationRow
conversation={item}
lastMessagePreview="…"
onPress={() => router.push('/(app)/conversations/' + item.id)}
/>
)}
refreshControl={
<RefreshControl
refreshing={refreshing}
onRefresh={onRefresh}
tintColor={colors.accent}
/>
}
ItemSeparatorComponent={() => <View style={styles.separator} />}
/>
)}
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, backgroundColor: colors.bg },
loading: { flex: 1, alignItems: 'center', justifyContent: 'center' },
empty: { flex: 1, alignItems: 'center', justifyContent: 'center', padding: 32, gap: 8 },
emptyText: { color: colors.text, fontSize: 16, fontWeight: '600' },
emptyHint: { color: colors.textMuted, fontSize: 13, textAlign: 'center', lineHeight: 18 },
error: { color: colors.danger, padding: 16, textAlign: 'center' },
separator: { height: 1, backgroundColor: colors.border, marginLeft: 68 },
logoutLink: { color: colors.accent, fontSize: 14, fontWeight: '600', paddingHorizontal: 8 },
});