diff --git a/mobile/src/navigation/BottomTabNavigator.tsx b/mobile/src/navigation/BottomTabNavigator.tsx index 59d25d42..77028a51 100644 --- a/mobile/src/navigation/BottomTabNavigator.tsx +++ b/mobile/src/navigation/BottomTabNavigator.tsx @@ -9,6 +9,7 @@ import WalletScreen from '../screens/WalletScreen'; import SwapScreen from '../screens/SwapScreen'; import P2PScreen from '../screens/P2PScreen'; import EducationScreen from '../screens/EducationScreen'; +import ForumScreen from '../screens/ForumScreen'; import BeCitizenScreen from '../screens/BeCitizenScreen'; import ReferralScreen from '../screens/ReferralScreen'; import ProfileScreen from '../screens/ProfileScreen'; @@ -19,6 +20,7 @@ export type BottomTabParamList = { Swap: undefined; P2P: undefined; Education: undefined; + Forum: undefined; BeCitizen: undefined; Referral: undefined; Profile: undefined; @@ -112,6 +114,18 @@ const BottomTabNavigator: React.FC = () => { }} /> + ( + + {focused ? '💬' : '📝'} + + ), + }} + /> + { + const { t } = useTranslation(); + + const [viewType, setViewType] = useState('categories'); + const [selectedCategory, setSelectedCategory] = useState(null); + const [threads, setThreads] = useState(MOCK_THREADS); + const [loading, setLoading] = useState(false); + const [refreshing, setRefreshing] = useState(false); + + const fetchThreads = async (categoryId?: string) => { + setLoading(true); + try { + // TODO: Fetch from Supabase + // const { data } = await supabase + // .from('forum_threads') + // .select('*') + // .eq('category_id', categoryId) + // .order('is_pinned', { ascending: false }) + // .order('last_activity', { ascending: false }); + + await new Promise((resolve) => setTimeout(resolve, 500)); + setThreads(MOCK_THREADS); + } catch (error) { + console.error('Failed to fetch threads:', error); + } finally { + setLoading(false); + setRefreshing(false); + } + }; + + const handleRefresh = () => { + setRefreshing(true); + fetchThreads(selectedCategory || undefined); + }; + + const handleCategoryPress = (categoryId: string, categoryName: string) => { + setSelectedCategory(categoryId); + setViewType('threads'); + fetchThreads(categoryId); + }; + + const formatTimeAgo = (dateString: string) => { + const date = new Date(dateString); + const now = new Date(); + const seconds = Math.floor((now.getTime() - date.getTime()) / 1000); + + if (seconds < 60) return 'Just now'; + if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`; + if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`; + return `${Math.floor(seconds / 86400)}d ago`; + }; + + const renderCategoryCard = ({ item }: { item: ForumCategory }) => ( + handleCategoryPress(item.id, item.name)}> + + + + {item.icon} + + + {item.name} + + {item.description} + + + + + + {item.threads_count} threads + + + + + + ); + + const renderThreadCard = ({ item }: { item: ForumThread }) => ( + + + {/* Thread Header */} + + {item.is_pinned && ( + + 📌 + + )} + + {item.title} + + {item.is_locked && ( + 🔒 + )} + + + {/* Thread Meta */} + + by {item.author} + + + + {/* Thread Stats */} + + + 💬 + {item.replies_count} + + + 👁️ + {item.views_count} + + + 🕐 + + {formatTimeAgo(item.last_activity)} + + + + + + ); + + const renderEmptyState = () => ( + + 💬 + No Threads Yet + + Be the first to start a discussion in this category + + + ); + + return ( + + {/* Header */} + + + + {viewType === 'categories' ? 'Forum' : 'Threads'} + + + {viewType === 'categories' + ? 'Join the community discussion' + : selectedCategory || 'All threads'} + + + {viewType === 'threads' && ( + setViewType('categories')} + > + ← Back + + )} + + + {/* Content */} + {loading && !refreshing ? ( + + + Loading... + + ) : viewType === 'categories' ? ( + item.id} + contentContainerStyle={styles.listContent} + refreshControl={ + + } + /> + ) : ( + item.id} + contentContainerStyle={styles.listContent} + ListEmptyComponent={renderEmptyState} + refreshControl={ + + } + /> + )} + + {/* Create Thread FAB */} + {viewType === 'threads' && ( + + ✏️ + + )} + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: AppColors.background, + }, + header: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'flex-start', + padding: 16, + paddingBottom: 12, + }, + title: { + fontSize: 28, + fontWeight: '700', + color: '#000', + marginBottom: 4, + }, + subtitle: { + fontSize: 14, + color: '#666', + }, + backButton: { + paddingHorizontal: 12, + paddingVertical: 8, + borderRadius: 8, + backgroundColor: '#F5F5F5', + }, + backButtonText: { + fontSize: 14, + fontWeight: '600', + color: KurdistanColors.kesk, + }, + listContent: { + padding: 16, + paddingTop: 8, + }, + categoryCard: { + padding: 16, + marginBottom: 12, + }, + categoryHeader: { + flexDirection: 'row', + alignItems: 'flex-start', + marginBottom: 12, + }, + categoryIcon: { + width: 48, + height: 48, + borderRadius: 12, + backgroundColor: '#F0F9F4', + justifyContent: 'center', + alignItems: 'center', + marginRight: 12, + }, + categoryIconText: { + fontSize: 24, + }, + categoryInfo: { + flex: 1, + }, + categoryName: { + fontSize: 18, + fontWeight: '700', + color: '#000', + marginBottom: 4, + }, + categoryDescription: { + fontSize: 14, + color: '#666', + lineHeight: 20, + }, + categoryFooter: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingTop: 12, + borderTopWidth: 1, + borderTopColor: '#F0F0F0', + }, + categoryStats: { + fontSize: 14, + color: '#666', + }, + categoryArrow: { + fontSize: 20, + color: KurdistanColors.kesk, + }, + threadCard: { + padding: 16, + marginBottom: 12, + }, + threadHeader: { + flexDirection: 'row', + alignItems: 'flex-start', + marginBottom: 8, + }, + pinnedBadge: { + marginRight: 8, + }, + pinnedIcon: { + fontSize: 16, + }, + threadTitle: { + flex: 1, + fontSize: 16, + fontWeight: '700', + color: '#000', + lineHeight: 22, + }, + lockedIcon: { + fontSize: 16, + marginLeft: 8, + }, + threadMeta: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + marginBottom: 12, + }, + threadAuthor: { + fontSize: 12, + color: '#666', + }, + threadStats: { + flexDirection: 'row', + gap: 16, + paddingTop: 12, + borderTopWidth: 1, + borderTopColor: '#F0F0F0', + }, + statItem: { + flexDirection: 'row', + alignItems: 'center', + gap: 4, + }, + statIcon: { + fontSize: 14, + }, + statText: { + fontSize: 12, + color: '#666', + }, + loadingContainer: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + }, + loadingText: { + marginTop: 12, + fontSize: 14, + color: '#666', + }, + emptyState: { + flex: 1, + justifyContent: 'center', + alignItems: 'center', + paddingVertical: 60, + }, + emptyIcon: { + fontSize: 64, + marginBottom: 16, + }, + emptyTitle: { + fontSize: 20, + fontWeight: '700', + color: '#000', + marginBottom: 8, + }, + emptyText: { + fontSize: 14, + color: '#666', + textAlign: 'center', + paddingHorizontal: 32, + }, + fab: { + position: 'absolute', + right: 20, + bottom: 20, + width: 56, + height: 56, + borderRadius: 28, + backgroundColor: KurdistanColors.kesk, + justifyContent: 'center', + alignItems: 'center', + shadowColor: '#000', + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.3, + shadowRadius: 8, + elevation: 8, + }, + fabIcon: { + fontSize: 24, + }, +}); + +export default ForumScreen;