diff --git a/client/src/components/Chat/Messages/MessageAudio.tsx b/client/src/components/Chat/Messages/MessageAudio.tsx index bdefffa6ec..d543f31d07 100644 --- a/client/src/components/Chat/Messages/MessageAudio.tsx +++ b/client/src/components/Chat/Messages/MessageAudio.tsx @@ -1,6 +1,7 @@ -import { useEffect } from 'react'; +import { useEffect, useRef, useState } from 'react'; import { useRecoilValue } from 'recoil'; import type { TMessage } from 'librechat-data-provider'; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '~/components/ui'; import { VolumeIcon, VolumeMuteIcon, Spinner } from '~/components/svg'; import { useLocalize, useTextToSpeech } from '~/hooks'; import store from '~/store'; @@ -14,32 +15,77 @@ type THoverButtons = { export default function MessageAudio({ index, message, isLast }: THoverButtons) { const localize = useLocalize(); const playbackRate = useRecoilValue(store.playbackRate); + const [audioText, setAudioText] = useState(localize('com_ui_info_read_aloud')); + const [tooltipOpen, setTooltipOpen] = useState(false); + const [wasLongPress, setWasLongPress] = useState(false); const { toggleSpeech, isSpeaking, isLoading, audioRef } = useTextToSpeech(message, isLast, index); + const isMouseDownRef = useRef(false); + const timerRef = useRef(null); + const counterRef = useRef(0); + const renderIcon = (size: string) => { if (isLoading) { return ; } + return isSpeaking ? : ; + }; - if (isSpeaking) { - return ; + const handleMouseDown = () => { + setWasLongPress(false); + setTooltipOpen(true); + if (isMouseDownRef.current) { + return; + } + isMouseDownRef.current = true; + counterRef.current = 2; + setAudioText(localize('com_ui_hold_mouse_download', counterRef.current.toString())); + timerRef.current = setInterval(() => { + counterRef.current--; + if (counterRef.current >= 0) { + setAudioText(localize('com_ui_hold_mouse_download', counterRef.current.toString())); + } + if (isMouseDownRef.current && counterRef.current === 0) { + setAudioText(localize('com_ui_downloading')); + toggleSpeech(true); + } + if (counterRef.current < 0 && timerRef.current) { + clearInterval(timerRef.current); + } + }, 1000); + + window.addEventListener('mouseup', handleMouseUp); + }; + + const handleMouseUp = () => { + if (counterRef.current > 0) { + toggleSpeech(false); } - return ; + if (counterRef.current === 0) { + setWasLongPress(true); + } + + setTooltipOpen(false); + isMouseDownRef.current = false; + if (timerRef.current) { + clearInterval(timerRef.current); + timerRef.current = null; + setAudioText(localize('com_ui_info_read_aloud')); + } + + window.removeEventListener('mouseup', handleMouseUp); }; useEffect(() => { const messageAudio = document.getElementById( `audio-${message.messageId}`, ) as HTMLAudioElement | null; - if (!messageAudio) { - return; - } if ( - playbackRate && - playbackRate > 0 && messageAudio && + playbackRate !== null && + playbackRate > 0 && messageAudio.playbackRate !== playbackRate ) { messageAudio.playbackRate = playbackRate; @@ -47,48 +93,55 @@ export default function MessageAudio({ index, message, isLast }: THoverButtons) }, [audioRef, isSpeaking, playbackRate, message.messageId]); return ( - <> - -