From 8992f58512d7d8d5ebeda52d593a71043aca515a Mon Sep 17 00:00:00 2001 From: Arthur Belleville Date: Sun, 20 Jul 2025 12:23:18 +0200 Subject: [PATCH] Rework routing --- .../app/{(home) => (app)}/(tabs)/_layout.tsx | 0 .../app/{(home) => (app)}/(tabs)/index.tsx | 0 .../app/{(home) => (app)}/(tabs)/planning.tsx | 0 .../app/{(home) => (app)}/(tabs)/settings.tsx | 4 +- .../app/{(home) => (app)}/(tabs)/tablos.tsx | 0 xtablo-expo/app/{(home) => (app)}/_layout.tsx | 0 .../app/{(home) => (app)}/channel/[cid].tsx | 0 .../app/{(home) => (app)}/channel/_layout.tsx | 0 .../app/{(home) => (app)}/user/profile.tsx | 4 +- xtablo-expo/app/(auth)/_layout.tsx | 26 ----- xtablo-expo/app/_layout.tsx | 54 ++++++---- xtablo-expo/app/{(auth) => }/login.tsx | 8 +- xtablo-expo/app/{(auth) => }/signup.tsx | 6 +- xtablo-expo/components/AppleLoginButton.tsx | 4 +- xtablo-expo/components/GoogleLoginButton.tsx | 4 +- xtablo-expo/components/LoadingView.tsx | 98 +++++++++++++++++++ xtablo-expo/components/Splash.tsx | 17 ++++ xtablo-expo/hooks/auth.ts | 18 ++++ xtablo-expo/hooks/tablos.ts | 6 +- xtablo-expo/hooks/user.ts | 4 +- xtablo-expo/stores/auth.tsx | 6 +- 21 files changed, 191 insertions(+), 68 deletions(-) rename xtablo-expo/app/{(home) => (app)}/(tabs)/_layout.tsx (100%) rename xtablo-expo/app/{(home) => (app)}/(tabs)/index.tsx (100%) rename xtablo-expo/app/{(home) => (app)}/(tabs)/planning.tsx (100%) rename xtablo-expo/app/{(home) => (app)}/(tabs)/settings.tsx (99%) rename xtablo-expo/app/{(home) => (app)}/(tabs)/tablos.tsx (100%) rename xtablo-expo/app/{(home) => (app)}/_layout.tsx (100%) rename xtablo-expo/app/{(home) => (app)}/channel/[cid].tsx (100%) rename xtablo-expo/app/{(home) => (app)}/channel/_layout.tsx (100%) rename xtablo-expo/app/{(home) => (app)}/user/profile.tsx (98%) delete mode 100644 xtablo-expo/app/(auth)/_layout.tsx rename xtablo-expo/app/{(auth) => }/login.tsx (94%) rename xtablo-expo/app/{(auth) => }/signup.tsx (96%) create mode 100644 xtablo-expo/components/LoadingView.tsx create mode 100644 xtablo-expo/components/Splash.tsx create mode 100644 xtablo-expo/hooks/auth.ts diff --git a/xtablo-expo/app/(home)/(tabs)/_layout.tsx b/xtablo-expo/app/(app)/(tabs)/_layout.tsx similarity index 100% rename from xtablo-expo/app/(home)/(tabs)/_layout.tsx rename to xtablo-expo/app/(app)/(tabs)/_layout.tsx diff --git a/xtablo-expo/app/(home)/(tabs)/index.tsx b/xtablo-expo/app/(app)/(tabs)/index.tsx similarity index 100% rename from xtablo-expo/app/(home)/(tabs)/index.tsx rename to xtablo-expo/app/(app)/(tabs)/index.tsx diff --git a/xtablo-expo/app/(home)/(tabs)/planning.tsx b/xtablo-expo/app/(app)/(tabs)/planning.tsx similarity index 100% rename from xtablo-expo/app/(home)/(tabs)/planning.tsx rename to xtablo-expo/app/(app)/(tabs)/planning.tsx diff --git a/xtablo-expo/app/(home)/(tabs)/settings.tsx b/xtablo-expo/app/(app)/(tabs)/settings.tsx similarity index 99% rename from xtablo-expo/app/(home)/(tabs)/settings.tsx rename to xtablo-expo/app/(app)/(tabs)/settings.tsx index c1b422b..e705b79 100644 --- a/xtablo-expo/app/(home)/(tabs)/settings.tsx +++ b/xtablo-expo/app/(app)/(tabs)/settings.tsx @@ -11,7 +11,7 @@ import { Linking, } from "react-native"; import { LinearGradient } from "expo-linear-gradient"; -import { useAuth } from "@/stores/auth"; +import { useAuthStore } from "@/stores/auth"; import { useUser } from "@/providers/UserProvider"; import { User, @@ -31,7 +31,7 @@ import { import { router } from "expo-router"; export default function SettingsScreen() { - const signOut = useAuth((state) => state.signOut); + const signOut = useAuthStore((state) => state.signOut); const user = useUser(); // Settings state diff --git a/xtablo-expo/app/(home)/(tabs)/tablos.tsx b/xtablo-expo/app/(app)/(tabs)/tablos.tsx similarity index 100% rename from xtablo-expo/app/(home)/(tabs)/tablos.tsx rename to xtablo-expo/app/(app)/(tabs)/tablos.tsx diff --git a/xtablo-expo/app/(home)/_layout.tsx b/xtablo-expo/app/(app)/_layout.tsx similarity index 100% rename from xtablo-expo/app/(home)/_layout.tsx rename to xtablo-expo/app/(app)/_layout.tsx diff --git a/xtablo-expo/app/(home)/channel/[cid].tsx b/xtablo-expo/app/(app)/channel/[cid].tsx similarity index 100% rename from xtablo-expo/app/(home)/channel/[cid].tsx rename to xtablo-expo/app/(app)/channel/[cid].tsx diff --git a/xtablo-expo/app/(home)/channel/_layout.tsx b/xtablo-expo/app/(app)/channel/_layout.tsx similarity index 100% rename from xtablo-expo/app/(home)/channel/_layout.tsx rename to xtablo-expo/app/(app)/channel/_layout.tsx diff --git a/xtablo-expo/app/(home)/user/profile.tsx b/xtablo-expo/app/(app)/user/profile.tsx similarity index 98% rename from xtablo-expo/app/(home)/user/profile.tsx rename to xtablo-expo/app/(app)/user/profile.tsx index dd38091..ee0183c 100644 --- a/xtablo-expo/app/(home)/user/profile.tsx +++ b/xtablo-expo/app/(app)/user/profile.tsx @@ -5,7 +5,7 @@ import { Text, TouchableOpacity, } from "react-native"; -import { useAuth } from "@/stores/auth"; +import { useAuthStore } from "@/stores/auth"; import { Avatar, Input } from "@rn-vui/themed"; import { Card } from "@rn-vui/themed"; import { useState } from "react"; @@ -22,7 +22,7 @@ import { } from "lucide-react-native"; export default function ProfileScreen() { - const signOut = useAuth((state) => state.signOut); + const signOut = useAuthStore((state) => state.signOut); const user = useUser(); const [displayName, setDisplayName] = useState(user.name || ""); diff --git a/xtablo-expo/app/(auth)/_layout.tsx b/xtablo-expo/app/(auth)/_layout.tsx deleted file mode 100644 index 586987b..0000000 --- a/xtablo-expo/app/(auth)/_layout.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { Redirect, Slot } from "expo-router"; -import { useAuth } from "@/stores/auth"; -import { ActivityIndicator } from "react-native"; -import { useGetUser } from "@/hooks/user"; -import { useEffect } from "react"; -import { useQueryClient } from "@tanstack/react-query"; - -export default function AuthLayout() { - const { loading, initialize } = useAuth(); - const queryClient = useQueryClient(); - const { user, isLoading: isUserLoading } = useGetUser(); - - const isLoading = loading || isUserLoading; - - useEffect(() => { - initialize(queryClient); - }, []); - - if (isLoading) { - return ; - } - if (user) { - return ; - } - return ; -} diff --git a/xtablo-expo/app/_layout.tsx b/xtablo-expo/app/_layout.tsx index cb6bcd8..d34e904 100644 --- a/xtablo-expo/app/_layout.tsx +++ b/xtablo-expo/app/_layout.tsx @@ -3,17 +3,20 @@ import { DefaultTheme, ThemeProvider, } from "@react-navigation/native"; -import { useFonts } from "expo-font"; import { Stack } from "expo-router"; import * as SplashScreen from "expo-splash-screen"; import { StatusBar } from "expo-status-bar"; -import { useEffect } from "react"; import "react-native-reanimated"; import { GestureHandlerRootView } from "react-native-gesture-handler"; import { useColorScheme } from "@/hooks/useColorScheme"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { cloneDeep } from "lodash"; -import { ActivityIndicator } from "react-native"; +import { ActivityIndicator, View, StyleSheet, Image } from "react-native"; +import { SplashScreenController } from "@/components/Splash"; +import { useInitializeApp } from "@/hooks/auth"; +import { ThemedView } from "@/components/ThemedView"; +import { ThemedText } from "@/components/ThemedText"; +import { LoadingView } from "@/components/LoadingView"; window.structuredClone = cloneDeep; @@ -33,19 +36,6 @@ const queryClient = new QueryClient({ export default function RootLayout() { const colorScheme = useColorScheme(); - const [loaded] = useFonts({ - SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"), - }); - - useEffect(() => { - if (loaded) { - SplashScreen.hideAsync(); - } - }, [loaded]); - - if (!loaded) { - return ; - } return ( @@ -53,14 +43,36 @@ export default function RootLayout() { - - - - - + + ); } + +const RootNavigator = () => { + const { isLoading, isLoggedIn } = useInitializeApp(); + + if (isLoading) { + return ; + } + + console.log("isLoggedIn", isLoggedIn); + + return ( + + + + + + + + + + + + + ); +}; diff --git a/xtablo-expo/app/(auth)/login.tsx b/xtablo-expo/app/login.tsx similarity index 94% rename from xtablo-expo/app/(auth)/login.tsx rename to xtablo-expo/app/login.tsx index 776b506..7d9f699 100644 --- a/xtablo-expo/app/(auth)/login.tsx +++ b/xtablo-expo/app/login.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { StyleSheet, View, Text, Image } from "react-native"; import { Button, Input } from "@rn-vui/themed"; -import { useAuth } from "@/stores/auth"; +import { useAuthStore } from "@/stores/auth"; import { Link } from "expo-router"; import { Mail, Lock } from "lucide-react-native"; import { GoogleLoginButton } from "@/components/GoogleLoginButton"; @@ -11,9 +11,9 @@ export default function Auth() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); - const login = useAuth((state) => state.login); - const authLoading = useAuth((state) => state.loading); - const performOAuth = useAuth((state) => state.performOAuth); + const login = useAuthStore((state) => state.login); + const authLoading = useAuthStore((state) => state.loading); + const performOAuth = useAuthStore((state) => state.performOAuth); return ( diff --git a/xtablo-expo/app/(auth)/signup.tsx b/xtablo-expo/app/signup.tsx similarity index 96% rename from xtablo-expo/app/(auth)/signup.tsx rename to xtablo-expo/app/signup.tsx index c15a983..b59ecc2 100644 --- a/xtablo-expo/app/(auth)/signup.tsx +++ b/xtablo-expo/app/signup.tsx @@ -1,7 +1,7 @@ import React, { useState } from "react"; import { StyleSheet, View, Text, Image } from "react-native"; import { Button, Input } from "@rn-vui/themed"; -import { useAuth } from "@/stores/auth"; +import { useAuthStore } from "@/stores/auth"; import { Link } from "expo-router"; import { Mail, Lock, User, Building2 } from "lucide-react-native"; @@ -12,8 +12,8 @@ export default function SignUp() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); - const signUp = useAuth((state) => state.signUp); - const authLoading = useAuth((state) => state.loading); + const signUp = useAuthStore((state) => state.signUp); + const authLoading = useAuthStore((state) => state.loading); return ( diff --git a/xtablo-expo/components/AppleLoginButton.tsx b/xtablo-expo/components/AppleLoginButton.tsx index f49eb2a..7c2804b 100644 --- a/xtablo-expo/components/AppleLoginButton.tsx +++ b/xtablo-expo/components/AppleLoginButton.tsx @@ -1,6 +1,6 @@ import React from "react"; import { StyleSheet, View, Text, TouchableOpacity } from "react-native"; -import { useAuth } from "@/stores/auth"; +import { useAuthStore } from "@/stores/auth"; import { Svg, Path } from "react-native-svg"; const AppleIcon = ({ color = "#fff", size = 20 }) => ( @@ -17,7 +17,7 @@ const AppleIcon = ({ color = "#fff", size = 20 }) => ( ); export const AppleLoginButton = ({ onPress }: { onPress: () => void }) => { - const authLoading = useAuth((state) => state.loading); + const authLoading = useAuthStore((state) => state.loading); return ( void }) => { - const authLoading = useAuth((state) => state.loading); + const authLoading = useAuthStore((state) => state.loading); return ( { + const rotation = useSharedValue(0); + const width = useSharedValue(80); + + rotation.value = withRepeat( + withTiming(360, { easing: Easing.linear, duration: 2000 }), + -1, + false + ); + width.value = withRepeat( + withTiming(100, { easing: Easing.linear, duration: 2000 }), + -1, + true + ); + + const rotationDeg = useDerivedValue(() => { + return `${rotation.value}deg`; + }); + + const animatedStyle = useAnimatedStyle(() => { + return { + transform: [{ rotate: rotationDeg.value }], + width: width.value, + }; + }); + + return ( + + + + + + + XTablo + + + Initialisation de l'application... + + + + ); +}; + +const styles = StyleSheet.create({ + loadingContainer: { + flex: 1, + justifyContent: "center", + alignItems: "center", + backgroundColor: "#f8fafc", + }, + loadingContent: { + alignItems: "center", + paddingHorizontal: 40, + }, + logoContainer: { + alignItems: "center", + width: 130, + height: 130, + }, + logo: { + width: 80, + height: 80, + shadowColor: "#000", + shadowOffset: { width: 0, height: 4 }, + shadowOpacity: 0.1, + shadowRadius: 8, + elevation: 4, + }, + title: { + fontSize: 28, + fontWeight: "bold", + textAlign: "center", + marginBottom: 8, + color: "#1f2937", + }, + subtitle: { + fontSize: 16, + textAlign: "center", + marginBottom: 32, + color: "#6b7280", + opacity: 0.8, + }, +}); diff --git a/xtablo-expo/components/Splash.tsx b/xtablo-expo/components/Splash.tsx new file mode 100644 index 0000000..09cf463 --- /dev/null +++ b/xtablo-expo/components/Splash.tsx @@ -0,0 +1,17 @@ +import { SplashScreen } from "expo-router"; +import { useInitializeApp } from "@/hooks/auth"; +import { useFonts } from "expo-font"; + +export function SplashScreenController() { + const { isLoading } = useInitializeApp(); + + const [loaded] = useFonts({ + SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"), + }); + + if (!isLoading && loaded) { + SplashScreen.hideAsync(); + } + + return null; +} diff --git a/xtablo-expo/hooks/auth.ts b/xtablo-expo/hooks/auth.ts new file mode 100644 index 0000000..f93b24c --- /dev/null +++ b/xtablo-expo/hooks/auth.ts @@ -0,0 +1,18 @@ +import { useEffect } from "react"; +import { useQueryClient } from "@tanstack/react-query"; +import { useAuthStore } from "@/stores/auth"; +import { useGetUser } from "@/hooks/user"; + +export const useInitializeApp = () => { + const { loading, initialize } = useAuthStore(); + const queryClient = useQueryClient(); + const { user, isLoading: isUserLoading } = useGetUser(); + + const isLoading = loading || isUserLoading; + + useEffect(() => { + initialize(queryClient); + }, []); + + return { isLoading, isLoggedIn: !!user }; +}; diff --git a/xtablo-expo/hooks/tablos.ts b/xtablo-expo/hooks/tablos.ts index d3ee201..d83b9dd 100644 --- a/xtablo-expo/hooks/tablos.ts +++ b/xtablo-expo/hooks/tablos.ts @@ -3,7 +3,7 @@ import { useUser } from "@/providers/UserProvider"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { TabloInsert, UserTablo } from "@/types/tablos.types"; import { api } from "@/lib/api"; -import { useAuth } from "@/stores/auth"; +import { useAuthStore } from "@/stores/auth"; import { Alert } from "react-native"; // type TabloInsert = Tablo["Insert"]; @@ -28,7 +28,7 @@ export const useTablosList = () => { }; export const useCreateTablo = () => { - const session = useAuth((state) => state.session); + const session = useAuthStore((state) => state.session); const queryClient = useQueryClient(); return useMutation({ @@ -53,7 +53,7 @@ export const useCreateTablo = () => { // Delete tablo (soft delete) export const useDeleteTablo = () => { - const session = useAuth((state) => state.session); + const session = useAuthStore((state) => state.session); const queryClient = useQueryClient(); return useMutation({ diff --git a/xtablo-expo/hooks/user.ts b/xtablo-expo/hooks/user.ts index 683b313..0abfe1e 100644 --- a/xtablo-expo/hooks/user.ts +++ b/xtablo-expo/hooks/user.ts @@ -1,6 +1,6 @@ import { api } from "@/lib/api"; import { Tables } from "@/types/database.types"; -import { useAuth } from "@/stores/auth"; +import { useAuthStore } from "@/stores/auth"; import { useQuery } from "@tanstack/react-query"; type User = Tables<"profiles"> & { @@ -8,7 +8,7 @@ type User = Tables<"profiles"> & { }; export const useGetUser = (): { user: User | null; isLoading: boolean } => { - const session = useAuth((state) => state.session); + const session = useAuthStore((state) => state.session); const { data, isLoading } = useQuery({ queryKey: ["user"], queryFn: async () => { diff --git a/xtablo-expo/stores/auth.tsx b/xtablo-expo/stores/auth.tsx index f7bf3e3..31f2855 100644 --- a/xtablo-expo/stores/auth.tsx +++ b/xtablo-expo/stores/auth.tsx @@ -10,6 +10,7 @@ import { QueryClient } from "@tanstack/react-query"; interface AuthState { session: Session | null; loading: boolean; + initialized: boolean; initialize: (queryClient: QueryClient) => Promise; setSession: (session: Session | null) => void; login: (email: string, password: string) => Promise; @@ -28,8 +29,9 @@ interface AuthState { WebBrowser.maybeCompleteAuthSession(); const redirectTo = makeRedirectUri({ path: "/(home)/(tabs)" }); -export const useAuth = create((set, get) => ({ +export const useAuthStore = create((set, get) => ({ loading: true, + initialized: false, session: null, setSession: (session: Session | null) => set({ session }), initialize: async (queryClient: QueryClient) => { @@ -61,6 +63,8 @@ export const useAuth = create((set, get) => ({ console.error("Auth initialization error:", error); } finally { set({ loading: false }); + set({ initialized: true }); + queryClient.invalidateQueries({ queryKey: ["user"] }); } }, login: async (email: string, password: string) => {