import { useEffect, useState } from 'react'; import { ActivityIndicator, FlatList, Pressable, StyleSheet, View } from 'react-native'; import { Image } from 'expo-image'; import { useLocalSearchParams, useRouter } from 'expo-router'; import { useNavigation } from '@react-navigation/native'; import { ThemedText } from '@/components/themed-text'; import { ThemedView } from '@/components/themed-view'; import { IconSymbol } from '@/components/ui/icon-symbol'; import { Colors, Fonts } from '@/constants/theme'; import { useTranslation } from '@/localization/i18n'; import { dbPromise, initCoreTables } from '@/services/db'; import { useColorScheme } from '@/hooks/use-color-scheme'; type EntryRow = { id: number; name: string; status: string | null; notes: string | null; completed_at: string | null; }; export default function TaskHistoryScreen() { const { t } = useTranslation(); const router = useRouter(); const navigation = useNavigation(); const params = useLocalSearchParams<{ from?: string | string[] }>(); const theme = useColorScheme() ?? 'light'; const palette = Colors[theme]; const fromParam = Array.isArray(params.from) ? params.from[0] : params.from; const pageSize = 12; const [entries, setEntries] = useState([]); const [status, setStatus] = useState(t('tasks.loading')); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); const [loadingMore, setLoadingMore] = useState(false); useEffect(() => { navigation.setOptions({ headerLeft: () => ( { if (fromParam === 'logbook') { router.replace('/logbook'); return; } if (fromParam === 'home') { router.replace('/'); return; } router.back(); }} hitSlop={10} style={{ paddingHorizontal: 8 }}> ), }); }, [fromParam, navigation, palette.text, router]); useEffect(() => { let isActive = true; async function loadEntries() { await fetchEntriesPage(1, true, isActive); } loadEntries(); return () => { isActive = false; }; }, [t]); async function fetchEntriesPage(pageToLoad: number, replace: boolean, isActive = true) { try { await initCoreTables(); const db = await dbPromise; const rows = await db.getAllAsync( `SELECT e.id, t.name, e.status, e.notes, e.completed_at FROM daily_task_entries e JOIN daily_tasks t ON t.id = e.task_id ORDER BY e.completed_at DESC LIMIT ? OFFSET ?;`, pageSize, (pageToLoad - 1) * pageSize ); if (!isActive) return; setEntries((prev) => (replace ? rows : [...prev, ...rows])); setHasMore(rows.length === pageSize); setPage(pageToLoad); if (replace) { setStatus(rows.length === 0 ? t('tasks.historyEmpty') : ''); } } catch (error) { if (isActive) setStatus(`Error: ${String(error)}`); } finally { if (isActive) setLoadingMore(false); } } async function handleLoadMore() { if (loadingMore || !hasMore) return; setLoadingMore(true); const nextPage = page + 1; await fetchEntriesPage(nextPage, false); } return ( String(item.id)} onEndReached={handleLoadMore} onEndReachedThreshold={0.4} renderItem={({ item }) => ( {item.name} {item.completed_at ? formatDate(item.completed_at) : '-'} {item.notes ? {item.notes} : null} )} ItemSeparatorComponent={() => } ListHeaderComponent={ {t('tasks.historyTitle')} {status ? ( {status} ) : null} } ListFooterComponent={ {loadingMore ? : null} } /> ); } function formatDate(value: string) { try { return new Date(value).toLocaleString(); } catch { return value; } } const styles = StyleSheet.create({ hero: { backgroundColor: '#E8E6DA', aspectRatio: 16 / 9, width: '100%', }, heroImage: { width: '100%', height: '100%', }, titleContainer: { gap: 8, paddingHorizontal: 16, paddingVertical: 12, }, section: { gap: 8, marginBottom: 16, paddingHorizontal: 16, }, card: { borderRadius: 12, borderWidth: 1, borderColor: '#C6C6C6', padding: 12, marginHorizontal: 16, gap: 6, backgroundColor: '#FFFFFF', }, meta: { opacity: 0.7, }, separator: { height: 12, }, footer: { height: 24, }, });