/** * "What's new" popup — pojawia się raz po dociągnięciu OTA (bundle ma nowszy changelog * niż ostatnio widziany). Self-contained: czyta CHANGELOG (bundlowany) + lastSeen ze * SecureStore. Po zamknięciu zapisuje najnowszy id, więc nie pokazuje się ponownie aż * do kolejnej aktualizacji. Montowany raz, nad nawigacją (App.tsx). */ import React from 'react'; import { Modal, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native'; import { CHANGELOG, NEWEST_CHANGELOG_ID, unseenEntries, type ChangelogEntry, } from '../changelog'; import { getLastSeenChangelog, setLastSeenChangelog } from '../storage'; import { theme } from '../theme'; export function WhatsNewModal() { const [entries, setEntries] = React.useState([]); const [visible, setVisible] = React.useState(false); React.useEffect(() => { let cancelled = false; (async () => { try { const lastSeen = await getLastSeenChangelog(); const unseen = unseenEntries(lastSeen); if (!cancelled && unseen.length > 0) { setEntries(unseen); setVisible(true); } } catch { // brak SecureStore / błąd — po prostu nie pokazujemy } })(); return () => { cancelled = true; }; }, []); const dismiss = React.useCallback(() => { setVisible(false); if (NEWEST_CHANGELOG_ID) setLastSeenChangelog(NEWEST_CHANGELOG_ID).catch(() => {}); }, []); if (!visible || entries.length === 0) return null; return ( What's new {entries.map((e) => ( {CHANGELOG.length > 1 ? {e.date} : null} {e.items.map((it, i) => ( {it} ))} ))} Got it ); } const styles = StyleSheet.create({ backdrop: { flex: 1, backgroundColor: 'rgba(0,0,0,0.7)', alignItems: 'center', justifyContent: 'center', padding: 24, }, card: { width: '100%', maxHeight: '80%', backgroundColor: theme.bgElevated, borderRadius: 16, borderColor: theme.border, borderWidth: 1, padding: 20, }, title: { color: theme.fg, fontSize: 20, fontWeight: '800', marginBottom: 14, }, scroll: { flexGrow: 0 }, entry: { marginBottom: 14 }, entryDate: { color: theme.accent, fontSize: 12, fontWeight: '800', textTransform: 'uppercase', letterSpacing: 0.5, marginBottom: 8, }, itemRow: { flexDirection: 'row', gap: 8, marginBottom: 8 }, bullet: { color: theme.accent, fontSize: 15, lineHeight: 21 }, itemText: { color: theme.fg, fontSize: 15, lineHeight: 21, flex: 1 }, btn: { marginTop: 8, backgroundColor: theme.accent, borderRadius: 10, paddingVertical: 12, alignItems: 'center', }, btnText: { color: theme.fg, fontSize: 15, fontWeight: '700' }, });