redesign completed
alot of work ahead
This commit is contained in:
1 parent
73d2c4c1ed
commit
febcdc111e
18 files changed
+1591
-845
No files matched your search
@@ -1,239 +1,118 @@
|
||||
import React, { useEffect, useRef } from "react";
|
||||
import {
|
||||
View,
|
||||
Text,
|
||||
StyleSheet,
|
||||
ScrollView,
|
||||
TouchableOpacity,
|
||||
Animated,
|
||||
} from "react-native";
|
||||
import { View, Text, StyleSheet, ScrollView, RefreshControl, Image } from "react-native";
|
||||
import { useUser } from "@clerk/clerk-expo";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
import { useRouter } from "expo-router";
|
||||
import { LinearGradient } from "expo-linear-gradient";
|
||||
import { useState, useCallback } from "react";
|
||||
import { theme } from "../../styles/theme";
|
||||
import { ActivityWidget } from "../../components/ActivityWidget";
|
||||
import { QuickActionGrid } from "../../components/QuickActionGrid";
|
||||
import { Ionicons } from "@expo/vector-icons";
|
||||
|
||||
export default function HomeScreen() {
|
||||
const { user, isLoaded } = useUser();
|
||||
const router = useRouter();
|
||||
const fadeAnim = useRef(new Animated.Value(0)).current;
|
||||
const { user } = useUser();
|
||||
const [refreshing, setRefreshing] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
Animated.timing(fadeAnim, {
|
||||
toValue: 1,
|
||||
duration: 500,
|
||||
useNativeDriver: true,
|
||||
}).start();
|
||||
const onRefresh = useCallback(() => {
|
||||
setRefreshing(true);
|
||||
setTimeout(() => {
|
||||
setRefreshing(false);
|
||||
}, 2000);
|
||||
}, []);
|
||||
|
||||
if (!isLoaded || !user) {
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<Text>Loading...</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const firstName = user.firstName || "User";
|
||||
const greeting = getGreeting();
|
||||
const getGreeting = () => {
|
||||
const hour = new Date().getHours();
|
||||
if (hour < 12) return "Good Morning";
|
||||
if (hour < 18) return "Good Afternoon";
|
||||
return "Good Evening";
|
||||
};
|
||||
|
||||
return (
|
||||
<ScrollView style={styles.container}>
|
||||
<Animated.View style={[styles.content, { opacity: fadeAnim }]}>
|
||||
{/* Gradient Header */}
|
||||
<LinearGradient
|
||||
colors={theme.gradients.primary}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
style={styles.header}
|
||||
>
|
||||
<Text style={styles.greeting}>{greeting}!</Text>
|
||||
<Text style={styles.name}>{firstName}</Text>
|
||||
</LinearGradient>
|
||||
|
||||
{/* Quick Stats with Glassmorphism */}
|
||||
<View style={styles.statsContainer}>
|
||||
<LinearGradient
|
||||
colors={['rgba(59, 130, 246, 0.1)', 'rgba(139, 92, 246, 0.1)']}
|
||||
style={[styles.statCard, theme.shadows.medium]}
|
||||
>
|
||||
<View style={styles.statIconContainer}>
|
||||
<LinearGradient
|
||||
colors={theme.gradients.primary}
|
||||
style={styles.statIconGradient}
|
||||
>
|
||||
<Ionicons name="calendar-outline" size={24} color="#fff" />
|
||||
</LinearGradient>
|
||||
</View>
|
||||
<Text style={styles.statValue}>0</Text>
|
||||
<Text style={styles.statLabel}>This Month</Text>
|
||||
</LinearGradient>
|
||||
|
||||
<LinearGradient
|
||||
colors={['rgba(239, 68, 68, 0.1)', 'rgba(236, 72, 153, 0.1)']}
|
||||
style={[styles.statCard, theme.shadows.medium]}
|
||||
>
|
||||
<View style={styles.statIconContainer}>
|
||||
<LinearGradient
|
||||
colors={theme.gradients.danger}
|
||||
style={styles.statIconGradient}
|
||||
>
|
||||
<Ionicons name="flame-outline" size={24} color="#fff" />
|
||||
</LinearGradient>
|
||||
</View>
|
||||
<Text style={styles.statValue}>0</Text>
|
||||
<Text style={styles.statLabel}>Day Streak</Text>
|
||||
</LinearGradient>
|
||||
|
||||
<LinearGradient
|
||||
colors={['rgba(245, 158, 11, 0.1)', 'rgba(217, 119, 6, 0.1)']}
|
||||
style={[styles.statCard, theme.shadows.medium]}
|
||||
>
|
||||
<View style={styles.statIconContainer}>
|
||||
<LinearGradient
|
||||
colors={theme.gradients.warning}
|
||||
style={styles.statIconGradient}
|
||||
>
|
||||
<Ionicons name="trophy-outline" size={24} color="#fff" />
|
||||
</LinearGradient>
|
||||
</View>
|
||||
<Text style={styles.statValue}>0</Text>
|
||||
<Text style={styles.statLabel}>Total Visits</Text>
|
||||
</LinearGradient>
|
||||
</View>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>Quick Actions</Text>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.actionButton, theme.shadows.medium]}
|
||||
onPress={() => router.push("/fitness-profile")}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={['rgba(236, 72, 153, 0.15)', 'rgba(236, 72, 153, 0.05)']}
|
||||
style={styles.actionIcon}
|
||||
>
|
||||
<Ionicons name="fitness-outline" size={24} color="#ec4899" />
|
||||
</LinearGradient>
|
||||
<View style={styles.actionContent}>
|
||||
<Text style={styles.actionTitle}>Fitness Profile</Text>
|
||||
<Text style={styles.actionSubtitle}>
|
||||
Manage your fitness information
|
||||
</Text>
|
||||
</View>
|
||||
<Ionicons name="chevron-forward" size={20} color="#9ca3af" />
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.actionButton, theme.shadows.medium]}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={['rgba(59, 130, 246, 0.15)', 'rgba(59, 130, 246, 0.05)']}
|
||||
style={styles.actionIcon}
|
||||
>
|
||||
<Ionicons name="log-in-outline" size={24} color="#2563eb" />
|
||||
</LinearGradient>
|
||||
<View style={styles.actionContent}>
|
||||
<Text style={styles.actionTitle}>Check In</Text>
|
||||
<Text style={styles.actionSubtitle}>
|
||||
Start your workout session
|
||||
</Text>
|
||||
</View>
|
||||
<Ionicons name="chevron-forward" size={20} color="#9ca3af" />
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.actionButton, theme.shadows.medium]}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={['rgba(16, 185, 129, 0.15)', 'rgba(16, 185, 129, 0.05)']}
|
||||
style={styles.actionIcon}
|
||||
>
|
||||
<Ionicons name="calendar-outline" size={24} color="#10b981" />
|
||||
</LinearGradient>
|
||||
<View style={styles.actionContent}>
|
||||
<Text style={styles.actionTitle}>View Schedule</Text>
|
||||
<Text style={styles.actionSubtitle}>
|
||||
Check your upcoming classes
|
||||
</Text>
|
||||
</View>
|
||||
<Ionicons name="chevron-forward" size={20} color="#9ca3af" />
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
style={[styles.actionButton, theme.shadows.medium]}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={['rgba(139, 92, 246, 0.15)', 'rgba(139, 92, 246, 0.05)']}
|
||||
style={styles.actionIcon}
|
||||
>
|
||||
<Ionicons name="card-outline" size={24} color="#8b5cf6" />
|
||||
</LinearGradient>
|
||||
<View style={styles.actionContent}>
|
||||
<Text style={styles.actionTitle}>Payments</Text>
|
||||
<Text style={styles.actionSubtitle}>View payment history</Text>
|
||||
</View>
|
||||
<Ionicons name="chevron-forward" size={20} color="#9ca3af" />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
|
||||
{/* Membership Info with Gradient */}
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>Membership</Text>
|
||||
<LinearGradient
|
||||
colors={theme.gradients.primary}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
style={[styles.membershipCard, theme.shadows.strong]}
|
||||
>
|
||||
<View style={styles.membershipHeader}>
|
||||
<Text style={styles.membershipType}>Basic Plan</Text>
|
||||
<View style={styles.statusBadge}>
|
||||
<Text style={styles.statusText}>Active</Text>
|
||||
<View style={styles.container}>
|
||||
<ScrollView
|
||||
contentContainerStyle={styles.scrollContent}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} tintColor={theme.colors.primary} />
|
||||
}
|
||||
>
|
||||
{/* Header Section */}
|
||||
<View style={styles.header}>
|
||||
<View>
|
||||
<Text style={styles.greeting}>{getGreeting()},</Text>
|
||||
<Text style={styles.name}>{user?.firstName || "Athlete"}</Text>
|
||||
</View>
|
||||
<View style={styles.avatarContainer}>
|
||||
{user?.imageUrl ? (
|
||||
<Image source={{ uri: user.imageUrl }} style={styles.avatar} />
|
||||
) : (
|
||||
<View style={styles.placeholderAvatar}>
|
||||
<Ionicons name="person" size={24} color="#fff" />
|
||||
</View>
|
||||
</View>
|
||||
<Text style={styles.membershipEmail}>
|
||||
{user.primaryEmailAddress?.emailAddress}
|
||||
</Text>
|
||||
<Text style={styles.membershipDate}>
|
||||
Member since {new Date(user.createdAt!).toLocaleDateString()}
|
||||
</Text>
|
||||
</LinearGradient>
|
||||
</View>
|
||||
|
||||
{/* Recent Activity */}
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>Recent Activity</Text>
|
||||
<View style={[styles.emptyState, theme.shadows.medium]}>
|
||||
<View style={styles.emptyIconContainer}>
|
||||
<LinearGradient
|
||||
colors={['rgba(209, 213, 219, 0.3)', 'rgba(209, 213, 219, 0.1)']}
|
||||
style={styles.emptyIconGradient}
|
||||
>
|
||||
<Ionicons name="barbell-outline" size={48} color="#9ca3af" />
|
||||
</LinearGradient>
|
||||
</View>
|
||||
<Text style={styles.emptyStateText}>No recent activity</Text>
|
||||
<Text style={styles.emptyStateSubtext}>
|
||||
Check in to start tracking your workouts
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</Animated.View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
function getGreeting(): string {
|
||||
const hour = new Date().getHours();
|
||||
if (hour < 12) return "Good morning";
|
||||
if (hour < 18) return "Good afternoon";
|
||||
return "Good evening";
|
||||
{/* Activity Widget */}
|
||||
<ActivityWidget
|
||||
steps={8432}
|
||||
calories={640}
|
||||
duration={45}
|
||||
/>
|
||||
|
||||
{/* Quick Actions */}
|
||||
<QuickActionGrid />
|
||||
|
||||
{/* Recent Activity Section */}
|
||||
<View style={styles.section}>
|
||||
<View style={styles.sectionHeader}>
|
||||
<Text style={styles.sectionTitle}>Recent Activity</Text>
|
||||
<Text style={styles.seeAll}>See All</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.activityCard}>
|
||||
<LinearGradient
|
||||
colors={['rgba(255, 255, 255, 0.8)', 'rgba(255, 255, 255, 0.5)']}
|
||||
style={[styles.recentItem, theme.shadows.subtle]}
|
||||
>
|
||||
<View style={styles.recentIconContainer}>
|
||||
<LinearGradient
|
||||
colors={theme.gradients.primary}
|
||||
style={styles.recentIcon}
|
||||
>
|
||||
<Ionicons name="barbell" size={20} color="#fff" />
|
||||
</LinearGradient>
|
||||
</View>
|
||||
<View style={styles.recentInfo}>
|
||||
<Text style={styles.recentTitle}>Upper Body Power</Text>
|
||||
<Text style={styles.recentSubtitle}>Today, 10:00 AM</Text>
|
||||
</View>
|
||||
<Text style={styles.recentValue}>45m</Text>
|
||||
</LinearGradient>
|
||||
|
||||
<LinearGradient
|
||||
colors={['rgba(255, 255, 255, 0.8)', 'rgba(255, 255, 255, 0.5)']}
|
||||
style={[styles.recentItem, theme.shadows.subtle]}
|
||||
>
|
||||
<View style={styles.recentIconContainer}>
|
||||
<LinearGradient
|
||||
colors={theme.gradients.success}
|
||||
style={styles.recentIcon}
|
||||
>
|
||||
<Ionicons name="bicycle" size={20} color="#fff" />
|
||||
</LinearGradient>
|
||||
</View>
|
||||
<View style={styles.recentInfo}>
|
||||
<Text style={styles.recentTitle}>Morning Cardio</Text>
|
||||
<Text style={styles.recentSubtitle}>Yesterday, 7:30 AM</Text>
|
||||
</View>
|
||||
<Text style={styles.recentValue}>30m</Text>
|
||||
</LinearGradient>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Bottom Spacer for Tab Bar */}
|
||||
<View style={{ height: 100 }} />
|
||||
</ScrollView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
@@ -241,167 +120,110 @@ const styles = StyleSheet.create({
|
||||
flex: 1,
|
||||
backgroundColor: theme.colors.background,
|
||||
},
|
||||
content: {
|
||||
paddingBottom: 20,
|
||||
scrollContent: {
|
||||
paddingTop: 60,
|
||||
},
|
||||
header: {
|
||||
padding: 24,
|
||||
paddingTop: 60,
|
||||
paddingBottom: 32,
|
||||
borderBottomLeftRadius: theme.borderRadius['2xl'],
|
||||
borderBottomRightRadius: theme.borderRadius['2xl'],
|
||||
marginBottom: 20,
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
paddingHorizontal: 24,
|
||||
marginBottom: 32,
|
||||
},
|
||||
greeting: {
|
||||
fontSize: theme.typography.fontSize.base,
|
||||
color: "rgba(255, 255, 255, 0.9)",
|
||||
fontSize: 16,
|
||||
color: theme.colors.gray600,
|
||||
fontWeight: "500",
|
||||
marginBottom: 4,
|
||||
},
|
||||
name: {
|
||||
fontSize: theme.typography.fontSize['4xl'],
|
||||
fontWeight: theme.typography.fontWeight.bold,
|
||||
color: theme.colors.white,
|
||||
},
|
||||
statsContainer: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
paddingHorizontal: 16,
|
||||
marginBottom: 24,
|
||||
gap: 12,
|
||||
},
|
||||
statCard: {
|
||||
flex: 1,
|
||||
backgroundColor: theme.colors.white,
|
||||
borderRadius: theme.borderRadius.xl,
|
||||
padding: 16,
|
||||
alignItems: "center",
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255, 255, 255, 0.3)",
|
||||
},
|
||||
statIconContainer: {
|
||||
marginBottom: 8,
|
||||
},
|
||||
statIconGradient: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
borderRadius: 24,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
statValue: {
|
||||
fontSize: theme.typography.fontSize['2xl'],
|
||||
fontWeight: theme.typography.fontWeight.bold,
|
||||
fontSize: 32,
|
||||
fontWeight: "800",
|
||||
color: theme.colors.gray900,
|
||||
marginTop: 4,
|
||||
marginBottom: 4,
|
||||
letterSpacing: -0.5,
|
||||
},
|
||||
statLabel: {
|
||||
fontSize: theme.typography.fontSize.xs,
|
||||
color: theme.colors.gray600,
|
||||
textAlign: "center",
|
||||
fontWeight: theme.typography.fontWeight.medium,
|
||||
avatarContainer: {
|
||||
shadowColor: "#000",
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 12,
|
||||
elevation: 5,
|
||||
},
|
||||
section: {
|
||||
marginBottom: 24,
|
||||
paddingHorizontal: 20,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: theme.typography.fontSize.xl,
|
||||
fontWeight: theme.typography.fontWeight.semibold,
|
||||
color: theme.colors.gray900,
|
||||
marginBottom: 12,
|
||||
},
|
||||
actionButton: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
backgroundColor: theme.colors.white,
|
||||
borderRadius: theme.borderRadius.xl,
|
||||
padding: 16,
|
||||
marginBottom: 12,
|
||||
},
|
||||
actionIcon: {
|
||||
avatar: {
|
||||
width: 56,
|
||||
height: 56,
|
||||
borderRadius: 28,
|
||||
borderRadius: 20,
|
||||
borderWidth: 2,
|
||||
borderColor: "#fff",
|
||||
},
|
||||
placeholderAvatar: {
|
||||
width: 56,
|
||||
height: 56,
|
||||
borderRadius: 20,
|
||||
backgroundColor: theme.colors.primary,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
marginRight: 12,
|
||||
borderWidth: 2,
|
||||
borderColor: "#fff",
|
||||
},
|
||||
actionContent: {
|
||||
flex: 1,
|
||||
section: {
|
||||
paddingHorizontal: 20,
|
||||
marginBottom: 24,
|
||||
},
|
||||
actionTitle: {
|
||||
fontSize: theme.typography.fontSize.base,
|
||||
fontWeight: theme.typography.fontWeight.semibold,
|
||||
color: theme.colors.gray900,
|
||||
marginBottom: 2,
|
||||
},
|
||||
actionSubtitle: {
|
||||
fontSize: theme.typography.fontSize.sm,
|
||||
color: theme.colors.gray600,
|
||||
},
|
||||
membershipCard: {
|
||||
borderRadius: theme.borderRadius.xl,
|
||||
padding: 20,
|
||||
},
|
||||
membershipHeader: {
|
||||
sectionHeader: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
marginBottom: 12,
|
||||
},
|
||||
membershipType: {
|
||||
fontSize: theme.typography.fontSize.xl,
|
||||
fontWeight: theme.typography.fontWeight.semibold,
|
||||
color: theme.colors.white,
|
||||
},
|
||||
statusBadge: {
|
||||
backgroundColor: "rgba(255, 255, 255, 0.25)",
|
||||
paddingHorizontal: 12,
|
||||
paddingVertical: 6,
|
||||
borderRadius: theme.borderRadius.lg,
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255, 255, 255, 0.3)",
|
||||
},
|
||||
statusText: {
|
||||
fontSize: theme.typography.fontSize.xs,
|
||||
fontWeight: theme.typography.fontWeight.semibold,
|
||||
color: theme.colors.white,
|
||||
},
|
||||
membershipEmail: {
|
||||
fontSize: theme.typography.fontSize.sm,
|
||||
color: "rgba(255, 255, 255, 0.9)",
|
||||
marginBottom: 4,
|
||||
},
|
||||
membershipDate: {
|
||||
fontSize: theme.typography.fontSize.xs,
|
||||
color: "rgba(255, 255, 255, 0.7)",
|
||||
},
|
||||
emptyState: {
|
||||
backgroundColor: theme.colors.white,
|
||||
borderRadius: theme.borderRadius.xl,
|
||||
padding: 32,
|
||||
alignItems: "center",
|
||||
},
|
||||
emptyIconContainer: {
|
||||
marginBottom: 16,
|
||||
},
|
||||
emptyIconGradient: {
|
||||
width: 96,
|
||||
height: 96,
|
||||
borderRadius: 48,
|
||||
sectionTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: "700",
|
||||
color: theme.colors.gray900,
|
||||
},
|
||||
seeAll: {
|
||||
fontSize: 14,
|
||||
color: theme.colors.primary,
|
||||
fontWeight: "600",
|
||||
},
|
||||
activityCard: {
|
||||
gap: 12,
|
||||
},
|
||||
recentItem: {
|
||||
flexDirection: "row",
|
||||
alignItems: "center",
|
||||
padding: 16,
|
||||
borderRadius: 20,
|
||||
backgroundColor: "#fff",
|
||||
borderWidth: 1,
|
||||
borderColor: "rgba(255, 255, 255, 0.6)",
|
||||
},
|
||||
recentIconContainer: {
|
||||
marginRight: 16,
|
||||
},
|
||||
recentIcon: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
borderRadius: 16,
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
},
|
||||
emptyStateText: {
|
||||
fontSize: theme.typography.fontSize.base,
|
||||
fontWeight: theme.typography.fontWeight.semibold,
|
||||
color: theme.colors.gray700,
|
||||
recentInfo: {
|
||||
flex: 1,
|
||||
},
|
||||
recentTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: "600",
|
||||
color: theme.colors.gray900,
|
||||
marginBottom: 4,
|
||||
},
|
||||
emptyStateSubtext: {
|
||||
fontSize: theme.typography.fontSize.sm,
|
||||
recentSubtitle: {
|
||||
fontSize: 12,
|
||||
color: theme.colors.gray500,
|
||||
textAlign: "center",
|
||||
},
|
||||
recentValue: {
|
||||
fontSize: 14,
|
||||
fontWeight: "600",
|
||||
color: theme.colors.gray900,
|
||||
},
|
||||
});
|
||||
Reference in new issue
Block a user