diff --git a/package-lock.json b/package-lock.json
index 09798bb..05a9261 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
"@expo-google-fonts/jetbrains-mono": "^0.4.1",
"@expo-google-fonts/space-grotesk": "^0.4.1",
"@expo/ui": "~56.0.14",
+ "@react-navigation/bottom-tabs": "^7.18.18",
"@tanstack/react-query": "^5.102.8",
"axios": "^1.20.0",
"babel-preset-expo": "^57.0.9",
@@ -2867,6 +2868,96 @@
}
}
},
+ "node_modules/@react-navigation/bottom-tabs": {
+ "version": "7.18.18",
+ "resolved": "https://registry.npmjs.org/@react-navigation/bottom-tabs/-/bottom-tabs-7.18.18.tgz",
+ "integrity": "sha512-8XEvMG7SeBEY1zPSM3Bt3T12db1Q+j9qAMakdoSVkbFBXo5+rbrsdz4kOVtCs7vXOEeJfOEIZlubgDb4zRFHAw==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-navigation/elements": "^2.9.40",
+ "color": "^4.2.3",
+ "sf-symbols-typescript": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@react-navigation/native": "^7.3.18",
+ "react": ">= 18.2.0",
+ "react-native": "*",
+ "react-native-safe-area-context": ">= 4.0.0",
+ "react-native-screens": ">= 4.0.0"
+ }
+ },
+ "node_modules/@react-navigation/core": {
+ "version": "7.21.13",
+ "resolved": "https://registry.npmjs.org/@react-navigation/core/-/core-7.21.13.tgz",
+ "integrity": "sha512-d1syMXra7RlJjGsjsIxEA14F6U+awFtQJeOfDc+V7x3y1mPqYQVTYnqnt3PR+fBBouUKaKsgKZsSR3MP5919Xg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@react-navigation/routers": "^7.6.4",
+ "escape-string-regexp": "^4.0.0",
+ "fast-deep-equal": "^3.1.3",
+ "nanoid": "^3.3.11",
+ "query-string": "^7.1.3",
+ "react-is": "^19.1.0",
+ "use-latest-callback": "^0.2.4",
+ "use-sync-external-store": "^1.5.0"
+ },
+ "peerDependencies": {
+ "react": ">= 18.2.0"
+ }
+ },
+ "node_modules/@react-navigation/elements": {
+ "version": "2.9.40",
+ "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-2.9.40.tgz",
+ "integrity": "sha512-x2fCMVKU23YeQvHSCFq2mVRYAbHqYNqb8kfWSEfIQ+XUX7dBXWd93/pr0w5jLjALDHDxQUaJKh4mIqr8q3zAwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color": "^4.2.3",
+ "use-latest-callback": "^0.2.4",
+ "use-sync-external-store": "^1.5.0"
+ },
+ "peerDependencies": {
+ "@react-native-masked-view/masked-view": ">= 0.2.0",
+ "@react-navigation/native": "^7.3.18",
+ "react": ">= 18.2.0",
+ "react-native": "*",
+ "react-native-safe-area-context": ">= 4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@react-native-masked-view/masked-view": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@react-navigation/native": {
+ "version": "7.3.18",
+ "resolved": "https://registry.npmjs.org/@react-navigation/native/-/native-7.3.18.tgz",
+ "integrity": "sha512-5lLYl0Kt85jcpEYxXzj2ecIKta+yo4EcAIOZoJe6jU9lbR0lu6sAQsotU3rlVAYL1VBY/xLemxs4WQ2LJs4Oew==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@react-navigation/core": "^7.21.13",
+ "escape-string-regexp": "^4.0.0",
+ "fast-deep-equal": "^3.1.3",
+ "nanoid": "^3.3.11",
+ "standard-navigation": "^0.0.8",
+ "use-latest-callback": "^0.2.4"
+ },
+ "peerDependencies": {
+ "react": ">= 18.2.0",
+ "react-native": "*"
+ }
+ },
+ "node_modules/@react-navigation/routers": {
+ "version": "7.6.4",
+ "resolved": "https://registry.npmjs.org/@react-navigation/routers/-/routers-7.6.4.tgz",
+ "integrity": "sha512-GI7eJm8/KsZUQaYcXvEExikKurRZRgEsSzyZ7faENfi65yqJBCXjDMwyN1pF6pNW1MoLH1ErDwDivFxY6BzD3w==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "nanoid": "^3.3.11"
+ }
+ },
"node_modules/@sinclair/typebox": {
"version": "0.27.10",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz",
@@ -9666,6 +9757,16 @@
"node": ">=6"
}
},
+ "node_modules/standard-navigation": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/standard-navigation/-/standard-navigation-0.0.8.tgz",
+ "integrity": "sha512-TyVbo7INUDWtsUWDFn8RR7kwR87U0S4xHfLfbbnyeC581TmmyqQ+eM+nPw8rQTSD8QitRVcYfPaSHr/QJiUy1g==",
+ "license": "MIT",
+ "peer": true,
+ "peerDependencies": {
+ "react": "*"
+ }
+ },
"node_modules/statuses": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz",
@@ -10191,6 +10292,15 @@
}
}
},
+ "node_modules/use-sync-external-store": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
+ "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
diff --git a/package.json b/package.json
index 4f0a2e4..9fceb80 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
"@expo-google-fonts/jetbrains-mono": "^0.4.1",
"@expo-google-fonts/space-grotesk": "^0.4.1",
"@expo/ui": "~56.0.14",
+ "@react-navigation/bottom-tabs": "^7.18.18",
"@tanstack/react-query": "^5.102.8",
"axios": "^1.20.0",
"babel-preset-expo": "^57.0.9",
diff --git a/src/app/(org)/_layout.tsx b/src/app/(org)/_layout.tsx
index da5f029..6e73749 100644
--- a/src/app/(org)/_layout.tsx
+++ b/src/app/(org)/_layout.tsx
@@ -1,12 +1,78 @@
+import { BahlaTabBar } from '@/components/navigation/BahlaTabBar';
+import { CreateActionSheet } from '@/components/organizer/CreateActionSheet';
import { useAuth } from '@/stores/authStore';
-import { Redirect, Stack } from 'expo-router';
+import { colors } from '@/theme/colors';
+import { Redirect, Tabs, router } from 'expo-router';
+import { CalendarDays, ChartColumn, LayoutDashboard, UserRound } from 'lucide-react-native';
+import { useState } from 'react';
export default function OrgLayout() {
const session = useAuth((state) => state.session);
-
+ const [createOpen, setCreateOpen] = useState(false);
+
if (!session) return ;
if (session.role !== 'org') return ;
- return ;
+ return (
+ <>
+ setCreateOpen(true)} />}
+ >
+ ,
+ }}
+ />
+ ,
+ }}
+ />
+ e.preventDefault() }}
+ />
+ ,
+ }}
+ />
+ ,
+ }}
+ />
+
+
+ setCreateOpen(false)}
+ onCreateEvent={() => {
+ setCreateOpen(false);
+ router.push('/create/event');
+ }}
+ onCreatePost={() => {
+ setCreateOpen(false);
+ router.push('/create/post');
+ }}
+ />
+ >
+ );
}
\ No newline at end of file
diff --git a/src/app/(org)/create.tsx b/src/app/(org)/create.tsx
new file mode 100644
index 0000000..b09e1ad
--- /dev/null
+++ b/src/app/(org)/create.tsx
@@ -0,0 +1,7 @@
+/* ------------------------------------------------------------------ *
+ * Route factice : réserve l'emplacement central de la tab bar.
+ * Elle n'est jamais atteinte — le listener de (org)/_layout la bloque.
+ * ------------------------------------------------------------------ */
+export default function CreatePlaceholder() {
+ return null;
+}
diff --git a/src/app/(tabs)/_layout.tsx b/src/app/(tabs)/_layout.tsx
index d9ec0e3..f229781 100644
--- a/src/app/(tabs)/_layout.tsx
+++ b/src/app/(tabs)/_layout.tsx
@@ -1,5 +1,8 @@
+import { BahlaTabBar } from '@/components/navigation/BahlaTabBar';
import { useAuth } from '@/stores/authStore';
-import { Redirect, Stack } from 'expo-router';
+import { colors } from '@/theme/colors';
+import { Redirect, Tabs } from 'expo-router';
+import { Compass, Map, Ticket, UserRound } from 'lucide-react-native';
export default function TabsLayout() {
const session = useAuth((state) => state.session);
@@ -8,5 +11,42 @@ export default function TabsLayout() {
if (session.role === 'org') return ;
- return ;
+ return (
+ }
+ >
+ ,
+ }}
+ />
+ ,
+ }}
+ />
+ ,
+ }}
+ />
+ ,
+ }}
+ />
+
+ );
}
\ No newline at end of file
diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx
index ade1a9d..f13524e 100644
--- a/src/app/_layout.tsx
+++ b/src/app/_layout.tsx
@@ -1,28 +1,27 @@
import { QueryClientProvider } from '@tanstack/react-query';
import { Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
-import { StatusBar } from 'expo-status-bar';
+import * as SystemUI from 'expo-system-ui';
import { useEffect } from 'react';
+import { StatusBar } from 'react-native';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { queryClient } from '../hooks/queryClient';
import { useAppFonts } from '../theme/typography';
import { useAuth } from '@/stores/authStore';
+import colors from '@/theme/colors';
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
const [fontsLoaded] = useAppFonts();
-
- if (!fontsLoaded) {
- return null;
- }
-
const hydrated = useAuth((state) => state.hydrated);
const hydrate = useAuth((state) => state.hydrate);
- useEffect(() => { hydrate(); }, []);
+ useEffect(() => {
+ hydrate();
+ }, [hydrate]);
const ready = hydrated && fontsLoaded;
@@ -30,13 +29,21 @@ export default function RootLayout() {
if (ready) SplashScreen.hideAsync();
}, [ready]);
+ useEffect(() => {
+ SystemUI.setBackgroundColorAsync(colors.deepBackground);
+ }, []);
+
if (!ready) return null;
return (
-
-
+
+
diff --git a/src/app/index.tsx b/src/app/index-old.tsx
similarity index 100%
rename from src/app/index.tsx
rename to src/app/index-old.tsx
diff --git a/src/app/index-old2.tsx b/src/app/index-old2.tsx
new file mode 100644
index 0000000..5164308
--- /dev/null
+++ b/src/app/index-old2.tsx
@@ -0,0 +1,5 @@
+import { Redirect } from 'expo-router';
+
+export default function IndexRoute() {
+ return ;
+}
diff --git a/src/components/navigation/BahlaTabBar.tsx b/src/components/navigation/BahlaTabBar.tsx
new file mode 100644
index 0000000..2ab060b
--- /dev/null
+++ b/src/components/navigation/BahlaTabBar.tsx
@@ -0,0 +1,277 @@
+/* ------------------------------------------------------------------ *
+ * BahlaTabBar — barre d'onglets floutée, partagée par (tabs) et (org).
+ *
+ * Une seule implémentation pour les deux parcours : le verre dépoli,
+ * le calcul de safe area, le rendu des items et l'accessibilité sont
+ * identiques. Seul le bouton central change, et il est optionnel.
+ *
+ * Usage :
+ * }> // utilisateur
+ * }>
+ *
+ * Dépendances : expo-blur · expo-linear-gradient ·
+ * react-native-safe-area-context · react-native-svg
+ * ------------------------------------------------------------------ */
+import { BlurView } from 'expo-blur';
+import { LinearGradient } from 'expo-linear-gradient';
+import { Tabs } from 'expo-router';
+import { useCallback, type ComponentProps } from 'react';
+import {
+ Platform,
+ Pressable,
+ StyleSheet,
+ Text,
+ View,
+ type ViewStyle,
+} from 'react-native';
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
+
+import { colors, gradientDirection, gradients } from '@/theme/colors';
+import { fontFamily, typography } from '@/theme/typography';
+import { Plus } from 'lucide-react-native';
+
+/** Hauteur de la barre hors safe area. La hauteur réelle y ajoute l'inset bas. */
+export const TAB_BAR_BASE_HEIGHT = 52;
+const FAB_SIZE = 62;
+
+/**
+ * expo-blur ne rend rien d'exploitable sous Android 9 : sans ce repli on
+ * obtient un rectangle transparent et des libellés illisibles.
+ */
+const SUPPORTS_BLUR = Platform.OS === 'ios' || Number(Platform.Version) >= 28;
+
+type ExpoRouterTabBarProps = Parameters<
+ NonNullable['tabBar']>
+>[0];
+
+type Props = ExpoRouterTabBarProps & {
+ /**
+ * Nom de la route factice qui occupe l'emplacement central.
+ * Absent côté utilisateur — il n'y a pas de bouton flottant.
+ */
+ fabRouteName?: string;
+ /** Appelé à la place d'une navigation quand on presse le bouton central. */
+ onFabPress?: () => void;
+ /** Libellé lu par les lecteurs d'écran sur le bouton central. */
+ fabAccessibilityLabel?: string;
+};
+
+export function BahlaTabBar({
+ state,
+ descriptors,
+ navigation,
+ fabRouteName,
+ onFabPress,
+ fabAccessibilityLabel = 'Créer',
+}: Props) {
+ const insets = useSafeAreaInsets();
+
+ const handlePress = useCallback(
+ (routeKey: string, routeName: string, isFocused: boolean) => {
+ const event = navigation.emit({
+ type: 'tabPress',
+ target: routeKey,
+ canPreventDefault: true,
+ });
+ if (isFocused || event.defaultPrevented) return;
+ navigation.navigate(routeName as never);
+ },
+ [navigation],
+ );
+
+ const handleLongPress = useCallback(
+ (routeKey: string) => {
+ navigation.emit({ type: 'tabLongPress', target: routeKey });
+ },
+ [navigation],
+ );
+
+ return (
+
+ {state.routes.map((route: (typeof state.routes)[number], index: number) => {
+ const { options } = descriptors[route.key];
+ const isFocused = state.index === index;
+ const tint = isFocused ? colors.primary : colors.textSecondary;
+
+ /* --- Emplacement central : bouton flottant, pas un onglet ---- */
+ if (fabRouteName && route.name === fabRouteName) {
+ return (
+
+ [
+ styles.fab,
+ pressed && styles.fabPressed,
+ ]}
+ >
+
+
+
+
+
+ );
+ }
+
+ /* --- Onglet ordinaire ---------------------------------------- */
+ const label =
+ typeof options.tabBarLabel === 'string'
+ ? options.tabBarLabel
+ : (options.title ?? route.name);
+
+ return (
+ handlePress(route.key, route.name, isFocused)}
+ onLongPress={() => handleLongPress(route.key)}
+ style={styles.item}
+ >
+
+ {options.tabBarIcon?.({
+ focused: isFocused,
+ color: tint,
+ size: 23,
+ })}
+ {options.tabBarBadge != null && (
+
+
+ {options.tabBarBadge}
+
+
+ )}
+
+
+ {label}
+
+
+ );
+ })}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ bar: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ bottom: 0,
+ flexDirection: 'row',
+ alignItems: 'flex-start',
+ paddingTop: 8,
+ borderTopWidth: StyleSheet.hairlineWidth * 2,
+ borderTopColor: colors.border,
+ overflow: 'visible',
+ } as ViewStyle,
+
+ item: {
+ flex: 1,
+ alignItems: 'center',
+ gap: 5,
+ // La zone tactile couvre toute la hauteur de la barre.
+ alignSelf: 'stretch',
+ paddingTop: 2,
+ },
+
+ label: {
+ ...typography.tabLabel,
+ fontFamily: undefined, // remplacé plus haut selon l'état
+ },
+
+ badge: {
+ position: 'absolute',
+ top: -4,
+ right: -8,
+ minWidth: 16,
+ height: 16,
+ paddingHorizontal: 4,
+ borderRadius: 999,
+ backgroundColor: colors.primary,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+
+ badgeText: {
+ ...typography.overlineSm,
+ fontSize: 9,
+ lineHeight: 16,
+ letterSpacing: 0,
+ color: colors.onPrimary,
+ },
+
+ /* Le créneau central est plus étroit que les onglets : 84 px fixes,
+ comme la colonne du même nom dans la maquette. */
+ fabSlot: {
+ width: 84,
+ alignItems: 'center',
+ },
+
+ fab: {
+ width: FAB_SIZE,
+ height: FAB_SIZE,
+ aspectRatio: 1,
+ borderRadius: 15,
+ // Remonte au-dessus de la barre et découpe un anneau dans le fond.
+ marginTop: -30,
+ overflow: 'hidden',
+ alignSelf: 'center',
+ borderWidth: 3,
+ borderColor: colors.deepBackground,
+ shadowColor: colors.primary,
+ shadowOffset: { width: 0, height: 14 },
+ shadowOpacity: 0.85,
+ shadowRadius: 17,
+ elevation: 12,
+ },
+
+ fabPressed: {
+ transform: [{ scale: 0.94 }],
+ shadowOpacity: 0.5,
+ },
+
+ fabGradient: {
+ width: '100%',
+ height: '100%',
+ aspectRatio: 1,
+ borderRadius: 15,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+});
+
+export default BahlaTabBar;
diff --git a/src/components/organizer/CreateActionSheet.tsx b/src/components/organizer/CreateActionSheet.tsx
new file mode 100644
index 0000000..3ec5ad7
--- /dev/null
+++ b/src/components/organizer/CreateActionSheet.tsx
@@ -0,0 +1,150 @@
+/* ------------------------------------------------------------------ *
+ * CreateActionSheet — le choix ouvert par le bouton central.
+ * Deux actions, rien d'autre : créer un événement, publier un post.
+ * ------------------------------------------------------------------ */
+import { LinearGradient } from 'expo-linear-gradient';
+import {
+ Modal,
+ Pressable,
+ StyleSheet,
+ Text,
+ View,
+ type ViewStyle,
+} from 'react-native';
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
+
+import { colors, gradientDirection, gradients } from '@/theme/colors';
+import { typography } from '@/theme/typography';
+import { Plus } from 'lucide-react-native';
+
+type Props = {
+ visible: boolean;
+ onClose: () => void;
+ onCreateEvent: () => void;
+ onCreatePost: () => void;
+};
+
+export function CreateActionSheet({
+ visible,
+ onClose,
+ onCreateEvent,
+ onCreatePost,
+}: Props) {
+ const insets = useSafeAreaInsets();
+
+ return (
+
+
+
+
+
+ [styles.row, pressed && styles.rowPressed]}
+ >
+
+
+
+
+ Créer un événement
+ Date, lieu, line-up, billetterie
+
+
+
+ [styles.row, pressed && styles.rowPressed]}
+ >
+
+
+
+
+ Publier un post
+ Sur un de tes événements à venir
+
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ scrim: {
+ flex: 1,
+ backgroundColor: 'rgba(6,6,12,0.62)',
+ } as ViewStyle,
+
+ sheet: {
+ backgroundColor: colors.surface,
+ borderTopLeftRadius: 28,
+ borderTopRightRadius: 28,
+ borderTopWidth: 1,
+ borderTopColor: colors.border,
+ paddingHorizontal: 20,
+ paddingTop: 10,
+ gap: 10,
+ },
+
+ handle: {
+ width: 44,
+ height: 5,
+ borderRadius: 999,
+ backgroundColor: colors.border,
+ alignSelf: 'center',
+ marginBottom: 10,
+ },
+
+ row: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: 14,
+ padding: 12,
+ borderRadius: 18,
+ backgroundColor: colors.surfaceElevated,
+ borderWidth: 1,
+ borderColor: colors.border,
+ },
+
+ rowPressed: { opacity: 0.7 },
+
+ iconBox: {
+ width: 48,
+ height: 48,
+ borderRadius: 15,
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+
+ iconBoxPlain: {
+ backgroundColor: colors.surface,
+ borderWidth: 1,
+ borderColor: colors.border,
+ },
+
+ rowText: { flex: 1, gap: 2 },
+
+ rowTitle: {
+ ...typography.label,
+ fontSize: 16,
+ color: colors.textPrimary,
+ },
+
+ rowSub: {
+ ...typography.caption,
+ color: colors.textSecondary,
+ },
+});
+
+export default CreateActionSheet;
diff --git a/src/hooks/useTabBarHeight.ts b/src/hooks/useTabBarHeight.ts
new file mode 100644
index 0000000..d6cc9c5
--- /dev/null
+++ b/src/hooks/useTabBarHeight.ts
@@ -0,0 +1,18 @@
+/* ------------------------------------------------------------------ *
+ * useTabBarHeight — hauteur réelle de la BahlaTabBar.
+ *
+ * La barre est en position absolue au-dessus du contenu. Tout écran qui
+ * défile doit réserver cette hauteur, sinon sa dernière carte finit
+ * masquée derrière le verre dépoli :
+ *
+ *
+ * ------------------------------------------------------------------ */
+import { TAB_BAR_BASE_HEIGHT } from '@/components/navigation/BahlaTabBar';
+import { useSafeAreaInsets } from 'react-native-safe-area-context';
+
+export function useTabBarHeight() {
+ const insets = useSafeAreaInsets();
+ return TAB_BAR_BASE_HEIGHT + insets.bottom;
+}
+
+export default useTabBarHeight;
diff --git a/src/stores/authStore.ts b/src/stores/authStore.ts
index 0e0a1c0..4ba9d05 100644
--- a/src/stores/authStore.ts
+++ b/src/stores/authStore.ts
@@ -8,6 +8,14 @@ export type Session = {
role: Role;
}
+// Temporaire: bypass de l'auth pour avancer sur la navigation.
+const DEV_MOCK_SESSION_ENABLED = true;
+const DEV_MOCK_SESSION: Session = {
+ userId: 'dev-user',
+ token: 'dev-token',
+ role: 'org',
+};
+
type AuthState = {
session: Session | null;
hydrated: boolean; // Le disque a été lu, la réponse est fiable
@@ -23,8 +31,18 @@ export const useAuth = create((set) => ({
hydrated: false,
hydrate: async () => {
+ if (DEV_MOCK_SESSION_ENABLED) {
+ set({ session: DEV_MOCK_SESSION, hydrated: true });
+ return;
+ }
+
const session = await SecureStore.getItemAsync(KEY);
- set({ session: session ? JSON.parse(session) : null, hydrated: true });
+ if (session) {
+ set({ session: JSON.parse(session), hydrated: true });
+ return;
+ }
+
+ set({ session: null, hydrated: true });
},
signIn: async (s: Session) => {