Files
mindfulness/client/app/(onboarding)/onboarding.tsx
2026-01-28 22:54:21 +08:00

105 lines
3.2 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 { useMemo, useState } from 'react';
import { Pressable, StyleSheet, Text, View } from 'react-native';
import { useRouter } from 'expo-router';
import { useTranslation } from 'react-i18next';
import { setOnboardingCompleted } from '@/src/storage/appStorage';
type OnboardingPage = {
title: string;
desc: string;
};
export default function OnboardingScreen() {
const router = useRouter();
const { t } = useTranslation();
// 35 页:这里默认 4 页,后续可按产品调整为 3 或 5
const pages = useMemo<OnboardingPage[]>(
() => [
{ title: t('onboarding.q1Title'), desc: t('onboarding.q1Desc') },
{ title: t('onboarding.q2Title'), desc: t('onboarding.q2Desc') },
{ title: t('onboarding.q3Title'), desc: t('onboarding.q3Desc') },
{ title: t('onboarding.q4Title'), desc: t('onboarding.q4Desc') }
],
[t]
);
const total = pages.length;
const [step, setStep] = useState(0);
const page = pages[Math.min(step, total - 1)];
async function finishAndNext() {
await setOnboardingCompleted(true);
router.replace('/(onboarding)/push-prompt');
}
async function onNext() {
if (step >= total - 1) {
await finishAndNext();
return;
}
setStep((s) => s + 1);
}
async function onSkipPage() {
// 每页可跳过:直接进入下一页(最后一页则结束)
await onNext();
}
async function onSkipAll() {
// 一键跳过整个 Onboarding
await finishAndNext();
}
return (
<View style={styles.container}>
<Text style={styles.progress}>
{t('onboarding.progress', { current: step + 1, total })}
</Text>
<View style={styles.card}>
<Text style={styles.title}>{page.title}</Text>
<Text style={styles.desc}>{page.desc}</Text>
</View>
<View style={styles.actions}>
<Pressable style={[styles.btn, styles.secondary]} onPress={onSkipPage}>
<Text style={[styles.btnText, styles.secondaryText]}>{t('onboarding.skip')}</Text>
</Pressable>
<Pressable style={[styles.btn, styles.primary]} onPress={onNext}>
<Text style={styles.btnText}>{t('onboarding.next')}</Text>
</Pressable>
</View>
<Pressable style={styles.skipAll} onPress={onSkipAll}>
<Text style={styles.skipAllText}>{t('onboarding.skipAll')}</Text>
</Pressable>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 20, justifyContent: 'center', gap: 16 },
progress: { textAlign: 'center', color: '#6B7280' },
card: {
borderRadius: 18,
padding: 20,
backgroundColor: '#FFFFFF',
borderWidth: StyleSheet.hairlineWidth,
borderColor: '#E5E7EB',
gap: 10
},
title: { fontSize: 22, fontWeight: '700', color: '#111827' },
desc: { fontSize: 16, lineHeight: 22, color: '#374151' },
actions: { flexDirection: 'row', gap: 12 },
btn: { flex: 1, paddingVertical: 14, borderRadius: 14, alignItems: 'center' },
primary: { backgroundColor: '#111827' },
secondary: { backgroundColor: '#F3F4F6' },
btnText: { fontSize: 16, fontWeight: '600', color: '#FFFFFF' },
secondaryText: { color: '#111827' },
skipAll: { alignItems: 'center', paddingTop: 10 },
skipAllText: { color: '#6B7280', textDecorationLine: 'underline' }
});