React Native mobile application for SAPIENS news platform. Consolidated all previous history into single commit. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
180 lines
5.2 KiB
TypeScript
180 lines
5.2 KiB
TypeScript
import { useState } from 'react';
|
|
import { View, Text, FlatList, ActivityIndicator, StyleSheet, SafeAreaView, TouchableOpacity, Image } from 'react-native';
|
|
import { useRouter } from 'expo-router';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { useOutlets } from '@/hooks/useApi';
|
|
import OutletCard from '@/components/OutletCard';
|
|
import BottomTabBar from '@/components/BottomTabBar';
|
|
import SearchModal from '@/components/SearchModal';
|
|
import LanguageModal from '@/components/LanguageModal';
|
|
import { useLanguage } from '@/contexts/LanguageContext';
|
|
|
|
export default function HomeScreen() {
|
|
const [activeFilter, setActiveFilter] = useState<'people' | 'topics' | 'companies'>('people');
|
|
const [showSearch, setShowSearch] = useState(false);
|
|
const [showLanguage, setShowLanguage] = useState(false);
|
|
const { language, setLanguage } = useLanguage();
|
|
const router = useRouter();
|
|
|
|
const { data: outlets, isLoading, error } = useOutlets(activeFilter, language);
|
|
|
|
if (error) {
|
|
return (
|
|
<SafeAreaView style={styles.container}>
|
|
<View style={styles.errorContainer}>
|
|
<Text style={styles.errorText}>Error: {error.message}</Text>
|
|
<Text style={styles.text}>Check if the server is running on localhost:8050</Text>
|
|
</View>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<SafeAreaView style={styles.container}>
|
|
{/* Header */}
|
|
<View style={styles.header}>
|
|
<View style={styles.headerLeft}>
|
|
<Image
|
|
source={require('@/assets/images/sapiens-logo.png')}
|
|
style={styles.logo}
|
|
resizeMode="contain"
|
|
/>
|
|
</View>
|
|
<View style={styles.headerRight}>
|
|
<TouchableOpacity style={styles.iconButton} onPress={() => setShowSearch(true)}>
|
|
<Ionicons name="search-outline" size={18} color="#333" />
|
|
</TouchableOpacity>
|
|
<TouchableOpacity style={styles.iconButton} onPress={() => setShowLanguage(true)}>
|
|
<Ionicons name="settings-outline" size={18} color="#333" />
|
|
</TouchableOpacity>
|
|
<TouchableOpacity style={styles.iconButton}>
|
|
<Ionicons name="person-circle-outline" size={20} color="#333" />
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
|
|
{/* Content Area */}
|
|
<View style={styles.contentContainer}>
|
|
{showSearch ? (
|
|
<SearchModal
|
|
visible={showSearch}
|
|
onClose={() => setShowSearch(false)}
|
|
/>
|
|
) : (
|
|
<>
|
|
{/* Outlets List */}
|
|
{isLoading ? (
|
|
<View style={styles.loadingContainer}>
|
|
<ActivityIndicator size="large" color="#007AFF" />
|
|
<Text style={styles.text}>Loading outlets...</Text>
|
|
</View>
|
|
) : (
|
|
<FlatList
|
|
data={outlets || []}
|
|
keyExtractor={(item: any, index) => `${activeFilter}-${item.id}-${index}`}
|
|
renderItem={({ item }: any) => (
|
|
<OutletCard
|
|
id={item.id}
|
|
name={item.name}
|
|
description={item.description || item.focusSubject || item.category}
|
|
category={item.category}
|
|
focusSubject={item.focusSubject}
|
|
avatar={item.avatar}
|
|
articleCount={item.articleCount || 0}
|
|
onPress={(id) => router.push(`/outlet/${id}`)}
|
|
/>
|
|
)}
|
|
contentContainerStyle={styles.list}
|
|
showsVerticalScrollIndicator={false}
|
|
ListEmptyComponent={
|
|
<Text style={styles.emptyText}>No outlets found</Text>
|
|
}
|
|
/>
|
|
)}
|
|
</>
|
|
)}
|
|
</View>
|
|
|
|
{/* Bottom Tab Bar */}
|
|
<BottomTabBar
|
|
activeTab={activeFilter}
|
|
onTabChange={setActiveFilter}
|
|
/>
|
|
|
|
{/* Language Modal */}
|
|
<LanguageModal
|
|
visible={showLanguage}
|
|
onClose={() => setShowLanguage(false)}
|
|
currentLanguage={language}
|
|
onSelectLanguage={setLanguage}
|
|
/>
|
|
</SafeAreaView>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
flex: 1,
|
|
backgroundColor: '#f5f5f5',
|
|
},
|
|
header: {
|
|
backgroundColor: '#fff',
|
|
paddingHorizontal: 16,
|
|
paddingVertical: 12,
|
|
borderBottomWidth: 1,
|
|
borderBottomColor: '#e0e0e0',
|
|
flexDirection: 'row',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
},
|
|
contentContainer: {
|
|
flex: 1,
|
|
overflow: 'hidden',
|
|
},
|
|
headerLeft: {
|
|
alignItems: 'flex-start',
|
|
},
|
|
logo: {
|
|
height: 16,
|
|
},
|
|
headerRight: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
gap: 8,
|
|
},
|
|
iconButton: {
|
|
padding: 8,
|
|
},
|
|
list: {
|
|
padding: 16,
|
|
paddingBottom: 16,
|
|
},
|
|
loadingContainer: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
},
|
|
errorContainer: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
alignItems: 'center',
|
|
padding: 20,
|
|
},
|
|
text: {
|
|
fontSize: 14,
|
|
color: '#666',
|
|
marginTop: 10,
|
|
},
|
|
errorText: {
|
|
fontSize: 16,
|
|
color: 'red',
|
|
fontWeight: 'bold',
|
|
},
|
|
emptyText: {
|
|
fontSize: 14,
|
|
color: '#666',
|
|
textAlign: 'center',
|
|
marginTop: 20,
|
|
},
|
|
});
|