Update files

This commit is contained in:
Mohamed Boudra
2026-02-08 16:15:49 +07:00
parent c5a27a4ddc
commit 1b7490d1ae
3 changed files with 58 additions and 21 deletions

View File

@@ -101,6 +101,7 @@ export function AgentInputArea({
const [isProcessing, setIsProcessing] = useState(false);
const [selectedImages, setSelectedImages] = useState<ImageAttachment[]>([]);
const [isCancellingAgent, setIsCancellingAgent] = useState(false);
const [sendError, setSendError] = useState<string | null>(null);
const [commandSelectedIndex, setCommandSelectedIndex] = useState(0);
const lastHandledFocusRequestIdRef = useRef<number | null>(null);
@@ -130,6 +131,13 @@ export function AgentInputArea({
setCommandSelectedIndex(0);
}, [commandFilter]);
// Clear send error when user edits the input
useEffect(() => {
if (sendError && userInput) {
setSendError(null);
}
}, [userInput, sendError]);
const { pickImages } = useImageAttachmentPicker();
const agentIdRef = useRef(agentId);
const sendAgentMessageRef = useRef<
@@ -256,28 +264,38 @@ export function AgentInputArea({
return;
}
const isControlledLocal = value !== undefined;
setSelectedImages([]);
setIsProcessing(true);
try {
await submitMessage(trimmedMessage, imageAttachments);
// Clear input only after successful submission
// For controlled inputs with onSubmitMessage, the parent handles clearing
// because agent creation is async (WebSocket) and errors come back later
if (onSubmitMessageRef.current) {
// Parent manages input state - don't clear here
// Parent will clear on success via onChangeText
} else if (isControlledLocal) {
// Clear input optimistically before awaiting server ack.
// Save values so we can restore on error.
const savedImages = imageAttachments;
if (!onSubmitMessageRef.current) {
if (value !== undefined) {
onChangeText?.("");
} else {
setUserInput("");
}
}
setSelectedImages([]);
setSendError(null);
setIsProcessing(true);
try {
await submitMessage(trimmedMessage, imageAttachments);
} catch (error) {
console.error("[AgentInput] Failed to send message:", error);
if (imageAttachments) {
setSelectedImages(imageAttachments);
// Restore input so the user never loses their message
if (!onSubmitMessageRef.current) {
if (value !== undefined) {
onChangeText?.(trimmedMessage);
} else {
setUserInput(trimmedMessage);
}
}
if (savedImages) {
setSelectedImages(savedImages);
}
setSendError(
error instanceof Error ? error.message : "Failed to send message"
);
} finally {
setIsProcessing(false);
}
@@ -449,7 +467,14 @@ export function AgentInputArea({
// Cancels current agent run before sending queued prompt
handleCancelAgent();
await submitMessage(item.text, item.images);
try {
await submitMessage(item.text, item.images);
} catch (error) {
updateQueue((current) => [item, ...current]);
setSendError(
error instanceof Error ? error.message : "Failed to send message"
);
}
}
const handleQueue = useCallback((payload: MessagePayload) => {
@@ -616,6 +641,10 @@ export function AgentInputArea({
/>
)}
{sendError && (
<Text style={styles.sendErrorText}>{sendError}</Text>
)}
{/* MessageInput handles everything: text, dictation, attachments, all buttons */}
<MessageInput
ref={messageInputRef}
@@ -623,7 +652,7 @@ export function AgentInputArea({
onChangeText={setUserInput}
onSubmit={handleSubmit}
isSubmitDisabled={isProcessing || isSubmitLoading}
isSubmitLoading={isSubmitLoading}
isSubmitLoading={isProcessing || isSubmitLoading}
images={selectedImages}
onPickImages={handlePickImage}
onRemoveImage={handleRemoveImage}
@@ -738,4 +767,8 @@ const styles = StyleSheet.create(((theme: Theme) => ({
queueSendButton: {
backgroundColor: theme.colors.palette.blue[600],
},
sendErrorText: {
color: theme.colors.palette.red[500],
fontSize: theme.fontSize.sm,
},
})) as any) as Record<string, any>;

View File

@@ -826,8 +826,7 @@ function WorkingIndicator() {
withTiming(1, { duration: bounceDuration }),
withTiming(0, { duration: bounceDuration })
),
-1,
true
-1
)
);
});

View File

@@ -2,6 +2,7 @@ import {
View,
TextInput,
Pressable,
ActivityIndicator,
NativeSyntheticEvent,
TextInputContentSizeChangeEventData,
TextInputKeyPressEventData,
@@ -533,7 +534,7 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
const hasImages = images.length > 0;
const hasSendableContent = value.trim().length > 0 || hasImages;
const shouldShowSendButton = hasSendableContent;
const shouldShowSendButton = hasSendableContent || isSubmitLoading;
const isConnected = client?.isConnected ?? false;
return (
@@ -675,7 +676,11 @@ export const MessageInput = forwardRef<MessageInputRef, MessageInputProps>(
styles.buttonDisabled,
]}
>
<ArrowUp size={20} color="white" />
{isSubmitLoading ? (
<ActivityIndicator size="small" color="white" />
) : (
<ArrowUp size={20} color="white" />
)}
</Pressable>
)}
</View>