UNPKG

react-native-ajora

Version:

The most complete AI agent UI for React Native

277 lines (275 loc) 9.58 kB
import React, { useState, useEffect, useCallback } from "react"; import { StyleSheet, Text, View, TouchableOpacity, ActivityIndicator, Dimensions, } from "react-native"; import Color from "../Color"; import MaterialIcons from "@expo/vector-icons/build/MaterialIcons"; // Get responsive card width (80% of screen width, max 400px, min 280px) const getCardWidth = () => { const screenWidth = Dimensions.get("window").width; const cardWidth = screenWidth * 0.8; return Math.min(Math.max(cardWidth, 280), 400); }; const ConfirmCard = ({ message, handleConfirm, }) => { const styles = createStyles(); return (<View style={styles.container}> <View style={styles.confirmCard}> <View style={styles.header}> <MaterialIcons name="verified" size={20} color={Color.cardForeground}/> <Text style={styles.confirmTitle}>Confirmation Required</Text> </View> <Text style={styles.messageText}>{message}</Text> <View style={styles.buttonContainer}> <TouchableOpacity style={[styles.button, styles.cancelButton]} onPress={() => handleConfirm(false)}> <Text style={styles.cancelButtonText}>Cancel</Text> </TouchableOpacity> <TouchableOpacity style={[styles.button, styles.confirmButton]} onPress={() => handleConfirm(true)}> <Text style={styles.confirmButtonText}>Confirm</Text> </TouchableOpacity> </View> </View> </View>); }; const ConfirmTool = ({ message, request, onResponse, submitQuery, }) => { const [loading, setLoading] = useState(false); const [error, setError] = useState(null); const [confirmed, setConfirmed] = useState(null); const styles = createStyles(); const { tool } = request; const { message: toolMessage = "Please confirm this action" } = tool.args || {}; const handleConfirm = useCallback(async (isConfirmed) => { setLoading(true); setError(null); try { const result = { confirmed: isConfirmed, message: isConfirmed ? "Action confirmed" : "Action cancelled", timestamp: new Date().toISOString(), }; setConfirmed(isConfirmed); // Send response back if (onResponse && request) { onResponse({ callId: request.callId, response: result, }); } } catch { setError("Failed to process confirmation"); if (onResponse && request) { onResponse({ callId: request.callId, response: { error: "Failed to process confirmation" }, }); } } finally { setLoading(false); } }, [request, onResponse]); useEffect(() => { if (request?.tool.name === "confirm_action") { // Use response data from the merged functionCall if (request.tool.response) { const responseData = request.tool.response; // Handle the new response format with output and error fields if (responseData.error) { setError(responseData.error); setLoading(false); return; } // Handle the response data - could be in output field or directly in response const confirmationData = responseData.output || responseData; setConfirmed(confirmationData.confirmed); setLoading(false); setError(null); } else { setLoading(false); setError(null); } } }, [request]); if (!request) { return (<View style={styles.container}> <View style={styles.errorContainer}> <Text style={styles.errorText}>No request provided</Text> </View> </View>); } if (loading) { return (<View style={styles.container}> <View style={styles.confirmCard}> <View style={styles.header}> <ActivityIndicator size="small" color={Color.primary}/> <Text style={styles.confirmTitle}>Processing...</Text> </View> <Text style={styles.messageText}> Please wait while we process your confirmation. </Text> </View> </View>); } if (error) { return (<View style={styles.container}> <View style={styles.errorContainer}> <View style={styles.header}> <MaterialIcons name="error-outline" size={20} color={Color.destructive}/> <Text style={styles.confirmTitle}>Error</Text> </View> <Text style={styles.messageText}>{error}</Text> <View style={styles.buttonContainer}> <TouchableOpacity style={[styles.button, styles.cancelButton]} onPress={() => { setError(null); setConfirmed(null); }}> <Text style={styles.cancelButtonText}>Dismiss</Text> </TouchableOpacity> </View> </View> </View>); } if (confirmed !== null) { return (<View style={styles.container}> <View style={styles.resultCard}> <View style={styles.header}> <MaterialIcons name={confirmed ? "check-circle" : "cancel"} size={20} color={confirmed ? Color.primary : Color.destructive}/> <Text style={styles.confirmTitle}> {confirmed ? "Action Confirmed" : "Action Cancelled"} </Text> </View> <Text style={styles.messageText}> {confirmed ? "Your action has been successfully confirmed." : "The action has been cancelled as requested."} </Text> <View style={styles.buttonContainer}> <TouchableOpacity style={[styles.button, styles.cancelButton]} onPress={() => { setConfirmed(null); setError(null); }}> <Text style={styles.cancelButtonText}>Close</Text> </TouchableOpacity> </View> </View> </View>); } return <ConfirmCard message={toolMessage} handleConfirm={handleConfirm}/>; }; ConfirmTool.displayName = "confirm_action"; export default ConfirmTool; const createStyles = () => { const cardWidth = getCardWidth(); return StyleSheet.create({ container: { marginVertical: 8, }, errorContainer: { width: cardWidth, padding: 16, backgroundColor: Color.card, borderRadius: 12, borderWidth: 1, borderColor: Color.destructive, alignItems: "center", shadowColor: Color.shadow, shadowOffset: { width: 0, height: 2, }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 2, }, errorText: { fontSize: 14, color: Color.destructive, textAlign: "center", }, confirmCard: { width: cardWidth, backgroundColor: Color.card, borderRadius: 12, padding: 16, borderWidth: 1, borderColor: Color.border, shadowColor: Color.shadow, shadowOffset: { width: 0, height: 2, }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 2, }, header: { flexDirection: "row", alignItems: "center", marginBottom: 12, gap: 8, }, confirmTitle: { fontSize: 16, fontWeight: "600", color: Color.cardForeground, }, messageText: { fontSize: 14, color: Color.mutedForeground, marginBottom: 16, lineHeight: 20, }, buttonContainer: { flexDirection: "row", justifyContent: "space-between", gap: 12, }, button: { flex: 1, paddingVertical: 12, paddingHorizontal: 16, borderRadius: 8, alignItems: "center", }, cancelButton: { backgroundColor: Color.secondary, borderWidth: 1, borderColor: Color.border, }, confirmButton: { backgroundColor: Color.primary, }, cancelButtonText: { fontSize: 14, fontWeight: "600", color: Color.secondaryForeground, }, confirmButtonText: { fontSize: 14, fontWeight: "600", color: Color.primaryForeground, }, resultCard: { width: cardWidth, backgroundColor: Color.card, borderRadius: 12, padding: 16, borderWidth: 1, borderColor: Color.border, alignItems: "center", shadowColor: Color.shadow, shadowOffset: { width: 0, height: 2, }, shadowOpacity: 0.1, shadowRadius: 4, elevation: 2, }, resultText: { fontSize: 16, fontWeight: "600", color: Color.cardForeground, }, }); }; //# sourceMappingURL=ConfirmTool.js.map