fixed sst to milliseconds and autoscroll the the third, attachments added shared volume, addes attachments at chats, updateded readme
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
KeyboardAvoidingView,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Image,
|
||||
Modal,
|
||||
} from 'react-native';
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
@@ -33,6 +34,8 @@ interface Attachment {
|
||||
type: 'image' | 'file' | 'audio';
|
||||
name: string;
|
||||
size?: number;
|
||||
uri?: string; // Lokaler Pfad oder data URI fuer Anzeige
|
||||
mimeType?: string;
|
||||
}
|
||||
|
||||
interface ChatMessage {
|
||||
@@ -87,10 +90,7 @@ const ChatScreen: React.FC = () => {
|
||||
console.error('[Chat] Fehler beim Laden des Verlaufs:', err);
|
||||
}
|
||||
};
|
||||
loadMessages().then(() => {
|
||||
// Auto-Scroll nach Laden des Verlaufs
|
||||
setTimeout(() => flatListRef.current?.scrollToEnd({ animated: false }), 200);
|
||||
});
|
||||
loadMessages();
|
||||
}, []);
|
||||
|
||||
// RVS-Nachrichten abonnieren
|
||||
@@ -222,19 +222,22 @@ const ChatScreen: React.FC = () => {
|
||||
);
|
||||
}, [messages]);
|
||||
|
||||
// Auto-Scroll bei neuen Nachrichten
|
||||
useEffect(() => {
|
||||
if (messages.length > 0) {
|
||||
// Laengerer Delay damit FlatList fertig gerendert hat
|
||||
setTimeout(() => {
|
||||
flatListRef.current?.scrollToEnd({ animated: false });
|
||||
}, 300);
|
||||
// Nochmal animiert fuer den Fall dass sich die Hoehe geaendert hat
|
||||
setTimeout(() => {
|
||||
flatListRef.current?.scrollToEnd({ animated: true });
|
||||
}, 600);
|
||||
// Auto-Scroll wird ueber onContentSizeChange der FlatList gesteuert
|
||||
const shouldAutoScroll = useRef(true);
|
||||
const handleContentSizeChange = useCallback(() => {
|
||||
if (shouldAutoScroll.current) {
|
||||
flatListRef.current?.scrollToEnd({ animated: false });
|
||||
}
|
||||
}, [messages]);
|
||||
}, []);
|
||||
const handleScrollBeginDrag = useCallback(() => {
|
||||
shouldAutoScroll.current = false;
|
||||
}, []);
|
||||
const handleScrollEndDrag = useCallback((e: any) => {
|
||||
// Auto-Scroll wieder aktivieren wenn User ganz unten ist
|
||||
const { contentOffset, contentSize, layoutMeasurement } = e.nativeEvent;
|
||||
const isAtBottom = contentOffset.y + layoutMeasurement.height >= contentSize.height - 50;
|
||||
shouldAutoScroll.current = isAtBottom;
|
||||
}, []);
|
||||
|
||||
// GPS-Position holen (optional)
|
||||
const getCurrentLocation = useCallback((): Promise<{ lat: number; lon: number } | null> => {
|
||||
@@ -306,12 +309,19 @@ const ChatScreen: React.FC = () => {
|
||||
setShowFileUpload(false);
|
||||
const location = await getCurrentLocation();
|
||||
|
||||
const isImage = file.type.startsWith('image/');
|
||||
const userMsg: ChatMessage = {
|
||||
id: nextId(),
|
||||
sender: 'user',
|
||||
text: `[Datei: ${file.name}]`,
|
||||
text: 'Anhang empfangen',
|
||||
timestamp: Date.now(),
|
||||
attachments: [{ type: 'file', name: file.name, size: file.size }],
|
||||
attachments: [{
|
||||
type: isImage ? 'image' : 'file',
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
uri: isImage && file.base64 ? `data:${file.type};base64,${file.base64}` : file.uri,
|
||||
mimeType: file.type,
|
||||
}],
|
||||
};
|
||||
setMessages(prev => [...prev, userMsg]);
|
||||
|
||||
@@ -332,9 +342,14 @@ const ChatScreen: React.FC = () => {
|
||||
const userMsg: ChatMessage = {
|
||||
id: nextId(),
|
||||
sender: 'user',
|
||||
text: `[Foto: ${photo.fileName}]`,
|
||||
text: 'Anhang empfangen',
|
||||
timestamp: Date.now(),
|
||||
attachments: [{ type: 'image', name: photo.fileName }],
|
||||
attachments: [{
|
||||
type: 'image',
|
||||
name: photo.fileName,
|
||||
uri: photo.base64 ? `data:${photo.type};base64,${photo.base64}` : undefined,
|
||||
mimeType: photo.type,
|
||||
}],
|
||||
};
|
||||
setMessages(prev => [...prev, userMsg]);
|
||||
|
||||
@@ -359,16 +374,35 @@ const ChatScreen: React.FC = () => {
|
||||
|
||||
return (
|
||||
<View style={[styles.messageBubble, isUser ? styles.userBubble : styles.ariaBubble]}>
|
||||
<Text style={[styles.messageText, isUser ? styles.userText : styles.ariaText]}>
|
||||
{item.text}
|
||||
</Text>
|
||||
{/* Anhang-Vorschau */}
|
||||
{item.attachments?.map((att, idx) => (
|
||||
<View key={idx} style={styles.attachmentBadge}>
|
||||
<Text style={styles.attachmentText}>
|
||||
{att.type === 'image' ? '\uD83D\uDDBC\uFE0F' : att.type === 'audio' ? '\uD83C\uDFA4' : '\uD83D\uDCC4'} {att.name}
|
||||
</Text>
|
||||
<View key={idx}>
|
||||
{att.type === 'image' && att.uri ? (
|
||||
<Image
|
||||
source={{ uri: att.uri }}
|
||||
style={styles.attachmentImage}
|
||||
resizeMode="contain"
|
||||
/>
|
||||
) : (
|
||||
<View style={styles.attachmentFile}>
|
||||
<Text style={styles.attachmentFileIcon}>
|
||||
{att.mimeType?.includes('pdf') ? '\uD83D\uDCC4' :
|
||||
att.mimeType?.includes('word') || att.mimeType?.includes('document') ? '\uD83D\uDCC3' :
|
||||
att.mimeType?.includes('sheet') || att.mimeType?.includes('excel') ? '\uD83D\uDCC8' :
|
||||
'\uD83D\uDCC1'}
|
||||
</Text>
|
||||
<Text style={styles.attachmentFileName} numberOfLines={1}>{att.name}</Text>
|
||||
{att.size ? <Text style={styles.attachmentFileSize}>{Math.round(att.size / 1024)}KB</Text> : null}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
))}
|
||||
{/* Text (nicht anzeigen wenn nur "Anhang empfangen" und ein Bild da ist) */}
|
||||
{!(item.text === 'Anhang empfangen' && item.attachments?.some(a => a.type === 'image' && a.uri)) && (
|
||||
<Text style={[styles.messageText, isUser ? styles.userText : styles.ariaText]}>
|
||||
{item.text}
|
||||
</Text>
|
||||
)}
|
||||
<Text style={styles.timestamp}>{time}</Text>
|
||||
</View>
|
||||
);
|
||||
@@ -401,6 +435,9 @@ const ChatScreen: React.FC = () => {
|
||||
renderItem={renderMessage}
|
||||
contentContainerStyle={styles.messageList}
|
||||
showsVerticalScrollIndicator={false}
|
||||
onContentSizeChange={handleContentSizeChange}
|
||||
onScrollBeginDrag={handleScrollBeginDrag}
|
||||
onScrollEndDrag={handleScrollEndDrag}
|
||||
ListEmptyComponent={
|
||||
<View style={styles.emptyContainer}>
|
||||
<Text style={styles.emptyIcon}>{'\uD83E\uDD16'}</Text>
|
||||
@@ -542,17 +579,34 @@ const styles = StyleSheet.create({
|
||||
ariaText: {
|
||||
color: '#E0E0F0',
|
||||
},
|
||||
attachmentBadge: {
|
||||
backgroundColor: 'rgba(255,255,255,0.1)',
|
||||
borderRadius: 6,
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 4,
|
||||
marginTop: 6,
|
||||
alignSelf: 'flex-start',
|
||||
attachmentImage: {
|
||||
width: '100%',
|
||||
height: 200,
|
||||
borderRadius: 8,
|
||||
marginBottom: 6,
|
||||
backgroundColor: '#0D0D1A',
|
||||
},
|
||||
attachmentText: {
|
||||
color: '#CCCCDD',
|
||||
fontSize: 12,
|
||||
attachmentFile: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
backgroundColor: 'rgba(255,255,255,0.1)',
|
||||
borderRadius: 8,
|
||||
padding: 10,
|
||||
marginBottom: 6,
|
||||
},
|
||||
attachmentFileIcon: {
|
||||
fontSize: 24,
|
||||
marginRight: 8,
|
||||
},
|
||||
attachmentFileName: {
|
||||
flex: 1,
|
||||
color: '#E0E0F0',
|
||||
fontSize: 13,
|
||||
},
|
||||
attachmentFileSize: {
|
||||
color: '#8888AA',
|
||||
fontSize: 11,
|
||||
marginLeft: 8,
|
||||
},
|
||||
timestamp: {
|
||||
color: 'rgba(255,255,255,0.4)',
|
||||
|
||||
Reference in New Issue
Block a user