import { View, Text, StyleSheet, TouchableOpacity, Image, Alert, ScrollView, ActivityIndicator, } from "react-native"; import { useUser, useClerk, useAuth } from "@clerk/clerk-expo"; import { useRouter } from "expo-router"; import { Ionicons } from "@expo/vector-icons"; import { useState, useEffect } from "react"; import { useTheme } from "../../contexts/ThemeContext"; import { MinimalCard } from "../../components/MinimalCard"; import { ListItem } from "../../components/ListItem"; import { MinimalButton } from "../../components/MinimalButton"; import { Badge } from "../../components/Badge"; import { IconContainer } from "../../components/IconContainer"; import { API_BASE_URL, API_ENDPOINTS } from "../../config/api"; import log from "../../utils/logger"; export default function ProfileScreen() { const { user } = useUser(); const { signOut } = useClerk(); const router = useRouter(); const { colors, typography, theme: activeTheme, setTheme } = useTheme(); const { getToken } = useAuth(); const [gyms, setGyms] = useState< Array<{ id: string; name: string; location?: string }> >([]); const [gymsLoading, setGymsLoading] = useState(false); const [selectedGymId, setSelectedGymId] = useState(null); const [currentGymId, setCurrentGymId] = useState(null); const [currentGymName, setCurrentGymName] = useState(null); useEffect(() => { const gid = ((user?.publicMetadata as any)?.gymId as string | undefined) ?? null; setCurrentGymId(gid ?? null); if (gid && gyms.length > 0) { const g = gyms.find((x) => x.id === gid); setCurrentGymName(g?.name ?? null); if (selectedGymId === null) setSelectedGymId(gid); } }, [user?.publicMetadata, gyms]); useEffect(() => { loadGyms(); }, []); const loadGyms = async () => { try { setGymsLoading(true); const token = await getToken(); const url = `${API_BASE_URL}${API_ENDPOINTS.GYMS}`; log.debug("Loading gyms", { url }); const res = await fetch(url, { headers: token ? { Authorization: `Bearer ${token}` } : undefined, }); const contentType = res.headers.get("content-type") || ""; if (!res.ok) { const text = await res.text().catch(() => ""); log.error( "Failed to fetch gyms - non-OK response", new Error(text.slice(0, 200)), { status: res.status }, ); setGyms([]); return; } if (!contentType.includes("application/json")) { const text = await res.text().catch(() => ""); log.error( "Failed to fetch gyms - expected JSON", new Error(text.slice(0, 200)), { contentType }, ); setGyms([]); return; } let data: any = null; try { data = await res.json(); } catch (e) { const text = await res.text().catch(() => ""); log.error("Failed to parse gyms JSON", e, { bodyPreview: text?.slice(0, 200), }); setGyms([]); return; } const list = Array.isArray(data) ? data : []; setGyms(list); const gid = currentGymId ?? ((user?.publicMetadata as any)?.gymId as string | undefined) ?? null; if (gid) { const g = list.find((x: any) => x.id === gid); setCurrentGymId(gid); setCurrentGymName(g?.name ?? null); if (selectedGymId === null) setSelectedGymId(gid); } } catch (err) { log.error("Failed to fetch gyms", err); setGyms([]); } finally { setGymsLoading(false); } }; const handleApplyGym = async () => { try { const token = await getToken(); const url = `${API_BASE_URL}${API_ENDPOINTS.USERS}/gym`; log.debug("Updating gym selection", { url, gymId: selectedGymId }); const res = await fetch(url, { method: "PATCH", headers: { "Content-Type": "application/json", ...(token ? { Authorization: `Bearer ${token}` } : {}), }, body: JSON.stringify({ gymId: selectedGymId }), }); const contentType = res.headers.get("content-type") || ""; if (!res.ok) { const text = await res.text().catch(() => ""); log.error( "Failed to update gym selection - non-OK response", new Error(text.slice(0, 200)), { status: res.status }, ); Alert.alert("Error", "Failed to update gym selection"); return; } if (contentType.includes("application/json")) { try { const data = await res.json(); log.debug("Gym selection updated", { data }); } catch (e) { const text = await res.text().catch(() => ""); log.error("Failed to parse update response JSON", e, { bodyPreview: text?.slice(0, 200), }); } } setCurrentGymId(selectedGymId); setCurrentGymName( selectedGymId ? (gyms.find((g) => g.id === selectedGymId)?.name ?? null) : null, ); try { await (user as any)?.reload?.(); } catch (e) { log.debug("Failed to reload user after gym update", { error: e }); } Alert.alert( "Success", selectedGymId ? "Gym selected successfully" : "Proceeding without gym", ); } catch (err) { log.error("Failed to update gym selection", err); Alert.alert("Error", "Failed to update gym selection"); } }; const handleSignOut = async () => { try { await signOut(); } catch (err) { log.error("Failed to sign out", err); } }; const confirmSignOut = () => { Alert.alert("Sign Out", "Are you sure you want to sign out?", [ { text: "Cancel", style: "cancel" }, { text: "Sign Out", style: "destructive", onPress: handleSignOut }, ]); }; const handleThemeChange = () => { Alert.alert("Choose Theme", "Select your preferred theme", [ { text: "Light", onPress: () => setTheme("light"), }, { text: "Dark", onPress: () => setTheme("dark"), }, { text: "System", onPress: () => setTheme("system"), }, { text: "Cancel", style: "cancel" }, ]); }; const getThemeLabel = () => { if (activeTheme === "light") return "Light"; if (activeTheme === "dark") return "Dark"; return "System"; }; return ( {/* Header Card */} {user?.imageUrl ? ( ) : ( )} {user?.fullName || "User"} {user?.primaryEmailAddress?.emailAddress} {/* Theme Settings */} Appearance } rightElement={ } onPress={handleThemeChange} /> {/* Account Settings */} Account } rightElement={ } onPress={() => router.push("/personal-details")} /> } rightElement={ } onPress={() => router.push("/fitness-profile")} /> } rightElement={ } /> {/* Gym Selection */} Gym Selection Refresh {currentGymName && ( Current Gym {currentGymName} )} {gymsLoading ? ( ) : ( <> setSelectedGymId(null)} > No Gym {gyms.map((gym) => ( setSelectedGymId(gym.id)} > {gym.name} ))} )} {/* Support */} Support } rightElement={ } /> } rightElement={ } /> {/* Sign Out */} Version 1.0.0 ); } const styles = StyleSheet.create({ container: { flex: 1, }, content: { padding: 24, paddingTop: 60, }, profileCard: { alignItems: "center", paddingVertical: 32, marginBottom: 24, }, avatarContainer: { position: "relative", }, avatar: { width: 100, height: 100, borderRadius: 50, }, placeholderAvatar: { width: 100, height: 100, borderRadius: 50, justifyContent: "center", alignItems: "center", }, section: { marginBottom: 24, }, sectionHeader: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", marginBottom: 12, }, divider: { height: 1, marginLeft: 52, }, currentGym: { marginBottom: 16, paddingBottom: 16, borderBottomWidth: 1, borderBottomColor: "rgba(0, 0, 0, 0.05)", }, loadingContainer: { paddingVertical: 20, alignItems: "center", }, gymScroll: { marginHorizontal: -16, }, gymScrollContent: { paddingHorizontal: 16, gap: 8, }, gymChip: { paddingHorizontal: 16, paddingVertical: 10, borderRadius: 20, borderWidth: 1.5, marginRight: 8, }, });