### What problem does this PR solve? feat: After the voice in the new conversation window is played, jump to the tab of the conversation #1877 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.11.0
| @@ -2,10 +2,11 @@ import { useDeleteMessage, useFeedback } from '@/hooks/chat-hooks'; | |||
| import { useSetModalState } from '@/hooks/common-hooks'; | |||
| import { IRemoveMessageById, useSpeechWithSse } from '@/hooks/logic-hooks'; | |||
| import { IFeedbackRequestBody } from '@/interfaces/request/chat'; | |||
| import { ConversationContext } from '@/pages/chat/context'; | |||
| import { getMessagePureId } from '@/utils/chat'; | |||
| import { hexStringToUint8Array } from '@/utils/common-util'; | |||
| import { SpeechPlayer } from 'openai-speech-stream-player'; | |||
| import { useCallback, useEffect, useRef, useState } from 'react'; | |||
| import { useCallback, useContext, useEffect, useRef, useState } from 'react'; | |||
| export const useSendFeedback = (messageId: string) => { | |||
| const { visible, hideModal, showModal } = useSetModalState(); | |||
| @@ -58,21 +59,24 @@ export const useSpeech = (content: string, audioBinary?: string) => { | |||
| const { read } = useSpeechWithSse(); | |||
| const player = useRef<SpeechPlayer>(); | |||
| const [isPlaying, setIsPlaying] = useState<boolean>(false); | |||
| const callback = useContext(ConversationContext); | |||
| const initialize = useCallback(async () => { | |||
| player.current = new SpeechPlayer({ | |||
| audio: ref.current!, | |||
| onPlaying: () => { | |||
| setIsPlaying(true); | |||
| callback?.(true); | |||
| }, | |||
| onPause: () => { | |||
| setIsPlaying(false); | |||
| callback?.(false); | |||
| }, | |||
| onChunkEnd: () => {}, | |||
| mimeType: 'audio/mpeg', | |||
| }); | |||
| await player.current.init(); | |||
| }, []); | |||
| }, [callback]); | |||
| const pause = useCallback(() => { | |||
| player.current?.pause(); | |||
| @@ -6,6 +6,7 @@ export interface PromptConfig { | |||
| parameters: Parameter[]; | |||
| prologue: string; | |||
| system: string; | |||
| tts?: boolean; | |||
| } | |||
| export interface Parameter { | |||
| @@ -19,6 +19,7 @@ import { | |||
| } from '@/hooks/chat-hooks'; | |||
| import { useFetchUserInfo } from '@/hooks/user-setting-hooks'; | |||
| import { memo } from 'react'; | |||
| import { ConversationContext } from '../context'; | |||
| import styles from './index.less'; | |||
| const ChatContainer = () => { | |||
| @@ -26,15 +27,16 @@ const ChatContainer = () => { | |||
| const { data: conversation } = useFetchNextConversation(); | |||
| const { | |||
| value, | |||
| ref, | |||
| loading, | |||
| sendLoading, | |||
| derivedMessages, | |||
| handleInputChange, | |||
| handlePressEnter, | |||
| value, | |||
| regenerateMessage, | |||
| removeMessageById, | |||
| redirectToNewConversation, | |||
| } = useSendNextMessage(); | |||
| const { visible, hideModal, documentId, selectedChunk, clickDocumentButton } = | |||
| @@ -52,33 +54,35 @@ const ChatContainer = () => { | |||
| <Flex flex={1} vertical className={styles.messageContainer}> | |||
| <div> | |||
| <Spin spinning={loading}> | |||
| {derivedMessages?.map((message, i) => { | |||
| return ( | |||
| <MessageItem | |||
| loading={ | |||
| message.role === MessageType.Assistant && | |||
| sendLoading && | |||
| derivedMessages.length - 1 === i | |||
| } | |||
| key={message.id} | |||
| item={message} | |||
| nickname={userInfo.nickname} | |||
| avatar={userInfo.avatar} | |||
| reference={buildMessageItemReference( | |||
| { | |||
| message: derivedMessages, | |||
| reference: conversation.reference, | |||
| }, | |||
| message, | |||
| )} | |||
| clickDocumentButton={clickDocumentButton} | |||
| index={i} | |||
| removeMessageById={removeMessageById} | |||
| regenerateMessage={regenerateMessage} | |||
| sendLoading={sendLoading} | |||
| ></MessageItem> | |||
| ); | |||
| })} | |||
| <ConversationContext.Provider value={redirectToNewConversation}> | |||
| {derivedMessages?.map((message, i) => { | |||
| return ( | |||
| <MessageItem | |||
| loading={ | |||
| message.role === MessageType.Assistant && | |||
| sendLoading && | |||
| derivedMessages.length - 1 === i | |||
| } | |||
| key={message.id} | |||
| item={message} | |||
| nickname={userInfo.nickname} | |||
| avatar={userInfo.avatar} | |||
| reference={buildMessageItemReference( | |||
| { | |||
| message: derivedMessages, | |||
| reference: conversation.reference, | |||
| }, | |||
| message, | |||
| )} | |||
| clickDocumentButton={clickDocumentButton} | |||
| index={i} | |||
| removeMessageById={removeMessageById} | |||
| regenerateMessage={regenerateMessage} | |||
| sendLoading={sendLoading} | |||
| ></MessageItem> | |||
| ); | |||
| })} | |||
| </ConversationContext.Provider> | |||
| </Spin> | |||
| </div> | |||
| <div ref={ref} /> | |||
| @@ -0,0 +1,5 @@ | |||
| import { createContext } from 'react'; | |||
| export const ConversationContext = createContext< | |||
| null | ((isPlaying: boolean) => void) | |||
| >(null); | |||
| @@ -32,6 +32,7 @@ import { | |||
| useCallback, | |||
| useEffect, | |||
| useMemo, | |||
| useRef, | |||
| useState, | |||
| } from 'react'; | |||
| import { useSearchParams } from 'umi'; | |||
| @@ -340,6 +341,17 @@ export const useSendNextMessage = () => { | |||
| removeMessageById, | |||
| removeMessagesAfterCurrentMessage, | |||
| } = useSelectNextMessages(); | |||
| const { data: dialog } = useFetchNextDialog(); | |||
| const currentConversationIdRef = useRef<string>(''); | |||
| const redirectToNewConversation = useCallback( | |||
| (isPlaying: boolean) => { | |||
| if (!conversationId && dialog?.prompt_config?.tts && !isPlaying) { | |||
| handleClickConversation(currentConversationIdRef.current); | |||
| } | |||
| }, | |||
| [dialog, handleClickConversation, conversationId], | |||
| ); | |||
| const sendMessage = useCallback( | |||
| async ({ | |||
| @@ -365,7 +377,9 @@ export const useSendNextMessage = () => { | |||
| if (currentConversationId) { | |||
| console.info('111'); | |||
| // new conversation | |||
| handleClickConversation(currentConversationId); | |||
| if (!dialog?.prompt_config?.tts) { | |||
| handleClickConversation(currentConversationId); | |||
| } | |||
| } else { | |||
| console.info('222'); | |||
| // fetchConversation(conversationId); | |||
| @@ -373,6 +387,7 @@ export const useSendNextMessage = () => { | |||
| } | |||
| }, | |||
| [ | |||
| dialog, | |||
| derivedMessages, | |||
| conversationId, | |||
| handleClickConversation, | |||
| @@ -390,6 +405,7 @@ export const useSendNextMessage = () => { | |||
| const data = await setConversation(message.content); | |||
| if (data.retcode === 0) { | |||
| const id = data.data.id; | |||
| currentConversationIdRef.current = id; | |||
| sendMessage({ | |||
| message, | |||
| currentConversationId: id, | |||
| @@ -463,6 +479,7 @@ export const useSendNextMessage = () => { | |||
| ref, | |||
| derivedMessages, | |||
| removeMessageById, | |||
| redirectToNewConversation, | |||
| }; | |||
| }; | |||