Files
mindfulness/client/app/(splash)/splash.tsx
2026-02-05 01:14:13 +08:00

199 lines
5.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, TouchableOpacity, Dimensions, Platform, Alert, Image } from 'react-native';
import { useRouter } from 'expo-router';
import * as WebBrowser from 'expo-web-browser';
import { 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';
// 导入 SVG 组件
import FlowersBg from '../../assets/images/index/flowers_endbg.svg';
import WelcomeBtn from '../../assets/images/index/welcome_btn.svg';
const { width, height } = Dimensions.get('window');
export default function SplashScreen() {
const router = useRouter();
const { t } = useTranslation();
const [showConsent, setShowConsent] = useState(false);
const [links, setLinks] = useState<{ privacy?: string; terms?: string }>({});
useEffect(() => {
checkConsent();
}, []);
const checkConsent = async () => {
const accepted = await getConsentAccepted();
setShowConsent(!accepted);
if (accepted) {
router.replace('/');
}
};
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'));
}
};
// 拉取协议链接(由后端按语言下发;默认 EN
useEffect(() => {
let cancelled = false;
(async () => {
try {
const res = await fetchLegalLinks();
if (cancelled) return;
setLinks({ privacy: res.privacyPolicyUrl, terms: res.termsOfUseUrl });
} catch (e) {
// 不阻塞主流程:失败时不崩溃,链接入口可不展示
if (__DEV__) console.log('[LegalLinks] 拉取失败splash:', e);
if (!cancelled) setLinks({});
}
})();
return () => {
cancelled = true;
};
}, []);
const bgDecorationTop = 363;
const bgDecorationHeight = height * 0.6;
const contentTop = bgDecorationTop + (bgDecorationHeight * 0.25);
return (
<View style={styles.container}>
{/* 中间的背景装饰 SVG (现在放在上面,作为上层) */}
<View style={[styles.bgDecorationContainer, { top: bgDecorationTop }]}>
<FlowersBg width={width + 10} height={bgDecorationHeight} />
</View>
{/* 顶部的花图片 (现在放在下面,作为下层) */}
<View style={styles.topImageContainer}>
<Image
source={require('../../assets/images/index/index_flowers.png')}
style={styles.topImage}
resizeMode="contain"
/>
</View>
{/* 文案内容 */}
<View style={[styles.contentContainer, { position: 'absolute', top: contentTop }]}>
<Text style={styles.titleText}>
{t('consent.title')}
{'\n'}
{t('consent.subtitle')}
</Text>
</View>
<SafeAreaView style={styles.bottomContainer} edges={['bottom']}>
{showConsent && (
<>
<TouchableOpacity
onPress={handleAgree}
activeOpacity={0.8}
style={styles.buttonWrapper}
accessibilityRole="button"
accessibilityLabel={t('consent.agree')}
>
<WelcomeBtn width={87} height={57} />
</TouchableOpacity>
<View style={styles.linksContainer}>
<TouchableOpacity
disabled={!links.privacy}
onPress={() => (links.privacy ? openLink(links.privacy) : undefined)}
>
<Text style={styles.linkText}>{t('consent.privacy')}</Text>
</TouchableOpacity>
<View style={styles.divider} />
<TouchableOpacity
disabled={!links.terms}
onPress={() => (links.terms ? openLink(links.terms) : undefined)}
>
<Text style={styles.linkText}>{t('consent.terms')}</Text>
</TouchableOpacity>
</View>
<Text style={styles.noticeText}>{t('consent.notice')}</Text>
</>
)}
</SafeAreaView>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5D3B5', // 匹配 Figma 背景色
alignItems: 'center',
},
topImageContainer: {
marginTop: 60,
zIndex: 1, // 降低层级
},
topImage: {
width: 308,
height: 354,
},
bgDecorationContainer: {
position: 'absolute',
left: -3,
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',
},
bottomContainer: {
position: 'absolute',
bottom: 60,
width: '100%',
alignItems: 'center',
zIndex: 4,
},
buttonWrapper: {
marginBottom: 40,
},
linksContainer: {
flexDirection: 'row',
alignItems: 'center',
},
noticeText: {
marginTop: 10,
paddingHorizontal: 28,
fontSize: 12,
lineHeight: 16,
textAlign: 'center',
color: 'rgba(119, 47, 0, 0.45)',
},
linkText: {
fontSize: 12,
color: 'rgba(119, 47, 0, 0.5)', // 使用半透明的文字颜色
textDecorationLine: 'underline',
},
divider: {
width: 1,
height: 12,
backgroundColor: 'rgba(119, 47, 0, 0.2)',
marginHorizontal: 15,
},
});