fix: Arabic STT + DM scroll + info sheet FormSheet

STT (Arabic):
- Deepgram hat nova-2-general ar/tr-Support eingestellt (400 Bad Request)
- Fix: einheitlich nova-3 für alle Sprachen inkl. ar/tr
- Stale Kommentar aus 2026-05-30 entfernt

DM scroll-to-bottom:
- onLayout auf FlatList hinzugefügt → zusätzlicher scrollToEnd nach
  initialem Layout-Pass (Android-specific race condition)
- onOpenImage im FlatList-Renderer auf Lightbox verdrahtet (war () => {})

Info-Sheet:
- Modal(pageSheet) → FormSheet mit initialHeightPct={0.85}
- Nutzt jetzt unsere eigene Sheet-Komponente konsistent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chahinebrini 2026-06-01 10:25:35 +02:00
parent 0533fcad71
commit 89391a807b
2 changed files with 89 additions and 116 deletions

View File

@ -28,6 +28,7 @@ import * as FileSystem from 'expo-file-system/legacy';
import { apiFetch } from '../lib/api';
import { ChatBubble, type ChatMsg, type MessageReaction } from '../components/chat/ChatBubble';
import { DmChatBackground } from '../components/chat/DmChatBackground';
import { FormSheet } from '../components/FormSheet';
import { useDmRealtime } from '../hooks/useChatRealtime';
import { useColors } from '../lib/theme';
import { useThemeStore } from '../stores/theme';
@ -532,7 +533,7 @@ export default function DmScreen() {
onLike={toggleLike}
onReact={toggleReaction}
onDelete={deleteMessage}
onOpenImage={() => {}}
onOpenImage={(url) => setLightboxUri(url)}
/>
)}
keyExtractor={(m) => m.id}
@ -545,6 +546,7 @@ export default function DmScreen() {
keyboardDismissMode="interactive"
keyboardShouldPersistTaps="handled"
onContentSizeChange={() => flatListRef.current?.scrollToEnd({ animated: false })}
onLayout={() => flatListRef.current?.scrollToEnd({ animated: false })}
/>
)}
</View>
@ -706,31 +708,19 @@ function DmInfoSheet({
);
return (
<Modal visible={visible} animationType="slide" presentationStyle="pageSheet" onRequestClose={onClose}>
<View style={{ flex: 1, backgroundColor: colors.bg }}>
{/* Header */}
<View style={{
flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16,
paddingTop: 16, paddingBottom: 12,
borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: colors.border,
}}>
<Text style={{ flex: 1, fontSize: 17, fontFamily: 'Nunito_700Bold', color: colors.text }}>
{t('chat.info')}
</Text>
<TouchableOpacity onPress={onClose} hitSlop={8} activeOpacity={0.7}>
<Ionicons name="close" size={24} color={colors.text} />
</TouchableOpacity>
</View>
<FormSheet
visible={visible}
onClose={onClose}
title={t('chat.info')}
initialHeightPct={0.85}
dismissOnBackdrop
>
<ScrollView contentContainerStyle={{ paddingBottom: 40 }}>
{/* Partner-Karte */}
<TouchableOpacity
activeOpacity={0.7}
onPress={onViewProfile}
style={{
flexDirection: 'row', alignItems: 'center',
padding: 16, gap: 14,
}}
style={{ flexDirection: 'row', alignItems: 'center', padding: 16, gap: 14 }}
>
{partner?.avatar ? (
<Image
@ -755,7 +745,7 @@ function DmInfoSheet({
{partner?.nickname ?? '…'}
</Text>
<Text style={{ fontSize: 13, fontFamily: 'Nunito_400Regular', color: colors.textMuted, marginTop: 2 }}>
{t('dm.view_profile', { defaultValue: 'Profil anzeigen' })}
{t('dm.view_profile')}
</Text>
</View>
<Ionicons name="chevron-forward" size={18} color={colors.textMuted} />
@ -764,22 +754,22 @@ function DmInfoSheet({
<View style={{ height: StyleSheet.hairlineWidth, backgroundColor: colors.border, marginHorizontal: 16 }} />
{/* Geteilte Medien */}
<View style={{ paddingHorizontal: 16, paddingTop: 20, paddingBottom: 12 }}>
<View style={{ paddingHorizontal: 16, paddingTop: 20, paddingBottom: 12, flexDirection: 'row', alignItems: 'baseline', gap: 6 }}>
<Text style={{ fontSize: 15, fontFamily: 'Nunito_700Bold', color: colors.text }}>
{t('dm.shared_media', { defaultValue: 'Geteilte Medien' })}
{t('dm.shared_media')}
</Text>
{sharedMedia.length > 0 && (
<Text style={{ fontSize: 13, fontFamily: 'Nunito_400Regular', color: colors.textMuted }}>
{' '}{sharedMedia.length}
{sharedMedia.length}
</Text>
)}
</Text>
</View>
{sharedMedia.length === 0 ? (
<View style={{ alignItems: 'center', paddingVertical: 32 }}>
<Ionicons name="images-outline" size={40} color={colors.textMuted} />
<Text style={{ marginTop: 10, fontSize: 13, fontFamily: 'Nunito_400Regular', color: colors.textMuted }}>
{t('dm.no_shared_media', { defaultValue: 'Keine geteilten Medien' })}
{t('dm.no_shared_media')}
</Text>
</View>
) : (
@ -801,8 +791,7 @@ function DmInfoSheet({
</View>
)}
</ScrollView>
</View>
</Modal>
</FormSheet>
);
}

View File

@ -84,34 +84,18 @@ export default defineEventHandler(async (event) => {
);
// Deepgram language mapping.
// Live-Diagnose (2026-05-30): nova-3 lehnt language=ar (und tr) mit
// 400 "No such model/language/tier combination found" ab — entgegen
// der vorherigen Annahme. Fallback für ar/tr: nova-2-general
// (multilingual auto-detect). Für alle anderen Sprachen bleibt nova-3
// (bessere Genauigkeit, diskrete language-codes).
// Stand 2026-06-01: nova-3 unterstützt alle Sprachen inkl. ar/tr.
// nova-2-general hat ar/tr-Support eingestellt ("No such model/language/tier
// combination found") — daher einheitlich nova-3 für alle Sprachen.
const deepgramLang =
language &&
["de", "en", "tr", "ar", "fr", "es", "pt", "it"].includes(language)
? language
: "de";
// nova-2-general unterstützt language=ar/tr (im Gegensatz zu nova-3).
// Ohne expliziten language-Param fällt nova-2 auf Auto-Detect zurück und
// misdetektiert arabisches Audio oft als Englisch (phonetisches Transcript
// wie "salam alaikum" statt "السلام عليكم") — Lyra antwortet dann nicht
// auf Arabisch. Mit language=ar wird der korrekte Acoustic-Model-Pfad
// verwendet und die Schrift bleibt arabisch.
const needsGeneralModel = ["ar", "tr"].includes(deepgramLang);
const deepgramUrl = needsGeneralModel
? `https://api.deepgram.com/v1/listen?language=${deepgramLang}&model=nova-2-general`
: `https://api.deepgram.com/v1/listen?language=${deepgramLang}&model=nova-3`;
const deepgramUrl = `https://api.deepgram.com/v1/listen?language=${deepgramLang}&model=nova-3`;
console.log(
"[transcribe] language:",
deepgramLang,
"model:",
needsGeneralModel ? "nova-2-general" : "nova-3",
);
console.log("[transcribe] language:", deepgramLang, "model: nova-3");
try {
const response = await fetch(deepgramUrl, {