From 66693614382dc9552855481a4bfa67776f02d303 Mon Sep 17 00:00:00 2001 From: Mohamed Marrouchi Date: Sat, 12 Oct 2024 10:26:14 +0100 Subject: [PATCH 1/3] feat: shadow dom + minor css --- widget/.npmignore | 17 +++ widget/README.md | 36 +++++ widget/package.json | 11 +- widget/public/index.html | 19 ++- widget/src/components/UserInput.scss | 2 +- widget/src/components/UserInput.tsx | 5 +- .../src/components/buttons/EmojiButton.scss | 15 +-- widget/src/components/buttons/FileButton.scss | 12 +- .../components/buttons/LocationButton.scss | 16 +-- widget/src/components/icons/EmojiIcon.tsx | 11 +- widget/src/components/icons/FileInputIcon.tsx | 7 +- widget/src/components/icons/LocationIcon.tsx | 5 +- .../components/messages/ButtonMessage.scss | 2 +- widget/src/constants/colors.ts | 126 +++++++++--------- 14 files changed, 172 insertions(+), 112 deletions(-) create mode 100644 widget/.npmignore diff --git a/widget/.npmignore b/widget/.npmignore new file mode 100644 index 00000000..46ce5535 --- /dev/null +++ b/widget/.npmignore @@ -0,0 +1,17 @@ +src +node_modules +build +public +tests +Dockerfile +.dockerignore +.eslintrc.json +index.html +package.json +tsconfig.app.json +tsconfig.json +tsconfig.node.json +vite.config.ts +dist/index.html +dist/shadow.html +vite.svg diff --git a/widget/README.md b/widget/README.md index bb512674..64f272ba 100644 --- a/widget/README.md +++ b/widget/README.md @@ -1,6 +1,8 @@ # Hexabot Live Chat Widget The [Hexabot](https://hexabot.ai/) Live Chat Widget is a React-based embeddable widget that allows users to integrate real-time chat functionality into their websites. It connects to the Hexabot API and facilitates seamless interaction between end-users and chatbots across multiple channels. +[Hexabot](https://hexabot.ai/) is an open-source chatbot / agent solution that allows users to create and manage AI-powered, multi-channel, and multilingual chatbots with ease. If you would like to learn more, please visit the [official github repo](https://github.com/Hexastack/Hexabot/). + ## Key Features - **Real-Time Chat:** Engage in real-time conversations with users directly through your website. - **Customizable:** Easily customize the widget's appearance and behavior to fit your brand and website. @@ -68,6 +70,40 @@ Once the widget is built, you can easily embed it into any webpage. Here's an ex ``` Replace the values in apiUrl and token with your configuration details. +To prevent the website css from conflicting with the chat widget css, we can leverage the shadow dom: +```js + + + + +
+ +``` + ## Customization You can customize the look and feel of the chat widget by modifying the widget’s scss styles or behavior. The widget allows you to: diff --git a/widget/package.json b/widget/package.json index 85a07b2e..4d7a3b03 100644 --- a/widget/package.json +++ b/widget/package.json @@ -1,11 +1,12 @@ { - "name": "hexabot-widget", - "private": true, - "version": "2.0.0", + "name": "hexabot-live-chat-widget", + "version": "2.0.0-rc.2", "description": "Hexabot is a solution for creating and managing chatbots across multiple channels, leveraging AI for advanced conversational capabilities. It provides a user-friendly interface for building, training, and deploying chatbots with integrated support for various messaging platforms.", "author": "Hexastack", "license": "AGPL-3.0-only", "type": "module", + "main": "dist/hexabot-widget.umd.js", + "style": "dist/style.css", "scripts": { "dev": "vite", "build": "tsc && vite build", @@ -38,5 +39,9 @@ "typescript": "^5.2.2", "vite": "^5.3.4", "vite-plugin-dts": "^4.0.2" + }, + "optionalDependencies": { + "@rollup/rollup-darwin-arm64": "^4.24.0", + "@esbuild/darwin-arm64": "^0.24.0" } } diff --git a/widget/public/index.html b/widget/public/index.html index 6904e828..08131b04 100644 --- a/widget/public/index.html +++ b/widget/public/index.html @@ -18,15 +18,26 @@
diff --git a/widget/src/components/UserInput.scss b/widget/src/components/UserInput.scss index 5ea10e54..60152fd7 100644 --- a/widget/src/components/UserInput.scss +++ b/widget/src/components/UserInput.scss @@ -8,7 +8,7 @@ border-bottom-left-radius: 10px; border-bottom-right-radius: 10px; transition: background-color 0.2s ease, box-shadow 0.2s ease; - border-top: 1px solid #eaeaea; + border: 1px solid #eaeaea; } .sc-user-input--text { diff --git a/widget/src/components/UserInput.tsx b/widget/src/components/UserInput.tsx index ba9f4625..12f4055c 100644 --- a/widget/src/components/UserInput.tsx +++ b/widget/src/components/UserInput.tsx @@ -137,10 +137,7 @@ const UserInput: React.FC = () => { const uploading = outgoingMessageState === OutgoingMessageState.uploading; return ( -
+
{suggestions.length > 0 && } {(file || uploading) && ( diff --git a/widget/src/components/buttons/EmojiButton.scss b/widget/src/components/buttons/EmojiButton.scss index 62492033..d740605a 100644 --- a/widget/src/components/buttons/EmojiButton.scss +++ b/widget/src/components/buttons/EmojiButton.scss @@ -5,6 +5,7 @@ padding: 0px; margin: 0px; outline: none; + opacity: .5; } .sc-user-input--emoji-icon-wrapper:focus { @@ -12,18 +13,14 @@ } .sc-user-input--emoji-icon { - width: 18px; - height: 18px; + width: 20px; + height: 20px; cursor: pointer; align-self: center; vertical-align: middle; } - .sc-user-input--emoji-icon-wrapper:focus .sc-user-input--emoji-icon path, - .sc-user-input--emoji-icon-wrapper:focus .sc-user-input--emoji-icon circle, - .sc-user-input--emoji-icon.active path, - .sc-user-input--emoji-icon.active circle, - .sc-user-input--emoji-icon:hover path, - .sc-user-input--emoji-icon:hover circle { - filter: contrast(15%); + .sc-user-input--emoji-icon-wrapper:focus, + .sc-user-input--emoji-icon-wrapper:hover { + opacity: 1; } \ No newline at end of file diff --git a/widget/src/components/buttons/FileButton.scss b/widget/src/components/buttons/FileButton.scss index 5c072a0e..3405d15d 100644 --- a/widget/src/components/buttons/FileButton.scss +++ b/widget/src/components/buttons/FileButton.scss @@ -1,6 +1,12 @@ .sc-user-input--file-wrapper { + background: none; + border: none; + padding: 0px; + margin: 0px; + outline: none; position: relative; cursor: pointer; + opacity: .5; } .sc-user-input--file-icon-wrapper { background: none; @@ -8,7 +14,7 @@ padding: 0px; margin: 0px; outline: none; - cursor: pointer; + cursor: pointer !important; } .sc-user-input--file-icon { @@ -19,6 +25,6 @@ vertical-align: middle; } -.sc-user-input--file-icon:hover path { - filter: contrast(15%); +.sc-user-input--file-wrapper:hover { + opacity: 1 } diff --git a/widget/src/components/buttons/LocationButton.scss b/widget/src/components/buttons/LocationButton.scss index b1420adc..a80a8c68 100644 --- a/widget/src/components/buttons/LocationButton.scss +++ b/widget/src/components/buttons/LocationButton.scss @@ -4,6 +4,7 @@ padding: 0px; margin: 0px; outline: none; + opacity: .5; } .sc-user-input--location-icon-wrapper:focus { @@ -11,20 +12,13 @@ } .sc-user-input--location-icon { - width: 16px; - height: 16px; + width: 20px; + height: 20px; cursor: pointer; align-self: center; vertical-align: middle; } -.sc-user-input--location-icon-wrapper:focus .sc-user-input--location-icon path, -.sc-user-input--location-icon-wrapper:focus - .sc-user-input--location-icon - circle, -.sc-user-input--location-icon.active path, -.sc-user-input--location-icon.active circle, -.sc-user-input--location-icon:hover path, -.sc-user-input--location-icon:hover circle { - filter: contrast(15%); +.sc-user-input--location-icon-wrapper:hover { + opacity: 1; } diff --git a/widget/src/components/icons/EmojiIcon.tsx b/widget/src/components/icons/EmojiIcon.tsx index 936896b6..65855b68 100644 --- a/widget/src/components/icons/EmojiIcon.tsx +++ b/widget/src/components/icons/EmojiIcon.tsx @@ -12,7 +12,7 @@ const EmojiIcon: FC> = ({ x = '0', y = '0', className = 'sc-user-input--emoji-icon', - viewBox = '0 0 37 37', + viewBox = '0 0 24 24', ...rest }) => { return ( @@ -24,10 +24,11 @@ const EmojiIcon: FC> = ({ viewBox={viewBox} {...rest} > - - - - + + + + + ); }; diff --git a/widget/src/components/icons/FileInputIcon.tsx b/widget/src/components/icons/FileInputIcon.tsx index 6b3b042d..24b311ed 100644 --- a/widget/src/components/icons/FileInputIcon.tsx +++ b/widget/src/components/icons/FileInputIcon.tsx @@ -12,7 +12,7 @@ const FileInputIcon: FC> = ({ x = '0', y = '0', className = 'sc-user-input--file-icon', - viewBox = '0 0 32 32', + viewBox = '0 0 24 24', ...rest }) => { return ( @@ -24,10 +24,7 @@ const FileInputIcon: FC> = ({ viewBox={viewBox} {...rest} > - + {' '} ); }; diff --git a/widget/src/components/icons/LocationIcon.tsx b/widget/src/components/icons/LocationIcon.tsx index 05f26692..14d8ed39 100644 --- a/widget/src/components/icons/LocationIcon.tsx +++ b/widget/src/components/icons/LocationIcon.tsx @@ -13,7 +13,7 @@ const LocationIcon: FC> = ({ y = '0', className = 'sc-user-input--location-icon', version = '1.1', - viewBox = '0 0 32 32', + viewBox = '0 0 24 24', ...rest }) => { return ( @@ -26,8 +26,7 @@ const LocationIcon: FC> = ({ viewBox={viewBox} {...rest} > - - + ); }; diff --git a/widget/src/components/messages/ButtonMessage.scss b/widget/src/components/messages/ButtonMessage.scss index 78b978af..c36c953a 100644 --- a/widget/src/components/messages/ButtonMessage.scss +++ b/widget/src/components/messages/ButtonMessage.scss @@ -18,7 +18,7 @@ margin: 2px; cursor: pointer; outline: 0; - font-size: 1rem; + font-size: .75rem; } } diff --git a/widget/src/constants/colors.ts b/widget/src/constants/colors.ts index d1b5553a..186574db 100644 --- a/widget/src/constants/colors.ts +++ b/widget/src/constants/colors.ts @@ -11,34 +11,34 @@ import { ColorState } from '../types/colors.types'; const colors: Record = { orange: { header: { - bg: '#E6A23D', + bg: '#FF851B', text: '#fff', }, launcher: { - bg: '#E6A23D', + bg: '#FF851B', }, messageList: { bg: '#fff', }, sent: { - bg: '#E6A23D', + bg: '#FF851B', text: '#fff', }, received: { bg: '#eaeaea', - text: '#222222', + text: '#000', }, userInput: { - bg: '#fff', - text: '#212121', + bg: '#fbfbfb', + text: '#000', }, button: { bg: '#ffffff', - text: '#E6A23D', - border: '#E6A23D', + text: '#FF851B', + border: '#FF851B', }, messageStatus: { - bg: '#E6A23D', + bg: '#FF851B', }, messageTime: { text: '#9C9C9C', @@ -46,139 +46,139 @@ const colors: Record = { }, red: { header: { - bg: '#AB1251', + bg: '#FF4136', text: '#fff', }, launcher: { - bg: '#AB1251', + bg: '#FF4136', }, messageList: { bg: '#fff', }, sent: { - bg: '#AB1251', + bg: '#FF4136', text: '#fff', }, received: { bg: '#eaeaea', - text: '#222222', + text: '#000', }, userInput: { - bg: '#fff', - text: '#212121', + bg: '#fbfbfb', + text: '#000', }, button: { bg: '#ffffff', - text: '#AB1251', - border: '#AB1251', + text: '#FF4136', + border: '#FF4136', }, messageStatus: { - bg: '#AB1251', + bg: '#FF4136', }, messageTime: { - text: '#9C9C9C', + text: '#FF4136', }, }, green: { header: { - bg: '#ABBD49', + bg: '#2ECC40', text: '#fff', }, launcher: { - bg: '#ABBD49', + bg: '#2ECC40', }, messageList: { bg: '#fff', }, sent: { - bg: '#4CAF50', + bg: '#2ECC40', text: '#fff', }, received: { bg: '#eaeaea', - text: '#222222', + text: '#000', }, userInput: { - bg: '#fff', - text: '#212121', + bg: '#fbfbfb', + text: '#000', }, button: { bg: '#ffffff', - text: '#ABBD49', - border: '#ABBD49', + text: '#2ECC40', + border: '#2ECC40', }, messageStatus: { - bg: '#ABBD49', + bg: '#2ECC40', }, messageTime: { - text: '#9C9C9C', + text: '#2ECC40', }, }, blue: { header: { - bg: '#108AA8', - text: '#ffffff', + bg: '#0074D9', + text: '#fff', }, launcher: { - bg: '#108AA8', + bg: '#0074D9', }, messageList: { - bg: '#ffffff', + bg: '#fff', }, sent: { - bg: '#108AA8', - text: '#ffffff', + bg: '#0074D9', + text: '#fff', }, received: { bg: '#eaeaea', - text: '#222222', + text: '#000', }, userInput: { - bg: '#f4f7f9', - text: '#565867', + bg: '#fbfbfb', + text: '#000', }, button: { bg: '#ffffff', - text: '#108AA8', - border: '#108AA8', + text: '#0074D9', + border: '#0074D9', }, messageStatus: { - bg: '#108AA8', + bg: '#0074D9', }, messageTime: { - text: '#9C9C9C', + text: '#0074D9', }, }, teal: { header: { - bg: '#279084', - text: '#ffffff', + bg: '#1BA089', + text: '#fff', }, launcher: { - bg: '#279084', + bg: '#1BA089', }, messageList: { - bg: '#ffffff', + bg: '#fff', }, sent: { - bg: '#279084', - text: '#ffffff', + bg: '#1BA089', + text: '#fff', }, received: { bg: '#eaeaea', - text: '#222222', + text: '#000', }, userInput: { - bg: '#f4f7f9', - text: '#565867', + bg: '#fbfbfb', + text: '#000', }, button: { bg: '#ffffff', - text: '#279084', - border: '#279084', + text: '#1BA089', + border: '#1BA089', }, messageStatus: { - bg: '#279084', + bg: '#1BA089', }, messageTime: { text: '#9C9C9C', @@ -186,26 +186,26 @@ const colors: Record = { }, dark: { header: { - bg: '#34495e', + bg: '#000', text: '#ecf0f1', }, launcher: { - bg: '#34495e', + bg: '#000', }, messageList: { - bg: '#2c3e50', + bg: '#FFF', }, sent: { - bg: '#7f8c8d', - text: '#ecf0f1', + bg: '#000', + text: '#FFF', }, received: { - bg: '#95a5a6', - text: '#ecf0f1', + bg: '#F0F0F0', + text: '#000', }, userInput: { - bg: '#34495e', - text: '#ecf0f1', + bg: '#FFF', + text: '#000', }, button: { bg: '#2c3e50', From b08ee24fc33b75da39fecfc64b7f2cb65a960b66 Mon Sep 17 00:00:00 2001 From: Mohamed Marrouchi Date: Sat, 12 Oct 2024 10:26:38 +0100 Subject: [PATCH 2/3] feat: detach widget docker service --- docker/docker-compose.dev.yml | 10 - docker/docker-compose.widget.dev.yml | 12 + docker/docker-compose.widget.yml | 13 + docker/docker-compose.yml | 11 - package-lock.json | 579 +++++++++++++++++++++++++++ 5 files changed, 604 insertions(+), 21 deletions(-) create mode 100644 docker/docker-compose.widget.dev.yml create mode 100644 docker/docker-compose.widget.yml diff --git a/docker/docker-compose.dev.yml b/docker/docker-compose.dev.yml index 97050324..7962ba4d 100644 --- a/docker/docker-compose.dev.yml +++ b/docker/docker-compose.dev.yml @@ -36,13 +36,3 @@ services: pull_policy: build volumes: - ../frontend/:/app/frontend/ - - widget: - build: - context: ../widget - target: development - pull_policy: build - volumes: - - ../widget/src:/app/src - ports: - - ${APP_WIDGET_PORT}:5173 diff --git a/docker/docker-compose.widget.dev.yml b/docker/docker-compose.widget.dev.yml new file mode 100644 index 00000000..c985b8d9 --- /dev/null +++ b/docker/docker-compose.widget.dev.yml @@ -0,0 +1,12 @@ +version: "3.9" + +services: + widget: + build: + context: ../widget + target: development + pull_policy: build + volumes: + - ../widget/src:/app/src + ports: + - ${APP_WIDGET_PORT}:5173 diff --git a/docker/docker-compose.widget.yml b/docker/docker-compose.widget.yml new file mode 100644 index 00000000..291071aa --- /dev/null +++ b/docker/docker-compose.widget.yml @@ -0,0 +1,13 @@ +version: "3.9" + +services: + widget: + container_name: widget + image: hexastack/hexabot-widget:latest + networks: + - app-network + depends_on: + api: + condition: service_healthy + ports: + - ${APP_WIDGET_PORT}:5173 diff --git a/docker/docker-compose.yml b/docker/docker-compose.yml index 12369bf7..44b21a25 100644 --- a/docker/docker-compose.yml +++ b/docker/docker-compose.yml @@ -52,17 +52,6 @@ services: volumes: - mongo-data:/data/db - widget: - container_name: widget - image: hexastack/hexabot-widget:latest - networks: - - app-network - depends_on: - api: - condition: service_healthy - ports: - - ${APP_WIDGET_PORT}:5173 - volumes: mongo-data: api-data: diff --git a/package-lock.json b/package-lock.json index 8facefe2..8b57ac1b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -977,6 +977,261 @@ "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", "license": "MIT" }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.24.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.24.0.tgz", + "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/linux-x64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", @@ -994,6 +1249,102 @@ "node": ">=12" } }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -2034,6 +2385,148 @@ } } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.5.tgz", + "integrity": "sha512-SU5cvamg0Eyu/F+kLeMXS7GoahL+OoizlclVFX3l5Ql6yNlywJJ0OuqTzUx0v+aHhPHEB/56CT06GQrRrGNYww==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.5.tgz", + "integrity": "sha512-S4pit5BP6E5R5C8S6tgU/drvgjtYW76FBuG6+ibG3tMvlD1h9LHVF9KmlmaUBQ8Obou7hEyS+0w+IR/VtxwNMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.24.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.24.0.tgz", + "integrity": "sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.5.tgz", + "integrity": "sha512-D8brJEFg5D+QxFcW6jYANu+Rr9SlKtTenmsX5hOSzNYVrK5oLAEMTUgKWYJP+wdKyCdeSwnapLsn+OVRFycuQg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.5.tgz", + "integrity": "sha512-PNqXYmdNFyWNg0ma5LdY8wP+eQfdvyaBAojAXgO7/gs0Q/6TQJVXAXe8gwW9URjbS0YAammur0fynYGiWsKlXw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.5.tgz", + "integrity": "sha512-kSSCZOKz3HqlrEuwKd9TYv7vxPYD77vHSUvM2y0YaTGnFc8AdI5TTQRrM1yIp3tXCKrSL9A7JLoILjtad5t8pQ==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.5.tgz", + "integrity": "sha512-oTXQeJHRbOnwRnRffb6bmqmUugz0glXaPyspp4gbQOPVApdpRrY/j7KP3lr7M8kTfQTyrBUzFjj5EuHAhqH4/w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.5.tgz", + "integrity": "sha512-qnOTIIs6tIGFKCHdhYitgC2XQ2X25InIbZFor5wh+mALH84qnFHvc+vmWUpyX97B0hNvwNUL4B+MB8vJvH65Fw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.5.tgz", + "integrity": "sha512-TMYu+DUdNlgBXING13rHSfUc3Ky5nLPbWs4bFnT+R6Vu3OvXkTkixvvBKk8uO4MT5Ab6lC3U7x8S8El2q5o56w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.5.tgz", + "integrity": "sha512-PTQq1Kz22ZRvuhr3uURH+U/Q/a0pbxJoICGSprNLAoBEkyD3Sh9qP5I0Asn0y0wejXQBbsVMRZRxlbGFD9OK4A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.5.tgz", + "integrity": "sha512-bR5nCojtpuMss6TDEmf/jnBnzlo+6n1UhgwqUvRoe4VIotC7FG1IKkyJbwsT7JDsF2jxR+NTnuOwiGv0hLyDoQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.22.5", "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.5.tgz", @@ -2060,6 +2553,45 @@ "linux" ] }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.5.tgz", + "integrity": "sha512-RXT8S1HP8AFN/Kr3tg4fuYrNxZ/pZf1HemC5Tsddc6HzgGnJm0+Lh5rAHJkDuW3StI0ynNXukidROMXYl6ew8w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.5.tgz", + "integrity": "sha512-ElTYOh50InL8kzyUD6XsnPit7jYCKrphmddKAe1/Ytt74apOxDq5YEcbsiKs0fR3vff3jEneMM+3I7jbqaMyBg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.5.tgz", + "integrity": "sha512-+lvL/4mQxSV8MukpkKyyvfwhH266COcWlXE/1qxwN08ajovta3459zrjLghYMgDerlzNwLAcFpvU+WWE5y6nAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rushstack/eslint-patch": { "version": "1.10.4", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.10.4.tgz", @@ -4419,6 +4951,22 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/esbuild/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/escalade": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", @@ -5420,6 +5968,20 @@ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "license": "ISC" }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -8012,6 +8574,19 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup/node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.22.5", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.5.tgz", + "integrity": "sha512-250ZGg4ipTL0TGvLlfACkIxS9+KLtIbn7BCZjsZj88zSg2Lvu3Xdw6dhAhfe/FjjXPVNCtcSp+WZjVsD3a/Zlw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -9369,6 +9944,10 @@ "typescript": "^5.2.2", "vite": "^5.3.4", "vite-plugin-dts": "^4.0.2" + }, + "optionalDependencies": { + "@esbuild/darwin-arm64": "^0.24.0", + "@rollup/rollup-darwin-arm64": "^4.24.0" } }, "widget/node_modules/@types/react": { From 04ce4df3488b6b1b8b7eec422520102c4883c9a0 Mon Sep 17 00:00:00 2001 From: Mohamed Marrouchi Date: Sat, 12 Oct 2024 19:43:34 +0100 Subject: [PATCH 3/3] fix: widget re-render issues, ws, build + rename pkg --- api/src/chat/services/block.service.ts | 4 ++ api/src/chat/services/bot.service.ts | 2 +- api/src/setting/seeds/setting.seed-model.ts | 2 +- api/src/websocket/websocket.gateway.spec.ts | 7 +-- api/src/websocket/websocket.gateway.ts | 16 ------ frontend/next.config.mjs | 4 +- frontend/package.json | 2 +- .../src/app-components/widget/ChatWidget.tsx | 2 +- package-lock.json | 12 ++-- widget/README.md | 11 ++++ widget/package.json | 4 +- widget/src/components/Launcher.tsx | 1 - widget/src/components/UserSubscription.tsx | 27 ++++++--- widget/src/providers/ChatProvider.tsx | 50 ++++++++--------- widget/src/providers/SocketProvider.tsx | 38 ++++++------- widget/src/translations/en/translation.json | 1 + widget/src/translations/fr/translation.json | 1 + widget/src/utils/SocketIoClient.ts | 56 +++++++++++++------ widget/vite.config.ts | 43 +++++++------- 19 files changed, 153 insertions(+), 130 deletions(-) diff --git a/api/src/chat/services/block.service.ts b/api/src/chat/services/block.service.ts index 585ec74f..6b34de94 100644 --- a/api/src/chat/services/block.service.ts +++ b/api/src/chat/services/block.service.ts @@ -63,6 +63,10 @@ export class BlockService extends BaseService { blocks: BlockFull[], event: EventWrapper, ): Promise { + if (!blocks.length) { + return undefined; + } + // Search for block matching a given event let block: BlockFull | undefined = undefined; const payload = event.getPayload(); diff --git a/api/src/chat/services/bot.service.ts b/api/src/chat/services/bot.service.ts index fb26c8d9..ac11ac24 100644 --- a/api/src/chat/services/bot.service.ts +++ b/api/src/chat/services/bot.service.ts @@ -443,7 +443,7 @@ export class BotService { }); if (!blocks.length) { - return this.logger.debug('No starting message blocks was found'); + this.logger.debug('No starting message blocks was found'); } // Search for a block match diff --git a/api/src/setting/seeds/setting.seed-model.ts b/api/src/setting/seeds/setting.seed-model.ts index 5882c4cb..ce239097 100644 --- a/api/src/setting/seeds/setting.seed-model.ts +++ b/api/src/setting/seeds/setting.seed-model.ts @@ -67,7 +67,7 @@ export const settingModels: SettingCreateDto[] = [ { group: 'nlp_settings', label: 'threshold', - value: 0.9, + value: 0.1, type: SettingType.number, config: { min: 0, diff --git a/api/src/websocket/websocket.gateway.spec.ts b/api/src/websocket/websocket.gateway.spec.ts index 1f255b30..690b3efe 100644 --- a/api/src/websocket/websocket.gateway.spec.ts +++ b/api/src/websocket/websocket.gateway.spec.ts @@ -68,11 +68,8 @@ describe('WebsocketGateway', () => { it('should connect successfully', async () => { ioClient.connect(); await new Promise((resolve) => { - // ioClient.on('connect', () => { - // console.log('connected'); - // }); - ioClient.on('message', (data) => { - expect(data.statusCode).toBe(200); + ioClient.on('connect', () => { + expect(true).toBe(true); resolve(); }); }); diff --git a/api/src/websocket/websocket.gateway.ts b/api/src/websocket/websocket.gateway.ts index 2d4b69c9..371b0f90 100644 --- a/api/src/websocket/websocket.gateway.ts +++ b/api/src/websocket/websocket.gateway.ts @@ -255,22 +255,6 @@ export class WebsocketGateway this.logger.debug(`Number of connected clients: ${sockets?.size}`); this.eventEmitter.emit(`hook:websocket:connection`, client); - // @TODO : Revisit once we don't use anymore in frontend - const response = new SocketResponse(); - client.send( - response - .setHeaders({ - 'access-control-allow-origin': - config.security.cors.allowOrigins.join(','), - vary: 'Origin', - 'access-control-allow-credentials': - config.security.cors.allowCredentials.toString(), - }) - .status(200) - .json({ - success: true, - }), - ); } async handleDisconnect(client: Socket): Promise { diff --git a/frontend/next.config.mjs b/frontend/next.config.mjs index 25dc2f7b..3fafa429 100644 --- a/frontend/next.config.mjs +++ b/frontend/next.config.mjs @@ -1,7 +1,7 @@ /** @type {import('next').NextConfig} */ import withTM from "next-transpile-modules"; -const nextConfig = withTM(["hexabot-widget"])({ +const nextConfig = withTM(["hexabot-chat-widget"])({ async rewrites() { return [ { @@ -11,7 +11,7 @@ const nextConfig = withTM(["hexabot-widget"])({ ]; }, webpack(config) { - if (process.env.NODE_ENV==="development") { + if (process.env.NODE_ENV === "development") { config.watchOptions = { poll: 1000, aggregateTimeout: 300, diff --git a/frontend/package.json b/frontend/package.json index e23deafc..a70edc76 100755 --- a/frontend/package.json +++ b/frontend/package.json @@ -29,7 +29,7 @@ "axios": "^1.7.7", "eazychart-css": "^0.2.1-alpha.0", "eazychart-react": "^0.8.0-alpha.0", - "hexabot-widget": "*", + "hexabot-chat-widget": "*", "next": "^14.2.13", "next-transpile-modules": "^10.0.1", "normalizr": "^3.6.2", diff --git a/frontend/src/app-components/widget/ChatWidget.tsx b/frontend/src/app-components/widget/ChatWidget.tsx index d89c3271..e8008c85 100644 --- a/frontend/src/app-components/widget/ChatWidget.tsx +++ b/frontend/src/app-components/widget/ChatWidget.tsx @@ -7,7 +7,7 @@ */ import { Avatar, Box } from "@mui/material"; -import UiChatWidget from "hexabot-widget/src/UiChatWidget"; +import UiChatWidget from "hexabot-chat-widget/src/UiChatWidget"; import { usePathname } from "next/navigation"; import { getAvatarSrc } from "@/components/inbox/helpers/mapMessages"; diff --git a/package-lock.json b/package-lock.json index 8b57ac1b..a7536a3c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,7 +63,7 @@ "axios": "^1.7.7", "eazychart-css": "^0.2.1-alpha.0", "eazychart-react": "^0.8.0-alpha.0", - "hexabot-widget": "*", + "hexabot-chat-widget": "*", "next": "^14.2.13", "next-transpile-modules": "^10.0.1", "normalizr": "^3.6.2", @@ -6356,6 +6356,10 @@ "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.5.tgz", "integrity": "sha512-G7HLD+WKcrOyJP5VQwYZNC3Z6FcQ7YYjEFiFoIj8PfEr73mu421o8B1N5DKUcc8K37EsJ2XXWA8DtrDz/2dReg==" }, + "node_modules/hexabot-chat-widget": { + "resolved": "widget", + "link": true + }, "node_modules/hexabot-cli": { "resolved": "cli", "link": true @@ -6364,10 +6368,6 @@ "resolved": "frontend", "link": true }, - "node_modules/hexabot-widget": { - "resolved": "widget", - "link": true - }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -9918,7 +9918,7 @@ } }, "widget": { - "name": "hexabot-widget", + "name": "hexabot-chat-widget", "version": "2.0.0", "license": "AGPL-3.0-only", "dependencies": { diff --git a/widget/README.md b/widget/README.md index 64f272ba..151149c7 100644 --- a/widget/README.md +++ b/widget/README.md @@ -104,6 +104,17 @@ To prevent the website css from conflicting with the chat widget css, we can lev ``` +If you would like to use the official widget and benefit from updates automatically, you can consider using the cdn url: +`https://cdn.jsdelivr.net/npm/hexabot-chat-widget@2.0.4/dist/` + +or lastest from major version: +`https://cdn.jsdelivr.net/npm/hexabot-chat-widget@2/dist/` + +JsDelivr uses the package published in the NPM registry : https://www.npmjs.com/package/hexabot-widget + +## Examples +As a proof of concept we developed a Wordpress plugin to embed the chat widget in a Wordpress website : [https://github.com/hexastack/hexabot-wordpress-live-chat-widget](https://github.com/hexastack/hexabot-wordpress-live-chat-widget) + ## Customization You can customize the look and feel of the chat widget by modifying the widget’s scss styles or behavior. The widget allows you to: diff --git a/widget/package.json b/widget/package.json index 4d7a3b03..d9814a36 100644 --- a/widget/package.json +++ b/widget/package.json @@ -1,6 +1,6 @@ { - "name": "hexabot-live-chat-widget", - "version": "2.0.0-rc.2", + "name": "hexabot-chat-widget", + "version": "2.0.0", "description": "Hexabot is a solution for creating and managing chatbots across multiple channels, leveraging AI for advanced conversational capabilities. It provides a user-friendly interface for building, training, and deploying chatbots with integrated support for various messaging platforms.", "author": "Hexastack", "license": "AGPL-3.0-only", diff --git a/widget/src/components/Launcher.tsx b/widget/src/components/Launcher.tsx index 31f2ec88..e85cf5d5 100644 --- a/widget/src/components/Launcher.tsx +++ b/widget/src/components/Launcher.tsx @@ -15,7 +15,6 @@ import { useChat } from '../providers/ChatProvider'; import { useColors } from '../providers/ColorProvider'; import { useSocketLifecycle } from '../providers/SocketProvider'; import { useWidget, WidgetContextType } from '../providers/WidgetProvider'; - import './Launcher.scss'; type LauncherProps = PropsWithChildren<{ diff --git a/widget/src/components/UserSubscription.tsx b/widget/src/components/UserSubscription.tsx index d754cd4e..de1c789a 100644 --- a/widget/src/components/UserSubscription.tsx +++ b/widget/src/components/UserSubscription.tsx @@ -6,7 +6,13 @@ * 2. All derivative works must include clear attribution to the original creator and software, Hexastack and Hexabot, in a prominent location (e.g., in the software's "About" section, documentation, and README file). */ -import React, { SyntheticEvent, useCallback, useEffect, useState } from 'react'; +import React, { + SyntheticEvent, + useCallback, + useEffect, + useRef, + useState, +} from 'react'; import { useTranslation } from '../hooks/useTranslation'; import { useChat } from '../providers/ChatProvider'; @@ -39,6 +45,7 @@ const UserSubscription: React.FC = () => { } = useChat(); const [firstName, setFirstName] = useState(''); const [lastName, setLastName] = useState(''); + const isInitialized = useRef(false); const handleSubmit = useCallback( async (event?: React.FormEvent) => { event?.preventDefault(); @@ -83,7 +90,7 @@ const UserSubscription: React.FC = () => { data: { type: TOutgoingMessageType.postback, data: { - text: 'GET_STARTED', //TODO:use translation here? + text: t('messages.get_started'), payload: 'GET_STARTED', }, author: profile.foreign_id, @@ -113,14 +120,18 @@ const UserSubscription: React.FC = () => { ); useEffect(() => { - const profile = localStorage.getItem('profile'); + // User already subscribed ? (example : refreshed the page) + if (!isInitialized.current) { + isInitialized.current = true; + const profile = localStorage.getItem('profile'); - if (profile) { - const parsedProfile = JSON.parse(profile); + if (profile) { + const parsedProfile = JSON.parse(profile); - setFirstName(parsedProfile.first_name); - setLastName(parsedProfile.last_name); - handleSubmit(); + setFirstName(parsedProfile.first_name); + setLastName(parsedProfile.last_name); + handleSubmit(); + } } }, [handleSubmit, setScreen]); diff --git a/widget/src/providers/ChatProvider.tsx b/widget/src/providers/ChatProvider.tsx index cd4fffcb..c5fefb55 100644 --- a/widget/src/providers/ChatProvider.tsx +++ b/widget/src/providers/ChatProvider.tsx @@ -241,15 +241,14 @@ const ChatProvider: React.FC<{ ) { if ('author' in newIOMessage) { newIOMessage.direction = - newIOMessage.author === participants[1].foreign_id || - newIOMessage.author === participants[1].id - ? Direction.sent - : Direction.received; + newIOMessage.author === 'chatbot' + ? Direction.received + : Direction.sent; newIOMessage.read = true; newIOMessage.delivery = true; } - messages.push(newIOMessage as TMessage); + setMessages([...messages, newIOMessage as TMessage]); setScroll(0); } @@ -310,30 +309,29 @@ const ChatProvider: React.FC<{ }>( `/webhook/${config.channel}/?verification_token=${config.token}&first_name=${firstName}&last_name=${lastName}`, ); - const { messages, profile } = body; - - localStorage.setItem('profile', JSON.stringify(profile)); - // @TODO : condition mix on id VS foreign_id - messages.forEach((message) => { - const direction = - message.author === profile.foreign_id || - message.author === profile.id - ? Direction.sent - : Direction.received; - - message.direction = direction; - if (message.direction === Direction.sent) { - message.read = true; - message.delivery = false; - } - }); - setMessages(messages); + + localStorage.setItem('profile', JSON.stringify(body.profile)); + setMessages( + body.messages.map((message) => { + return { + ...message, + direction: + message.author === body.profile.foreign_id || + message.author === body.profile.id + ? Direction.sent + : Direction.received, + read: message.direction === Direction.sent || message.read, + delivery: + message.direction === Direction.sent || message.delivery, + } as TMessage; + }), + ); setParticipants([ ...participants, { - id: profile.foreign_id, - foreign_id: profile.foreign_id, - name: `${profile.first_name} ${profile.last_name}`, + id: body.profile.foreign_id, + foreign_id: body.profile.foreign_id, + name: `${body.profile.first_name} ${body.profile.last_name}`, }, ]); setConnectionState(3); diff --git a/widget/src/providers/SocketProvider.tsx b/widget/src/providers/SocketProvider.tsx index 5174b953..2497b92b 100644 --- a/widget/src/providers/SocketProvider.tsx +++ b/widget/src/providers/SocketProvider.tsx @@ -12,43 +12,42 @@ import { useContext, useEffect, useRef, - useState, } from 'react'; import { useConfig } from './ConfigProvider'; -import { builSocketIoClient, SocketIoClient } from '../utils/SocketIoClient'; +import { getSocketIoClient, SocketIoClient } from '../utils/SocketIoClient'; interface socketContext { socket: SocketIoClient; - connected: boolean; } const socketContext = createContext({ socket: {} as SocketIoClient, - connected: false, }); export const SocketProvider = (props: PropsWithChildren) => { const config = useConfig(); - const socketRef = useRef(builSocketIoClient(config)); - const [connected, setConnected] = useState(false); - - useEffect(() => { - socketRef.current.init({ + const socketRef = useRef( + getSocketIoClient(config, { onConnect: () => { - setConnected(true); + // eslint-disable-next-line no-console + console.info( + 'Hexabot Live Chat : Successfully established WS Connection!', + ); }, onConnectError: () => { - setConnected(false); + // eslint-disable-next-line no-console + console.error('Hexabot Live Chat : Failed to establish WS Connection!'); }, onDisconnect: () => { - setConnected(false); + // eslint-disable-next-line no-console + console.info('Hexabot Live Chat : Disconnected WS.'); }, - }); - }, []); + }), + ); return ( - + {props.children} ); @@ -58,12 +57,6 @@ export const useSocket = () => { return useContext(socketContext); }; -export const useSocketConnected = () => { - const { connected } = useSocket(); - - return connected; -}; - export const useSubscribe = (event: string, callback: (arg: T) => void) => { const { socket } = useSocket(); @@ -83,5 +76,6 @@ export const useSocketLifecycle = () => { return () => { socket.disconnect(); }; - }, [socket]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); }; diff --git a/widget/src/translations/en/translation.json b/widget/src/translations/en/translation.json index 89df5389..5e499db9 100644 --- a/widget/src/translations/en/translation.json +++ b/widget/src/translations/en/translation.json @@ -11,6 +11,7 @@ "back": "Back" }, "messages": { + "get_started": "Get Started", "file_message": { "browser_audio_unsupport": "Browser does not support the audio element.", "browser_video_unsupport": "Browser does not support the video element.", diff --git a/widget/src/translations/fr/translation.json b/widget/src/translations/fr/translation.json index dcf9119b..acd2bda9 100644 --- a/widget/src/translations/fr/translation.json +++ b/widget/src/translations/fr/translation.json @@ -11,6 +11,7 @@ "back": "Retour" }, "messages": { + "get_started": "Démarrer", "file_message": { "browser_audio_unsupport": "Le navigateur ne prend pas en charge l'élément audio.", "browser_video_unsupport": "Le navigateur ne prend pas en charge l'élément vidéo.", diff --git a/widget/src/utils/SocketIoClient.ts b/widget/src/utils/SocketIoClient.ts index 8baa1d37..a90859a3 100644 --- a/widget/src/utils/SocketIoClient.ts +++ b/widget/src/utils/SocketIoClient.ts @@ -16,6 +16,12 @@ import { type SocketIoClientConfig = Partial; +type SocketIoEventHandlers = { + onConnect?: () => void; + onDisconnect?: (reason: string, details: unknown) => void; + onConnectError?: (error: Error) => void; +}; + export class SocketIoClient { /** * Default configuration for the socket client @@ -50,9 +56,11 @@ export class SocketIoClient { private config: SocketIoClientConfig; - private initialized: boolean = false; - - constructor(apiUrl: string, socketConfig?: SocketIoClientConfig) { + constructor( + apiUrl: string, + socketConfig: SocketIoClientConfig, + handlers: SocketIoEventHandlers, + ) { this.config = { ...SocketIoClient.defaultConfig, ...socketConfig, @@ -61,6 +69,7 @@ export class SocketIoClient { const url = new URL(apiUrl); this.socket = io(url.origin, this.config); + this.init(handlers); } /** @@ -71,16 +80,10 @@ export class SocketIoClient { onConnect, onDisconnect, onConnectError, - }: { - onConnect?: () => void; - onDisconnect?: (reason: string, details: unknown) => void; - onConnectError?: (error: Error) => void; - }) { - if (!this.initialized) this.socket.connect(); + }: SocketIoEventHandlers) { onConnect && this.uniqueOn('connect', onConnect); onDisconnect && this.uniqueOn('disconnect', onDisconnect); onConnectError && this.uniqueOn('connect_error', onConnectError); - this.initialized = true; } /** @@ -100,7 +103,6 @@ export class SocketIoClient { */ public disconnect() { this.socket.disconnect(); - this.initialized = false; } /** @@ -184,10 +186,28 @@ export class SocketIoClient { } } -export const builSocketIoClient = (config: Config) => - new SocketIoClient(config.apiUrl, { - query: { - channel: config.channel, - verification_token: config.token, - }, - }); +let socketIoClient: SocketIoClient; + +/** + * Returns a singleton instance of the socket io client + * + * @param config The socket connection config + * @param handlers Event handlers + * @returns Socket io client instance + */ +export const getSocketIoClient = (config: Config, handlers: SocketIoEventHandlers) => { + if (!socketIoClient) { + socketIoClient = new SocketIoClient( + config.apiUrl, + { + query: { + channel: config.channel, + verification_token: config.token, + }, + }, + handlers, + ); + } + + return socketIoClient; +}; diff --git a/widget/vite.config.ts b/widget/vite.config.ts index 998d7ad1..67d5795e 100644 --- a/widget/vite.config.ts +++ b/widget/vite.config.ts @@ -5,28 +5,31 @@ import { defineConfig } from "vite"; import dts from "vite-plugin-dts"; -export default defineConfig({ - plugins: [react(), dts()], - server: { - host: "0.0.0.0", - }, - define: { - "process.env": process.env, - }, - build: { - lib: { - entry: resolve(__dirname, "src/ChatWidget.tsx"), - name: "HexabotWidget", - fileName: (format) => `hexabot-widget.${format}.js`, +export default defineConfig(({ mode }) => { + return { + plugins: [react(), dts()], + server: { + host: '0.0.0.0', }, - rollupOptions: { - external: ["react", "react-dom"], - output: { - globals: { - react: "React", - "react-dom": "ReactDOM", + define: { + 'process.env': + mode === 'development' ? { 'process.env': process.env } : {}, + }, + build: { + lib: { + entry: resolve(__dirname, 'src/ChatWidget.tsx'), + name: 'HexabotWidget', + fileName: (format) => `hexabot-widget.${format}.js`, + }, + rollupOptions: { + external: ['react', 'react-dom'], + output: { + globals: { + react: 'React', + 'react-dom': 'ReactDOM', + }, }, }, }, - }, + }; });