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:
@@ -5,7 +5,7 @@ import {
|
|||||||
Alert, StyleSheet,
|
Alert, StyleSheet,
|
||||||
} from 'react-native'
|
} from 'react-native'
|
||||||
import { useAuthStore } from '@/lib/store'
|
import { useAuthStore } from '@/lib/store'
|
||||||
import { C } from '../_layout'
|
import { C } from '@/lib/theme'
|
||||||
|
|
||||||
export default function LoginScreen() {
|
export default function LoginScreen() {
|
||||||
const [email, setEmail] = useState('')
|
const [email, setEmail] = useState('')
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Tabs } from 'expo-router'
|
import { Tabs } from 'expo-router'
|
||||||
import { BookOpen, Search, Home, User } from 'lucide-react-native'
|
import { BookOpen, Search, Home, User } from 'lucide-react-native'
|
||||||
import { C } from '../_layout'
|
import { C } from '@/lib/theme'
|
||||||
|
|
||||||
export default function TabsLayout() {
|
export default function TabsLayout() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { CalendarDays, Search, BookOpen, Clock, ChevronRight } from 'lucide-reac
|
|||||||
import { apiFetch } from '@/lib/api'
|
import { apiFetch } from '@/lib/api'
|
||||||
import { ENDPOINTS } from '@/lib/config'
|
import { ENDPOINTS } from '@/lib/config'
|
||||||
import { useAuthStore } from '@/lib/store'
|
import { useAuthStore } from '@/lib/store'
|
||||||
import { C } from '../_layout'
|
import { C } from '@/lib/theme'
|
||||||
|
|
||||||
interface Note {
|
interface Note {
|
||||||
id: string
|
id: string
|
||||||
@@ -45,7 +45,7 @@ export default function HomeScreen() {
|
|||||||
const res = await apiFetch(ENDPOINTS.dailyNote)
|
const res = await apiFetch(ENDPOINTS.dailyNote)
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
const data = await res.json()
|
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) => (
|
: recentNotes.map((note, i) => (
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
key={note.id}
|
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 }]}
|
style={[s.noteRow, i === recentNotes.length - 1 && { borderBottomWidth: 0 }]}
|
||||||
activeOpacity={0.6}
|
activeOpacity={0.6}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { useRouter } from 'expo-router'
|
|||||||
import { ChevronRight, BookOpen, Folder } from 'lucide-react-native'
|
import { ChevronRight, BookOpen, Folder } from 'lucide-react-native'
|
||||||
import { apiFetch } from '@/lib/api'
|
import { apiFetch } from '@/lib/api'
|
||||||
import { ENDPOINTS } from '@/lib/config'
|
import { ENDPOINTS } from '@/lib/config'
|
||||||
import { C } from '../_layout'
|
import { C } from '@/lib/theme'
|
||||||
|
|
||||||
interface Notebook {
|
interface Notebook {
|
||||||
id: string
|
id: string
|
||||||
@@ -72,7 +72,7 @@ export default function NotebooksScreen() {
|
|||||||
contentContainerStyle={s.list}
|
contentContainerStyle={s.list}
|
||||||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={() => { setRefreshing(true); load() }} tintColor={C.brand} />}
|
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={() => { setRefreshing(true); load() }} tintColor={C.brand} />}
|
||||||
renderItem={({ item }) => (
|
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' }]}>
|
<View style={[s.iconWrap, { backgroundColor: (item.color || C.brand) + '18' }]}>
|
||||||
<NotebookIcon icon={item.icon} color={item.color} />
|
<NotebookIcon icon={item.icon} color={item.color} />
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { View, Text, TouchableOpacity, ScrollView, Alert, StyleSheet } from 'rea
|
|||||||
import { SafeAreaView } from 'react-native-safe-area-context'
|
import { SafeAreaView } from 'react-native-safe-area-context'
|
||||||
import { LogOut, CreditCard, Globe } from 'lucide-react-native'
|
import { LogOut, CreditCard, Globe } from 'lucide-react-native'
|
||||||
import { useAuthStore } from '@/lib/store'
|
import { useAuthStore } from '@/lib/store'
|
||||||
import { C } from '../_layout'
|
import { C } from '@/lib/theme'
|
||||||
|
|
||||||
const TIER_LABELS: Record<string, string> = {
|
const TIER_LABELS: Record<string, string> = {
|
||||||
FREE: 'Gratuit',
|
FREE: 'Gratuit',
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import { Search as SearchIcon, X } from 'lucide-react-native'
|
|||||||
import { useRouter } from 'expo-router'
|
import { useRouter } from 'expo-router'
|
||||||
import { apiFetch } from '@/lib/api'
|
import { apiFetch } from '@/lib/api'
|
||||||
import { ENDPOINTS } from '@/lib/config'
|
import { ENDPOINTS } from '@/lib/config'
|
||||||
import { C } from '../_layout'
|
import { C } from '@/lib/theme'
|
||||||
|
|
||||||
interface SearchResult {
|
interface SearchResult {
|
||||||
id: string
|
id: string
|
||||||
@@ -67,7 +67,7 @@ export default function SearchScreen() {
|
|||||||
keyExtractor={(item) => item.id}
|
keyExtractor={(item) => item.id}
|
||||||
contentContainerStyle={s.list}
|
contentContainerStyle={s.list}
|
||||||
renderItem={({ item }) => (
|
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.cardTitle} numberOfLines={1}>{item.title || 'Sans titre'}</Text>
|
||||||
{item.snippet && <Text style={s.snippet} numberOfLines={2}>{item.snippet}</Text>}
|
{item.snippet && <Text style={s.snippet} numberOfLines={2}>{item.snippet}</Text>}
|
||||||
{item.notebookName && <Text style={s.nb}>{item.notebookName}</Text>}
|
{item.notebookName && <Text style={s.nb}>{item.notebookName}</Text>}
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import { SafeAreaProvider } from 'react-native-safe-area-context'
|
|||||||
import { StatusBar } from 'expo-status-bar'
|
import { StatusBar } from 'expo-status-bar'
|
||||||
import { View, ActivityIndicator, StyleSheet } from 'react-native'
|
import { View, ActivityIndicator, StyleSheet } from 'react-native'
|
||||||
import { useAuthStore } from '@/lib/store'
|
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() {
|
export default function RootLayout() {
|
||||||
const { user, loading, restore } = useAuthStore()
|
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({
|
const s = StyleSheet.create({
|
||||||
loader: { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: C.paper },
|
loader: { flex: 1, alignItems: 'center', justifyContent: 'center', backgroundColor: C.paper },
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { ArrowLeft, Share2 } from 'lucide-react-native'
|
|||||||
import { WebView } from 'react-native-webview'
|
import { WebView } from 'react-native-webview'
|
||||||
import { apiFetch } from '@/lib/api'
|
import { apiFetch } from '@/lib/api'
|
||||||
import { ENDPOINTS } from '@/lib/config'
|
import { ENDPOINTS } from '@/lib/config'
|
||||||
import { C } from '../_layout'
|
import { C } from '@/lib/theme'
|
||||||
|
|
||||||
interface Note {
|
interface Note {
|
||||||
id: string
|
id: string
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { useLocalSearchParams, useRouter } from 'expo-router'
|
|||||||
import { ArrowLeft } from 'lucide-react-native'
|
import { ArrowLeft } from 'lucide-react-native'
|
||||||
import { apiFetch } from '@/lib/api'
|
import { apiFetch } from '@/lib/api'
|
||||||
import { ENDPOINTS } from '@/lib/config'
|
import { ENDPOINTS } from '@/lib/config'
|
||||||
import { C } from '../_layout'
|
import { C } from '@/lib/theme'
|
||||||
|
|
||||||
interface Note {
|
interface Note {
|
||||||
id: string
|
id: string
|
||||||
@@ -56,7 +56,7 @@ export default function NotebookScreen() {
|
|||||||
contentContainerStyle={s.list}
|
contentContainerStyle={s.list}
|
||||||
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={() => { setRefreshing(true); load() }} tintColor={C.brand} />}
|
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={() => { setRefreshing(true); load() }} tintColor={C.brand} />}
|
||||||
renderItem={({ item }) => (
|
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.cardTitle} numberOfLines={1}>{item.title || 'Sans titre'}</Text>
|
||||||
<Text style={s.cardDate}>
|
<Text style={s.cardDate}>
|
||||||
{new Date(item.updatedAt).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' })}
|
{new Date(item.updatedAt).toLocaleDateString('fr-FR', { day: 'numeric', month: 'short' })}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ interface AuthState {
|
|||||||
user: User | null
|
user: User | null
|
||||||
loading: boolean
|
loading: boolean
|
||||||
login: (email: string, password: string) => Promise<void>
|
login: (email: string, password: string) => Promise<void>
|
||||||
|
loginWithToken: (token: string, user: User) => Promise<void>
|
||||||
logout: () => Promise<void>
|
logout: () => Promise<void>
|
||||||
restore: () => Promise<void>
|
restore: () => Promise<void>
|
||||||
}
|
}
|
||||||
|
|||||||
12
memento-mobile/lib/theme.ts
Normal file
12
memento-mobile/lib/theme.ts
Normal 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',
|
||||||
|
}
|
||||||
@@ -26,7 +26,8 @@
|
|||||||
"react-native-screens": "~4.16.0",
|
"react-native-screens": "~4.16.0",
|
||||||
"react-native-svg": "15.12.0",
|
"react-native-svg": "15.12.0",
|
||||||
"react-native-webview": "13.15.0",
|
"react-native-webview": "13.15.0",
|
||||||
"zustand": "^5.0.2"
|
"zustand": "^5.0.2",
|
||||||
|
"expo-web-browser": "~14.1.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.2",
|
"@babel/core": "^7.25.2",
|
||||||
|
|||||||
Reference in New Issue
Block a user