diff --git a/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx index 10cb239d4746..8f8a9db520e6 100644 --- a/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx +++ b/src/app/(main)/chat/(workspace)/@conversation/features/ChatInput/Desktop/Footer/index.tsx @@ -13,7 +13,7 @@ import { useSendMessage } from '@/features/ChatInput/useSend'; import { useAgentStore } from '@/store/agent'; import { agentSelectors } from '@/store/agent/slices/chat'; import { useChatStore } from '@/store/chat'; -import { chatSelectors } from '@/store/chat/selectors'; +import { chatSelectors, topicSelectors } from '@/store/chat/selectors'; import { filesSelectors, useFileStore } from '@/store/file'; import { useUserStore } from '@/store/user'; import { modelProviderSelectors, preferenceSelectors } from '@/store/user/selectors'; @@ -61,10 +61,20 @@ const Footer = memo(({ setExpand }) => { const { theme, styles } = useStyles(); - const [isAIGenerating, stopGenerateMessage] = useChatStore((s) => [ + const [ + isAIGenerating, + isHasMessageLoading, + isCreatingMessage, + isCreatingTopic, + stopGenerateMessage, + ] = useChatStore((s) => [ chatSelectors.isAIGenerating(s), + chatSelectors.isHasMessageLoading(s), + chatSelectors.isCreatingMessage(s), + topicSelectors.isCreatingTopic(s), s.stopGenerateMessage, ]); + const isImageUploading = useFileStore(filesSelectors.isImageUploading); const model = useAgentStore(agentSelectors.currentAgentModel); @@ -93,6 +103,9 @@ const Footer = memo(({ setExpand }) => { const wrapperShortcut = useCmdEnterToSend ? enter : cmdEnter; + const buttonDisabled = + isImageUploading || isHasMessageLoading || isCreatingTopic || isCreatingMessage; + return ( (({ setExpand }) => { ) : ( - + )} diff --git a/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/TopicItem.tsx b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/TopicItem.tsx index 42252183f25f..896018467acf 100644 --- a/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/TopicItem.tsx +++ b/src/app/(main)/chat/(workspace)/@topic/features/TopicListContent/TopicItem.tsx @@ -20,13 +20,16 @@ const useStyles = createStyles(({ css, token, isDarkMode }) => ({ container: css` cursor: pointer; - width: calc(100% - 16px); margin-block: 2px; margin-inline: 8px; padding: 8px; border-radius: ${token.borderRadius}px; + &.topic-item { + width: calc(100% - 16px); + } + &:hover { background: ${token.colorFillSecondary}; } @@ -52,7 +55,7 @@ const TopicItem = memo(({ title, active, id, fav }) => { return ( { diff --git a/src/store/chat/slices/message/action.ts b/src/store/chat/slices/message/action.ts index 6386dce3fdb0..e930b7456397 100644 --- a/src/store/chat/slices/message/action.ts +++ b/src/store/chat/slices/message/action.ts @@ -232,6 +232,7 @@ export const chatMessage: StateCreator< // if message is empty or no files, then stop if (!message && isNoFile) return; + set({ isCreatingMessage: true }, false, 'creatingMessage/start'); const newMessage: CreateMessageParams = { content: message, @@ -310,9 +311,10 @@ export const chatMessage: StateCreator< // Get the current messages to generate AI response const messages = chatSelectors.currentChats(get()); - await internal_coreProcessMessage(messages, id, { isWelcomeQuestion }); + set({ isCreatingMessage: false }, false, 'creatingMessage/stop'); + // if autoCreateTopic is false, then stop if (!agentConfig.enableAutoCreateTopic) return; @@ -707,7 +709,6 @@ export const chatMessage: StateCreator< internal_createMessage: async (message, context) => { const { internal_createTmpMessage, refreshMessages, internal_toggleMessageLoading } = get(); let tempId = context?.tempMessageId; - if (!tempId) { // use optimistic update to avoid the slow waiting tempId = internal_createTmpMessage(message); @@ -721,7 +722,6 @@ export const chatMessage: StateCreator< } internal_toggleMessageLoading(false, tempId); - return id; }, diff --git a/src/store/chat/slices/message/initialState.ts b/src/store/chat/slices/message/initialState.ts index 40df9dfcc543..cd6223cb6057 100644 --- a/src/store/chat/slices/message/initialState.ts +++ b/src/store/chat/slices/message/initialState.ts @@ -12,6 +12,7 @@ export interface ChatMessageState { */ chatLoadingIds: string[]; inputMessage: string; + isCreatingMessage: boolean; /** * is the message is editing */ @@ -35,6 +36,7 @@ export const initialMessageState: ChatMessageState = { activeId: 'inbox', chatLoadingIds: [], inputMessage: '', + isCreatingMessage: false, messageEditingIds: [], messageLoadingIds: [], messagesInit: false, diff --git a/src/store/chat/slices/message/selectors.ts b/src/store/chat/slices/message/selectors.ts index 6deddc081b0b..5a15e002fe5b 100644 --- a/src/store/chat/slices/message/selectors.ts +++ b/src/store/chat/slices/message/selectors.ts @@ -125,6 +125,9 @@ const isCurrentChatLoaded = (s: ChatStore) => !!s.messagesMap[currentChatKey(s)] const isMessageEditing = (id: string) => (s: ChatStore) => s.messageEditingIds.includes(id); const isMessageLoading = (id: string) => (s: ChatStore) => s.messageLoadingIds.includes(id); +const isHasMessageLoading = (s: ChatStore) => s.messageLoadingIds.length > 0; +const isCreatingMessage = (s: ChatStore) => s.isCreatingMessage; + const isMessageGenerating = (id: string) => (s: ChatStore) => s.chatLoadingIds.includes(id); const isToolCallStreaming = (id: string, index: number) => (s: ChatStore) => { const isLoading = s.toolCallingStreamIds[id]; @@ -146,7 +149,9 @@ export const chatSelectors = { getMessageById, getTraceIdByMessageId, isAIGenerating, + isCreatingMessage, isCurrentChatLoaded, + isHasMessageLoading, isMessageEditing, isMessageGenerating, isMessageLoading, diff --git a/src/store/chat/slices/topic/action.ts b/src/store/chat/slices/topic/action.ts index da19ab1b1985..1404ab7451b9 100644 --- a/src/store/chat/slices/topic/action.ts +++ b/src/store/chat/slices/topic/action.ts @@ -80,13 +80,14 @@ export const chatTopic: StateCreator< const { activeId, internal_createTopic } = get(); const messages = chatSelectors.currentChats(get()); + + set({ creatingTopic: true }, false, n('creatingTopic/start')); const topicId = await internal_createTopic({ sessionId: activeId, title: t('topic.defaultTitle', { ns: 'chat' }), messages: messages.map((m) => m.id), }); - - // get().internal_updateTopicLoading(topicId, true); + set({ creatingTopic: false }, false, n('creatingTopic/end')); return topicId; }, diff --git a/src/store/chat/slices/topic/initialState.ts b/src/store/chat/slices/topic/initialState.ts index 25f9eb6c3a00..d5d83624ae11 100644 --- a/src/store/chat/slices/topic/initialState.ts +++ b/src/store/chat/slices/topic/initialState.ts @@ -3,6 +3,7 @@ import { ChatTopic } from '@/types/topic'; export interface ChatTopicState { // TODO: need to add the null to the type activeTopicId?: string; + creatingTopic: boolean; isSearchingTopic: boolean; searchTopics: ChatTopic[]; topicLoadingIds: string[]; @@ -17,6 +18,7 @@ export interface ChatTopicState { export const initialTopicState: ChatTopicState = { activeTopicId: null as any, + creatingTopic: false, isSearchingTopic: false, searchTopics: [], topicLoadingIds: [], diff --git a/src/store/chat/slices/topic/selectors.ts b/src/store/chat/slices/topic/selectors.ts index 5fd80f103f91..3c896e27cb84 100644 --- a/src/store/chat/slices/topic/selectors.ts +++ b/src/store/chat/slices/topic/selectors.ts @@ -21,6 +21,7 @@ const getTopicById = (id: string) => (s: ChatStore): ChatTopic | undefined => currentTopics(s)?.find((topic) => topic.id === id); +const isCreatingTopic = (s: ChatStore) => s.creatingTopic; export const topicSelectors = { currentActiveTopic, @@ -29,5 +30,6 @@ export const topicSelectors = { currentUnFavTopics, displayTopics, getTopicById, + isCreatingTopic, searchTopics, };