// components/profile/ProfileHeader.tsx import React, { useEffect } from 'react'; import { View, TouchableOpacity, ImageBackground, Platform, ActivityIndicator } from 'react-native'; import { Text } from '@/components/ui/text'; import { useRouter } from 'expo-router'; import { Copy, QrCode } from 'lucide-react-native'; import { useTheme } from '@react-navigation/native'; import type { CustomTheme } from '@/lib/theme'; import { Alert, Clipboard } from 'react-native'; import { nip19 } from 'nostr-tools'; import UserAvatar from '@/components/UserAvatar'; import { NDKUser } from '@nostr-dev-kit/ndk'; interface ProfileHeaderProps { user: NDKUser | null; bannerImageUrl?: string; defaultBannerUrl?: string; followersCount: number; followingCount: number; refreshStats: () => Promise; isStatsLoading: boolean; } /** * Profile header component displaying banner, avatar, user details, and stats * Pure presentational component (no hooks except for UI behavior) */ const ProfileHeader: React.FC = ({ user, bannerImageUrl, defaultBannerUrl, followersCount, followingCount, refreshStats, isStatsLoading, }) => { const router = useRouter(); const theme = useTheme() as CustomTheme; // Extract user profile data with fallbacks const profileImageUrl = user?.profile?.image || user?.profile?.picture || (user?.profile as any)?.avatar; const displayName = (user?.profile?.displayName || user?.profile?.name || 'Nostr User'); const username = (user?.profile?.nip05 || '@user'); const aboutText = user?.profile?.about || (user?.profile as any)?.description; const pubkey = user?.pubkey; // Debug banner image loading useEffect(() => { console.log('Banner image state in ProfileHeader:', { bannerImageUrl, defaultBannerUrl, pubkey: pubkey?.substring(0, 8) }); }, [bannerImageUrl, defaultBannerUrl, pubkey]); // Generate npub format for display const npubFormat = React.useMemo(() => { if (!pubkey) return ''; try { const npub = nip19.npubEncode(pubkey); return npub; } catch (error) { console.error('Error encoding npub:', error); return ''; } }, [pubkey]); // Get shortened npub display version const shortenedNpub = React.useMemo(() => { if (!npubFormat) return ''; return `${npubFormat.substring(0, 8)}...${npubFormat.substring(npubFormat.length - 5)}`; }, [npubFormat]); // Handle profile edit button press const handleEditProfilePress = () => { if (router) { router.push('/profile/settings'); } }; // Copy npub to clipboard const handleCopyButtonPress = () => { if (pubkey) { try { const npub = nip19.npubEncode(pubkey); Clipboard.setString(npub); Alert.alert('Copied', 'Public key copied to clipboard in npub format'); console.log('npub copied to clipboard:', npub); } catch (error) { console.error('Error copying npub:', error); Alert.alert('Error', 'Failed to copy public key'); } } }; // Show QR code alert (placeholder) const handleQrButtonPress = () => { Alert.alert('QR Code', 'QR Code functionality will be implemented soon', [ { text: 'OK' } ]); }; return ( {/* Banner Image */} {bannerImageUrl ? ( { console.error(`Banner image loading error: ${JSON.stringify(e.nativeEvent)}`); console.error(`Failed URL: ${bannerImageUrl}`); }} onLoad={() => { console.log(`Banner image loaded successfully: ${bannerImageUrl}`); }} > ) : ( {defaultBannerUrl ? 'Loading banner...' : 'No banner image'} )} {/* Left side - Avatar */} {/* Edit Profile button - positioned to the right */} Edit Profile {/* Profile info */} {displayName} {username} {/* Display npub below username with sharing options */} {npubFormat && ( {shortenedNpub} )} {/* Follower stats */} {/* About text */} {aboutText && ( {aboutText} )} {/* Divider */} ); }; // ProfileStats subcomponent interface ProfileStatsProps { followersCount: number; followingCount: number; refreshStats: () => Promise; isLoading: boolean; } const ProfileStats: React.FC = ({ followersCount, followingCount, refreshStats, isLoading }) => { const [isManuallyRefreshing, setIsManuallyRefreshing] = React.useState(false); // Enhanced manual refresh function with visual feedback const triggerManualRefresh = React.useCallback(async () => { if (isManuallyRefreshing) return; // Prevent multiple simultaneous refreshes try { setIsManuallyRefreshing(true); console.log(`[${Platform.OS}] Manual refresh triggered by user tap`); await refreshStats(); } catch (error) { console.error(`[${Platform.OS}] Error during manual refresh:`, error); } finally { // Short delay before removing loading indicator for better UX setTimeout(() => setIsManuallyRefreshing(false), 500); } }, [isManuallyRefreshing, refreshStats]); // Always show actual values when available, regardless of loading state // Only show dots when we have no values at all const followingDisplay = followingCount > 0 ? followingCount.toLocaleString() : (isLoading || isManuallyRefreshing ? '...' : '0'); const followersDisplay = followersCount > 0 ? followersCount.toLocaleString() : (isLoading || isManuallyRefreshing ? '...' : '0'); return ( {followingDisplay} following {isManuallyRefreshing && ( )} {followersDisplay} followers {isManuallyRefreshing && ( )} ); }; export default ProfileHeader;