From 12d1e3dfdd8e0f440958a92ec817c3863d9cdf22 Mon Sep 17 00:00:00 2001 From: Antigravity Date: Fri, 29 May 2026 17:14:48 +0000 Subject: [PATCH] mobile: fix note rendering (HTML direct) + quick actions sans doublons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - note/[id].tsx: contenu TipTap = HTML -> afficher directement dans WebView (plus d'inline MD parser - c'était la cause du contenu vide) + javaScriptEnabled=true explicite (requis Android) + gestion erreur avec message visible + hitSlop sur bouton retour pour meilleur tap area - home.tsx: quick actions uniques (Note du jour / Nouvelle note / Révision) - retiré Carnets et Recherche qui doublaient le tab bar du bas Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- memento-mobile/app/(tabs)/home.tsx | 14 +-- memento-mobile/app/note/[id].tsx | 168 ++++++++++++----------------- 2 files changed, 73 insertions(+), 109 deletions(-) diff --git a/memento-mobile/app/(tabs)/home.tsx b/memento-mobile/app/(tabs)/home.tsx index 01c8a08..ff96da0 100644 --- a/memento-mobile/app/(tabs)/home.tsx +++ b/memento-mobile/app/(tabs)/home.tsx @@ -5,7 +5,7 @@ import { } from 'react-native' import { SafeAreaView } from 'react-native-safe-area-context' import { useRouter } from 'expo-router' -import { CalendarDays, Search, BookOpen, Clock, ChevronRight } from 'lucide-react-native' +import { CalendarDays, PenLine, GraduationCap, Clock, ChevronRight } from 'lucide-react-native' import { apiFetch } from '@/lib/api' import { ENDPOINTS } from '@/lib/config' import { useAuthStore } from '@/lib/store' @@ -71,7 +71,7 @@ export default function HomeScreen() { - {/* Quick actions */} + {/* Quick actions — actions uniques, pas de doublons avec le tab bar */} @@ -79,17 +79,17 @@ export default function HomeScreen() { Note du jour - router.push('/(tabs)/notebooks')} style={s.quickCard} activeOpacity={0.7}> + router.push('/(tabs)/search')} style={s.quickCard} activeOpacity={0.7}> - + - Carnets + Nouvelle note router.push('/(tabs)/search')} style={s.quickCard} activeOpacity={0.7}> - + - Recherche + Révision diff --git a/memento-mobile/app/note/[id].tsx b/memento-mobile/app/note/[id].tsx index 59ad99c..ffb53b8 100644 --- a/memento-mobile/app/note/[id].tsx +++ b/memento-mobile/app/note/[id].tsx @@ -19,16 +19,17 @@ interface Note { notebookName?: string } -// Le markdown est parsé côté WebView (JS natif) — évite tout problème Metro bundler +// Le contenu TipTap est stocké en HTML — on l'enveloppe dans un style CSS propre function buildHtml(content: string, title: string) { - // On passe le contenu comme JSON pour éviter les problèmes d'échappement - const safeContent = JSON.stringify(content) - const safeTitle = JSON.stringify(title || 'Sans titre') + const safeTitle = title.replace(//g, '>') + // Détecter si le contenu est déjà du HTML ou du texte brut + const isHtml = content.trimStart().startsWith('<') + const body = isHtml ? content : `

${content.replace(/\n/g, '
')}

` return ` - + -
-
-
- +
${safeTitle}
+
+
${body}
` } @@ -144,12 +98,18 @@ export default function NoteScreen() { const { id } = useLocalSearchParams<{ id: string }>() const [note, setNote] = useState(null) const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) const router = useRouter() useEffect(() => { - apiFetch(ENDPOINTS.note(id)) - .then((r) => r.json()) + if (!id) return + apiFetch(ENDPOINTS.note(id as string)) + .then((r) => { + if (!r.ok) throw new Error(`Erreur ${r.status}`) + return r.json() + }) .then((data) => setNote(data.note ?? null)) + .catch((e) => setError(e.message)) .finally(() => setLoading(false)) }, [id]) @@ -161,33 +121,37 @@ export default function NoteScreen() { return ( - router.back()} style={s.backBtn}> + router.back()} style={s.backBtn} hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}> {note?.title ?? '…'} - - - + {note && ( + + + + )} - {loading - ? - : note - ? - : Note introuvable.} + {loading && } + {error && {error}} + {!loading && !error && !note && Note introuvable.} + {note && ( + + )} ) } const s = StyleSheet.create({ safe: { flex: 1, backgroundColor: C.paper }, - header: { flexDirection: 'row', alignItems: 'center', gap: 12, paddingHorizontal: 16, paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: C.border }, + header: { flexDirection: 'row', alignItems: 'center', gap: 12, paddingHorizontal: 16, paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: C.border, backgroundColor: C.paper }, backBtn: { padding: 4 }, headerTitle: { flex: 1, fontSize: 15, fontWeight: '600', color: C.ink }, shareBtn: { padding: 4 },