diff --git a/src/components/Form/response.tsx b/src/components/Form/response.tsx
index bfd1021..3fbb57a 100644
--- a/src/components/Form/response.tsx
+++ b/src/components/Form/response.tsx
@@ -3,11 +3,11 @@ import { useEffect, useRef, useState } from 'react';
import { IconBtn } from '../../components/Button';
import { Input } from '../../components/Input';
import { Roles } from '../../config/enums';
+import { TYPING_TIMEOUT } from '../../config/env';
import { useAppDispatch, useAppSelector } from '../../hooks';
import { fillUserHistoryData, getChat, setLastGroupPointer, setTypingTimeoutExpired } from '../../store/slices/chat';
import { getQueryParam, uuidV4 } from '../../utils';
import { layoutFoot as variant } from '../Layout/variants';
-import { TYPING_TIMEOUT } from '../../config/env';
export const ResponseForm = () => {
const dispatch = useAppDispatch();
diff --git a/src/components/Stream/assistant.tsx b/src/components/Stream/assistant.tsx
index 643a3cd..b459f3b 100644
--- a/src/components/Stream/assistant.tsx
+++ b/src/components/Stream/assistant.tsx
@@ -6,18 +6,21 @@ import { useAppSelector } from '../../hooks';
import { AssistantProps } from '../../interfaces/component';
import { getChat, sortBySequence } from '../../store/slices/chat';
import { getMeta } from '../../store/slices/meta';
-import { getQueryParam, extractVideoCode, uuidV4 } from '../../utils';
+import { extractVideoCode, getQueryParam, uuidV4 } from '../../utils';
+import { replaceNewRowSymbols } from '../../utils/formatting';
import MarkdownLink from '../Markdown/link';
import { Media } from '../Media';
import OptionList from './options';
import { flickerEffect } from './variants';
-import { replaceNewRowSymbols } from '../../utils/formatting';
const Assistant = ({ message, itemId }: AssistantProps) => {
const term = getQueryParam();
const { isStreaming } = useAppSelector(getChat);
const { pd } = useAppSelector(getMeta);
- const isLast = useAppSelector((state) => state.chat.record[term].historyIds.length - 1 === state.chat.record[term].historyIds.indexOf(itemId));
+ const isLast = useAppSelector((state) => {
+ const threadId = state.chat.thread[term];
+ return state.chat.record[threadId].historyIds.length - 1 === state.chat.record[threadId].historyIds.indexOf(itemId)
+ });
const { base: baseFlicker } = flickerEffect({ isTyping: isStreaming && isLast });
const sortedContent = [...message.content].sort(sortBySequence);
@@ -72,7 +75,7 @@ const Assistant = ({ message, itemId }: AssistantProps) => {
key={uuidV4()}
e2e="assistant-img"
image={it[it.type]?.url || ''}
- background={'url("' + it[it.type]?.url + '")'}
+ background={`url("${it[it.type]?.url}")`}
type={Definition.image}
title={it[it.type]?.title}
/>
diff --git a/src/components/Stream/base.tsx b/src/components/Stream/base.tsx
index d31ac1b..66e38d5 100644
--- a/src/components/Stream/base.tsx
+++ b/src/components/Stream/base.tsx
@@ -8,8 +8,15 @@ import { streamBase, streamRow } from './variants';
export const StreamBase = () => {
const { aiProfile } = useAppSelector(getConfig);
const term = getQueryParam();
- const historyIds = useAppSelector((state) => state.chat.record[term]?.historyIds || []);
- const firstMessage = useAppSelector((state) => state.chat.record[term]?.historyData[historyIds[0]]);
+ const historyIds = useAppSelector((state) => {
+ const threadId = state.chat.thread[term];
+ return state.chat.record[threadId]?.historyIds || []
+ });
+ const firstMessage = useAppSelector((state) => {
+ const threadId = state.chat.thread[term];
+
+ return state.chat.record[threadId]?.historyData[historyIds[0]]
+ });
const { base, second, date } = streamBase();
const { base: baseRow } = streamRow();
const time = formatDateByLocale(firstMessage?.time || new Date().getTime());
diff --git a/src/components/Stream/bubble.tsx b/src/components/Stream/bubble.tsx
index 576c2e1..b9895ed 100644
--- a/src/components/Stream/bubble.tsx
+++ b/src/components/Stream/bubble.tsx
@@ -10,7 +10,10 @@ import { streamBubble as variant } from './variants';
const StreamBubble = ({ itemId }: { itemId: string }) => {
const term = getQueryParam();
- const record = useAppSelector((state) => state.chat.record[term].historyData[itemId]);
+ const record = useAppSelector((state) => {
+ const threadId = state.chat.thread[term];
+ return state.chat.record[threadId].historyData[itemId];
+ });
return (record && (
{
const term = getQueryParam();
const { cid, systemType, marketing, pd } = useAppSelector(getMeta);
const { translations, purpose, specialUrls } = useAppSelector(getConfig);
- const { isLoading, isStreaming, record } = useAppSelector(getChat);
+ const { isLoading, isStreaming, record, thread } = useAppSelector(getChat);
const { error: streamError } = useAppSelector((store) => store.chat);
const storedLink = useAppSelector((store) => store.intentions.link);
const { error: emailError, current: currentEmail } = useAppSelector(getEmailIntentions);
@@ -39,12 +39,13 @@ export const useFootProps = () => {
ctaHref: '',
};
- if (!record[term]) {
- return staticProps
+ const currentThread = thread[term];
+ if (!currentThread) {
+ return staticProps;
}
- const lastMsgId = [...record[term].historyIds].pop();
- const lastMsg = lastMsgId && record[term].historyData[lastMsgId];
+ const lastMsgId = [...record[currentThread].historyIds].pop();
+ const lastMsg = lastMsgId && record[currentThread].historyData[lastMsgId];
const isLastAssistantMsg = lastMsg && lastMsg.role === Roles.assistant;
const link = isLastAssistantMsg && storedLink;
const noButtonChoices = !(lastMsg && lastMsg?.content.find((m) => m.buttons));
diff --git a/src/interfaces/index.ts b/src/interfaces/index.ts
index e9aad32..929a83d 100644
--- a/src/interfaces/index.ts
+++ b/src/interfaces/index.ts
@@ -26,17 +26,17 @@ interface VideoProps {
export interface VideoMessage {
type: Definition.video;
- video: VideoProps
+ video: VideoProps;
}
interface ImageProps {
url: string;
- title: string
- alt: string | null
+ title: string;
+ alt: string | null;
}
export interface ImageMessage {
type: Definition.image;
- image: ImageProps
+ image: ImageProps;
}
export interface ButtonOptions {
@@ -49,22 +49,34 @@ export interface ButtonOptions {
}
export interface BaseOptions extends ButtonOptions {
- id: string, link: string, noStream: boolean
+ id: string;
+ link: string;
+ noStream: boolean;
}
export interface OptionsListProps {
- options: Array
| undefined
+ options: Array | undefined;
}
export type AssistantMessageTypeUnion =
- TextMessage | ButtonsMessage | EmailMessage | VideoMessage | ImageMessage | PaymentMessage;
+ | TextMessage
+ | ButtonsMessage
+ | EmailMessage
+ | VideoMessage
+ | ImageMessage
+ | PaymentMessage;
export type SupportedMessageTypes =
- Definition.text | Definition.buttons | Definition.payment | Definition.email | Definition.video | Definition.image;
+ | Definition.text
+ | Definition.buttons
+ | Definition.payment
+ | Definition.email
+ | Definition.video
+ | Definition.image;
export interface AssistantRecord {
- type: SupportedMessageTypes,
- sequence: number,
+ type: SupportedMessageTypes;
+ sequence: number;
text?: string;
video?: VideoProps;
image?: ImageProps;
@@ -109,18 +121,22 @@ export interface MessageProperties {
}
export type PredefinedMessagePayload = {
- content: string,
- buttons?: Array,
-}
+ content: string;
+ buttons?: Array;
+};
export type UserMessageContent = {
- groupId: string, sent: boolean, resend: boolean, text: string, sequence: number
-}
+ groupId: string;
+ sent: boolean;
+ resend: boolean;
+ text: string;
+ sequence: number;
+};
export interface UserHistoryData {
id: string;
role: Roles.user;
- content: Array
+ content: Array;
}
export interface UserHistoryDataFiller {
@@ -147,7 +163,7 @@ export interface AssistantHistoryInitialMessage {
export interface ClientMessage {
role: Roles.assistant | Roles.user;
term: string;
- user_id: string;
+ userId: string;
message: string;
messageId: string;
region: string;
diff --git a/src/interfaces/store.ts b/src/interfaces/store.ts
index eae63ad..d448a03 100644
--- a/src/interfaces/store.ts
+++ b/src/interfaces/store.ts
@@ -7,7 +7,7 @@ interface Outgoing {
user_id: string;
role: Roles.user;
message: string;
-};
+}
export interface ChatState {
outgoing: Outgoing;
@@ -30,8 +30,11 @@ export interface ChatState {
}
>;
historyIds: Array;
- }
- }
+ };
+ };
+ thread: {
+ [key: string]: string;
+ };
}
export interface ConfigState {
@@ -78,15 +81,15 @@ export interface MetaState {
export interface IntentionsState {
email: {
- current: string,
- success: boolean,
- error: boolean,
- isLoading: boolean,
- },
+ current: string;
+ success: boolean;
+ error: boolean;
+ isLoading: boolean;
+ };
response: {
- isFormVisible: boolean,
- error: boolean,
- },
+ isFormVisible: boolean;
+ error: boolean;
+ };
payment: {
isButtonVisible: boolean;
isFormVisible: boolean;
@@ -96,5 +99,5 @@ export interface IntentionsState {
messaging: {
isVisible: boolean;
};
- link: string
+ link: string;
}
diff --git a/src/middleware/socket.ts b/src/middleware/socket.ts
index 6ed1a4d..0343a23 100644
--- a/src/middleware/socket.ts
+++ b/src/middleware/socket.ts
@@ -9,12 +9,12 @@ import {
AssistantRecord,
ClientMessage,
SocketHistoryRecord,
- UserMessageContent
-} from '../interfaces'
+ UserMessageContent,
+} from '../interfaces';
import {
fillAssistantHistoryData,
fillInitialMessage,
- initiateRecordByTerm,
+ initiateRecordByThread,
resendMessage,
resetError,
resetHistory,
@@ -23,7 +23,6 @@ import {
setClosed,
setConnected,
setError,
- updateHistoryByTerm,
setIsLoading,
setIsStreaming,
setLastGroupPointer,
@@ -31,6 +30,7 @@ import {
setTypingTimeoutExpired,
showResendIcon,
syncMessageStatus,
+ updateHistoryByTerm,
} from '../store/slices/chat';
import { setConfig } from '../store/slices/config';
import { setRegion } from '../store/slices/meta';
@@ -40,7 +40,7 @@ let socket: Socket;
const chatMiddleware: Middleware = (store) => (next) => (action: Action & { $isSync: boolean | undefined }) => {
const { meta, chat, config } = store.getState();
- const term = getQueryParam();
+ const thread = chat.thread[getQueryParam()];
const onError = () => {
const { config } = store.getState();
@@ -49,7 +49,7 @@ const chatMiddleware: Middleware = (store) => (next) => (action: Action & { $isS
};
const dispatchRetry = () => {
- store.dispatch(showResendIcon({ itemId: [...chat.record[term].historyIds].pop() }));
+ store.dispatch(showResendIcon({ itemId: [...chat.record[thread].historyIds].pop() }));
onError();
};
@@ -59,7 +59,15 @@ const chatMiddleware: Middleware = (store) => (next) => (action: Action & { $isS
}
if (socket && socket.connected && data.message.trim() !== '') {
- socket.emit(Events.chat, { time: new Date().getTime(), ...data }, withTimeout(dispatchRetry));
+ socket.emit(
+ Events.chat,
+ {
+ time: new Date().getTime(),
+ threadId: chat.thread[getQueryParam()],
+ ...data,
+ },
+ withTimeout(dispatchRetry),
+ );
store.dispatch(resetError());
return;
}
@@ -77,7 +85,9 @@ const chatMiddleware: Middleware = (store) => (next) => (action: Action & { $isS
store.dispatch(setError(config.translations.error));
};
- const message = chat.record[term].historyData[itemId].content.map((item: { text: string }) => item.text).join(['\n']);
+ const message = chat.record[thread].historyData[itemId].content
+ .map((item: { text: string }) => item.text)
+ .join(['\n']);
if (socket?.connected && message.trim() !== '') {
socket.volatile.emit(
Events.chat,
@@ -85,11 +95,11 @@ const chatMiddleware: Middleware = (store) => (next) => (action: Action & { $isS
role: Roles.user,
message,
term: getQueryParam(),
- user_id: meta.cid,
- messageId: itemId,
region: meta.region,
+ messageId: itemId,
+ userId: meta.cid,
},
- withTimeout(onResendError)
+ withTimeout(onResendError),
);
store.dispatch(resetError());
} else {
@@ -101,10 +111,10 @@ const chatMiddleware: Middleware = (store) => (next) => (action: Action & { $isS
handleMessageSending({
role: Roles.user,
message: action.payload,
- term,
- user_id: meta.cid,
+ term: getQueryParam(),
region: meta.region,
- messageId: [...chat.record[term].historyIds].pop(),
+ userId: meta.cid,
+ messageId: [...chat.record[thread].historyIds].pop(),
});
}
@@ -126,24 +136,26 @@ const chatMiddleware: Middleware = (store) => (next) => (action: Action & { $isS
}
if (setTypingTimeoutExpired.match(action) && action.payload && !('$isSync' in action)) {
- const messageId = [...chat.record[term].historyIds].pop();
- const currentMessage = chat.record[term].historyData[messageId];
- const lastMessage = chat.record[term].historyData[messageId].content.map(({ text }: { text: UserMessageContent }) => text).join('\n');
+ const messageId = [...chat.record[thread].historyIds].pop();
+ const currentMessage = chat.record[thread].historyData[messageId];
+ const lastMessage = chat.record[thread].historyData[messageId].content
+ .map(({ text }: { text: UserMessageContent }) => text)
+ .join('\n');
if (currentMessage.role === Roles.user && lastMessage.trim() !== '') {
handleMessageSending({
role: Roles.user,
message: lastMessage,
- term,
- user_id: meta.cid,
+ term: getQueryParam(),
region: meta.region,
- messageId
+ messageId: messageId,
+ userId: meta.cid,
});
}
}
if (resendMessage.match(action) && !('$isSync' in action)) {
- // @ts-ignore must understand why the action is of type never??
+ // @ts-expect-error must understand why the action is of type never??
handleMessageResending(action.payload);
}
@@ -159,91 +171,114 @@ const chatMiddleware: Middleware = (store) => (next) => (action: Action & { $isS
socket.on(Events.connect, () => {
const { meta } = store.getState();
socket.sendBuffer = [];
- socket.emit(Events.chatHistory, { user_id: meta.cid, region: meta.region, term: getQueryParam() });
+ socket.emit(Events.chatHistory, { userId: meta.cid, region: meta.region, term: getQueryParam() });
store.dispatch(setConnected(true));
store.dispatch(setLastGroupPointer(uuidV4()));
- store.dispatch(initiateRecordByTerm());
});
- socket.on(Events.chatHistory, ({ history: servedHistory, errors, region, term: servedTerm, threadId }
- : { history: Array, errors: string[], region: string, term: string, threadId: string }) => {
+ socket.on(
+ Events.chatHistory,
+ ({
+ history: servedHistory,
+ errors,
+ region,
+ term: servedTerm,
+ threadId,
+ }: {
+ history: Array;
+ errors: string[];
+ region: string;
+ term: string;
+ threadId: string;
+ }) => {
+ store.dispatch(resetIsLoading());
+ store.dispatch(setIsStreaming(false));
+ store.dispatch(setRegion(region));
+ store.dispatch(initiateRecordByThread({ threadId }));
- store.dispatch(resetIsLoading());
- store.dispatch(setIsStreaming(false));
- store.dispatch(setRegion(region));
- const { config, meta } = store.getState();
+ const { config, meta } = store.getState();
- if (errors.length) {
- store.dispatch(setError(errors[0]));
- return;
- }
-
- if (servedHistory.length) {
- store.dispatch(syncMessageStatus({ history: servedHistory, term: servedTerm }));
- store.dispatch(updateHistoryByTerm({ history: servedHistory, term: servedTerm }));
- return
- }
+ if (errors.length) {
+ store.dispatch(setError(errors[0]));
+ return;
+ }
- !action.$isSync && store.dispatch(resetHistory({ term }));
- !action.$isSync && store.dispatch(setIsLoading());
- let interval = 0;
- config.aiProfile.initialMessage
- .forEach((element: AssistantHistoryInitialMessage, index: number, arr: Array) => {
- interval += 1000;
-
- setTimeout(() => {
-
- !action.$isSync && store.dispatch(fillInitialMessage({ message: element, term }));
-
- if (arr.length === index + 1) {
- !action.$isSync && config.aiProfile.initialMessage.forEach((message: SocketHistoryRecord) =>
- handleMessageSending({
- role: Roles.assistant,
- term: getQueryParam(),
- user_id: meta.cid,
- message: JSON.stringify(message.content),
- messageId: message.id,
- region: meta.region,
- }));
- store.dispatch(resetIsLoading());
- }
- }, interval);
+ if (servedHistory.length) {
+ store.dispatch(syncMessageStatus({ history: servedHistory, term: servedTerm }));
+ store.dispatch(updateHistoryByTerm({ history: servedHistory, term: servedTerm }));
+ return;
+ }
- });
- });
+ !action.$isSync && store.dispatch(resetHistory({ term: getQueryParam(), thread }));
+ !action.$isSync && store.dispatch(setIsLoading());
+ let interval = 0;
+ config.aiProfile.initialMessage.forEach(
+ (element: AssistantHistoryInitialMessage, index: number, arr: Array) => {
+ interval += 1000;
+
+ setTimeout(() => {
+ !action.$isSync && store.dispatch(fillInitialMessage({ message: element, term: getQueryParam() }));
+
+ if (arr.length === index + 1) {
+ !action.$isSync &&
+ config.aiProfile.initialMessage.forEach((message: SocketHistoryRecord) =>
+ handleMessageSending({
+ role: Roles.assistant,
+ term: getQueryParam(),
+ message: JSON.stringify(message.content),
+ region: meta.region,
+ userId: meta.cid,
+ messageId: message.id,
+ }),
+ );
+ store.dispatch(resetIsLoading());
+ }
+ }, interval);
+ },
+ );
+ },
+ );
- socket.on(Events.streamStart, ({ id, term }: { id: string, term: string }) => {
+ socket.on(Events.streamStart, ({ id, term }: { id: string; term: string }) => {
store.dispatch(setIsStreaming(true));
store.dispatch(resetIsLoading());
store.dispatch(resetOutgoing());
store.dispatch(resetError());
- store.dispatch(fillAssistantHistoryData({
- id, term
- }));
+ store.dispatch(
+ fillAssistantHistoryData({
+ id,
+ term,
+ }),
+ );
});
- socket.on(Events.streamData, (data: AssistantRecord
- & {
- id: string;
- term: string;
- errors: Array;
- }) => {
+ socket.on(
+ Events.streamData,
+ (
+ data: AssistantRecord & {
+ id: string;
+ term: string;
+ errors: Array;
+ },
+ ) => {
+ store.dispatch(
+ fillAssistantHistoryData({
+ id: data.id,
+ sequence: data.sequence,
+ term: data.term,
+ content: {
+ type: data.type,
+ [data.type]: data[data.type],
+ sequence: data.sequence,
+ },
+ }),
+ );
- store.dispatch(fillAssistantHistoryData({
- id: data.id,
- sequence: data.sequence,
- term: data.term,
- content: {
- type: data.type,
- [data.type]: data[data.type],
- sequence: data.sequence
+ if (data.errors?.length && !chat.error) {
+ store.dispatch(setError(data.errors[0]));
}
- }));
-
- if (data.errors?.length && !chat.error) {
- store.dispatch(setError(data.errors[0]));
- }
- });
+ },
+ );
socket.on(Events.streamEnd, () => {
store.dispatch(setIsStreaming(false));
diff --git a/src/store/initialState.ts b/src/store/initialState.ts
index 5aa7089..258414c 100644
--- a/src/store/initialState.ts
+++ b/src/store/initialState.ts
@@ -11,7 +11,7 @@ export const meta: MetaState = {
marketing: {
screen: {},
lastUtmParams: {},
- utmParams: {}
+ utmParams: {},
},
};
@@ -39,7 +39,7 @@ export const config: ConfigState = {
code: '',
image: '',
title: '',
- }
+ },
};
export const chat: ChatState = {
@@ -57,6 +57,7 @@ export const chat: ChatState = {
closed: false,
isStreaming: false,
record: {},
+ thread: {},
};
export const intentions: IntentionsState = {
@@ -79,7 +80,7 @@ export const intentions: IntentionsState = {
messaging: {
isVisible: false,
},
- link: ''
+ link: '',
};
export default { meta, config, intentions, chat };
diff --git a/src/store/slices/chat.ts b/src/store/slices/chat.ts
index 2368eb7..64573f9 100644
--- a/src/store/slices/chat.ts
+++ b/src/store/slices/chat.ts
@@ -2,7 +2,7 @@ import { createSlice, Draft, PayloadAction } from '@reduxjs/toolkit';
import produce from 'immer';
import { getUnifiedSequence } from '../../config';
-import { Definition, QueryParams, Roles } from '../../config/enums';
+import { Definition, Roles } from '../../config/enums';
import {
AssistantHistoryDataFiller,
AssistantHistoryInitialMessage,
@@ -30,63 +30,72 @@ const configSlice = createSlice({
resetOutgoing(state) {
state.outgoing = initialState.outgoing;
},
- initiateRecordByTerm(state) {
- const term = getQueryParam();
- if (!state.record[term]) {
- state.record[term] = {
- historyIds: [],
- historyData: {},
- };
- }
+ initiateRecordByThread(state, { payload: { threadId } }) {
+ return produce(state, (draft) => {
+ if (!draft.record[threadId]) {
+ draft.record[threadId] = {
+ historyIds: [],
+ historyData: {},
+ };
+ draft.thread[getQueryParam()] = threadId;
+ }
+ });
},
syncMessageStatus(state, { payload: { term, history } }) {
if (term !== getQueryParam()) {
return;
}
+ const thread = state.thread[term];
return produce(state, (draft) => {
- const unsentMessages = draft.record[term].historyIds.filter((id) => !history.find((record: SocketHistoryRecord) => record.id === id));
+ const unsentMessages = draft.record[thread].historyIds.filter(
+ (id) => !history.find((record: SocketHistoryRecord) => record.id === id),
+ );
unsentMessages.forEach(
(id) =>
- (draft.record[term].historyData[id].content = draft.record[term].historyData[id].content.map((record) => ({
- ...record,
- sent: false,
- resend: true,
- }))),
+ (draft.record[thread].historyData[id].content = draft.record[thread].historyData[id].content.map(
+ (record) => ({
+ ...record,
+ sent: false,
+ resend: true,
+ }),
+ )),
);
- })
+ });
},
- updateHistoryByTerm(state, { payload: { history, term } }: PayloadAction<{ history: Array, term: string }>) {
+ updateHistoryByTerm(
+ state,
+ { payload: { history, term } }: PayloadAction<{ history: Array; term: string }>,
+ ) {
if (term !== getQueryParam()) {
- return
+ return;
}
return produce(state, (draft) => {
const clientIds = draft.record[term].historyIds;
const serverIds = history.map(({ id }) => id);
+ const thread = state.thread[term];
// DEV NOTE: here i search for ids that are not recorded in the client
for (let i = 0; i < serverIds.length; i++) {
const currentId = serverIds[i];
if (!clientIds.includes(currentId)) {
clientIds.splice(i, 0, currentId);
- draft.record[term].historyData[currentId] = history[i];
+ draft.record[thread].historyData[currentId] = history[i];
}
}
- draft.record[term].historyIds = clientIds;
+ draft.record[thread].historyIds = clientIds;
});
},
- addPredefinedAssistantMessage(
- state,
- { payload }: PayloadAction
- ) {
+ addPredefinedAssistantMessage(state, { payload }: PayloadAction) {
const id = uuidV4();
const term = getQueryParam();
+ const thread = state.thread[term];
return produce(state, (draft: Draft): void => {
- draft.record[term].historyIds.push(id);
- draft.record[term].historyData[id] = {
+ draft.record[thread].historyIds.push(id);
+ draft.record[thread].historyData[id] = {
id,
role: Roles.assistant,
content: [
@@ -96,12 +105,16 @@ const configSlice = createSlice({
};
});
},
- fillAssistantHistoryData(state, { payload: { id, content, sequence, term } }: PayloadAction) {
+ fillAssistantHistoryData(
+ state,
+ { payload: { id, content, sequence, term } }: PayloadAction,
+ ) {
return produce(state, (draft: Draft) => {
+ const thread = state.thread[term];
- if (draft.record[term] && !draft.record[term].historyData[id]) {
- draft.record[term].historyData[id] = { id, role: Roles.assistant, content: [] };
- draft.record[term].historyIds.push(id);
+ if (draft.record[thread] && !draft.record[thread].historyData[id]) {
+ draft.record[thread].historyData[id] = { id, role: Roles.assistant, content: [] };
+ draft.record[thread].historyIds.push(id);
return;
}
@@ -116,8 +129,10 @@ const configSlice = createSlice({
};
// check for duplicates and unite them
- if (draft.record[term].historyData[id].content.some(
- (record) => record.sequence === data.sequence && record.type === data.type)
+ if (
+ draft.record[term].historyData[id].content.some(
+ (record) => record.sequence === data.sequence && record.type === data.type,
+ )
) {
draft.record[term].historyData[id].content = getUnifiedSequence(
draft.record[term].historyData[id].content as Array,
@@ -129,15 +144,13 @@ const configSlice = createSlice({
draft.record[term].historyData[id].content.push(data);
});
},
- fillUserHistoryData(
- state,
- { payload: { id, content, term } }: PayloadAction
- ) {
-
+ fillUserHistoryData(state, { payload: { id, content, term } }: PayloadAction) {
return produce(state, (draft: Draft) => {
let belongsTo;
- if (content.groupId && draft.record[term]?.historyData) {
- Object.entries(draft.record[term].historyData).forEach(([key, value]) => {
+ const thread = state.thread[term];
+
+ if (content.groupId && draft.record[thread]?.historyData) {
+ Object.entries(draft.record[thread].historyData).forEach(([key, value]) => {
if ([...value.content].find((el) => el.groupId === content.groupId)) {
belongsTo = key;
}
@@ -146,27 +159,30 @@ const configSlice = createSlice({
if (belongsTo) {
// this is due to keyboard interaction we send messages after timeout
- const userMessageRecord = draft.record[term].historyData[belongsTo];
+ const userMessageRecord = draft.record[thread].historyData[belongsTo];
userMessageRecord.content.push(content);
return;
}
- if (!draft.record[term].historyData[id]) {
- draft.record[term].historyData[id] = { id, role: Roles.user, content: [content] };
- draft.record[term].historyIds.push(id);
+ if (!draft.record[thread].historyData[id]) {
+ draft.record[thread].historyData[id] = { id, role: Roles.user, content: [content] };
+ draft.record[thread].historyIds.push(id);
}
});
},
fillInitialMessage(
state,
- { payload: { message, term } }: PayloadAction<{ message: AssistantHistoryInitialMessage, term: string }>
+ { payload: { message, term } }: PayloadAction<{ message: AssistantHistoryInitialMessage; term: string }>,
) {
const currentTerm = getQueryParam();
if (term !== currentTerm) {
- return
+ return;
}
- state.record[term].historyIds.push(message.id);
- state.record[term].historyData[message.id] = {
+
+ const thread = state.thread[term];
+
+ state.record[thread].historyIds.push(message.id);
+ state.record[thread].historyData[message.id] = {
id: message.id,
role: Roles.assistant,
time: new Date().getTime(),
@@ -191,7 +207,11 @@ const configSlice = createSlice({
showResendIcon(state, { payload }: PayloadAction<{ itemId: string }>) {
const term = getQueryParam();
return produce(state, (draft: Draft) => {
- draft.record[term].historyData[payload.itemId].content = draft.record[term].historyData[payload.itemId].content.map((record) => ({
+ const thread = state.thread[term];
+
+ draft.record[thread].historyData[payload.itemId].content = draft.record[thread].historyData[
+ payload.itemId
+ ].content.map((record) => ({
...record,
sent: false,
resend: true,
@@ -204,7 +224,11 @@ const configSlice = createSlice({
resendMessage(state, { payload }: PayloadAction<{ itemId: string }>) {
const term = getQueryParam();
return produce(state, (draft: Draft) => {
- draft.record[term].historyData[payload.itemId].content = draft.record[term].historyData[payload.itemId].content.map((record) => ({
+ const thread = state.thread[term];
+
+ draft.record[thread].historyData[payload.itemId].content = draft.record[thread].historyData[
+ payload.itemId
+ ].content.map((record) => ({
...record,
sent: true,
resend: false,
@@ -220,14 +244,14 @@ const configSlice = createSlice({
setIsStreaming(state, { payload }: PayloadAction) {
state.isStreaming = payload;
},
- resetHistory(state, { payload: { term } }) {
+ resetHistory(state, { payload: { term, thread } }) {
const currentTerm = getQueryParam();
if (currentTerm !== term) {
- return
+ return;
}
- state.record[term] = { historyData: {}, historyIds: [] };
+ state.record[thread] = { historyData: {}, historyIds: [] };
},
},
});
@@ -235,8 +259,11 @@ const configSlice = createSlice({
export const getChat = (state: { chat: ChatState }) => state.chat;
export const userMessageFindOne = (state: { chat: ChatState }) => {
const term = getQueryParam();
- return state.chat.record[term].historyIds.find((historyId) => state.chat.record[term].historyData[historyId].role === Roles.user);
-}
+ const threadId = state.chat.thread[term];
+ return state.chat.record[threadId].historyIds.find(
+ (historyId) => state.chat.record[threadId].historyData[historyId].role === Roles.user,
+ );
+};
export const sortBySequence = (a: AssistantRecord, b: AssistantRecord) => a.sequence - b.sequence;
export const {
@@ -259,8 +286,8 @@ export const {
fillUserHistoryData,
resetHistory,
fillInitialMessage,
- initiateRecordByTerm,
- syncMessageStatus
+ initiateRecordByThread,
+ syncMessageStatus,
} = configSlice.actions;
export default configSlice.reducer;