92 lines
2.7 KiB
TypeScript

import { useCallback, useEffect, useMemo, useRef } from 'react'
import { TextArea } from '@janhq/joi'
import { useAtom, useAtomValue } from 'jotai'
import { twMerge } from 'tailwind-merge'
import { currentPromptAtom } from '@/containers/Providers/Jotai'
import { getCurrentChatMessagesAtom } from '@/helpers/atoms/ChatMessage.atom'
import { spellCheckAtom } from '@/helpers/atoms/Setting.atom'
import {
getActiveThreadIdAtom,
isGeneratingResponseAtom,
} from '@/helpers/atoms/Thread.atom'
type Props = {
isSettingActive: boolean
onSendMessageClick: (message: string) => void
}
const ChatTextInput: React.FC<Props> = ({
isSettingActive,
onSendMessageClick,
}) => {
const messages = useAtomValue(getCurrentChatMessagesAtom)
const [currentPrompt, setCurrentPrompt] = useAtom(currentPromptAtom)
const textareaRef = useRef<HTMLTextAreaElement>(null)
const activeThreadId = useAtomValue(getActiveThreadIdAtom)
const spellCheck = useAtomValue(spellCheckAtom)
const isGeneratingResponse = useAtomValue(isGeneratingResponseAtom)
const disabled = useMemo(() => !activeThreadId, [activeThreadId])
const onChange = useCallback(
(e: React.ChangeEvent<HTMLTextAreaElement>) => {
setCurrentPrompt(e.target.value)
},
[setCurrentPrompt]
)
useEffect(() => {
textareaRef.current?.focus()
})
useEffect(() => {
if (textareaRef.current?.clientHeight) {
textareaRef.current.style.height = isSettingActive ? '100px' : '40px'
textareaRef.current.style.height = textareaRef.current.scrollHeight + 'px'
textareaRef.current.style.overflow =
textareaRef.current.clientHeight >= 390 ? 'auto' : 'hidden'
}
}, [textareaRef.current?.clientHeight, currentPrompt, isSettingActive])
const onKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === 'Enter' && !e.shiftKey && !e.nativeEvent.isComposing) {
e.preventDefault()
if (isGeneratingResponse) return
const lastMessage = messages[messages.length - 1]
if (!lastMessage || lastMessage.status !== 'in_progress') {
onSendMessageClick(currentPrompt)
return
}
}
},
[messages, isGeneratingResponse, currentPrompt, onSendMessageClick]
)
return (
<TextArea
className={twMerge(
'relative max-h-[400px] resize-none pr-20',
isSettingActive && 'pb-14 pr-16'
)}
spellCheck={spellCheck}
data-testid="txt-input-chat"
style={{ height: isSettingActive ? '100px' : '40px' }}
ref={textareaRef}
onKeyDown={onKeyDown}
placeholder="Ask me anything"
disabled={disabled}
value={currentPrompt}
onChange={onChange}
/>
)
}
export default ChatTextInput