mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-20 22:51:06 +08:00
### 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)
117 lines
3.1 KiB
TypeScript
117 lines
3.1 KiB
TypeScript
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, useContext, useEffect, useRef, useState } from 'react';
|
|
|
|
export const useSendFeedback = (messageId: string) => {
|
|
const { visible, hideModal, showModal } = useSetModalState();
|
|
const { feedback, loading } = useFeedback();
|
|
|
|
const onFeedbackOk = useCallback(
|
|
async (params: IFeedbackRequestBody) => {
|
|
const ret = await feedback({
|
|
...params,
|
|
messageId: getMessagePureId(messageId),
|
|
});
|
|
|
|
if (ret === 0) {
|
|
hideModal();
|
|
}
|
|
},
|
|
[feedback, hideModal, messageId],
|
|
);
|
|
|
|
return {
|
|
loading,
|
|
onFeedbackOk,
|
|
visible,
|
|
hideModal,
|
|
showModal,
|
|
};
|
|
};
|
|
|
|
export const useRemoveMessage = (
|
|
messageId: string,
|
|
removeMessageById?: IRemoveMessageById['removeMessageById'],
|
|
) => {
|
|
const { deleteMessage, loading } = useDeleteMessage();
|
|
|
|
const onRemoveMessage = useCallback(async () => {
|
|
const pureId = getMessagePureId(messageId);
|
|
if (pureId) {
|
|
const retcode = await deleteMessage(pureId);
|
|
if (retcode === 0) {
|
|
removeMessageById?.(messageId);
|
|
}
|
|
}
|
|
}, [deleteMessage, messageId, removeMessageById]);
|
|
|
|
return { onRemoveMessage, loading };
|
|
};
|
|
|
|
export const useSpeech = (content: string, audioBinary?: string) => {
|
|
const ref = useRef<HTMLAudioElement>(null);
|
|
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();
|
|
}, []);
|
|
|
|
const speech = useCallback(async () => {
|
|
const response = await read({ text: content });
|
|
if (response) {
|
|
player?.current?.feedWithResponse(response);
|
|
}
|
|
}, [read, content]);
|
|
|
|
const handleRead = useCallback(async () => {
|
|
if (isPlaying) {
|
|
setIsPlaying(false);
|
|
pause();
|
|
} else {
|
|
setIsPlaying(true);
|
|
speech();
|
|
}
|
|
}, [setIsPlaying, speech, isPlaying, pause]);
|
|
|
|
useEffect(() => {
|
|
if (audioBinary) {
|
|
const units = hexStringToUint8Array(audioBinary);
|
|
if (units) {
|
|
player.current?.feed(units);
|
|
}
|
|
}
|
|
}, [audioBinary]);
|
|
|
|
useEffect(() => {
|
|
initialize();
|
|
}, [initialize]);
|
|
|
|
return { ref, handleRead, isPlaying };
|
|
};
|