import { Stack } from "expo-router/stack"; import { ConvexProvider, ConvexReactClient } from "convex/react"; import { useState, createContext, useContext, useEffect, useCallback, Component, type ReactNode, type ErrorInfo } from "react"; import { View, Text, Pressable, ScrollView } from "react-native"; import AsyncStorage from "@react-native-async-storage/async-storage"; import { ThemeProvider, useTheme } from "../components/theme"; import { ToastProvider } from "../components/ui/toast"; const CONVEX_URL = process.env.EXPO_PUBLIC_CONVEX_URL; function getConvexClient(): ConvexReactClient | null { if (!CONVEX_URL) { console.error("Missing EXPO_PUBLIC_CONVEX_URL environment variable"); return null; } try { return new ConvexReactClient(CONVEX_URL); } catch (e) { console.error("Failed to create Convex client:", e); return null; } } const convex = getConvexClient(); type AuthState = { token: string | null; userId: string | null; isAuthenticated: boolean; login: (token: string, userId: string) => Promise; logout: () => Promise; }; const AuthContext = createContext({ token: null, userId: null, isAuthenticated: false, login: async () => {}, logout: async () => {}, }); export function useAuth() { return useContext(AuthContext); } const TOKEN_KEY = "mojmajstor_auth_token"; const USER_ID_KEY = "mojmajstor_auth_userId"; class ErrorBoundary extends Component<{ children: ReactNode }, { hasError: boolean; error: Error | null }> { state: { hasError: boolean; error: Error | null } = { hasError: false, error: null }; static getDerivedStateFromError(error: Error) { return { hasError: true, error }; } reset = () => { this.setState({ hasError: false, error: null }); }; render() { if (this.state.hasError) { return ; } return this.props.children; } } function ErrorFallback({ error, onReset }: { error: Error; onReset: () => void }) { const theme = useTheme(); return ( ⚠️ Настана грешка Приложението наиде на неочекувана грешка. {__DEV__ && error?.message ? ( {error.message} ) : null} Обиди се повторно ); } function ThemedNavigator() { const theme = useTheme(); return ( ); } export default function RootLayout() { const [token, setToken] = useState(null); const [userId, setUserId] = useState(null); const [loaded, setLoaded] = useState(false); useEffect(() => { Promise.all([ AsyncStorage.getItem(TOKEN_KEY), AsyncStorage.getItem(USER_ID_KEY), ]).then(([t, u]) => { if (t) setToken(t); if (u) setUserId(u); setLoaded(true); }); }, []); const login = useCallback(async (newToken: string, newUserId: string) => { await AsyncStorage.setItem(TOKEN_KEY, newToken); await AsyncStorage.setItem(USER_ID_KEY, newUserId); setToken(newToken); setUserId(newUserId); }, []); const logout = useCallback(async () => { await AsyncStorage.removeItem(TOKEN_KEY); await AsyncStorage.removeItem(USER_ID_KEY); setToken(null); setUserId(null); }, []); if (!loaded) return null; if (!convex) { return ( ); } return ( ); } function MissingConfigScreen() { const theme = useTheme(); return ( ⚙️ Конфигурациска грешка Недостасува EXPO_PUBLIC_CONVEX_URL. Проверете ја конфигурацијата за изградба. ); }