import { NativeStackScreenProps } from '@react-navigation/native-stack'; import React, { useCallback, useEffect, useState } from 'react'; import { ActivityIndicator, FlatList, RefreshControl, StyleSheet, Text, TouchableOpacity, View, } from 'react-native'; import * as api from '../api/endpoints'; import { Project } from '../api/types'; import { useSession } from '../auth/session'; import { getCursor, getProjects, saveProjectList, setActiveProjectId } from '../db/repositories'; import { isOnline } from '../net/connectivity'; import { initialPull, runSync } from '../sync/engine'; import { RootStackParamList } from '../navigation/types'; type Props = NativeStackScreenProps; function normalize(res: { data?: Project[] } | Project[]): Project[] { return Array.isArray(res) ? res : res.data ?? []; } export function ProjectsScreen({ navigation }: Props) { const { user, signOut } = useSession(); const [projects, setProjects] = useState([]); const [loading, setLoading] = useState(false); const [opening, setOpening] = useState(null); const load = useCallback(async () => { setLoading(true); try { if (await isOnline()) { const list = normalize(await api.listProjects()); await saveProjectList(list); } setProjects(await getProjects()); } catch { setProjects(await getProjects()); // fallback offline } finally { setLoading(false); } }, []); useEffect(() => { void load(); }, [load]); const openProject = useCallback( async (p: Project) => { setOpening(p.id); try { await setActiveProjectId(p.id); const online = await isOnline(); if (online) { const cursor = await getCursor(p.id); if (cursor) await runSync(p.id); else await initialPull(p.id); } navigation.navigate('ProjectDetail', { projectId: p.id, name: p.name }); } finally { setOpening(null); } }, [navigation], ); return ( Hola, {user?.name ?? ''} Salir String(p.id)} refreshControl={} ListEmptyComponent={ loading ? null : No hay proyectos. } renderItem={({ item }) => ( openProject(item)}> {item.name} {item.reference ? {item.reference} : null} {opening === item.id ? ( ) : ( )} )} /> ); } const styles = StyleSheet.create({ container: { flex: 1 }, header: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', padding: 16, }, hello: { fontSize: 16, fontWeight: '600' }, logout: { color: '#b00020', fontWeight: '600' }, row: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16, paddingVertical: 14, borderTopWidth: StyleSheet.hairlineWidth, borderColor: '#ddd', }, name: { fontSize: 16, fontWeight: '600' }, ref: { fontSize: 13, color: '#666', marginTop: 2 }, chevron: { fontSize: 24, color: '#999' }, empty: { textAlign: 'center', marginTop: 40, color: '#888' }, });