Set up Expo SDK 56 project with TypeScript and Expo Router (file-based). Backend: - Full Convex schema (users, accounts, sessions, ads, categories, reviews, chats, messages, posts) deployed to self-hosted instance - Custom email/password auth with SHA-256 hashing via Convex actions - Query/mutation scaffolds for all domain tables - Convex environment variables configured on backend App structure: - Root layout with ConvexProvider, ThemeProvider, AuthContext - (auth) group: login and register screens (Macedonian UI) with role selection (handyman/customer) - (tabs) group: 5 tabs (Home, Explore, Posts, Chat, Profile) each with nested Stack layouts - Placeholder detail screens for ad, post, chat, review flows - 404 not-found screen in Macedonian UI primitives (components/ui/): - Button (4 variants), Input, Card, Loading, Avatar, Rating, Badge - ThemeProvider with light/dark palette via React context All user-facing strings in Macedonian. Code identifiers in English. TypeScript passes with zero errors.
81 lines
3.3 KiB
TypeScript
81 lines
3.3 KiB
TypeScript
import { View, Text, Pressable, ScrollView } from "react-native";
|
||
import { useRouter } from "expo-router";
|
||
import { useQuery, useMutation } from "convex/react";
|
||
import { api } from "../../../convex/_generated/api";
|
||
import { useAuth } from "../../_layout";
|
||
import { useTheme } from "../../../components/theme";
|
||
import { Button } from "../../../components/ui/button";
|
||
|
||
export default function ProfileScreen() {
|
||
const router = useRouter();
|
||
const theme = useTheme();
|
||
const { token, isAuthenticated, logout } = useAuth();
|
||
const user = useQuery(api.users.getCurrentUser, isAuthenticated ? { token: token! } : "skip");
|
||
const logoutMutation = useMutation(api.auth.logout);
|
||
|
||
async function handleLogout() {
|
||
if (token) {
|
||
try {
|
||
await logoutMutation({ token });
|
||
} catch {}
|
||
}
|
||
await logout();
|
||
router.replace("/(auth)/login");
|
||
}
|
||
|
||
if (!isAuthenticated) {
|
||
return (
|
||
<View style={{ flex: 1, alignItems: "center", justifyContent: "center", backgroundColor: theme.colors.background, padding: theme.spacing.lg }}>
|
||
<Text style={{ fontSize: 48, marginBottom: 16 }}>👤</Text>
|
||
<Text style={{ fontSize: 18, fontWeight: "600", color: theme.colors.text, marginBottom: 8 }}>
|
||
Најавете се
|
||
</Text>
|
||
<Text style={{ color: theme.colors.textSecondary, textAlign: "center", marginBottom: theme.spacing.lg }}>
|
||
За да ги видите вашиот профил и податоци.
|
||
</Text>
|
||
<Button onPress={() => router.push("/(auth)/login")}>Најави се</Button>
|
||
</View>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<ScrollView contentInsetAdjustmentBehavior="automatic" contentContainerStyle={{ padding: theme.spacing.lg, gap: theme.spacing.md }}>
|
||
<View style={{ alignItems: "center", paddingVertical: theme.spacing.lg }}>
|
||
<View style={{
|
||
width: 80,
|
||
height: 80,
|
||
borderRadius: 40,
|
||
backgroundColor: theme.colors.primaryLight,
|
||
alignItems: "center",
|
||
justifyContent: "center",
|
||
}}>
|
||
<Text style={{ color: theme.colors.primary, fontWeight: "700", fontSize: 28 }}>
|
||
{user?.name?.charAt(0)?.toUpperCase() || "?"}
|
||
</Text>
|
||
</View>
|
||
<Text style={{ fontSize: 20, fontWeight: "700", color: theme.colors.text, marginTop: theme.spacing.md }}>
|
||
{user?.name || "Корисник"}
|
||
</Text>
|
||
<Text style={{ color: theme.colors.textSecondary }}>
|
||
{user?.role === "handyman" ? "Мајстор" : "Клиент"}
|
||
</Text>
|
||
</View>
|
||
|
||
<View style={{ backgroundColor: theme.colors.card, borderRadius: theme.radius.lg, padding: theme.spacing.md, gap: theme.spacing.sm }}>
|
||
<Pressable style={{ paddingVertical: theme.spacing.sm }}>
|
||
<Text style={{ color: theme.colors.text, fontSize: 16 }}>Мои огласи</Text>
|
||
</Pressable>
|
||
<Pressable style={{ paddingVertical: theme.spacing.sm }}>
|
||
<Text style={{ color: theme.colors.text, fontSize: 16 }}>Оцени</Text>
|
||
</Pressable>
|
||
<Pressable style={{ paddingVertical: theme.spacing.sm }}>
|
||
<Text style={{ color: theme.colors.text, fontSize: 16 }}>Поставки</Text>
|
||
</Pressable>
|
||
</View>
|
||
|
||
<Button variant="destructive" onPress={handleLogout}>
|
||
Одјави се
|
||
</Button>
|
||
</ScrollView>
|
||
);
|
||
} |