diff --git a/android/src/screens/ChatScreen.tsx b/android/src/screens/ChatScreen.tsx index 63ce67b..6d58c84 100644 --- a/android/src/screens/ChatScreen.tsx +++ b/android/src/screens/ChatScreen.tsx @@ -94,6 +94,7 @@ const ChatScreen: React.FC = () => { const [fullscreenImage, setFullscreenImage] = useState(null); const [searchQuery, setSearchQuery] = useState(''); const [searchVisible, setSearchVisible] = useState(false); + const [pendingAttachment, setPendingAttachment] = useState<{file: any, isPhoto: boolean} | null>(null); const flatListRef = useRef(null); const messageIdCounter = useRef(0); @@ -400,6 +401,13 @@ const ChatScreen: React.FC = () => { const sendTextMessage = useCallback(async () => { const text = inputText.trim(); + + // Wenn pending Anhang vorhanden → Anhang + Text zusammen senden + if (pendingAttachment) { + sendPendingAttachment(text); + return; + } + if (!text) return; setInputText(''); @@ -419,7 +427,7 @@ const ChatScreen: React.FC = () => { text, ...(location && { location }), }); - }, [inputText, getCurrentLocation]); + }, [inputText, getCurrentLocation, pendingAttachment, sendPendingAttachment]); // Sprachaufnahme abgeschlossen const handleVoiceRecording = useCallback(async (result: RecordingResult) => { @@ -441,87 +449,79 @@ const ChatScreen: React.FC = () => { }); }, [getCurrentLocation]); - // Datei senden + // Datei auswaehlen → als pending speichern (nicht sofort senden) const handleFileSelected = useCallback(async (file: FileData) => { setShowFileUpload(false); - const location = await getCurrentLocation(); + setPendingAttachment({ file, isPhoto: false }); + setInputText(''); // Focus auf Textfeld + }, []); - const isImage = file.type.startsWith('image/'); + // Foto auswaehlen → als pending speichern (nicht sofort senden) + const handlePhotoSelected = useCallback(async (photo: PhotoData) => { + setShowCameraUpload(false); + setPendingAttachment({ file: photo, isPhoto: true }); + setInputText(''); // Focus auf Textfeld + }, []); + + // Pending Anhang + Text/Sprache senden + const sendPendingAttachment = useCallback(async (messageText: string) => { + if (!pendingAttachment) return; + const { file, isPhoto } = pendingAttachment; + const location = await getCurrentLocation(); const msgId = nextId(); - let imageUri = isImage && file.base64 ? `data:${file.type};base64,${file.base64}` : file.uri; + + // Chat-Nachricht erstellen + const isImage = isPhoto || (file.type && file.type.startsWith('image/')); + const name = isPhoto ? file.fileName : file.name; + const base64 = file.base64 || ''; + const mimeType = file.type || ''; + const imageUri = isImage && base64 ? `data:${mimeType};base64,${base64}` : file.uri; const userMsg: ChatMessage = { id: msgId, sender: 'user', - text: 'Anhang empfangen', + text: messageText || 'Anhang', timestamp: Date.now(), attachments: [{ type: isImage ? 'image' : 'file', - name: file.name, + name, size: file.size, uri: imageUri, - mimeType: file.type, + mimeType, }], }; setMessages(prev => [...prev, userMsg]); - // Anhang auf Disk speichern fuer Persistenz - if (file.base64) { - persistAttachment(file.base64, msgId, file.name).then(filePath => { + // Auf Disk speichern + if (base64) { + persistAttachment(base64, msgId, name).then(filePath => { setMessages(prev => prev.map(m => m.id === msgId ? { ...m, attachments: m.attachments?.map(a => ({ ...a, uri: filePath })) } : m )); }).catch(() => {}); } + // Datei an RVS senden rvs.send('file', { - name: file.name, - type: file.type, + name, + type: mimeType, size: file.size, - base64: file.base64, + base64, + ...(isPhoto && file.width && { width: file.width, height: file.height }), ...(location && { location }), }); - }, [getCurrentLocation]); - // Foto senden - const handlePhotoSelected = useCallback(async (photo: PhotoData) => { - setShowCameraUpload(false); - const location = await getCurrentLocation(); - - const msgId = nextId(); - const dataUri = photo.base64 ? `data:${photo.type};base64,${photo.base64}` : undefined; - - const userMsg: ChatMessage = { - id: msgId, - sender: 'user', - text: 'Anhang empfangen', - timestamp: Date.now(), - attachments: [{ - type: 'image', - name: photo.fileName, - uri: dataUri, - mimeType: photo.type, - }], - }; - setMessages(prev => [...prev, userMsg]); - - // Foto auf Disk speichern fuer Persistenz - if (photo.base64) { - persistAttachment(photo.base64, msgId, photo.fileName).then(filePath => { - setMessages(prev => prev.map(m => - m.id === msgId ? { ...m, attachments: m.attachments?.map(a => ({ ...a, uri: filePath })) } : m - )); - }).catch(() => {}); + // Wenn Text dabei ist, als separate Chat-Nachricht senden (damit ARIA weiss was zu tun ist) + if (messageText) { + rvs.send('chat', { + text: messageText, + ...(location && { location }), + }); } - rvs.send('file', { - name: photo.fileName, - type: photo.type, - base64: photo.base64, - width: photo.width, - height: photo.height, - ...(location && { location }), - }); + setPendingAttachment(null); + setInputText(''); + }, [pendingAttachment, getCurrentLocation]); }, [getCurrentLocation]); // --- Rendering --- @@ -670,6 +670,28 @@ const ChatScreen: React.FC = () => { } /> + {/* Pending Anhang Vorschau */} + {pendingAttachment && ( + + {pendingAttachment.file.type?.startsWith('image/') || pendingAttachment.isPhoto ? ( + + ) : ( + {'\uD83D\uDCC4'} + )} + + {pendingAttachment.isPhoto ? pendingAttachment.file.fileName : pendingAttachment.file.name} + + setPendingAttachment(null)}> + X + + + )} + {/* Eingabebereich */} {/* Datei-Buttons */} @@ -692,7 +714,7 @@ const ChatScreen: React.FC = () => { style={styles.textInput} value={inputText} onChangeText={setInputText} - placeholder="Nachricht an ARIA..." + placeholder={pendingAttachment ? "Text zum Anhang (optional)..." : "Nachricht an ARIA..."} placeholderTextColor="#555570" multiline maxLength={4000} @@ -701,7 +723,7 @@ const ChatScreen: React.FC = () => { /> {/* Senden oder Sprache */} - {inputText.trim() ? ( + {inputText.trim() || pendingAttachment ? ( {'\u2B06\uFE0F'} @@ -932,6 +954,27 @@ const styles = StyleSheet.create({ wakeWordIcon: { fontSize: 16, }, + pendingBar: { + flexDirection: 'row', + alignItems: 'center', + backgroundColor: '#1E1E2E', + paddingHorizontal: 12, + paddingVertical: 8, + borderTopWidth: 1, + borderTopColor: '#2A2A3E', + }, + pendingThumb: { + width: 40, + height: 40, + borderRadius: 6, + marginRight: 8, + backgroundColor: '#0D0D1A', + }, + pendingName: { + flex: 1, + color: '#E0E0F0', + fontSize: 13, + }, searchBar: { flexDirection: 'row', alignItems: 'center',