91 lines
5.2 KiB
TypeScript

// Chat-Bubble + Spezial-Cards (Spiele/Überwunden) für den SOS-Chat-Stream
// sowie GameHeader für die aktive Spiel-Session.
import { View, Text, Pressable, StyleSheet } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { type GameType, GAME_META, GamePickerGrid } from './UrgeGames';
import { RiveAvatar, type Emotion as LyraEmotion } from '../RiveAvatar';
export type CardType = 'games' | 'overcome';
export type SosMsg = { id: string; role: 'user' | 'assistant'; content: string; cardType?: CardType; timestamp: Date };
// ── GameCard ─────────────────────────────────────────────────────────────────
export function GameCard({ onSelect }: { onSelect: (game: GameType) => void }) {
return (
<View style={st.gameCard}>
<Text style={st.gameCardTitle}>Welches Spiel?</Text>
<GamePickerGrid onSelect={onSelect} />
</View>
);
}
// ── OvercomeCard ─────────────────────────────────────────────────────────────
export function OvercomeCard() {
return (
<View style={st.overcomeCard}>
<Text style={{ fontSize: 36 }}>🎉</Text>
<Text style={st.overcomeTitle}>Gut gemacht.</Text>
<Text style={st.overcomeSub}>Du hast den Impuls überwunden.</Text>
</View>
);
}
// ── MessageRow ───────────────────────────────────────────────────────────────
type MessageRowProps = {
item: SosMsg;
onGameSelect: (game: GameType) => void;
onBreathingDone: () => void;
onSpeak?: (text: string) => Promise<void> | void;
};
export default function MessageRow({ item, onGameSelect }: MessageRowProps) {
const isUser = item.role === 'user';
if (item.cardType === 'games') return <View style={st.msgRowAssistant}><GameCard onSelect={onGameSelect} /></View>;
if (item.cardType === 'overcome') return <View style={st.msgRowAssistant}><OvercomeCard /></View>;
return (
<View style={[st.msgRow, isUser ? st.msgRowUser : st.msgRowAssistant]}>
<View style={[st.bubbleCol, isUser ? st.bubbleColUser : st.bubbleColAssistant]}>
<View style={[st.bubble, isUser ? st.bubbleUser : st.bubbleAssistant]}>
<Text style={[st.bubbleText, isUser ? st.bubbleTextUser : st.bubbleTextAssistant]}>{item.content}</Text>
</View>
</View>
</View>
);
}
// ── GameHeader ───────────────────────────────────────────────────────────────
export function GameHeader({ game, emotion, onBack }: { game: GameType; emotion: LyraEmotion; onBack: () => void }) {
const meta = GAME_META.find((g) => g.id === game);
return (
<View style={st.gameHeader}>
<Pressable style={st.backBtn} onPress={onBack} hitSlop={12}><Ionicons name="chevron-back" size={22} color="#374151" /></Pressable>
<View style={{ alignItems: 'center', flex: 1 }}>
<View style={{ transform: [{ scale: 0.65 }], marginBottom: -8 }}><RiveAvatar emotion={emotion} size="sm" /></View>
<Text style={{ fontFamily: 'Nunito_700Bold', fontSize: 13, color: '#111827', marginTop: 2 }}>{meta?.id ?? game}</Text>
</View>
<View style={{ width: 40 }} />
</View>
);
}
const st = StyleSheet.create({
msgRow: { flexDirection: 'row', marginBottom: 4, alignItems: 'flex-end' },
msgRowUser: { justifyContent: 'flex-end' },
msgRowAssistant: { justifyContent: 'flex-start', marginBottom: 6 },
bubbleCol: { maxWidth: '75%', gap: 2 },
bubbleColUser: { alignItems: 'flex-end' },
bubbleColAssistant: { alignItems: 'flex-start' },
bubble: { borderRadius: 20, paddingHorizontal: 14, paddingVertical: 9 },
bubbleUser: { backgroundColor: '#007AFF', borderBottomRightRadius: 4 },
bubbleAssistant: { backgroundColor: '#f0f0f0', borderBottomLeftRadius: 4 },
bubbleText: { fontSize: 15, lineHeight: 22 },
bubbleTextUser: { color: '#ffffff', fontFamily: 'Nunito_400Regular' },
bubbleTextAssistant: { color: '#1a1a1a', fontFamily: 'Nunito_400Regular' },
gameCard: { backgroundColor: '#f0f9ff', borderRadius: 16, borderWidth: 1, borderColor: '#bae6fd', padding: 12, maxWidth: '92%' },
gameCardTitle: { fontFamily: 'Nunito_700Bold', color: '#0369a1', fontSize: 13, marginBottom: 10 },
overcomeCard: { backgroundColor: '#f0fdf4', borderRadius: 16, borderWidth: 1, borderColor: '#86efac', padding: 16, alignItems: 'center', maxWidth: '88%', gap: 4 },
overcomeTitle: { fontFamily: 'Nunito_800ExtraBold', fontSize: 18, color: '#15803d' },
overcomeSub: { fontFamily: 'Nunito_400Regular', fontSize: 13, color: '#166534', textAlign: 'center' },
gameHeader: { flexDirection: 'row', alignItems: 'center', paddingHorizontal: 12, paddingVertical: 8, borderBottomWidth: 1, borderBottomColor: '#f3f4f6', backgroundColor: '#fff' },
backBtn: { width: 40, height: 40, borderRadius: 20, backgroundColor: '#f3f4f6', alignItems: 'center', justifyContent: 'center' },
});