Back-Button: - OnboardingNavContext liefert der Shell einen optionalen goBack-Handler (kein prop-drilling durch 8 Slides). - OnboardingShell: chevron-back links neben der Progress-Bar wenn goBack gesetzt ist. - Controller: goToLinearPrevious() + BACK_ALLOWED-Liste. Back nur auf privacy/nickname/diga_choice/plan/payment — NICHT welcome (erste), done (final), diga_code (eigener onBack), protection (Backend-Step + Permission-Flow). Language-Switcher: - WelcomeSlide: 4 Sprach-Pills (DE/EN/FR/AR) oben rechts. User kommt während Onboarding nicht zu Settings — sonst kein Weg die Sprache zu wechseln. setLanguage persistiert + flippt RTL für AR.
143 lines
3.9 KiB
TypeScript
143 lines
3.9 KiB
TypeScript
import { Text, TouchableOpacity, View } from 'react-native';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { Ionicons } from '@expo/vector-icons';
|
|
import { useColors } from '../../../lib/theme';
|
|
import { useLanguageStore, type AppLanguage } from '../../../stores/language';
|
|
import { OnboardingShell } from '../OnboardingShell';
|
|
import { LyraBubble } from '../LyraBubble';
|
|
import { CTABar } from '../CTABar';
|
|
|
|
const LANGS: { code: AppLanguage; label: string }[] = [
|
|
{ code: 'de', label: 'DE' },
|
|
{ code: 'en', label: 'EN' },
|
|
{ code: 'fr', label: 'FR' },
|
|
{ code: 'ar', label: 'AR' },
|
|
];
|
|
|
|
/**
|
|
* Kompakter Sprach-Umschalter — nur auf der Welcome-Slide. Während des
|
|
* Onboardings kommt der User nicht zu Settings, kann die Sprache also sonst
|
|
* nirgends ändern. setLanguage persistiert in AsyncStorage + flippt RTL für AR.
|
|
*/
|
|
function LanguagePills({ colors }: { colors: import('../../../lib/theme').ColorScheme }) {
|
|
const language = useLanguageStore((s) => s.language);
|
|
const setLanguage = useLanguageStore((s) => s.setLanguage);
|
|
return (
|
|
<View style={{ flexDirection: 'row', gap: 8, justifyContent: 'flex-end' }}>
|
|
{LANGS.map((l) => {
|
|
const active = language === l.code;
|
|
return (
|
|
<TouchableOpacity
|
|
key={l.code}
|
|
onPress={() => setLanguage(l.code)}
|
|
activeOpacity={0.7}
|
|
style={{
|
|
paddingHorizontal: 12,
|
|
paddingVertical: 6,
|
|
borderRadius: 8,
|
|
backgroundColor: active ? colors.brandOrange : colors.surfaceElevated,
|
|
}}
|
|
>
|
|
<Text
|
|
style={{
|
|
fontFamily: 'Nunito_700Bold',
|
|
fontSize: 13,
|
|
color: active ? '#ffffff' : colors.textMuted,
|
|
}}
|
|
>
|
|
{l.label}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
);
|
|
})}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Slide 1: Lyra stellt sich vor + erklärt die Mission in einem Satz.
|
|
* Hat den langen Empathie-Touch, weil's der erste Eindruck nach Signup ist.
|
|
*/
|
|
export function WelcomeSlide({
|
|
onNext,
|
|
current,
|
|
total,
|
|
}: {
|
|
onNext: () => void;
|
|
current: number;
|
|
total: number;
|
|
}) {
|
|
const { t } = useTranslation();
|
|
const colors = useColors();
|
|
|
|
return (
|
|
<OnboardingShell
|
|
current={current}
|
|
total={total}
|
|
cta={<CTABar primaryLabel={t('onboarding.welcome.cta_primary')} onPrimary={onNext} />}
|
|
>
|
|
<View style={{ marginBottom: 16 }}>
|
|
<LanguagePills colors={colors} />
|
|
</View>
|
|
|
|
<LyraBubble text={t('onboarding.lyra.welcome.body')} emotion="happy" />
|
|
|
|
<View style={{ marginTop: 28, gap: 12 }}>
|
|
<BulletRow
|
|
icon="eye-off-outline"
|
|
text={t('onboarding.welcome.bullet_anon')}
|
|
colors={colors}
|
|
/>
|
|
<BulletRow
|
|
icon="shield-checkmark-outline"
|
|
text={t('onboarding.welcome.bullet_protect')}
|
|
colors={colors}
|
|
/>
|
|
<BulletRow
|
|
icon="people-outline"
|
|
text={t('onboarding.welcome.bullet_community')}
|
|
colors={colors}
|
|
/>
|
|
</View>
|
|
</OnboardingShell>
|
|
);
|
|
}
|
|
|
|
function BulletRow({
|
|
icon,
|
|
text,
|
|
colors,
|
|
}: {
|
|
icon: keyof typeof Ionicons.glyphMap;
|
|
text: string;
|
|
colors: import('../../../lib/theme').ColorScheme;
|
|
}) {
|
|
return (
|
|
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
|
|
<View
|
|
style={{
|
|
width: 36,
|
|
height: 36,
|
|
borderRadius: 12,
|
|
backgroundColor: colors.surfaceElevated,
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
}}
|
|
>
|
|
<Ionicons name={icon} size={20} color={colors.brandOrange} />
|
|
</View>
|
|
<Text
|
|
style={{
|
|
flex: 1,
|
|
fontFamily: 'Nunito_600SemiBold',
|
|
fontSize: 15,
|
|
lineHeight: 21,
|
|
color: colors.text,
|
|
}}
|
|
>
|
|
{text}
|
|
</Text>
|
|
</View>
|
|
);
|
|
}
|