feat(mobile): add iOS/Android mobile app with SSO login, camera upload, and push notifications
Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
/**
|
||||
* ProfileScreen – authenticated user profile and settings.
|
||||
*/
|
||||
|
||||
import React from "react";
|
||||
import {
|
||||
Alert,
|
||||
Image,
|
||||
Pressable,
|
||||
ScrollView,
|
||||
StyleSheet,
|
||||
Switch,
|
||||
Text,
|
||||
View,
|
||||
} from "react-native";
|
||||
import { useAuth } from "../context/AuthContext";
|
||||
|
||||
export default function ProfileScreen() {
|
||||
const { user, signOut, baseUrl } = useAuth();
|
||||
|
||||
function handleSignOut() {
|
||||
Alert.alert("Sign out", "Are you sure you want to sign out?", [
|
||||
{ text: "Cancel", style: "cancel" },
|
||||
{
|
||||
text: "Sign out",
|
||||
style: "destructive",
|
||||
onPress: signOut,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
if (!user) {
|
||||
return (
|
||||
<View style={styles.center}>
|
||||
<Text style={styles.emptyText}>Not signed in</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<ScrollView style={styles.scroll} contentContainerStyle={styles.content}>
|
||||
{/* Avatar + name */}
|
||||
<View style={styles.profileCard}>
|
||||
{user.avatar_url ? (
|
||||
<Image
|
||||
source={{ uri: user.avatar_url }}
|
||||
style={styles.avatar}
|
||||
accessibilityLabel={`Avatar for ${user.display_name ?? user.owner_id}`}
|
||||
/>
|
||||
) : (
|
||||
<View style={[styles.avatar, styles.avatarPlaceholder]}>
|
||||
<Text style={styles.avatarInitial}>
|
||||
{(user.display_name ?? user.owner_id).charAt(0).toUpperCase()}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
<Text style={styles.displayName}>{user.display_name ?? user.owner_id}</Text>
|
||||
{user.email && <Text style={styles.email}>{user.email}</Text>}
|
||||
{user.is_admin && <Text style={styles.adminBadge}>Admin</Text>}
|
||||
</View>
|
||||
|
||||
{/* Server info */}
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionTitle}>Connection</Text>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.rowLabel}>Server</Text>
|
||||
<Text style={styles.rowValue} numberOfLines={1}>
|
||||
{baseUrl || "–"}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={styles.row}>
|
||||
<Text style={styles.rowLabel}>User ID</Text>
|
||||
<Text style={styles.rowValue} numberOfLines={1}>
|
||||
{user.owner_id}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* Danger zone */}
|
||||
<View style={styles.section}>
|
||||
<Pressable
|
||||
style={styles.signOutButton}
|
||||
onPress={handleSignOut}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Sign out"
|
||||
>
|
||||
<Text style={styles.signOutText}>Sign out</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</ScrollView>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
scroll: { flex: 1, backgroundColor: "#f9fafb" },
|
||||
content: { padding: 20 },
|
||||
center: {
|
||||
flex: 1,
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "#f9fafb",
|
||||
},
|
||||
emptyText: { color: "#6b7280", fontSize: 16 },
|
||||
profileCard: {
|
||||
alignItems: "center",
|
||||
backgroundColor: "#fff",
|
||||
borderRadius: 16,
|
||||
padding: 24,
|
||||
marginBottom: 20,
|
||||
shadowColor: "#000",
|
||||
shadowOpacity: 0.06,
|
||||
shadowOffset: { width: 0, height: 4 },
|
||||
shadowRadius: 12,
|
||||
elevation: 3,
|
||||
},
|
||||
avatar: {
|
||||
width: 80,
|
||||
height: 80,
|
||||
borderRadius: 40,
|
||||
marginBottom: 14,
|
||||
},
|
||||
avatarPlaceholder: {
|
||||
backgroundColor: "#1e40af",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
avatarInitial: {
|
||||
color: "#fff",
|
||||
fontSize: 32,
|
||||
fontWeight: "700",
|
||||
},
|
||||
displayName: {
|
||||
fontSize: 20,
|
||||
fontWeight: "700",
|
||||
color: "#111827",
|
||||
marginBottom: 4,
|
||||
},
|
||||
email: { fontSize: 14, color: "#6b7280", marginBottom: 6 },
|
||||
adminBadge: {
|
||||
backgroundColor: "#dbeafe",
|
||||
color: "#1e40af",
|
||||
fontSize: 11,
|
||||
fontWeight: "700",
|
||||
paddingHorizontal: 10,
|
||||
paddingVertical: 3,
|
||||
borderRadius: 12,
|
||||
overflow: "hidden",
|
||||
},
|
||||
section: {
|
||||
backgroundColor: "#fff",
|
||||
borderRadius: 12,
|
||||
padding: 16,
|
||||
marginBottom: 16,
|
||||
shadowColor: "#000",
|
||||
shadowOpacity: 0.04,
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowRadius: 6,
|
||||
elevation: 2,
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 13,
|
||||
fontWeight: "700",
|
||||
color: "#6b7280",
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: 0.5,
|
||||
marginBottom: 12,
|
||||
},
|
||||
row: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
paddingVertical: 8,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: "#f3f4f6",
|
||||
},
|
||||
rowLabel: { fontSize: 14, color: "#374151" },
|
||||
rowValue: {
|
||||
fontSize: 14,
|
||||
color: "#6b7280",
|
||||
maxWidth: "60%",
|
||||
textAlign: "right",
|
||||
},
|
||||
signOutButton: {
|
||||
backgroundColor: "#fee2e2",
|
||||
borderRadius: 10,
|
||||
paddingVertical: 14,
|
||||
alignItems: "center",
|
||||
minHeight: 48,
|
||||
},
|
||||
signOutText: {
|
||||
color: "#dc2626",
|
||||
fontWeight: "700",
|
||||
fontSize: 15,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user