UNPKG

react-native-ajora

Version:

The most complete AI agent UI for React Native

180 lines (179 loc) 5.83 kB
import React from "react"; import { StyleSheet, View, Text, TouchableOpacity, } from "react-native"; import { MaterialIcons } from "@expo/vector-icons"; import Color from "./Color"; // Optional expo-audio import for audio playback functionality let useAudioPlayer = null; try { const expoAudio = require("expo-audio"); useAudioPlayer = expoAudio.useAudioPlayer; } catch (error) { // expo-audio not available, audio playback will be disabled console.warn("expo-audio not available, audio playback disabled"); } const styles = StyleSheet.create({ container: { backgroundColor: Color.card, borderRadius: 12, borderWidth: 1, borderColor: Color.border, shadowColor: Color.shadow, shadowOffset: { width: 0, height: 2, }, shadowOpacity: 0.08, shadowRadius: 4, elevation: 2, maxWidth: 280, minWidth: 200, minHeight: 80, overflow: "hidden", }, audioCard: { flexDirection: "row", alignItems: "center", padding: 16, minHeight: 80, }, audioInfo: { flex: 1, }, audioTitle: { fontSize: 16, fontWeight: "600", color: Color.foreground, marginBottom: 4, }, audioSubtitle: { fontSize: 14, color: Color.mutedForeground, }, audioDuration: { fontSize: 12, color: Color.mutedForeground, marginTop: 2, }, playButton: { width: 48, height: 48, borderRadius: 24, backgroundColor: Color.primary, justifyContent: "center", alignItems: "center", marginLeft: 12, shadowColor: Color.shadow, shadowOffset: { width: 0, height: 2, }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 2, }, playIcon: { color: Color.primaryForeground, }, }); export function MessageAudio({ currentMessage, containerStyle, onPress, }) { if (currentMessage == null) return null; const supportedAudioMimeTypes = [ "audio/wav", "audio/mp3", "audio/aiff", "audio/aac", "audio/ogg", "audio/flac", ]; const audioPart = currentMessage.parts?.find((part) => part.fileData?.mimeType && supportedAudioMimeTypes.includes(part.fileData.mimeType)); const audioData = audioPart?.fileData; // Initialize audio player with the audio URI (if expo-audio is available) const player = useAudioPlayer ? useAudioPlayer(audioData?.fileUri) : null; const handlePress = () => { if (onPress) { onPress(); } else if (player) { // Toggle play/pause if (player.playing) { player.pause(); } else { player.play(); } } }; // Get audio file name from the structured data or fallback to URI parsing const getAudioFileName = () => { if (audioData?.displayName) { return audioData.displayName; } if (audioData?.fileUri) { const pathParts = audioData.fileUri.split("/"); const fileName = pathParts[pathParts.length - 1]; const cleanFileName = fileName.split("?")[0]; return cleanFileName || "Audio"; } return "Audio"; }; // Get audio file extension from mimeType or filename const getAudioFileType = () => { if (audioData?.mimeType) { // Extract format from mimeType (e.g., "audio/mp4" -> "MP4") const format = audioData.mimeType.split("/")[1]; return format?.toUpperCase() || "AUDIO"; } const fileName = getAudioFileName(); const lastDotIndex = fileName.lastIndexOf("."); if (lastDotIndex === -1) return "AUDIO"; return fileName.substring(lastDotIndex + 1).toUpperCase(); }; // Format file size for display const formatFileSize = (bytes) => { if (!bytes || bytes === 0) return ""; const k = 1024; const sizes = ["B", "KB", "MB", "GB"]; const i = Math.floor(Math.log(bytes) / Math.log(k)); return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + " " + sizes[i]; }; const audioFileName = getAudioFileName(); const audioFileType = getAudioFileType(); // Get the appropriate icon based on playing state const getPlayIcon = () => { if (!player) return "play-arrow"; return player.playing ? "pause" : "play-arrow"; }; // If no audio data is found, show a fallback message if (!audioData) { return (<TouchableOpacity style={[styles.container, containerStyle]} onPress={handlePress} activeOpacity={0.7}> <View style={styles.audioCard}> <View style={styles.audioInfo}> <Text style={styles.audioTitle}>Audio Message</Text> <Text style={styles.audioSubtitle}>No audio data available</Text> </View> <View style={styles.playButton}> <MaterialIcons name={getPlayIcon()} size={20} style={styles.playIcon}/> </View> </View> </TouchableOpacity>); } return (<TouchableOpacity style={[styles.container, containerStyle]} onPress={handlePress} activeOpacity={0.7}> <View style={styles.audioCard}> <View style={styles.audioInfo}> <Text style={styles.audioTitle} numberOfLines={1}> {audioFileName} </Text> </View> <View style={styles.playButton}> <MaterialIcons name={getPlayIcon()} size={20} style={styles.playIcon}/> </View> </View> </TouchableOpacity>); } //# sourceMappingURL=MessageAudio.js.map