import { View, Text, ScrollView, TouchableOpacity, ActivityIndicator, StyleSheet, Platform, } from 'react-native' import { SafeAreaView } from 'react-native-safe-area-context' import { useLocalSearchParams, useRouter } from 'expo-router' import { useEffect } from 'react' import { Ionicons } from '@expo/vector-icons' import { useNewsDetail } from '@/hooks/useNews' import { useNewsReadStore } from '@/store/news.store' import { AttachmentRow } from '@/components/news/AttachmentRow' import { Badge } from '@/components/ui/Badge' import { NEWS_KATEGORIE_LABELS } from '@innungsapp/shared/types' import { format } from 'date-fns' import { de } from 'date-fns/locale' // --------------------------------------------------------------------------- // Lightweight markdown renderer (headings, bold, bullets, paragraphs) // --------------------------------------------------------------------------- function MarkdownBody({ source }: { source: string }) { const blocks = source.split(/\n\n+/) return ( {blocks.map((block, i) => { const trimmed = block.trim() if (!trimmed) return null // H2 ## if (trimmed.startsWith('## ')) { return ( {trimmed.slice(3)} ) } // H3 ### if (trimmed.startsWith('### ')) { return ( {trimmed.slice(4)} ) } // H1 # if (trimmed.startsWith('# ')) { return ( {trimmed.slice(2)} ) } // Bullet list if (trimmed.startsWith('- ')) { const items = trimmed.split('\n').filter(Boolean) return ( {items.map((line, j) => { const text = line.replace(/^-\s+/, '') return ( {renderInline(text)} ) })} ) } // Paragraph (with inline bold) return ( {renderInline(trimmed)} ) })} ) } /** Render **bold** inline within a Text node */ function renderInline(text: string): React.ReactNode[] { const parts = text.split(/(\*\*.*?\*\*)/) return parts.map((part, i) => { if (part.startsWith('**') && part.endsWith('**')) { return ( {part.slice(2, -2)} ) } return {part} }) } // --------------------------------------------------------------------------- // Screen // --------------------------------------------------------------------------- export default function NewsDetailScreen() { const { id } = useLocalSearchParams<{ id: string }>() const router = useRouter() const { data: news, isLoading, onOpen } = useNewsDetail(id) const markRead = useNewsReadStore((s) => s.markRead) useEffect(() => { if (news) { markRead(news.id) // sofort lokal markieren → Badge weg beim Zurückgehen onOpen() } }, [news?.id]) if (isLoading) { return ( ) } if (!news) return null const initials = (news.author?.name ?? 'I') .split(' ') .map((n) => n.charAt(0)) .slice(0, 2) .join('') return ( {/* Nav bar */} router.navigate('/(app)/news')} style={styles.backBtn} activeOpacity={0.7} > Neuigkeiten {/* ── Hero header ────────────────────────────────────────── */} {news.title} {/* Author + date row */} {initials} {news.author?.name ?? 'Innung'} {news.publishedAt && ( {format(new Date(news.publishedAt), 'dd. MMMM yyyy', { locale: de })} )} {/* ── Separator ──────────────────────────────────────────── */} {/* ── Article body ───────────────────────────────────────── */} {/* ── Attachments ────────────────────────────────────────── */} {news.attachments.length > 0 && ( ANHÄNGE ({news.attachments.length}) {news.attachments.map((a, idx) => ( {idx > 0 && } ))} )} {/* Bottom spacer */} ) } // --------------------------------------------------------------------------- // Styles // --------------------------------------------------------------------------- const styles = StyleSheet.create({ safeArea: { flex: 1, backgroundColor: '#FAFAFA', }, loadingContainer: { flex: 1, backgroundColor: '#FAFAFA', alignItems: 'center', justifyContent: 'center', }, // Nav navBar: { backgroundColor: '#FAFAFA', paddingHorizontal: 16, paddingVertical: 10, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: '#E2E8F0', }, backBtn: { flexDirection: 'row', alignItems: 'center', gap: 2, alignSelf: 'flex-start', }, backText: { fontSize: 15, fontWeight: '600', color: '#003B7E', }, // Hero hero: { backgroundColor: '#FFFFFF', paddingHorizontal: 20, paddingTop: 22, paddingBottom: 20, gap: 12, }, heroTitle: { fontSize: 24, fontWeight: '800', color: '#0F172A', letterSpacing: -0.5, lineHeight: 32, marginTop: 4, }, metaRow: { flexDirection: 'row', alignItems: 'center', gap: 10, marginTop: 4, }, avatarCircle: { width: 38, height: 38, borderRadius: 19, backgroundColor: '#003B7E', alignItems: 'center', justifyContent: 'center', }, avatarText: { color: '#FFFFFF', fontSize: 13, fontWeight: '700', letterSpacing: 0.5, }, authorName: { fontSize: 14, fontWeight: '600', color: '#0F172A', lineHeight: 18, }, dateText: { fontSize: 12, color: '#94A3B8', marginTop: 1, }, heroSeparator: { height: 4, backgroundColor: '#F1F5F9', }, // Scroll scrollContent: { flexGrow: 1, }, // Attachments attachmentsSection: { marginHorizontal: 20, marginTop: 28, }, attachmentsHeader: { flexDirection: 'row', alignItems: 'center', gap: 6, marginBottom: 10, }, attachmentsLabel: { fontSize: 11, fontWeight: '700', color: '#64748B', letterSpacing: 0.8, }, attachmentsCard: { backgroundColor: '#FFFFFF', borderRadius: 16, paddingHorizontal: 16, overflow: 'hidden', ...Platform.select({ ios: { shadowColor: '#1C1917', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.06, shadowRadius: 10, }, android: { elevation: 2 }, }), }, attachmentsDivider: { height: StyleSheet.hairlineWidth, backgroundColor: '#F1F5F9', }, }) // Markdown styles const md = StyleSheet.create({ container: { backgroundColor: '#FFFFFF', paddingHorizontal: 20, paddingTop: 22, paddingBottom: 8, gap: 14, }, h1: { fontSize: 22, fontWeight: '800', color: '#0F172A', letterSpacing: -0.3, lineHeight: 30, }, h2: { fontSize: 18, fontWeight: '700', color: '#0F172A', letterSpacing: -0.2, lineHeight: 26, marginTop: 8, paddingBottom: 6, borderBottomWidth: 2, borderBottomColor: '#EFF6FF', }, h3: { fontSize: 16, fontWeight: '700', color: '#1E293B', lineHeight: 24, marginTop: 4, }, paragraph: { fontSize: 16, color: '#334155', lineHeight: 28, letterSpacing: 0.1, }, list: { gap: 8, }, listItem: { flexDirection: 'row', alignItems: 'flex-start', gap: 10, }, bullet: { width: 6, height: 6, borderRadius: 3, backgroundColor: '#003B7E', marginTop: 10, flexShrink: 0, }, listText: { flex: 1, fontSize: 16, color: '#334155', lineHeight: 28, }, })