This commit is contained in:
2026-07-30 10:03:37 +02:00
parent 018b751723
commit c9e776ae98
19 changed files with 1256 additions and 223 deletions

View File

@@ -7,6 +7,7 @@ import {
Image,
KeyboardAvoidingView,
Platform,
ScrollView,
StyleSheet,
Text,
TouchableOpacity,
@@ -312,6 +313,7 @@ export default function OnboardingChatScreen() {
});
const flatListRef = useRef<FlatList>(null);
const inputScrollRef = useRef<ScrollView>(null);
const floatAnim = useRef(new Animated.Value(0)).current;
// Float animation loop for the background monstera pot
@@ -382,6 +384,10 @@ export default function OnboardingChatScreen() {
}, 100);
}, [messages, isTyping]);
useEffect(() => {
inputScrollRef.current?.scrollTo({ y: 0, animated: false });
}, [currentStep]);
const selectOption = (id: string, label: string) => {
// Add user bubble
const userMsg: Message = {
@@ -509,7 +515,7 @@ export default function OnboardingChatScreen() {
if (currentStep === 0) {
return (
<View style={styles.optionsWrap}>
<Text style={styles.stepTitle}>{copy.stepSituation}</Text>
<Text style={styles.stepTitle} maxFontSizeMultiplier={1.3}>{copy.stepSituation}</Text>
<View style={styles.optionsVertical}>
{([
{ id: 'acute', label: copy.situations.acute, icon: 'alert-circle-outline' as const },
@@ -519,7 +525,7 @@ export default function OnboardingChatScreen() {
<TouchableOpacity key={opt.id} style={[styles.optionRow, { borderColor: colors.border }]} onPress={() => selectOption(opt.id, opt.label)}>
<View style={styles.optionRowLeft}>
<Ionicons name={opt.icon} size={18} color={colors.primary} style={{ marginRight: 10 }} />
<Text style={[styles.rowText, { color: colors.text }]}>{opt.label}</Text>
<Text style={[styles.rowText, { color: colors.text }]} maxFontSizeMultiplier={1.3}>{opt.label}</Text>
</View>
<Ionicons name="chevron-forward" size={16} color={colors.textMuted} />
</TouchableOpacity>
@@ -533,7 +539,7 @@ export default function OnboardingChatScreen() {
if (currentStep === 1) {
return (
<View style={styles.optionsWrap}>
<Text style={styles.stepTitle}>{copy.stepSymptom}</Text>
<Text style={styles.stepTitle} maxFontSizeMultiplier={1.3}>{copy.stepSymptom}</Text>
<View style={styles.optionsGrid}>
{([
{ id: 'yellow', label: copy.symptoms.yellow, icon: 'leaf-outline' as const },
@@ -545,7 +551,7 @@ export default function OnboardingChatScreen() {
]).map((opt) => (
<TouchableOpacity key={opt.id} style={[styles.pillBtn, { borderColor: colors.border }]} onPress={() => selectOption(opt.id, opt.label)}>
<Ionicons name={opt.icon} size={15} color={colors.primary} style={{ marginRight: 4 }} />
<Text style={[styles.pillLabel, { color: colors.text }]}>{opt.label}</Text>
<Text style={[styles.pillLabel, { color: colors.text }]} maxFontSizeMultiplier={1.3}>{opt.label}</Text>
</TouchableOpacity>
))}
</View>
@@ -557,7 +563,7 @@ export default function OnboardingChatScreen() {
if (currentStep === 2) {
return (
<View style={styles.optionsWrap}>
<Text style={styles.stepTitle}>{copy.stepLight}</Text>
<Text style={styles.stepTitle} maxFontSizeMultiplier={1.3}>{copy.stepLight}</Text>
<View style={styles.optionsVertical}>
{([
{ id: 'bright_indirect', label: copy.lights.bright_indirect.title, sub: copy.lights.bright_indirect.sub, icon: 'wb-twilight' },
@@ -566,8 +572,8 @@ export default function OnboardingChatScreen() {
]).map((opt) => (
<TouchableOpacity key={opt.id} style={[styles.bentoOption, { borderColor: colors.border }]} onPress={() => selectOption(opt.id, opt.label)}>
<View style={styles.bentoCopy}>
<Text style={[styles.bentoTitle, { color: colors.text }]}>{opt.label}</Text>
<Text style={[styles.bentoSub, { color: colors.textSecondary }]}>{opt.sub}</Text>
<Text style={[styles.bentoTitle, { color: colors.text }]} maxFontSizeMultiplier={1.3}>{opt.label}</Text>
<Text style={[styles.bentoSub, { color: colors.textSecondary }]} maxFontSizeMultiplier={1.3}>{opt.sub}</Text>
</View>
<Ionicons name={opt.icon === 'sunny' ? 'sunny-outline' : opt.icon === 'brightness-4' ? 'contrast' : 'partly-sunny-outline'} size={20} color={colors.primary} />
</TouchableOpacity>
@@ -582,7 +588,7 @@ export default function OnboardingChatScreen() {
if (currentStep === 3) {
return (
<View style={styles.optionsWrap}>
<Text style={styles.stepTitle}>{copy.stepExperience}</Text>
<Text style={styles.stepTitle} maxFontSizeMultiplier={1.3}>{copy.stepExperience}</Text>
<View style={styles.optionsVertical}>
{([
{ id: 'beginner', label: copy.experiences.beginner, icon: 'leaf-outline' as const },
@@ -592,7 +598,7 @@ export default function OnboardingChatScreen() {
<TouchableOpacity key={opt.id} style={[styles.optionRow, { borderColor: colors.border }]} onPress={() => selectOption(opt.id, opt.label)}>
<View style={styles.optionRowLeft}>
<Ionicons name={opt.icon} size={18} color={colors.primary} style={{ marginRight: 10 }} />
<Text style={[styles.rowText, { color: colors.text }]}>{opt.label}</Text>
<Text style={[styles.rowText, { color: colors.text }]} maxFontSizeMultiplier={1.3}>{opt.label}</Text>
</View>
<Ionicons name="chevron-forward" size={16} color={colors.textMuted} />
</TouchableOpacity>
@@ -608,7 +614,7 @@ export default function OnboardingChatScreen() {
if (currentStep === 4) {
return (
<View style={styles.optionsWrap}>
<Text style={styles.stepTitle}>{copy.stepSource}</Text>
<Text style={styles.stepTitle} maxFontSizeMultiplier={1.3}>{copy.stepSource}</Text>
<View style={styles.optionsGrid}>
{([
{ id: 'app_store', label: copy.sources.app_store, icon: 'logo-apple' as const },
@@ -623,7 +629,7 @@ export default function OnboardingChatScreen() {
]).map((opt) => (
<TouchableOpacity key={opt.id} style={[styles.pillBtn, { borderColor: colors.border }]} onPress={() => selectOption(opt.id, opt.label)}>
<Ionicons name={opt.icon} size={15} color={colors.primary} style={{ marginRight: 4 }} />
<Text style={[styles.pillLabel, { color: colors.text }]}>{opt.label}</Text>
<Text style={[styles.pillLabel, { color: colors.text }]} maxFontSizeMultiplier={1.3}>{opt.label}</Text>
</TouchableOpacity>
))}
</View>
@@ -636,7 +642,7 @@ export default function OnboardingChatScreen() {
return (
<View style={styles.footerWrap}>
<TouchableOpacity style={[styles.continueCta, { backgroundColor: colors.primary }]} onPress={onFinish} activeOpacity={0.86}>
<Text style={[styles.continueCtaText, { color: colors.onPrimary }]}>{copy.continue}</Text>
<Text style={[styles.continueCtaText, { color: colors.onPrimary }]} maxFontSizeMultiplier={1.2}>{copy.continue}</Text>
</TouchableOpacity>
</View>
);
@@ -734,9 +740,18 @@ export default function OnboardingChatScreen() {
/>
</View>
{/* Input/Selection Panel */}
{/* Input/Selection Panel. Scrollbar und in der Hoehe gedeckelt: der
Attributions-Schritt hat 9 Optionen, die bei grosser Systemschrift
sonst den Chatverlauf und den Weiter-Button verdraengen. */}
<View style={styles.inputPanel}>
{renderOptions()}
<ScrollView
ref={inputScrollRef}
contentContainerStyle={styles.inputPanelContent}
showsVerticalScrollIndicator={false}
bounces={false}
>
{renderOptions()}
</ScrollView>
</View>
</SafeAreaView>
</KeyboardAvoidingView>
@@ -902,12 +917,16 @@ const styles = StyleSheet.create({
borderRadius: 3,
},
inputPanel: {
flexShrink: 1,
maxHeight: '58%',
borderTopWidth: StyleSheet.hairlineWidth,
borderTopColor: 'rgba(255,255,255,0.08)',
backgroundColor: '#131e14',
},
inputPanelContent: {
paddingTop: 12,
paddingBottom: 24,
paddingHorizontal: 16,
backgroundColor: '#131e14',
},
optionsWrap: {
gap: 10,
@@ -989,7 +1008,8 @@ const styles = StyleSheet.create({
paddingVertical: 8,
},
continueCta: {
height: 56,
minHeight: 56,
paddingVertical: 16,
borderRadius: 14,
alignItems: 'center',
justifyContent: 'center',