redesign take 1
This commit is contained in:
1 parent
1143f8ca02
commit
e3a3c3fccf
19 files changed
+2783
-1493
No files matched your search
@@ -11,11 +11,13 @@ import {
|
||||
import { useUser, useClerk, useAuth } from "@clerk/clerk-expo";
|
||||
import { useRouter } from "expo-router";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import { theme } from "../../styles/theme";
|
||||
import { AnimatedButton } from "../../components/AnimatedButton";
|
||||
import { GradientBackground } from "../../components/GradientBackground";
|
||||
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";
|
||||
|
||||
@@ -23,24 +25,9 @@ export default function ProfileScreen() {
|
||||
const { user } = useUser();
|
||||
const { signOut } = useClerk();
|
||||
const router = useRouter();
|
||||
|
||||
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 },
|
||||
]);
|
||||
};
|
||||
|
||||
// Gym selection state and handlers
|
||||
const { colors, typography, theme: activeTheme, setTheme } = useTheme();
|
||||
const { getToken } = useAuth();
|
||||
|
||||
const [gyms, setGyms] = useState<
|
||||
Array<{ id: string; name: string; location?: string }>
|
||||
>([]);
|
||||
@@ -60,7 +47,6 @@ export default function ProfileScreen() {
|
||||
}
|
||||
}, [user?.publicMetadata, gyms]);
|
||||
|
||||
// Auto-load gyms on mount
|
||||
useEffect(() => {
|
||||
loadGyms();
|
||||
}, []);
|
||||
@@ -80,9 +66,7 @@ export default function ProfileScreen() {
|
||||
log.error(
|
||||
"Failed to fetch gyms - non-OK response",
|
||||
new Error(text.slice(0, 200)),
|
||||
{
|
||||
status: res.status,
|
||||
},
|
||||
{ status: res.status },
|
||||
);
|
||||
setGyms([]);
|
||||
return;
|
||||
@@ -92,9 +76,7 @@ export default function ProfileScreen() {
|
||||
log.error(
|
||||
"Failed to fetch gyms - expected JSON",
|
||||
new Error(text.slice(0, 200)),
|
||||
{
|
||||
contentType,
|
||||
},
|
||||
{ contentType },
|
||||
);
|
||||
setGyms([]);
|
||||
return;
|
||||
@@ -149,9 +131,7 @@ export default function ProfileScreen() {
|
||||
log.error(
|
||||
"Failed to update gym selection - non-OK response",
|
||||
new Error(text.slice(0, 200)),
|
||||
{
|
||||
status: res.status,
|
||||
},
|
||||
{ status: res.status },
|
||||
);
|
||||
Alert.alert("Error", "Failed to update gym selection");
|
||||
return;
|
||||
@@ -167,14 +147,12 @@ export default function ProfileScreen() {
|
||||
});
|
||||
}
|
||||
}
|
||||
// Update current gym state for immediate UI reflection
|
||||
setCurrentGymId(selectedGymId);
|
||||
setCurrentGymName(
|
||||
selectedGymId
|
||||
? (gyms.find((g) => g.id === selectedGymId)?.name ?? null)
|
||||
: null,
|
||||
);
|
||||
// Attempt to reload Clerk user metadata so current gym reflects server state
|
||||
try {
|
||||
await (user as any)?.reload?.();
|
||||
} catch (e) {
|
||||
@@ -190,372 +168,476 @@ export default function ProfileScreen() {
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<GradientBackground variant="primary" style={styles.header}>
|
||||
<View style={styles.profileCard}>
|
||||
<View style={styles.avatarContainer}>
|
||||
{user?.imageUrl ? (
|
||||
<Image source={{ uri: user.imageUrl }} style={styles.avatar} />
|
||||
) : (
|
||||
<View style={styles.placeholderAvatar}>
|
||||
<Ionicons name="person" size={40} color="#fff" />
|
||||
</View>
|
||||
)}
|
||||
<View style={styles.editBadge}>
|
||||
<Ionicons name="pencil" size={12} color={theme.colors.primary} />
|
||||
</View>
|
||||
</View>
|
||||
<Text style={styles.name}>{user?.fullName || "User"}</Text>
|
||||
<Text style={styles.email}>
|
||||
{user?.primaryEmailAddress?.emailAddress}
|
||||
</Text>
|
||||
<View style={styles.memberBadge}>
|
||||
<Text style={styles.memberText}>Premium Member</Text>
|
||||
</View>
|
||||
</View>
|
||||
</GradientBackground>
|
||||
const handleSignOut = async () => {
|
||||
try {
|
||||
await signOut();
|
||||
} catch (err) {
|
||||
log.error("Failed to sign out", err);
|
||||
}
|
||||
};
|
||||
|
||||
<View style={styles.content}>
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>Account</Text>
|
||||
<View style={[styles.infoCard, theme.shadows.subtle]}>
|
||||
<TouchableOpacity
|
||||
style={styles.infoRow}
|
||||
onPress={() => router.push("/personal-details")}
|
||||
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 (
|
||||
<ScrollView
|
||||
style={[styles.container, { backgroundColor: colors.background }]}
|
||||
contentContainerStyle={styles.content}
|
||||
>
|
||||
{/* Header Card */}
|
||||
<MinimalCard variant="elevated" style={styles.profileCard}>
|
||||
<View style={styles.avatarContainer}>
|
||||
{user?.imageUrl ? (
|
||||
<Image source={{ uri: user.imageUrl }} style={styles.avatar} />
|
||||
) : (
|
||||
<View
|
||||
style={[
|
||||
styles.placeholderAvatar,
|
||||
{ backgroundColor: colors.primary },
|
||||
]}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={["rgba(59, 130, 246, 0.1)", "rgba(59, 130, 246, 0.05)"]}
|
||||
style={styles.iconContainer}
|
||||
<Ionicons name="person" size={40} color={colors.white} />
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<Text
|
||||
style={[typography.h2, { color: colors.textPrimary, marginTop: 16 }]}
|
||||
>
|
||||
{user?.fullName || "User"}
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
typography.body,
|
||||
{ color: colors.textSecondary, marginTop: 4 },
|
||||
]}
|
||||
>
|
||||
{user?.primaryEmailAddress?.emailAddress}
|
||||
</Text>
|
||||
<Badge
|
||||
label="Premium Member"
|
||||
variant="primary"
|
||||
style={{ marginTop: 12 }}
|
||||
/>
|
||||
</MinimalCard>
|
||||
|
||||
{/* Theme Settings */}
|
||||
<View style={styles.section}>
|
||||
<Text
|
||||
style={[
|
||||
typography.h3,
|
||||
{ color: colors.textPrimary, marginBottom: 12 },
|
||||
]}
|
||||
>
|
||||
Appearance
|
||||
</Text>
|
||||
<MinimalCard variant="default">
|
||||
<ListItem
|
||||
title="Theme"
|
||||
subtitle={`Current: ${getThemeLabel()}`}
|
||||
leftIcon={
|
||||
<IconContainer
|
||||
variant="colored"
|
||||
backgroundColor={`${colors.accent}20`}
|
||||
>
|
||||
<Ionicons
|
||||
name="color-palette"
|
||||
size={20}
|
||||
color={colors.accent}
|
||||
/>
|
||||
</IconContainer>
|
||||
}
|
||||
rightElement={
|
||||
<Ionicons
|
||||
name="chevron-forward"
|
||||
size={20}
|
||||
color={colors.textTertiary}
|
||||
/>
|
||||
}
|
||||
onPress={handleThemeChange}
|
||||
/>
|
||||
</MinimalCard>
|
||||
</View>
|
||||
|
||||
{/* Account Settings */}
|
||||
<View style={styles.section}>
|
||||
<Text
|
||||
style={[
|
||||
typography.h3,
|
||||
{ color: colors.textPrimary, marginBottom: 12 },
|
||||
]}
|
||||
>
|
||||
Account
|
||||
</Text>
|
||||
<MinimalCard variant="default">
|
||||
<ListItem
|
||||
title="Personal Details"
|
||||
leftIcon={
|
||||
<IconContainer
|
||||
variant="colored"
|
||||
backgroundColor={`${colors.primary}20`}
|
||||
>
|
||||
<Ionicons
|
||||
name="person-outline"
|
||||
size={20}
|
||||
color={theme.colors.primary}
|
||||
color={colors.primary}
|
||||
/>
|
||||
</LinearGradient>
|
||||
<Text style={styles.infoLabel}>Personal Details</Text>
|
||||
</IconContainer>
|
||||
}
|
||||
rightElement={
|
||||
<Ionicons
|
||||
name="chevron-forward"
|
||||
size={20}
|
||||
color={theme.colors.gray400}
|
||||
color={colors.textTertiary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.divider} />
|
||||
<TouchableOpacity
|
||||
style={styles.infoRow}
|
||||
onPress={() => router.push("/fitness-profile")}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={["rgba(16, 185, 129, 0.1)", "rgba(16, 185, 129, 0.05)"]}
|
||||
style={styles.iconContainer}
|
||||
}
|
||||
onPress={() => router.push("/personal-details")}
|
||||
/>
|
||||
<View style={[styles.divider, { backgroundColor: colors.border }]} />
|
||||
<ListItem
|
||||
title="Fitness Profile"
|
||||
leftIcon={
|
||||
<IconContainer
|
||||
variant="colored"
|
||||
backgroundColor={`${colors.success}20`}
|
||||
>
|
||||
<Ionicons
|
||||
name="fitness-outline"
|
||||
size={20}
|
||||
color={theme.colors.success}
|
||||
color={colors.success}
|
||||
/>
|
||||
</LinearGradient>
|
||||
<Text style={styles.infoLabel}>Fitness Profile</Text>
|
||||
</IconContainer>
|
||||
}
|
||||
rightElement={
|
||||
<Ionicons
|
||||
name="chevron-forward"
|
||||
size={20}
|
||||
color={theme.colors.gray400}
|
||||
color={colors.textTertiary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.divider} />
|
||||
<TouchableOpacity style={styles.infoRow}>
|
||||
<LinearGradient
|
||||
colors={["rgba(245, 158, 11, 0.1)", "rgba(245, 158, 11, 0.05)"]}
|
||||
style={styles.iconContainer}
|
||||
}
|
||||
onPress={() => router.push("/fitness-profile")}
|
||||
/>
|
||||
<View style={[styles.divider, { backgroundColor: colors.border }]} />
|
||||
<ListItem
|
||||
title="Notifications"
|
||||
leftIcon={
|
||||
<IconContainer
|
||||
variant="colored"
|
||||
backgroundColor={`${colors.warning}20`}
|
||||
>
|
||||
<Ionicons
|
||||
name="notifications-outline"
|
||||
size={20}
|
||||
color={theme.colors.warning}
|
||||
color={colors.warning}
|
||||
/>
|
||||
</LinearGradient>
|
||||
<Text style={styles.infoLabel}>Notifications</Text>
|
||||
</IconContainer>
|
||||
}
|
||||
rightElement={
|
||||
<Ionicons
|
||||
name="chevron-forward"
|
||||
size={20}
|
||||
color={theme.colors.gray400}
|
||||
color={colors.textTertiary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
</MinimalCard>
|
||||
</View>
|
||||
|
||||
{/* Gym Selection */}
|
||||
<View
|
||||
style={[styles.infoCard, theme.shadows.subtle, { marginTop: 12 }]}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: 12,
|
||||
}}
|
||||
{/* Gym Selection */}
|
||||
<View style={styles.section}>
|
||||
<View style={styles.sectionHeader}>
|
||||
<Text style={[typography.h3, { color: colors.textPrimary }]}>
|
||||
Gym Selection
|
||||
</Text>
|
||||
<TouchableOpacity onPress={loadGyms}>
|
||||
<Text
|
||||
style={[
|
||||
typography.body,
|
||||
{ color: colors.primary, fontWeight: "600" },
|
||||
]}
|
||||
>
|
||||
<Text style={styles.infoLabel}>
|
||||
Gym{" "}
|
||||
{currentGymName
|
||||
? `(Current: ${currentGymName})`
|
||||
: currentGymId
|
||||
? `(Current: ${currentGymId})`
|
||||
: "(Current: None)"}
|
||||
Refresh
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<MinimalCard variant="default">
|
||||
{currentGymName && (
|
||||
<View style={styles.currentGym}>
|
||||
<Text
|
||||
style={[typography.caption, { color: colors.textTertiary }]}
|
||||
>
|
||||
Current Gym
|
||||
</Text>
|
||||
<Text
|
||||
style={[
|
||||
typography.h3,
|
||||
{ color: colors.textPrimary, marginTop: 4 },
|
||||
]}
|
||||
>
|
||||
{currentGymName}
|
||||
</Text>
|
||||
<TouchableOpacity onPress={loadGyms}>
|
||||
<Text style={{ color: theme.colors.primary }}>
|
||||
Refresh Gyms
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
)}
|
||||
|
||||
{gymsLoading ? (
|
||||
<ActivityIndicator style={{ padding: 12 }} />
|
||||
) : (
|
||||
{gymsLoading ? (
|
||||
<View style={styles.loadingContainer}>
|
||||
<ActivityIndicator color={colors.primary} />
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<ScrollView
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
style={{ paddingHorizontal: 12, paddingBottom: 12 }}
|
||||
style={styles.gymScroll}
|
||||
contentContainerStyle={styles.gymScrollContent}
|
||||
>
|
||||
<View style={{ flexDirection: "row" }}>
|
||||
<TouchableOpacity
|
||||
<TouchableOpacity
|
||||
style={[
|
||||
styles.gymChip,
|
||||
{
|
||||
backgroundColor:
|
||||
selectedGymId === null
|
||||
? `${colors.primary}20`
|
||||
: colors.surfaceElevated,
|
||||
borderColor:
|
||||
selectedGymId === null ? colors.primary : colors.border,
|
||||
},
|
||||
]}
|
||||
onPress={() => setSelectedGymId(null)}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
styles.infoRow,
|
||||
selectedGymId === null && {
|
||||
backgroundColor: "rgba(59, 130, 246, 0.1)",
|
||||
typography.body,
|
||||
{
|
||||
color:
|
||||
selectedGymId === null
|
||||
? colors.primary
|
||||
: colors.textSecondary,
|
||||
fontWeight: selectedGymId === null ? "600" : "400",
|
||||
},
|
||||
]}
|
||||
onPress={() => setSelectedGymId(null)}
|
||||
>
|
||||
<Text style={styles.infoLabel}>Proceed without gym</Text>
|
||||
</TouchableOpacity>
|
||||
{gyms.map((gym) => (
|
||||
<TouchableOpacity
|
||||
key={gym.id}
|
||||
No Gym
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
{gyms.map((gym) => (
|
||||
<TouchableOpacity
|
||||
key={gym.id}
|
||||
style={[
|
||||
styles.gymChip,
|
||||
{
|
||||
backgroundColor:
|
||||
selectedGymId === gym.id
|
||||
? `${colors.primary}20`
|
||||
: colors.surfaceElevated,
|
||||
borderColor:
|
||||
selectedGymId === gym.id
|
||||
? colors.primary
|
||||
: colors.border,
|
||||
},
|
||||
]}
|
||||
onPress={() => setSelectedGymId(gym.id)}
|
||||
>
|
||||
<Text
|
||||
style={[
|
||||
styles.infoRow,
|
||||
selectedGymId === gym.id && {
|
||||
backgroundColor: "rgba(59, 130, 246, 0.1)",
|
||||
typography.body,
|
||||
{
|
||||
color:
|
||||
selectedGymId === gym.id
|
||||
? colors.primary
|
||||
: colors.textSecondary,
|
||||
fontWeight: selectedGymId === gym.id ? "600" : "400",
|
||||
},
|
||||
]}
|
||||
onPress={() => setSelectedGymId(gym.id)}
|
||||
>
|
||||
<Text style={styles.infoLabel}>{gym.name}</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
{gym.name}
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</ScrollView>
|
||||
)}
|
||||
|
||||
<View style={{ padding: 12 }}>
|
||||
<AnimatedButton
|
||||
title="Apply Gym Selection"
|
||||
<MinimalButton
|
||||
title="Apply Selection"
|
||||
onPress={handleApplyGym}
|
||||
variant="primary"
|
||||
style={{ marginTop: 4 }}
|
||||
icon={
|
||||
<Ionicons
|
||||
name="checkmark-outline"
|
||||
size={20}
|
||||
color={theme.colors.white}
|
||||
/>
|
||||
}
|
||||
style={{ marginTop: 16 }}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</MinimalCard>
|
||||
</View>
|
||||
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>Support</Text>
|
||||
<View style={[styles.infoCard, theme.shadows.subtle]}>
|
||||
<TouchableOpacity style={styles.infoRow}>
|
||||
<LinearGradient
|
||||
colors={["rgba(139, 92, 246, 0.1)", "rgba(139, 92, 246, 0.05)"]}
|
||||
style={styles.iconContainer}
|
||||
{/* Support */}
|
||||
<View style={styles.section}>
|
||||
<Text
|
||||
style={[
|
||||
typography.h3,
|
||||
{ color: colors.textPrimary, marginBottom: 12 },
|
||||
]}
|
||||
>
|
||||
Support
|
||||
</Text>
|
||||
<MinimalCard variant="default">
|
||||
<ListItem
|
||||
title="Help Center"
|
||||
leftIcon={
|
||||
<IconContainer
|
||||
variant="colored"
|
||||
backgroundColor={`${colors.info}20`}
|
||||
>
|
||||
<Ionicons
|
||||
name="help-circle-outline"
|
||||
size={20}
|
||||
color={theme.colors.secondary}
|
||||
color={colors.info}
|
||||
/>
|
||||
</LinearGradient>
|
||||
<Text style={styles.infoLabel}>Help Center</Text>
|
||||
</IconContainer>
|
||||
}
|
||||
rightElement={
|
||||
<Ionicons
|
||||
name="chevron-forward"
|
||||
size={20}
|
||||
color={theme.colors.gray400}
|
||||
color={colors.textTertiary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
<View style={styles.divider} />
|
||||
<TouchableOpacity style={styles.infoRow}>
|
||||
<LinearGradient
|
||||
colors={[
|
||||
"rgba(107, 114, 128, 0.1)",
|
||||
"rgba(107, 114, 128, 0.05)",
|
||||
]}
|
||||
style={styles.iconContainer}
|
||||
>
|
||||
}
|
||||
/>
|
||||
<View style={[styles.divider, { backgroundColor: colors.border }]} />
|
||||
<ListItem
|
||||
title="Privacy & Security"
|
||||
leftIcon={
|
||||
<IconContainer variant="subtle">
|
||||
<Ionicons
|
||||
name="shield-checkmark-outline"
|
||||
size={20}
|
||||
color={theme.colors.gray600}
|
||||
color={colors.textSecondary}
|
||||
/>
|
||||
</LinearGradient>
|
||||
<Text style={styles.infoLabel}>Privacy & Security</Text>
|
||||
</IconContainer>
|
||||
}
|
||||
rightElement={
|
||||
<Ionicons
|
||||
name="chevron-forward"
|
||||
size={20}
|
||||
color={theme.colors.gray400}
|
||||
color={colors.textTertiary}
|
||||
/>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
}
|
||||
/>
|
||||
</MinimalCard>
|
||||
</View>
|
||||
|
||||
<AnimatedButton
|
||||
{/* Sign Out */}
|
||||
<View style={styles.section}>
|
||||
<MinimalButton
|
||||
title="Sign Out"
|
||||
onPress={confirmSignOut}
|
||||
variant="danger"
|
||||
style={styles.signOutButton}
|
||||
icon={<Ionicons name="log-out-outline" size={20} color="#fff" />}
|
||||
size="lg"
|
||||
/>
|
||||
|
||||
<Text style={styles.version}>Version 1.0.0</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<Text
|
||||
style={[
|
||||
typography.caption,
|
||||
{
|
||||
color: colors.textTertiary,
|
||||
textAlign: "center",
|
||||
marginBottom: 100,
|
||||
},
|
||||
]}
|
||||
>
|
||||
Version 1.0.0
|
||||
</Text>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.colors.background,
|
||||
},
|
||||
header: {
|
||||
content: {
|
||||
padding: 24,
|
||||
paddingTop: 60,
|
||||
paddingBottom: 30,
|
||||
borderBottomLeftRadius: theme.borderRadius.xl,
|
||||
borderBottomRightRadius: theme.borderRadius.xl,
|
||||
alignItems: "center",
|
||||
},
|
||||
profileCard: {
|
||||
alignItems: "center",
|
||||
paddingVertical: 32,
|
||||
marginBottom: 24,
|
||||
},
|
||||
avatarContainer: {
|
||||
position: "relative",
|
||||
marginBottom: 16,
|
||||
},
|
||||
avatar: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
borderRadius: 50,
|
||||
borderWidth: 4,
|
||||
borderColor: "rgba(255, 255, 255, 0.3)",
|
||||
},
|
||||
placeholderAvatar: {
|
||||
width: 100,
|
||||
height: 100,
|
||||
borderRadius: 50,
|
||||
backgroundColor: "rgba(255, 255, 255, 0.2)",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
borderWidth: 4,
|
||||
borderColor: "rgba(255, 255, 255, 0.3)",
|
||||
},
|
||||
editBadge: {
|
||||
position: "absolute",
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
backgroundColor: theme.colors.white,
|
||||
width: 32,
|
||||
height: 32,
|
||||
borderRadius: 16,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 3,
|
||||
},
|
||||
name: {
|
||||
fontSize: theme.typography.fontSize["2xl"],
|
||||
fontWeight: theme.typography.fontWeight.bold,
|
||||
color: theme.colors.white,
|
||||
marginBottom: 4,
|
||||
},
|
||||
email: {
|
||||
fontSize: theme.typography.fontSize.sm,
|
||||
color: "rgba(255, 255, 255, 0.8)",
|
||||
marginBottom: 12,
|
||||
},
|
||||
memberBadge: {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.2)",
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 6,
|
||||
borderRadius: theme.borderRadius.full,
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255, 255, 255, 0.3)",
|
||||
},
|
||||
memberText: {
|
||||
color: theme.colors.white,
|
||||
fontSize: theme.typography.fontSize.xs,
|
||||
fontWeight: theme.typography.fontWeight.bold,
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
content: {
|
||||
padding: 20,
|
||||
marginTop: -20,
|
||||
},
|
||||
section: {
|
||||
marginBottom: 24,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: theme.typography.fontSize.lg,
|
||||
fontWeight: theme.typography.fontWeight.bold,
|
||||
color: theme.colors.gray900,
|
||||
marginBottom: 12,
|
||||
marginLeft: 4,
|
||||
},
|
||||
infoCard: {
|
||||
backgroundColor: theme.colors.white,
|
||||
borderRadius: theme.borderRadius.xl,
|
||||
padding: 8,
|
||||
borderWidth: 1,
|
||||
borderColor: theme.colors.gray100,
|
||||
},
|
||||
infoRow: {
|
||||
sectionHeader: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
padding: 12,
|
||||
},
|
||||
iconContainer: {
|
||||
width: 36,
|
||||
height: 36,
|
||||
borderRadius: 10,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: 12,
|
||||
},
|
||||
infoLabel: {
|
||||
flex: 1,
|
||||
fontSize: theme.typography.fontSize.base,
|
||||
color: theme.colors.gray900,
|
||||
fontWeight: theme.typography.fontWeight.medium,
|
||||
marginBottom: 12,
|
||||
},
|
||||
divider: {
|
||||
height: 1,
|
||||
backgroundColor: theme.colors.gray100,
|
||||
marginLeft: 60,
|
||||
marginLeft: 52,
|
||||
},
|
||||
signOutButton: {
|
||||
marginTop: 8,
|
||||
currentGym: {
|
||||
marginBottom: 16,
|
||||
paddingBottom: 16,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: "rgba(0, 0, 0, 0.05)",
|
||||
},
|
||||
version: {
|
||||
textAlign: "center",
|
||||
marginTop: 24,
|
||||
color: theme.colors.gray400,
|
||||
fontSize: theme.typography.fontSize.xs,
|
||||
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,
|
||||
},
|
||||
});
|
||||
Reference in new issue
Block a user