diff --git a/mobile/src/navigation/BottomTabNavigator.tsx b/mobile/src/navigation/BottomTabNavigator.tsx index ebbfd822..34184dc5 100644 --- a/mobile/src/navigation/BottomTabNavigator.tsx +++ b/mobile/src/navigation/BottomTabNavigator.tsx @@ -7,6 +7,7 @@ import { KurdistanColors } from '../theme/colors'; import DashboardScreen from '../screens/DashboardScreen'; import WalletScreen from '../screens/WalletScreen'; import SwapScreen from '../screens/SwapScreen'; +import P2PScreen from '../screens/P2PScreen'; import BeCitizenScreen from '../screens/BeCitizenScreen'; import ReferralScreen from '../screens/ReferralScreen'; import ProfileScreen from '../screens/ProfileScreen'; @@ -15,6 +16,7 @@ export type BottomTabParamList = { Home: undefined; Wallet: undefined; Swap: undefined; + P2P: undefined; BeCitizen: undefined; Referral: undefined; Profile: undefined; @@ -84,6 +86,18 @@ const BottomTabNavigator: React.FC = () => { }} /> + ( + + {focused ? '💱' : '💰'} + + ), + }} + /> + { + const { t } = useTranslation(); + const { selectedAccount } = usePolkadot(); + + const [activeTab, setActiveTab] = useState('buy'); + const [offers, setOffers] = useState([]); + const [loading, setLoading] = useState(true); + const [refreshing, setRefreshing] = useState(false); + const [showCreateOffer, setShowCreateOffer] = useState(false); + + useEffect(() => { + fetchOffers(); + }, [activeTab, selectedAccount]); + + const fetchOffers = async () => { + setLoading(true); + try { + let offersData: P2PFiatOffer[] = []; + + if (activeTab === 'buy') { + // Buy = looking for sell offers + offersData = await getActiveOffers(); + } else if (activeTab === 'my-offers' && selectedAccount) { + // TODO: Implement getUserOffers from shared library + offersData = []; + } + + // Enrich with reputation (simplified for now) + const enrichedOffers: OfferWithReputation[] = offersData.map((offer) => ({ + ...offer, + })); + + setOffers(enrichedOffers); + } catch (error) { + console.error('Fetch offers error:', error); + } finally { + setLoading(false); + setRefreshing(false); + } + }; + + const handleRefresh = () => { + setRefreshing(true); + fetchOffers(); + }; + + const getTrustLevelColor = ( + level: 'new' | 'basic' | 'intermediate' | 'advanced' | 'verified' + ) => { + const colors = { + new: '#999', + basic: KurdistanColors.zer, + intermediate: '#2196F3', + advanced: KurdistanColors.kesk, + verified: '#9C27B0', + }; + return colors[level]; + }; + + const getTrustLevelLabel = ( + level: 'new' | 'basic' | 'intermediate' | 'advanced' | 'verified' + ) => { + const labels = { + new: 'New', + basic: 'Basic', + intermediate: 'Intermediate', + advanced: 'Advanced', + verified: 'Verified', + }; + return labels[level]; + }; + + const renderOfferCard = ({ item }: { item: OfferWithReputation }) => ( + + {/* Seller Info */} + + + + + {item.seller_wallet.slice(0, 2).toUpperCase()} + + + + + {item.seller_wallet.slice(0, 6)}...{item.seller_wallet.slice(-4)} + + {item.seller_reputation && ( + + + + {item.seller_reputation.completed_trades} trades + + + )} + + + + {item.seller_reputation?.verified_merchant && ( + + ✓ + + )} + + + {/* Offer Details */} + + + Amount + + {item.amount_crypto} {item.token} + + + + + Price + + {item.price_per_unit.toFixed(2)} {item.fiat_currency}/{item.token} + + + + + Total + + {item.fiat_amount.toFixed(2)} {item.fiat_currency} + + + + {item.payment_method_name && ( + + Payment + + + )} + + + Limits + + {item.min_order_amount || 0} - {item.max_order_amount || item.fiat_amount}{' '} + {item.fiat_currency} + + + + + Time Limit + {item.time_limit_minutes} min + + + + {/* Action Button */} + + + ); + + const renderEmptyState = () => ( + + 📭 + No Offers Available + + {activeTab === 'my-offers' + ? 'You haven\'t created any offers yet' + : 'No active offers at the moment'} + + {activeTab === 'my-offers' && ( + + )} + + ); + + return ( + + {/* Header */} + + + P2P Trading + Buy and sell crypto with local currency + + setShowCreateOffer(true)} + > + + Post Ad + + + + {/* Tabs */} + + setActiveTab('buy')} + > + + Buy + + + + setActiveTab('sell')} + > + + Sell + + + + setActiveTab('my-offers')} + > + + My Offers + + + + + {/* Offer List */} + {loading && !refreshing ? ( + + + Loading offers... + + ) : ( + item.id} + contentContainerStyle={styles.listContent} + ListEmptyComponent={renderEmptyState} + refreshControl={ + + } + /> + )} + + {/* TODO: Create Offer Modal */} + {/* TODO: Trade Modal */} + + ); +}; + +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', + }, + createButton: { + backgroundColor: KurdistanColors.kesk, + paddingHorizontal: 16, + paddingVertical: 10, + borderRadius: 8, + }, + createButtonText: { + color: '#FFFFFF', + fontSize: 14, + fontWeight: '600', + }, + tabs: { + flexDirection: 'row', + paddingHorizontal: 16, + borderBottomWidth: 1, + borderBottomColor: '#E0E0E0', + marginBottom: 16, + }, + tab: { + flex: 1, + paddingVertical: 12, + alignItems: 'center', + borderBottomWidth: 2, + borderBottomColor: 'transparent', + }, + activeTab: { + borderBottomColor: KurdistanColors.kesk, + }, + tabText: { + fontSize: 16, + fontWeight: '600', + color: '#666', + }, + activeTabText: { + color: KurdistanColors.kesk, + }, + listContent: { + padding: 16, + paddingTop: 0, + }, + offerCard: { + padding: 16, + marginBottom: 16, + }, + sellerRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'flex-start', + marginBottom: 16, + paddingBottom: 16, + borderBottomWidth: 1, + borderBottomColor: '#F0F0F0', + }, + sellerInfo: { + flexDirection: 'row', + alignItems: 'center', + }, + sellerAvatar: { + width: 48, + height: 48, + borderRadius: 24, + backgroundColor: KurdistanColors.kesk, + justifyContent: 'center', + alignItems: 'center', + }, + sellerAvatarText: { + fontSize: 18, + fontWeight: '700', + color: '#FFFFFF', + }, + sellerDetails: { + marginLeft: 12, + }, + sellerName: { + fontSize: 16, + fontWeight: '600', + color: '#000', + marginBottom: 4, + }, + reputationRow: { + flexDirection: 'row', + alignItems: 'center', + gap: 8, + }, + tradesCount: { + fontSize: 12, + color: '#666', + }, + verifiedBadge: { + width: 24, + height: 24, + borderRadius: 12, + backgroundColor: KurdistanColors.kesk, + justifyContent: 'center', + alignItems: 'center', + }, + verifiedIcon: { + fontSize: 14, + color: '#FFFFFF', + fontWeight: '700', + }, + offerDetails: { + marginBottom: 16, + }, + detailRow: { + flexDirection: 'row', + justifyContent: 'space-between', + alignItems: 'center', + paddingVertical: 8, + }, + detailLabel: { + fontSize: 14, + color: '#666', + }, + detailValue: { + fontSize: 14, + fontWeight: '600', + color: '#000', + }, + totalValue: { + fontSize: 16, + color: KurdistanColors.kesk, + }, + tradeButton: { + marginTop: 8, + }, + 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', + marginBottom: 24, + }, +}); + +export default P2PScreen;