-
Notifications
You must be signed in to change notification settings - Fork 21
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
3156: Align Typing Indicator and hide on new message #3159
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's working great.. but it needs a bit of cleaning up 🚀
Also I think it needs a release note 🙂
@@ -17,6 +17,7 @@ const InitialMessage = styled.div` | |||
|
|||
const TypingIndicatorWrapper = styled(Message)` | |||
width: max-content; | |||
margin-left: 33px; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔧 The robot icon is 24px + 8px gap = 32px
And keep in mind that we started to use a multiply of 4 for ui.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The robot icon width is 25px in my branch - is this a recent change?
"And keep in mind that we started to use a multiply of 4 for ui." What exactly does this mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have a new design coming for integreat you can check it out:
https://www.figma.com/design/o5p3ArtrrmOqG6lJggAQE5/%F0%9F%9F%A8-Designsystem-Integreat-(MUI)?node-id=2831-17369&p=f&t=2YiZzVRBygegXzei-0
Its better to use numbers for styling that is dividable by 4 like 32 or 16 for ex. to keep styling consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So should I change the icon width to 24px then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ve kept it at 33px for now. I think aligning to 'multiples of 4' makes sense once the new design is introduced and the CSS is updated consistently. Changing this now would introduce an inconsistency with the current icon size (25px) rather than improving it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you didn't change this, it is optional. However, I agree with @bahaaTuffaha that we generally prefer to have multiples of 4. Obviously only makes sense if you change the icon size to 24 as well.
On another note however, I think setting a hardcoded width based on some value in some other file is usually not best practice and may break in the future. A better solution would perhaps be to use the whole ChatMessage
component normally instead. Seems like that is not easily possible though since it requires a normal message model. So not sure what exactly to do here :/
At least at the moment this is broken on rtl layouts (e.g. arbabic) due to the use of margin-left
:
const ChatConversation = ({ messages, hasError, className }: ChatConversationProps): ReactElement => { | ||
const { t } = useTranslation('chat') | ||
const [messagesCount, setMessagesCount] = useState(0) | ||
const [typingIndicatorVisible, setTypingIndicatorVisible] = useState(false) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔧 Undo this line.
useEffect(() => { | ||
if (waitingForAnswer) { | ||
setTypingIndicatorVisible(true) | ||
|
||
const typingIndicatorTimeout = setTimeout(() => { | ||
setTypingIndicatorVisible(false) | ||
}, TYPING_INDICATOR_TIMEOUT) | ||
|
||
return () => clearTimeout(typingIndicatorTimeout) | ||
} | ||
return () => undefined | ||
}, [waitingForAnswer]) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can reuse the same scrolling functionality for both and adding an optional arg as callback that wont be called unless it finish changing the state.
const scrollToBottom = async (beforeScroll?: () => void) => {
if (beforeScroll) {
await beforeScroll()
}
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' })
}
useEffect(() => {
if (messagesCount < messages.length) {
scrollToBottom()
setMessagesCount(messages.length)
}
}, [messages, messagesCount, typingIndicatorVisible])
useEffect(() => {
if (waitingForAnswer) {
scrollToBottom(() => setTypingIndicatorVisible(true))
const timeout = setTimeout(() => setTypingIndicatorVisible(false), 60000)
return () => clearTimeout(timeout)
}
setTypingIndicatorVisible(false)
}, [waitingForAnswer])
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This '() => setTypingIndicatorVisible(true)' is a synchronous function and to my understanding finishes when the state is changed (doesn't wait for rendering). I think the easiest solution is to have the useEffect for 'TypingIndicatorVisible' (because then it is already rendered)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 hmm the function contents are synchronous but am waiting for that callback await beforeScroll()
to be done before changing the scrolling ref...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But if you await a synchronous function it just immediately executes it.
From what I understood:
- With your version we catch it at the point of state-changed. (Edit: This is actually not true, its only after the state changes are queued)
- With my version we are a little bit further down the line: state-changed > virtual DOM changed > actual DOM changed (although not yet rendered)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have decided to keep the useEffect() logic, in order to trigger the scrolling after the DOM was actually changed. But I have kept the scrolldown function as you suggested it.
Where do I create a release note? I havent done that yet (btw this is not a new feature, but a bug fix) |
There is a folder called If the release note is unnecessary for this bug we can remove it 😄 |
3baca01
to
7dbb9e1
Compare
@@ -17,6 +17,7 @@ const InitialMessage = styled.div` | |||
|
|||
const TypingIndicatorWrapper = styled(Message)` | |||
width: max-content; | |||
margin-left: 33px; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you didn't change this, it is optional. However, I agree with @bahaaTuffaha that we generally prefer to have multiples of 4. Obviously only makes sense if you change the icon size to 24 as well.
On another note however, I think setting a hardcoded width based on some value in some other file is usually not best practice and may break in the future. A better solution would perhaps be to use the whole ChatMessage
component normally instead. Seems like that is not easily possible though since it requires a normal message model. So not sure what exactly to do here :/
At least at the moment this is broken on rtl layouts (e.g. arbabic) due to the use of margin-left
:
const scrollToBottom = async (beforeScroll?: () => void | Promise<void>) => { | ||
if (beforeScroll) { | ||
await beforeScroll() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔧 This is never actually used, is it?
const scrollToBottom = async (beforeScroll?: () => void | Promise<void>) => { | |
if (beforeScroll) { | |
await beforeScroll() | |
} | |
const scrollToBottom = () => { |
useEffect(() => { | ||
if (typingIndicatorVisible) { | ||
scrollToBottom() | ||
} | ||
}, [typingIndicatorVisible]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔 Why is this necessary?
Short Description
I aligned the Typing Indicator with the other messages' position & when a new message from Zammad comes in, the Indicator will be hidden.
Proposed Changes
Side Effects
Testing
Resolved Issues
Fixes: #3156