mobile: fix navigation (typed routes), extract C tokens to lib/theme.ts

- lib/theme.ts: C design tokens dans fichier dédié (plus d'import circulaire _layout)
- app/_layout.tsx: importe C depuis @/lib/theme, ré-exporte pour compatibilité
- Tous les écrans: import C depuis '@/lib/theme' au lieu de '../_layout'
- Toutes les navigations: router.push({ pathname, params }) au lieu de template strings
  -> Fix réel du bug 'impossible d'ouvrir carnet/note' avec Expo Router v6
- package.json: expo-web-browser ajouté (pour Google OAuth étape suivante)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Antigravity
2026-05-29 17:03:14 +00:00
parent 45877db706
commit d2145f761d
12 changed files with 32 additions and 26 deletions

View File

@@ -5,7 +5,7 @@ import {
Alert, StyleSheet,
} from 'react-native'
import { useAuthStore } from '@/lib/store'
import { C } from '../_layout'
import { C } from '@/lib/theme'
export default function LoginScreen() {
const [email, setEmail] = useState('')

View File

@@ -1,6 +1,6 @@
import { Tabs } from 'expo-router'
import { BookOpen, Search, Home, User } from 'lucide-react-native'
import { C } from '../_layout'
import { C } from '@/lib/theme'
export default function TabsLayout() {
return (

View File

@@ -9,7 +9,7 @@ import { CalendarDays, Search, BookOpen, Clock, ChevronRight } from 'lucide-reac
import { apiFetch } from '@/lib/api'
import { ENDPOINTS } from '@/lib/config'
import { useAuthStore } from '@/lib/store'
import { C } from '../_layout'
import { C } from '@/lib/theme'
interface Note {
id: string
@@ -45,7 +45,7 @@ export default function HomeScreen() {
const res = await apiFetch(ENDPOINTS.dailyNote)
if (res.ok) {
const data = await res.json()
router.push(`/note/${data.id}`)
router.push({ pathname: '/note/[id]', params: { id: data.id } })
}
}
@@ -107,7 +107,7 @@ export default function HomeScreen() {
: recentNotes.map((note, i) => (
<TouchableOpacity
key={note.id}
onPress={() => router.push(`/note/${note.id}`)}
onPress={() => router.push({ pathname: '/note/[id]', params: { id: note.id } })}
style={[s.noteRow, i === recentNotes.length - 1 && { borderBottomWidth: 0 }]}
activeOpacity={0.6}
>

View File

@@ -8,7 +8,7 @@ import { useRouter } from 'expo-router'
import { ChevronRight, BookOpen, Folder } from 'lucide-react-native'
import { apiFetch } from '@/lib/api'
import { ENDPOINTS } from '@/lib/config'
import { C } from '../_layout'
import { C } from '@/lib/theme'
interface Notebook {
id: string
@@ -72,7 +72,7 @@ export default function NotebooksScreen() {
contentContainerStyle={s.list}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={() => { setRefreshing(true); load() }} tintColor={C.brand} />}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => router.push(`/notebook/${item.id}`)} style={s.card} activeOpacity={0.7}>
<TouchableOpacity onPress={() => router.push({ pathname: '/notebook/[id]', params: { id: item.id } })} style={s.card} activeOpacity={0.7}>
<View style={[s.iconWrap, { backgroundColor: (item.color || C.brand) + '18' }]}>
<NotebookIcon icon={item.icon} color={item.color} />
</View>

View File

@@ -2,7 +2,7 @@ import { View, Text, TouchableOpacity, ScrollView, Alert, StyleSheet } from 'rea
import { SafeAreaView } from 'react-native-safe-area-context'
import { LogOut, CreditCard, Globe } from 'lucide-react-native'
import { useAuthStore } from '@/lib/store'
import { C } from '../_layout'
import { C } from '@/lib/theme'
const TIER_LABELS: Record<string, string> = {
FREE: 'Gratuit',

View File

@@ -8,7 +8,7 @@ import { Search as SearchIcon, X } from 'lucide-react-native'
import { useRouter } from 'expo-router'
import { apiFetch } from '@/lib/api'
import { ENDPOINTS } from '@/lib/config'
import { C } from '../_layout'
import { C } from '@/lib/theme'
interface SearchResult {
id: string
@@ -67,7 +67,7 @@ export default function SearchScreen() {
keyExtractor={(item) => item.id}
contentContainerStyle={s.list}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => router.push(`/note/${item.id}`)} style={s.card}>
<TouchableOpacity onPress={() => router.push({ pathname: '/note/[id]', params: { id: item.id } })} style={s.card}>
<Text style={s.cardTitle} numberOfLines={1}>{item.title || 'Sans titre'}</Text>
{item.snippet && <Text style={s.snippet} numberOfLines={2}>{item.snippet}</Text>}
{item.notebookName && <Text style={s.nb}>{item.notebookName}</Text>}

View File

@@ -4,6 +4,10 @@ import { SafeAreaProvider } from 'react-native-safe-area-context'
import { StatusBar } from 'expo-status-bar'
import { View, ActivityIndicator, StyleSheet } from 'react-native'
import { useAuthStore } from '@/lib/store'
import { C } from '@/lib/theme'
// Ré-exporter C pour la compatibilité avec les anciens imports
export { C } from '@/lib/theme'
export default function RootLayout() {
const { user, loading, restore } = useAuthStore()
@@ -35,18 +39,6 @@ export default function RootLayout() {
)
}
export const C = {
brand: '#A47148',
ink: '#1A1A18',
paper: '#FAFAF8',
concrete: '#8A8A82',
border: '#E8E6E0',
white: '#FFFFFF',
rose: '#e11d48',
roseBg: '#fff1f2',
roseBorder: '#fecdd3',
}
const s = StyleSheet.create({
loader: { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: C.paper },
})

View File

@@ -9,7 +9,7 @@ import { ArrowLeft, Share2 } from 'lucide-react-native'
import { WebView } from 'react-native-webview'
import { apiFetch } from '@/lib/api'
import { ENDPOINTS } from '@/lib/config'
import { C } from '../_layout'
import { C } from '@/lib/theme'
interface Note {
id: string

View File

@@ -7,7 +7,7 @@ import { useLocalSearchParams, useRouter } from 'expo-router'
import { ArrowLeft } from 'lucide-react-native'
import { apiFetch } from '@/lib/api'
import { ENDPOINTS } from '@/lib/config'
import { C } from '../_layout'
import { C } from '@/lib/theme'
interface Note {
id: string
@@ -56,7 +56,7 @@ export default function NotebookScreen() {
contentContainerStyle={s.list}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={() => { setRefreshing(true); load() }} tintColor={C.brand} />}
renderItem={({ item }) => (
<TouchableOpacity onPress={() => router.push(`/note/${item.id}`)} style={s.card}>
<TouchableOpacity onPress={() => router.push({ pathname: '/note/[id]', params: { id: item.id } })} style={s.card}>
<Text style={s.cardTitle} numberOfLines={1}>{item.title || 'Sans titre'}</Text>
<Text style={s.cardDate}>
{new Date(item.updatedAt).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' })}

View File

@@ -13,6 +13,7 @@ interface AuthState {
user: User | null
loading: boolean
login: (email: string, password: string) => Promise<void>
loginWithToken: (token: string, user: User) => Promise<void>
logout: () => Promise<void>
restore: () => Promise<void>
}

View File

@@ -0,0 +1,12 @@
// Design tokens partagés — ne pas importer depuis _layout pour éviter les circularités
export const C = {
brand: '#A47148',
ink: '#1A1A18',
paper: '#FAFAF8',
concrete: '#8A8A82',
border: '#E8E6E0',
white: '#FFFFFF',
rose: '#e11d48',
roseBg: '#fff1f2',
roseBorder: '#fecdd3',
}

View File

@@ -26,7 +26,8 @@
"react-native-screens": "~4.16.0",
"react-native-svg": "15.12.0",
"react-native-webview": "13.15.0",
"zustand": "^5.0.2"
"zustand": "^5.0.2",
"expo-web-browser": "~14.1.6"
},
"devDependencies": {
"@babel/core": "^7.25.2",