react-native-ajora
Version:
The most complete AI agent UI for React Native
215 lines • 9.82 kB
JavaScript
export const ajoraReducer = (state, action) => {
switch (action.type) {
case "ADD_MESSAGES": {
const { messages } = action.payload;
const threadId = messages[0].thread_id;
const existingMessages = state.messages[threadId] || [];
const updatedMessages = [...messages, ...existingMessages];
return {
...state,
messages: { ...state.messages, [threadId]: updatedMessages },
};
}
case "ADD_FUNCTION_RESPONSE": {
const { message } = action.payload;
const threadId = message.thread_id;
const threadMessages = state.messages[threadId] || [];
const messageIndex = threadMessages.findIndex((msg) => msg._id === message._id);
let updatedMessages;
if (messageIndex !== -1) {
// Merge with existing message, preserving existing functionCall once set
updatedMessages = [...threadMessages];
const existingMessage = threadMessages[messageIndex];
const findText = (parts) => parts?.find((p) => p.text)?.text;
const findFunctionCall = (parts) => {
const fc = parts?.find((p) => p.functionCall)?.functionCall;
if (!fc)
return undefined;
// Treat empty objects as absent
const hasContent = Object.keys(fc).length > 0;
return hasContent ? fc : undefined;
};
const findFunctionResponse = (parts) => parts?.find((p) => p.functionResponse)?.functionResponse;
const incomingText = findText(message.parts);
const incomingFnCall = findFunctionCall(message.parts);
const incomingFnResp = findFunctionResponse(message.parts);
const existingText = findText(existingMessage.parts);
const existingFnCall = findFunctionCall(existingMessage.parts);
const existingFnResp = findFunctionResponse(existingMessage.parts);
// Prefer incoming values if provided, otherwise keep existing
const mergedText = incomingText ?? existingText;
const mergedFnCall = incomingFnCall ?? existingFnCall;
const mergedFnResp = incomingFnResp ?? existingFnResp;
const mergedParts = []; // preserve order: text, functionCall, functionResponse
if (mergedText)
mergedParts.push({ text: mergedText });
if (mergedFnCall)
mergedParts.push({ functionCall: mergedFnCall });
if (mergedFnResp)
mergedParts.push({ functionResponse: mergedFnResp });
updatedMessages[messageIndex] = {
...existingMessage,
...message,
// Ensure stable _id and createdAt from existing when streaming
_id: existingMessage._id,
createdAt: existingMessage.createdAt,
parts: mergedParts,
};
}
else {
updatedMessages = [...threadMessages, message];
}
return {
...state,
messages: { ...state.messages, [threadId]: updatedMessages },
};
}
case "UPDATE_STREAMING_MESSAGE": {
const { message } = action.payload;
const threadId = message.thread_id;
const threadMessages = state.messages[threadId] || [];
const messageIndex = threadMessages.findIndex((msg) => msg._id === message._id);
let updatedMessages;
if (messageIndex !== -1) {
// Merge with existing message, preserving existing functionCall once set
updatedMessages = [...threadMessages];
const existingMessage = threadMessages[messageIndex];
const findText = (parts) => parts?.find((p) => p.text)?.text;
const findFunctionCall = (parts) => {
const fc = parts?.find((p) => p.functionCall)?.functionCall;
if (!fc)
return undefined;
// Treat empty objects as absent
const hasContent = Object.keys(fc).length > 0;
return hasContent ? fc : undefined;
};
const findFunctionResponse = (parts) => parts?.find((p) => p.functionResponse)?.functionResponse;
const incomingText = findText(message.parts);
const incomingFnCall = findFunctionCall(message.parts);
const incomingFnResp = findFunctionResponse(message.parts);
const existingText = findText(existingMessage.parts);
const existingFnCall = findFunctionCall(existingMessage.parts);
const existingFnResp = findFunctionResponse(existingMessage.parts);
// Prefer incoming values if provided, otherwise keep existing
const mergedText = incomingText ?? existingText;
const mergedFnCall = incomingFnCall ?? existingFnCall;
const mergedFnResp = incomingFnResp ?? existingFnResp;
const mergedParts = []; // preserve order: text, functionCall, functionResponse
if (mergedText)
mergedParts.push({ text: mergedText });
if (mergedFnCall)
mergedParts.push({ functionCall: mergedFnCall });
if (mergedFnResp)
mergedParts.push({ functionResponse: mergedFnResp });
updatedMessages[messageIndex] = {
...existingMessage,
...message,
// Ensure stable _id and createdAt from existing when streaming
_id: existingMessage._id,
createdAt: existingMessage.createdAt,
parts: mergedParts,
};
}
else {
updatedMessages = [message, ...threadMessages];
}
return {
...state,
messages: { ...state.messages, [threadId]: updatedMessages },
};
}
case "ADD_NEW_THREAD": {
// Don't create thread locally - let server handle it
// Just clear the active thread ID so the next message will create a new thread
return {
...state,
activeThreadId: null,
};
}
case "UPDATE_THREAD_TITLE": {
const { thread } = action.payload;
const { title, id } = thread;
const threads = [...state.threads];
const threadIndex = threads.findIndex((t) => t.id === id);
if (threadIndex !== -1) {
threads[threadIndex] = { ...threads[threadIndex], title };
}
else {
// If the thread doesn't exist yet, add it so header/UI can resolve it
threads.push({ id, title });
}
return { ...state, threads };
}
case "SWITCH_THREAD": {
return { ...state, activeThreadId: action.payload.threadId };
}
case "SET_THINKING": {
return { ...state, isThinking: action.payload.isThinking };
}
case "SET_MODE": {
return { ...state, mode: action.payload.mode };
}
case "CLEAR_STREAM": {
return { ...state, stream: [] };
}
case "REMOVE_MESSAGE": {
const { messageId, threadId } = action.payload;
const currentMessages = state.messages[threadId] || [];
const filteredMessages = currentMessages.filter((m) => m._id !== messageId);
return {
...state,
messages: { ...state.messages, [threadId]: filteredMessages },
};
}
case "SET_THREADS": {
return { ...state, threads: action.payload.threads };
}
case "SET_MESSAGES": {
return {
...state,
messages: {
...state.messages,
[action.payload.threadId]: action.payload.messages,
},
};
}
case "ADD_EARLIER_MESSAGES": {
const { messages, threadId, pagination } = action.payload;
const existingMessages = state.messages[threadId] || [];
return {
...state,
messages: {
...state.messages,
[threadId]: [...existingMessages, ...messages],
},
};
}
case "SET_LOADING_MESSAGES": {
return { ...state, isLoadingMessages: action.payload.isLoading };
}
case "SET_COMPLETE": {
return { ...state, isComplete: action.payload.isComplete };
}
case "SET_ATTACHEMENT": {
return { ...state, attachement: action.payload.attachement };
}
case "UPDATE_ATTACHEMENT": {
const existingAttachement = state.attachement;
if (existingAttachement) {
return { ...state, attachement: action.payload.attachement };
}
else {
return state;
}
}
case "CLEAR_ATTACHEMENT": {
return { ...state, attachement: undefined };
}
case "SET_IS_RECORDING": {
return { ...state, isRecording: action.payload.isRecording };
}
default:
return state;
}
};
//# sourceMappingURL=ajoraReducer.js.map