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) => {