(feat: attach) handle them locally from message input

This commit is contained in:
ManishMadan2882
2025-04-02 15:33:35 +05:30
parent 95e189d1d8
commit 856824316b
4 changed files with 26 additions and 35 deletions

View File

@@ -1,7 +1,7 @@
import { useEffect, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useDarkTheme } from '../hooks';
import { useSelector } from 'react-redux';
import { useSelector, useDispatch } from 'react-redux';
import userService from '../api/services/userService';
import endpoints from '../api/endpoints';
import PaperPlane from '../assets/paper_plane.svg';
@@ -15,14 +15,13 @@ import { selectSelectedDocs, selectToken } from '../preferences/preferenceSlice'
import { ActiveState } from '../models/misc';
import Upload from '../upload/Upload';
import ClipIcon from '../assets/clip.svg';
import { setAttachments } from '../conversation/conversationSlice';
interface MessageInputProps {
value: string;
onChange: (e: React.ChangeEvent<HTMLTextAreaElement>) => void;
onSubmit: () => void;
loading: boolean;
onAttachmentChange?: (attachments: { fileName: string; id: string }[]) => void;
}
interface UploadState {
@@ -39,7 +38,6 @@ export default function MessageInput({
onChange,
onSubmit,
loading,
onAttachmentChange,
}: MessageInputProps) {
const { t } = useTranslation();
const [isDarkTheme] = useDarkTheme();
@@ -53,6 +51,8 @@ export default function MessageInput({
const selectedDocs = useSelector(selectSelectedDocs);
const token = useSelector(selectToken);
const dispatch = useDispatch();
const handleFileAttachment = (e: React.ChangeEvent<HTMLInputElement>) => {
if (!e.target.files || e.target.files.length === 0) return;
@@ -213,21 +213,18 @@ export default function MessageInput({
console.log('Selected document:', doc);
};
useEffect(() => {
if (onAttachmentChange) {
const completedAttachments = uploads
.filter(upload => upload.status === 'completed' && upload.attachment_id)
.map(upload => ({
fileName: upload.fileName,
id: upload.attachment_id as string
}));
onAttachmentChange(completedAttachments);
}
}, [uploads, onAttachmentChange]);
const handleSubmit = () => {
const completedAttachments = uploads
.filter(upload => upload.status === 'completed' && upload.attachment_id)
.map(upload => ({
fileName: upload.fileName,
id: upload.attachment_id as string
}));
dispatch(setAttachments(completedAttachments));
onSubmit();
setUploads(prevUploads => prevUploads.filter(upload => upload.status !== 'completed'));
};
return (
<div className="flex flex-col w-full mx-2">

View File

@@ -29,11 +29,6 @@ import { ActiveState } from '../models/misc';
import ConversationMessages from './ConversationMessages';
import MessageInput from '../components/MessageInput';
interface AttachmentState {
fileName: string;
id: string;
}
export default function Conversation() {
const token = useSelector(selectToken);
const queries = useSelector(selectQueries);
@@ -50,7 +45,6 @@ export default function Conversation() {
useState<ActiveState>('INACTIVE');
const [files, setFiles] = useState<File[]>([]);
const [handleDragActive, setHandleDragActive] = useState<boolean>(false);
const [attachments, setAttachments] = useState<AttachmentState[]>([]);
const onDrop = useCallback((acceptedFiles: File[]) => {
setUploadModalState('ACTIVE');
@@ -101,13 +95,11 @@ export default function Conversation() {
isRetry = false,
updated = null,
indx = undefined,
attachments = [],
}: {
question: string;
isRetry?: boolean;
updated?: boolean | null;
indx?: number;
attachments?: { fileName: string; id: string }[];
}) => {
if (updated === true) {
!isRetry &&
@@ -116,11 +108,8 @@ export default function Conversation() {
} else {
question = question.trim();
if (question === '') return;
!isRetry && dispatch(addQuery({ prompt: question, attachments }));
fetchStream.current = dispatch(fetchAnswer({
question,
attachments: attachments.map(a => a.id)
}));
!isRetry && dispatch(addQuery({ prompt: question }));
fetchStream.current = dispatch(fetchAnswer({ question }));
}
};
@@ -172,10 +161,8 @@ export default function Conversation() {
});
} else {
handleQuestion({
question: input,
attachments: attachments
question: input,
});
setAttachments([]);
}
setInput('');
}
@@ -260,7 +247,6 @@ export default function Conversation() {
onChange={(e) => setInput(e.target.value)}
onSubmit={handleQuestionSubmission}
loading={status === 'loading'}
onAttachmentChange={setAttachments}
/>
</div>

View File

@@ -13,6 +13,7 @@ export interface ConversationState {
queries: Query[];
status: Status;
conversationId: string | null;
attachments?: { fileName: string; id: string }[];
}
export interface Answer {

View File

@@ -13,6 +13,7 @@ const initialState: ConversationState = {
queries: [],
status: 'idle',
conversationId: null,
attachments: [],
};
const API_STREAMING = import.meta.env.VITE_API_STREAMING === 'true';
@@ -27,8 +28,8 @@ export function handleAbort() {
export const fetchAnswer = createAsyncThunk<
Answer,
{ question: string; indx?: number; attachments?: string[] }
>('fetchAnswer', async ({ question, indx, attachments }, { dispatch, getState }) => {
{ question: string; indx?: number }
>('fetchAnswer', async ({ question, indx }, { dispatch, getState }) => {
if (abortController) {
abortController.abort();
}
@@ -37,6 +38,8 @@ export const fetchAnswer = createAsyncThunk<
let isSourceUpdated = false;
const state = getState() as RootState;
const attachments = state.conversation.attachments?.map(a => a.id) || [];
if (state.preference) {
if (API_STREAMING) {
await handleFetchAnswerSteaming(
@@ -257,6 +260,9 @@ export const conversationSlice = createSlice({
const { index, message } = action.payload;
state.queries[index].error = message;
},
setAttachments: (state, action: PayloadAction<{ fileName: string; id: string }[]>) => {
state.attachments = action.payload;
},
},
extraReducers(builder) {
builder
@@ -289,5 +295,6 @@ export const {
updateStreamingSource,
updateToolCalls,
setConversation,
setAttachments,
} = conversationSlice.actions;
export default conversationSlice.reducer;