From 08ce8fa40da303200a12b4f5a565b3a47c606e63 Mon Sep 17 00:00:00 2001 From: Martin Lopez Date: Tue, 29 Jul 2025 14:15:41 -0300 Subject: [PATCH 1/3] feat: add support for display unread messages badge Closes #40 --- .../vaadin/addons/chatassistant/ChatAssistant.java | 10 +++++++++- .../resources/META-INF/frontend/react/animated-fab.tsx | 10 ++++++++++ .../META-INF/frontend/styles/chat-assistant-styles.css | 4 ++++ .../vaadin/addons/chatassistant/ChatAssistantDemo.java | 2 ++ 4 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistant.java b/src/main/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistant.java index 6bb4b25..f894a4f 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistant.java +++ b/src/main/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistant.java @@ -189,7 +189,7 @@ private void initializeAvatar() { avatar.setSizeFull(); this.getElement().appendChild(avatar.getElement()); this.addAttachListener(ev -> this.getElement().executeJs("return;") - .then(ev2 -> this.getElement().executeJs("this.childNodes[1].childNodes[0].appendChild($0)", avatar.getElement()) + .then(ev2 -> this.getElement().executeJs("this.childNodes[1].childNodes[0].childNodes[0].appendChild($0)", avatar.getElement()) .then(ev3 -> chatWindow.setTarget(avatar)))); } @@ -385,5 +385,13 @@ public void setAvatarProvider(SerializableSupplier avatarProvider) { this.avatarProvider = avatarProvider; this.initializeAvatar(); } + + public Integer getUnreadMessages() { + return getState("unreadMessages", Integer.class); + } + + public void setUnreadMessages(Integer unreadMessages) { + setState("unreadMessages",unreadMessages); + } } diff --git a/src/main/resources/META-INF/frontend/react/animated-fab.tsx b/src/main/resources/META-INF/frontend/react/animated-fab.tsx index fbe9517..e3601ed 100644 --- a/src/main/resources/META-INF/frontend/react/animated-fab.tsx +++ b/src/main/resources/META-INF/frontend/react/animated-fab.tsx @@ -3,6 +3,7 @@ import React from 'react'; import {useState} from 'react'; import Draggable from 'react-draggable'; import Fab from '@mui/material/Fab'; +import Badge from '@mui/material/Badge'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import { ReactAdapterElement, type RenderHooks } from 'Frontend/generated/flow/ReactAdapter'; @@ -14,6 +15,12 @@ const lumoTheme = createTheme({ dark: 'var(--lumo-primary-color-20pct)', contrastText: 'rgb(var(--lumo-primary-contrast-color))', }, + warning: { + main: 'var(--lumo-warning-color)', + light: 'var(--lumo-warning-color-50pct)', + dark: 'var(--lumo-warning-color-20pct)', + contrastText: 'rgb(var(--lumo-warning-contrast-color))', + } }, components: { MuiFab: { @@ -36,6 +43,7 @@ class AnimatedFABElement extends ReactAdapterElement { protected override render(hooks: RenderHooks): ReactElement | null { const [isDragging, setIsDragging] = useState(false); + const [unreadMessages, setUnreadMessages] = hooks.useState('unreadMessages'); const eventControl = (event: { type: any; }) => { if (event.type === 'mousemove' || event.type === 'touchmove') { setIsDragging(true) @@ -63,11 +71,13 @@ class AnimatedFABElement extends ReactAdapterElement { right: 16 }} > + + diff --git a/src/main/resources/META-INF/frontend/styles/chat-assistant-styles.css b/src/main/resources/META-INF/frontend/styles/chat-assistant-styles.css index 8e61f9e..fb61b54 100644 --- a/src/main/resources/META-INF/frontend/styles/chat-assistant-styles.css +++ b/src/main/resources/META-INF/frontend/styles/chat-assistant-styles.css @@ -33,4 +33,8 @@ vaadin-vertical-layout.chat-assistant-container-vertical-layout { Together, these two rotations position the resize handle in the upper-left corner. This new position is more suitable for resizing the chat window because the chat bubble is positioned by default in the bottom-right of the view. */ +} + +.MuiBadge-badge { + z-index: 2000 !important; } \ No newline at end of file diff --git a/src/test/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistantDemo.java b/src/test/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistantDemo.java index 8aa03d0..5591522 100644 --- a/src/test/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistantDemo.java +++ b/src/test/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistantDemo.java @@ -97,6 +97,8 @@ public void run() { chatAssistant.sendMessage(CustomMessage.builder().content("Hello, I am here to assist you") .messageTime(LocalDateTime.now()) .name("Assistant").avatar("chatbot.png").tagline("Generated by assistant").build()); + + chatAssistant.setUnreadMessages(4); add(message, chat, chatWithThinking, chatAssistant); } From b9c6a3798bfdcd072d9f078d6472f087b0d89f43 Mon Sep 17 00:00:00 2001 From: Martin Lopez Date: Thu, 31 Jul 2025 09:10:47 -0300 Subject: [PATCH 2/3] WIP: docs: add missing javadocs --- .../vaadin/addons/chatassistant/ChatAssistant.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistant.java b/src/main/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistant.java index f894a4f..86b7f80 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistant.java +++ b/src/main/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistant.java @@ -386,10 +386,18 @@ public void setAvatarProvider(SerializableSupplier avatarProvider) { this.initializeAvatar(); } + /** + * Return the number of unread messages to be displayed in the chat assistant. + * @return the number of unread messages + */ public Integer getUnreadMessages() { return getState("unreadMessages", Integer.class); } + /** + * Sets the number of unread messages to be displayed in the chat assistant. + * @param unreadMessages + */ public void setUnreadMessages(Integer unreadMessages) { setState("unreadMessages",unreadMessages); } From 998954e7459f07a1b09f5e9590296644d31719ab Mon Sep 17 00:00:00 2001 From: Martin Lopez Date: Thu, 31 Jul 2025 16:33:31 -0300 Subject: [PATCH 3/3] WIP: feat: update unreadMessages methods to handle null values --- .../vaadin/addons/chatassistant/ChatAssistant.java | 7 ++++--- .../resources/META-INF/frontend/react/animated-fab.tsx | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistant.java b/src/main/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistant.java index 86b7f80..5d1eca2 100644 --- a/src/main/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistant.java +++ b/src/main/java/com/flowingcode/vaadin/addons/chatassistant/ChatAssistant.java @@ -390,15 +390,16 @@ public void setAvatarProvider(SerializableSupplier avatarProvider) { * Return the number of unread messages to be displayed in the chat assistant. * @return the number of unread messages */ - public Integer getUnreadMessages() { - return getState("unreadMessages", Integer.class); + public int getUnreadMessages() { + Integer unreadMessages = getState("unreadMessages", Integer.class); + return unreadMessages==null?0:unreadMessages; } /** * Sets the number of unread messages to be displayed in the chat assistant. * @param unreadMessages */ - public void setUnreadMessages(Integer unreadMessages) { + public void setUnreadMessages(int unreadMessages) { setState("unreadMessages",unreadMessages); } diff --git a/src/main/resources/META-INF/frontend/react/animated-fab.tsx b/src/main/resources/META-INF/frontend/react/animated-fab.tsx index e3601ed..3b5ac6e 100644 --- a/src/main/resources/META-INF/frontend/react/animated-fab.tsx +++ b/src/main/resources/META-INF/frontend/react/animated-fab.tsx @@ -43,7 +43,7 @@ class AnimatedFABElement extends ReactAdapterElement { protected override render(hooks: RenderHooks): ReactElement | null { const [isDragging, setIsDragging] = useState(false); - const [unreadMessages, setUnreadMessages] = hooks.useState('unreadMessages'); + const [unreadMessages] = hooks.useState('unreadMessages'); const eventControl = (event: { type: any; }) => { if (event.type === 'mousemove' || event.type === 'touchmove') { setIsDragging(true)