import React, { useEffect, useRef, useState } from 'react'; import { View, Text, StyleSheet, TouchableOpacity, Platform, Alert, Image, useWindowDimensions } from 'react-native'; import { useRouter } from 'expo-router'; import * as WebBrowser from 'expo-web-browser'; import { Trans, useTranslation } from 'react-i18next'; import { SafeAreaView } from 'react-native-safe-area-context'; import { setConsentAccepted, getConsentAccepted } from '../../src/storage/appStorage'; import { fetchLegalLinks } from '@/src/services/legalApi'; import { getOnboardingCompleted } from '@/src/storage/appStorage'; import { API_BASE_URL } from '@/src/constants/env'; import { isTraditionalChineseLocaleTag } from '@/src/i18n/locale'; // 导入 SVG 组件 import FlowersBg from '../../assets/images/index/flowers_endbg.svg'; import WelcomeBtn from '../../assets/images/index/welcome_btn.svg'; // 繁中開屏 consent 文案:寫死在元件內,避免 Metro/iOS bundle 快取導致永遠顯示舊文案。 // 若需修改,請改這裡並同步 client/src/i18n/locales/zh-TW.json 的 consent 區塊。 const ZH_TW_CONSENT = { title: '我們知道,', subtitle: '當媽媽很不容易。', subtitleSecondary: '這裡給你一些溫柔的肯定與提醒', }; export default function SplashScreen() { const router = useRouter(); const { t, i18n } = useTranslation(); const { width, height } = useWindowDimensions(); const [showConsent, setShowConsent] = useState(false); const isTablet = Platform.OS === 'ios' && Math.min(width, height) >= 768; // 繁中時強制使用上方常數(含 zh-TW / zh-Hant / zh-Hant-TW),其餘用 i18n const isZhTW = isTraditionalChineseLocaleTag(i18n.language || ''); const title = isZhTW ? ZH_TW_CONSENT.title : t('consent.title'); const subtitle = isZhTW ? ZH_TW_CONSENT.subtitle : t('consent.subtitle'); const subtitleSecondary = isZhTW ? ZH_TW_CONSENT.subtitleSecondary : t('consent.subtitleSecondary'); useEffect(() => { if (typeof __DEV__ !== 'undefined' && __DEV__ && showConsent) { console.log('[i18n consent] language=', i18n.language, 'title=', title, 'subtitle=', subtitle); } }, [showConsent, i18n.language, title, subtitle]); const [links, setLinks] = useState<{ privacy?: string; terms?: string }>({}); const [linksLoading, setLinksLoading] = useState(false); const mountedRef = useRef(true); useEffect(() => { checkConsent(); }, []); useEffect(() => { return () => { mountedRef.current = false; }; }, []); const checkConsent = async () => { const accepted = await getConsentAccepted(); setShowConsent(!accepted); if (accepted) { // 已同意协议则直接分发到目标页,避免先回到 /(index)再二次跳转导致“闪一下” const completed = await getOnboardingCompleted(); if (completed) { router.replace('/(app)/home'); } else { router.replace('/(onboarding)/onboarding'); } } }; const handleAgree = async () => { await setConsentAccepted(true); setShowConsent(false); // 跳转到 onboarding 流程 router.replace('/(onboarding)/onboarding'); }; const openLink = async (url: string) => { try { await WebBrowser.openBrowserAsync(url); } catch (error) { Alert.alert(t('common.error'), t('common.openLinkError')); } }; async function refreshLegalLinks(): Promise<{ privacy?: string; terms?: string }> { if (mountedRef.current) setLinksLoading(true); try { const res = await fetchLegalLinks(); const next = { privacy: res.privacyPolicyUrl, terms: res.termsOfUseUrl }; if (mountedRef.current) setLinks(next); return next; } catch (e) { // 不阻塞主流程:失败时不崩溃,链接入口仍可点(会提示) if (__DEV__) console.log('[LegalLinks] 拉取失败(splash):', e); if (mountedRef.current) setLinks({}); return {}; } finally { if (mountedRef.current) setLinksLoading(false); } } async function handleOpenLegal(type: 'privacy' | 'terms') { const currentUrl = type === 'privacy' ? links.privacy : links.terms; if (currentUrl) { await openLink(currentUrl); return; } // 链接还没拿到/拉取失败:点击时主动再拉一次,避免“点了没反应” const next = await refreshLegalLinks(); const nextUrl = type === 'privacy' ? next.privacy : next.terms; if (nextUrl) { await openLink(nextUrl); return; } const msg = typeof __DEV__ !== 'undefined' && __DEV__ ? t('consent.linkUnavailableDev', { baseUrl: API_BASE_URL }) : t('consent.linkUnavailable'); Alert.alert(t('common.notice'), msg); } // 拉取协议链接(由后端按语言下发;默认 EN) useEffect(() => { void refreshLegalLinks(); }, []); const bgDecorationHeight = isTablet ? Math.round(height * 0.55) : height * 0.6; const bgDecorationTop = height - bgDecorationHeight; const contentTop = bgDecorationTop + (bgDecorationHeight * 0.25); const contentWidth = isTablet ? Math.min(640, Math.floor(width * 0.76)) : width; const topImageWidth = isTablet ? Math.min(430, Math.floor(width * 0.48)) : 308; const topImageHeight = isTablet ? Math.min(460, Math.floor(height * 0.45)) : 354; const topImageMarginTop = isTablet ? 148 : 60; const bgWidth = width + 10; const bgLeft = -3; const buttonSize = isTablet ? { width: 108, height: 70 } : { width: 87, height: 57 }; const bottomOffset = isTablet ? 28 : 60; const buttonBottomGap = isTablet ? 28 : 40; const noticeStyle = isTablet ? { fontSize: 14, lineHeight: 20, paddingHorizontal: 32, maxWidth: contentWidth } : null; const noticeLinkStyle = isTablet ? { fontSize: 14 } : null; const titleStyle = isTablet ? { fontSize: 50, lineHeight: 60 } : null; const subtitleStyle = isTablet ? { marginTop: 10, fontSize: 18, lineHeight: 24 } : null; return ( {/* 中间的背景装饰 SVG (现在放在上面,作为上层) */} {/* 顶部的花图片 (现在放在下面,作为下层) */} {/* 文案内容:主標題兩行 + 可選二級標題(字號更小、顏色更淺);繁中為元件內常數,其餘用 i18n */} {title} {'\n'} {subtitle} {subtitleSecondary ? ( {subtitleSecondary} ) : null} {showConsent && ( <> void handleOpenLegal('privacy')} suppressHighlighting /> ), terms: ( void handleOpenLegal('terms')} suppressHighlighting /> ), }} /> )} ); } const styles = StyleSheet.create({ container: { flex: 1, width: '100%', height: '100%', alignSelf: 'stretch', backgroundColor: '#F5D3B5', // 匹配 Figma 背景色 alignItems: 'center', }, topImageContainer: { zIndex: 1, // 降低层级 }, topImage: { width: 308, height: 354, }, bgDecorationContainer: { position: 'absolute', zIndex: 2, // 提高层级,使其覆盖在图片之上 }, contentContainer: { alignItems: 'center', zIndex: 3, }, titleText: { fontSize: 30, lineHeight: 42, color: '#772F00', // 匹配 Figma 文字颜色 textAlign: 'center', fontWeight: '600', fontFamily: Platform.OS === 'ios' ? 'STIX Two Text' : 'serif', }, consentSubtitleSecondary: { marginTop: 12, fontSize: 16, lineHeight: 22, color: 'rgba(119, 47, 0, 0.6)', textAlign: 'center', fontFamily: Platform.OS === 'ios' ? 'STIX Two Text' : 'serif', }, bottomContainer: { position: 'absolute', alignItems: 'center', zIndex: 4, }, buttonWrapper: { marginBottom: 40, }, noticeText: { marginTop: 10, paddingHorizontal: 28, fontSize: 12, lineHeight: 16, textAlign: 'center', color: 'rgba(119, 47, 0, 0.45)', }, noticeLinkText: { fontSize: 12, // 颜色区分:协议链接更醒目 color: 'rgba(119, 47, 0, 0.75)', textDecorationLine: 'underline', fontWeight: '600', }, noticeLinkTextDisabled: { opacity: 0.55, }, });