Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

3029: Add typing indicator for automatic replies in chat #3056

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion web/src/components/ChatConversation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import ChatMessageModel from 'shared/api/models/ChatMessageModel'

import ChatMessage from './ChatMessage'
import ChatMessage, { Message } from './ChatMessage'

const Container = styled.div`
font-size: ${props => props.theme.fonts.hintFontSize};
Expand All @@ -15,6 +15,10 @@
margin-bottom: 12px;
`

const TypingIndicator = styled(Message)`
width: max-content;
`

const ErrorSendingStatus = styled.div`
background-color: ${props => props.theme.colors.invalidInput};
border-radius: 5px;
Expand All @@ -29,30 +33,53 @@
className?: string
}

const TYPING_INDICATOR_TIMEOUT = 60000

const ChatConversation = ({ messages, hasError, className }: ChatConversationProps): ReactElement => {
const { t } = useTranslation('chat')
const [messagesCount, setMessagesCount] = useState(0)
const [showTypingIndicator, setShowTypingIndicator] = useState(false)
const messagesEndRef = useRef<HTMLDivElement>(null)
const lastUserMessage = messages[messages.length - 1]
const beforelastUserMessage = messages[messages.length - 2]
lunars97 marked this conversation as resolved.
Show resolved Hide resolved

useEffect(() => {
if (messagesCount < messages.length) {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
setMessagesCount(messages.length)
}
}, [messages, messagesCount])

useEffect(() => {
if (lastUserMessage?.userIsAuthor || beforelastUserMessage?.userIsAuthor) {
setShowTypingIndicator(true)

const typingIndicatorTimeout = setTimeout(() => {
setShowTypingIndicator(false)
}, TYPING_INDICATOR_TIMEOUT)

return () => clearTimeout(typingIndicatorTimeout)
}
return () => undefined
}, [lastUserMessage?.userIsAuthor, beforelastUserMessage?.userIsAuthor])

return (
<Container className={className}>
{messages.length > 0 ? (
<>
{!hasError && <InitialMessage>{t('initialMessage')}</InitialMessage>}
{messages.map((message, index) => (
<ChatMessage
message={message}
key={message.id}
showIcon={messages[index - 1]?.userIsAuthor !== message.userIsAuthor}
/>
))}
{showTypingIndicator && (
<TypingIndicator>
<strong>...</strong>
</TypingIndicator>
)}

Check warning on line 82 in web/src/components/ChatConversation.tsx

View check run for this annotation

CodeScene Delta Analysis / CodeScene Cloud Delta Analysis (main)

❌ New issue: Complex Method

ChatConversation has a cyclomatic complexity of 14, threshold = 10. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.
<div ref={messagesEndRef} />
</>
) : (
Expand Down
9 changes: 5 additions & 4 deletions web/src/components/ChatMessage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { TFunction } from 'i18next'
import React, { ReactElement } from 'react'
import React, { ReactElement, ReactNode } from 'react'
import { useTranslation } from 'react-i18next'
import styled from 'styled-components'

Expand All @@ -9,7 +9,7 @@ import { ChatBot, ChatPerson } from '../assets'
import RemoteContent from './RemoteContent'
import Icon from './base/Icon'

const Message = styled.div`
export const Message = styled.div`
border-radius: 5px;
padding: 8px;
border: 1px solid ${props => props.theme.colors.textDecorationColor};
Expand Down Expand Up @@ -47,7 +47,7 @@ const Circle = styled.div`
font-size: ${props => props.theme.fonts.decorativeFontSizeSmall};
`

type ChatMessageProps = { message: ChatMessageModel; showIcon: boolean }
type ChatMessageProps = { message: ChatMessageModel; showIcon: boolean; children?: ReactNode }
lunars97 marked this conversation as resolved.
Show resolved Hide resolved

const getIcon = (userIsAuthor: boolean, isAutomaticAnswer: boolean, t: TFunction<'chat'>): ReactElement => {
if (userIsAuthor) {
Expand All @@ -57,14 +57,15 @@ const getIcon = (userIsAuthor: boolean, isAutomaticAnswer: boolean, t: TFunction
return <Icon src={icon} />
}

const ChatMessage = ({ message, showIcon }: ChatMessageProps): ReactElement => {
const ChatMessage = ({ message, showIcon, children }: ChatMessageProps): ReactElement => {
const { t } = useTranslation('chat')
const { body, userIsAuthor, isAutomaticAnswer } = message

return (
<Container $isAuthor={userIsAuthor}>
<IconContainer $visible={showIcon}>{getIcon(userIsAuthor, isAutomaticAnswer, t)}</IconContainer>
<Message data-testid={message.id}>
{children}
<RemoteContent html={body} smallText />
</Message>
</Container>
Expand Down
Loading