import React, { useEffect, useMemo, useState } from 'react'; import { Alert, FlatList, Image, Pressable, StyleSheet, Text, View } from 'react-native'; import { useTranslation } from 'react-i18next'; import { LinearGradient } from 'expo-linear-gradient'; import { Switch } from 'react-native'; import Animated, { Easing, FadeIn, FadeOut, SlideInLeft, SlideInRight, SlideOutLeft, SlideOutRight, } from 'react-native-reanimated'; import SheetModal from '@/components/ui/SheetModal'; import { MOCK_CONTENT } from '@/src/constants/mockContent'; import { getDailyReminderSettings, getFavorites, setDailyReminderSettings, type DailyReminderSettings, } from '@/src/storage/appStorage'; import AvatarIcon from '@/assets/images/home/Profile/Default_avatar.png'; import MyLikeIcon from '@/assets/images/home/Profile/mylike.svg'; import SmallComponentIcon from '@/assets/images/home/Profile/small_component.svg'; import RemindIcon from '@/assets/images/home/Profile/remind.svg'; import PrivacyIcon from '@/assets/images/home/Profile/privacy Policy.svg'; import TermsIcon from '@/assets/images/home/Profile/terms_of_use.svg'; import LanguageIcon from '@/assets/images/home/Profile/language.svg'; type Props = { visible: boolean; name?: string; onClose: () => void; }; type Page = 'root' | 'favorites' | 'dailyReminder' | 'widget'; type NavDirection = 'forward' | 'back'; export default function ProfileModal({ visible, name, onClose }: Props) { const { t } = useTranslation(); const [page, setPage] = useState('root'); const [navDirection, setNavDirection] = useState('forward'); const isRoot = page === 'root'; // 关闭弹窗时重置为首页,避免下次打开停留在二级页 useEffect(() => { if (!visible) { setNavDirection('back'); setPage('root'); } }, [visible]); function go(next: Page, direction: NavDirection) { setNavDirection(direction); setPage(next); } function handleClose() { setNavDirection('back'); setPage('root'); onClose(); } function handleSheetClose() { // 需求:二级页点击 X 等同于点击“返回” if (!isRoot) { go('root', 'back'); return; } handleClose(); } const title = useMemo(() => { if (page === 'favorites') return t('profile.favorites'); if (page === 'dailyReminder') return t('dailyReminder.title'); if (page === 'widget') return t('profile.widget'); return t('profile.title'); }, [page, t]); const transition = useMemo(() => { const duration = 220; const easing = Easing.out(Easing.cubic); // 进入二级页:从右侧滑入;返回:从左侧滑入 const entering = navDirection === 'forward' ? SlideInRight.duration(duration).easing(easing) : SlideInLeft.duration(duration).easing(easing); // 离开:进入二级页时旧页面向左滑出;返回时旧页面向右滑出 const exiting = navDirection === 'forward' ? SlideOutLeft.duration(duration).easing(easing) : SlideOutRight.duration(duration).easing(easing); return { entering, exiting, fadeIn: FadeIn.duration(duration).easing(easing), fadeOut: FadeOut.duration(duration).easing(easing), }; }, [navDirection]); return ( {!isRoot && ( go('root', 'back')} hitSlop={8} accessibilityRole="button" accessibilityLabel={t('common.back')} style={styles.backRow} > ‹ {t('common.back')} )} {page === 'root' ? ( go('favorites', 'forward')} onOpenWidget={() => go('widget', 'forward')} onOpenDailyReminder={() => go('dailyReminder', 'forward')} /> ) : page === 'favorites' ? ( ) : page === 'dailyReminder' ? ( go('root', 'back')} /> ) : ( )} ); } function toastTodo(t: (key: string) => string) { Alert.alert(t('profile.todoTitle'), t('profile.todoDesc')); } function RootPage({ name, onOpenFavorites, onOpenWidget, onOpenDailyReminder, }: { name?: string; onOpenFavorites: () => void; onOpenWidget: () => void; onOpenDailyReminder: () => void; }) { const { t } = useTranslation(); return ( <> {name || 'Hali'} } title={t('profile.dailyReminder')} onPress={onOpenDailyReminder} /> } title={t('profile.privacy')} onPress={() => toastTodo(t)} /> } title={t('profile.terms')} onPress={() => toastTodo(t)} /> } title={t('profile.language')} onPress={() => toastTodo(t)} /> ); } function FavoritesPage({ visible }: { visible: boolean }) { const { t } = useTranslation(); const [ids, setIds] = useState([]); // 每次进入该页刷新一次,确保展示最新“喜欢” useEffect(() => { if (!visible) return; let cancelled = false; (async () => { const list = await getFavorites(); if (!cancelled) setIds(list); })(); return () => { cancelled = true; }; }, [visible]); const items = useMemo(() => { const map = new Map(MOCK_CONTENT.map((c) => [c.id, c])); return ids.map((id) => map.get(id)).filter(Boolean) as { id: string; text: string }[]; }, [ids]); return ( {items.length === 0 ? ( {t('favorites.empty')} ) : ( it.id} contentContainerStyle={styles.favList} renderItem={({ item }) => ( {item.text} )} /> )} ); } function DailyReminderPage({ visible, onDone }: { visible: boolean; onDone: () => void }) { const { t } = useTranslation(); const [loading, setLoading] = useState(false); const [timesPerDay, setTimesPerDay] = useState(3); const [pushEnabled, setPushEnabled] = useState(false); useEffect(() => { let cancelled = false; if (!visible) return; setLoading(true); (async () => { const s = await getDailyReminderSettings(); if (cancelled) return; setTimesPerDay(s.timesPerDay); setPushEnabled(s.pushEnabled); setLoading(false); })(); return () => { cancelled = true; }; }, [visible]); function clamp(next: number) { return Math.min(10, Math.max(1, next)); } async function onOk() { if (loading) return; setLoading(true); const next: DailyReminderSettings = { timesPerDay, pushEnabled }; await setDailyReminderSettings(next); setLoading(false); onDone(); } return ( <> setTimesPerDay((v) => clamp(v - 1))} hitSlop={10} style={styles.circleBtn} accessibilityRole="button" accessibilityLabel={t('dailyReminder.minus')} > {timesPerDay} {t('dailyReminder.timesUnit')} setTimesPerDay((v) => clamp(v + 1))} hitSlop={10} style={styles.circleBtn} accessibilityRole="button" accessibilityLabel={t('dailyReminder.plus')} > {t('dailyReminder.pushLabel')} {t('dailyReminder.ok')} ); } function WidgetPage() { const { t } = useTranslation(); return ( {t('widget.previewDate')} 13:45 {t('widget.lockScreen')} {t('widget.previewQuote')} {t('widget.homeScreen')} {t('settings.widgetDesc')} ); } function QuickCard({ title, onPress, children, }: { title: string; onPress: () => void; children: React.ReactNode; }) { return ( {title} {children} ); } function ListItem({ icon, title, onPress, }: { icon: React.ReactNode; title: string; onPress: () => void; }) { return ( {icon} {title} ); } const styles = StyleSheet.create({ pageWrap: { // 给页面切换动画一个稳定的容器,避免布局抖动 }, backRow: { alignSelf: 'flex-start', paddingVertical: 4, paddingHorizontal: 2, marginBottom: 6, }, backText: { color: 'rgba(94,42,40,0.75)', fontSize: 13, fontWeight: '700', }, header: { alignItems: 'center', paddingTop: 6, paddingBottom: 14, }, // 头像是默认图片:234x183,没有圆角/边框/背景颜色 avatarImage: { width: 234, height: 183, marginBottom: 10, }, name: { color: '#5E2A28', fontSize: 18, fontWeight: '700', }, quickRow: { flexDirection: 'row', gap: 12, paddingBottom: 14, }, quickCard: { flex: 1, borderRadius: 16, padding: 14, backgroundColor: 'rgba(244,214,194,0.65)', }, quickTitle: { color: '#5E2A28', fontSize: 14, fontWeight: '700', marginBottom: 10, }, quickIconWrap: { alignItems: 'center', justifyContent: 'center', flex: 1 }, list: { borderRadius: 16, backgroundColor: 'rgba(255,255,255,0.75)', overflow: 'hidden', marginBottom: 8, }, item: { height: 52, paddingHorizontal: 14, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: 'rgba(94,42,40,0.10)', }, itemLeft: { flexDirection: 'row', alignItems: 'center', gap: 10, }, itemIcon: { width: 28, height: 28, borderRadius: 10, backgroundColor: 'rgba(244,214,194,0.55)', alignItems: 'center', justifyContent: 'center', }, itemText: { color: '#5E2A28', fontSize: 15, fontWeight: '600', }, chevron: { color: 'rgba(94,42,40,0.45)', fontSize: 20, marginTop: -2, }, favContainer: { borderRadius: 16, backgroundColor: 'rgba(255,255,255,0.75)', overflow: 'hidden', marginBottom: 8, }, favEmpty: { color: 'rgba(94,42,40,0.55)', fontSize: 15, textAlign: 'center', paddingVertical: 26, paddingHorizontal: 14, }, favList: { paddingBottom: 10, }, favRow: { paddingHorizontal: 14, paddingVertical: 14, borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: 'rgba(94,42,40,0.10)', backgroundColor: 'rgba(255,255,255,0.15)', }, favText: { color: '#5E2A28', fontSize: 15, lineHeight: 22, fontWeight: '600', }, counter: { flexDirection: 'row', alignItems: 'center', justifyContent: 'center', gap: 22, paddingTop: 8, paddingBottom: 22, }, circleBtn: { width: 42, height: 42, borderRadius: 21, backgroundColor: 'rgba(244,214,194,0.75)', alignItems: 'center', justifyContent: 'center', }, circleText: { color: '#5E2A28', fontSize: 20, fontWeight: '700', marginTop: -1, }, countCenter: { flexDirection: 'row', alignItems: 'flex-end', gap: 6, }, countNumber: { color: '#111', fontSize: 54, fontWeight: '800', letterSpacing: -1, }, countUnit: { color: '#111', fontSize: 16, fontWeight: '700', marginBottom: 10, }, remindRow: { height: 54, borderRadius: 16, backgroundColor: 'rgba(255,255,255,0.75)', paddingHorizontal: 14, flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginBottom: 18, }, rowLeft: { flexDirection: 'row', alignItems: 'center', gap: 10 }, rowIcon: { width: 28, height: 28, borderRadius: 10, backgroundColor: 'rgba(244,214,194,0.55)', alignItems: 'center', justifyContent: 'center', }, rowText: { color: '#5E2A28', fontSize: 15, fontWeight: '600', }, okPressable: { marginBottom: 6, }, okBtn: { height: 52, borderRadius: 26, alignItems: 'center', justifyContent: 'center', }, okBtnDisabled: { opacity: 0.7 }, okText: { color: '#fff', fontSize: 16, fontWeight: '700', }, widgetContent: { paddingBottom: 18, gap: 14, }, widgetRow: { flexDirection: 'row', gap: 12, }, widgetCard: { flex: 1, borderRadius: 16, overflow: 'hidden', backgroundColor: 'rgba(255,255,255,0.75)', borderWidth: StyleSheet.hairlineWidth, borderColor: 'rgba(94,42,40,0.12)', }, widgetPreviewInner: { height: 120, padding: 12, justifyContent: 'center', }, widgetPreviewLock: { backgroundColor: 'rgba(244,214,194,0.65)', }, widgetPreviewHome: { backgroundColor: 'rgba(243,208,225,0.55)', }, widgetPreviewDate: { color: 'rgba(94,42,40,0.70)', fontSize: 12, fontWeight: '600', marginBottom: 6, }, widgetPreviewTime: { color: '#ffffff', fontSize: 44, fontWeight: '800', letterSpacing: 1, }, widgetPreviewQuote: { color: '#5E2A28', fontSize: 14, lineHeight: 20, fontWeight: '700', }, widgetCardLabel: { paddingVertical: 10, textAlign: 'center', color: '#5E2A28', fontSize: 13, fontWeight: '700', }, widgetDesc: { color: 'rgba(94,42,40,0.75)', fontSize: 13, lineHeight: 18, }, });