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:
parent
0533fcad71
commit
89391a807b
@ -28,6 +28,7 @@ import * as FileSystem from 'expo-file-system/legacy';
|
|||||||
import { apiFetch } from '../lib/api';
|
import { apiFetch } from '../lib/api';
|
||||||
import { ChatBubble, type ChatMsg, type MessageReaction } from '../components/chat/ChatBubble';
|
import { ChatBubble, type ChatMsg, type MessageReaction } from '../components/chat/ChatBubble';
|
||||||
import { DmChatBackground } from '../components/chat/DmChatBackground';
|
import { DmChatBackground } from '../components/chat/DmChatBackground';
|
||||||
|
import { FormSheet } from '../components/FormSheet';
|
||||||
import { useDmRealtime } from '../hooks/useChatRealtime';
|
import { useDmRealtime } from '../hooks/useChatRealtime';
|
||||||
import { useColors } from '../lib/theme';
|
import { useColors } from '../lib/theme';
|
||||||
import { useThemeStore } from '../stores/theme';
|
import { useThemeStore } from '../stores/theme';
|
||||||
@ -532,7 +533,7 @@ export default function DmScreen() {
|
|||||||
onLike={toggleLike}
|
onLike={toggleLike}
|
||||||
onReact={toggleReaction}
|
onReact={toggleReaction}
|
||||||
onDelete={deleteMessage}
|
onDelete={deleteMessage}
|
||||||
onOpenImage={() => {}}
|
onOpenImage={(url) => setLightboxUri(url)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
keyExtractor={(m) => m.id}
|
keyExtractor={(m) => m.id}
|
||||||
@ -545,6 +546,7 @@ export default function DmScreen() {
|
|||||||
keyboardDismissMode="interactive"
|
keyboardDismissMode="interactive"
|
||||||
keyboardShouldPersistTaps="handled"
|
keyboardShouldPersistTaps="handled"
|
||||||
onContentSizeChange={() => flatListRef.current?.scrollToEnd({ animated: false })}
|
onContentSizeChange={() => flatListRef.current?.scrollToEnd({ animated: false })}
|
||||||
|
onLayout={() => flatListRef.current?.scrollToEnd({ animated: false })}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
@ -706,103 +708,90 @@ function DmInfoSheet({
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal visible={visible} animationType="slide" presentationStyle="pageSheet" onRequestClose={onClose}>
|
<FormSheet
|
||||||
<View style={{ flex: 1, backgroundColor: colors.bg }}>
|
visible={visible}
|
||||||
{/* Header */}
|
onClose={onClose}
|
||||||
<View style={{
|
title={t('chat.info')}
|
||||||
flexDirection: 'row', alignItems: 'center', paddingHorizontal: 16,
|
initialHeightPct={0.85}
|
||||||
paddingTop: 16, paddingBottom: 12,
|
dismissOnBackdrop
|
||||||
borderBottomWidth: StyleSheet.hairlineWidth, borderBottomColor: colors.border,
|
>
|
||||||
}}>
|
<ScrollView contentContainerStyle={{ paddingBottom: 40 }}>
|
||||||
<Text style={{ flex: 1, fontSize: 17, fontFamily: 'Nunito_700Bold', color: colors.text }}>
|
{/* Partner-Karte */}
|
||||||
{t('chat.info')}
|
<TouchableOpacity
|
||||||
</Text>
|
activeOpacity={0.7}
|
||||||
<TouchableOpacity onPress={onClose} hitSlop={8} activeOpacity={0.7}>
|
onPress={onViewProfile}
|
||||||
<Ionicons name="close" size={24} color={colors.text} />
|
style={{ flexDirection: 'row', alignItems: 'center', padding: 16, gap: 14 }}
|
||||||
</TouchableOpacity>
|
>
|
||||||
</View>
|
{partner?.avatar ? (
|
||||||
|
<Image
|
||||||
<ScrollView contentContainerStyle={{ paddingBottom: 40 }}>
|
source={{ uri: partner.avatar }}
|
||||||
{/* Partner-Karte */}
|
style={{ width: 56, height: 56, borderRadius: 28 }}
|
||||||
<TouchableOpacity
|
contentFit="cover"
|
||||||
activeOpacity={0.7}
|
cachePolicy="memory-disk"
|
||||||
onPress={onViewProfile}
|
/>
|
||||||
style={{
|
|
||||||
flexDirection: 'row', alignItems: 'center',
|
|
||||||
padding: 16, gap: 14,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{partner?.avatar ? (
|
|
||||||
<Image
|
|
||||||
source={{ uri: partner.avatar }}
|
|
||||||
style={{ width: 56, height: 56, borderRadius: 28 }}
|
|
||||||
contentFit="cover"
|
|
||||||
cachePolicy="memory-disk"
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<View style={{
|
|
||||||
width: 56, height: 56, borderRadius: 28,
|
|
||||||
backgroundColor: colors.brandOrange + '30',
|
|
||||||
alignItems: 'center', justifyContent: 'center',
|
|
||||||
}}>
|
|
||||||
<Text style={{ fontSize: 20, fontFamily: 'Nunito_700Bold', color: colors.brandOrange }}>
|
|
||||||
{partner?.nickname?.[0]?.toUpperCase() ?? '?'}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
<View style={{ flex: 1 }}>
|
|
||||||
<Text style={{ fontSize: 17, fontFamily: 'Nunito_700Bold', color: colors.text }}>
|
|
||||||
{partner?.nickname ?? '…'}
|
|
||||||
</Text>
|
|
||||||
<Text style={{ fontSize: 13, fontFamily: 'Nunito_400Regular', color: colors.textMuted, marginTop: 2 }}>
|
|
||||||
{t('dm.view_profile', { defaultValue: 'Profil anzeigen' })}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
<Ionicons name="chevron-forward" size={18} color={colors.textMuted} />
|
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<View style={{ height: StyleSheet.hairlineWidth, backgroundColor: colors.border, marginHorizontal: 16 }} />
|
|
||||||
|
|
||||||
{/* Geteilte Medien */}
|
|
||||||
<View style={{ paddingHorizontal: 16, paddingTop: 20, paddingBottom: 12 }}>
|
|
||||||
<Text style={{ fontSize: 15, fontFamily: 'Nunito_700Bold', color: colors.text }}>
|
|
||||||
{t('dm.shared_media', { defaultValue: 'Geteilte Medien' })}
|
|
||||||
{sharedMedia.length > 0 && (
|
|
||||||
<Text style={{ fontSize: 13, fontFamily: 'Nunito_400Regular', color: colors.textMuted }}>
|
|
||||||
{' '}{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' })}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
) : (
|
) : (
|
||||||
<View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: MEDIA_GAP, paddingHorizontal: MEDIA_GAP }}>
|
<View style={{
|
||||||
{[...sharedMedia].reverse().map((m) => (
|
width: 56, height: 56, borderRadius: 28,
|
||||||
<TouchableOpacity
|
backgroundColor: colors.brandOrange + '30',
|
||||||
key={m.id}
|
alignItems: 'center', justifyContent: 'center',
|
||||||
activeOpacity={0.8}
|
}}>
|
||||||
onPress={() => onImagePress(m.attachmentUrl!)}
|
<Text style={{ fontSize: 20, fontFamily: 'Nunito_700Bold', color: colors.brandOrange }}>
|
||||||
>
|
{partner?.nickname?.[0]?.toUpperCase() ?? '?'}
|
||||||
<Image
|
</Text>
|
||||||
source={{ uri: m.attachmentUrl! }}
|
|
||||||
style={{ width: MEDIA_SIZE, height: MEDIA_SIZE }}
|
|
||||||
contentFit="cover"
|
|
||||||
cachePolicy="memory-disk"
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
))}
|
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
</ScrollView>
|
<View style={{ flex: 1 }}>
|
||||||
</View>
|
<Text style={{ fontSize: 17, fontFamily: 'Nunito_700Bold', color: colors.text }}>
|
||||||
</Modal>
|
{partner?.nickname ?? '…'}
|
||||||
|
</Text>
|
||||||
|
<Text style={{ fontSize: 13, fontFamily: 'Nunito_400Regular', color: colors.textMuted, marginTop: 2 }}>
|
||||||
|
{t('dm.view_profile')}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
<Ionicons name="chevron-forward" size={18} color={colors.textMuted} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<View style={{ height: StyleSheet.hairlineWidth, backgroundColor: colors.border, marginHorizontal: 16 }} />
|
||||||
|
|
||||||
|
{/* Geteilte Medien */}
|
||||||
|
<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')}
|
||||||
|
</Text>
|
||||||
|
{sharedMedia.length > 0 && (
|
||||||
|
<Text style={{ fontSize: 13, fontFamily: 'Nunito_400Regular', color: colors.textMuted }}>
|
||||||
|
{sharedMedia.length}
|
||||||
|
</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')}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
) : (
|
||||||
|
<View style={{ flexDirection: 'row', flexWrap: 'wrap', gap: MEDIA_GAP, paddingHorizontal: MEDIA_GAP }}>
|
||||||
|
{[...sharedMedia].reverse().map((m) => (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={m.id}
|
||||||
|
activeOpacity={0.8}
|
||||||
|
onPress={() => onImagePress(m.attachmentUrl!)}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
source={{ uri: m.attachmentUrl! }}
|
||||||
|
style={{ width: MEDIA_SIZE, height: MEDIA_SIZE }}
|
||||||
|
contentFit="cover"
|
||||||
|
cachePolicy="memory-disk"
|
||||||
|
/>
|
||||||
|
</TouchableOpacity>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</ScrollView>
|
||||||
|
</FormSheet>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -84,34 +84,18 @@ export default defineEventHandler(async (event) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Deepgram language mapping.
|
// Deepgram language mapping.
|
||||||
// Live-Diagnose (2026-05-30): nova-3 lehnt language=ar (und tr) mit
|
// Stand 2026-06-01: nova-3 unterstützt alle Sprachen inkl. ar/tr.
|
||||||
// 400 "No such model/language/tier combination found" ab — entgegen
|
// nova-2-general hat ar/tr-Support eingestellt ("No such model/language/tier
|
||||||
// der vorherigen Annahme. Fallback für ar/tr: nova-2-general
|
// combination found") — daher einheitlich nova-3 für alle Sprachen.
|
||||||
// (multilingual auto-detect). Für alle anderen Sprachen bleibt nova-3
|
|
||||||
// (bessere Genauigkeit, diskrete language-codes).
|
|
||||||
const deepgramLang =
|
const deepgramLang =
|
||||||
language &&
|
language &&
|
||||||
["de", "en", "tr", "ar", "fr", "es", "pt", "it"].includes(language)
|
["de", "en", "tr", "ar", "fr", "es", "pt", "it"].includes(language)
|
||||||
? language
|
? language
|
||||||
: "de";
|
: "de";
|
||||||
|
|
||||||
// nova-2-general unterstützt language=ar/tr (im Gegensatz zu nova-3).
|
const deepgramUrl = `https://api.deepgram.com/v1/listen?language=${deepgramLang}&model=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`;
|
|
||||||
|
|
||||||
console.log(
|
console.log("[transcribe] language:", deepgramLang, "model: nova-3");
|
||||||
"[transcribe] language:",
|
|
||||||
deepgramLang,
|
|
||||||
"model:",
|
|
||||||
needsGeneralModel ? "nova-2-general" : "nova-3",
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await fetch(deepgramUrl, {
|
const response = await fetch(deepgramUrl, {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user