From 55ebe75861c71a8ce59b944f06e253b521af1af8 Mon Sep 17 00:00:00 2001 From: hexastack Date: Wed, 22 Jan 2025 08:42:37 +0100 Subject: [PATCH 1/6] fix: remove unnecessary state change --- .../src/app-components/inputs/PasswordInput.tsx | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/frontend/src/app-components/inputs/PasswordInput.tsx b/frontend/src/app-components/inputs/PasswordInput.tsx index f7bc47ec..98bcf871 100644 --- a/frontend/src/app-components/inputs/PasswordInput.tsx +++ b/frontend/src/app-components/inputs/PasswordInput.tsx @@ -1,31 +1,25 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 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 VisibilityOffOutlinedIcon from "@mui/icons-material/VisibilityOffOutlined"; import VisibilityOutlinedIcon from "@mui/icons-material/VisibilityOutlined"; import { IconButton, InputAdornment, TextFieldProps } from "@mui/material"; -import React, { forwardRef, useState } from "react"; +import { forwardRef, useState } from "react"; import { Input } from "./Input"; export const PasswordInput = forwardRef( ({ onChange, InputProps, value, ...rest }, ref) => { - const [password, setPassword] = useState(value as string); const [showPassword, setShowPassword] = useState(false); const handleTogglePasswordVisibility = () => { setShowPassword(!showPassword); }; - const handleChange = (event: React.ChangeEvent) => { - setPassword(event.target.value); - if (onChange) { - onChange(event); - } - }; return ( ( type={showPassword ? "text" : "password"} {...rest} defaultValue={value} - onChange={handleChange} + onChange={onChange} InputProps={{ ...InputProps, - endAdornment: password && ( + endAdornment: ( {showPassword ? ( From f4699ab17bd5d11045f529df5c9eb0591d5ad32a Mon Sep 17 00:00:00 2001 From: abdou6666 Date: Wed, 22 Jan 2025 17:12:53 +0100 Subject: [PATCH 2/6] feat: display toasters using react-query callback on success,failure --- frontend/public/locales/en/translation.json | 3 ++- frontend/public/locales/fr/translation.json | 3 ++- frontend/src/contexts/auth.context.tsx | 10 +++------- frontend/src/hooks/entities/auth-hooks.ts | 8 ++++++++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/translation.json index 838d68d1..50e41ba0 100644 --- a/frontend/public/locales/en/translation.json +++ b/frontend/public/locales/en/translation.json @@ -109,7 +109,8 @@ "code_is_required": "Language code is required", "text_is_required": "Text is required", "invalid_file_type": "Invalid file type. Please select a file in the supported format.", - "select_category": "Select a flow" + "select_category": "Select a flow", + "logout_failed": "Something went wrong while disconnecting" }, "menu": { "terms": "Terms of Use", diff --git a/frontend/public/locales/fr/translation.json b/frontend/public/locales/fr/translation.json index 521b911a..7ce2c0dd 100644 --- a/frontend/public/locales/fr/translation.json +++ b/frontend/public/locales/fr/translation.json @@ -109,7 +109,8 @@ "code_is_required": "Le code est requis", "text_is_required": "Texte requis", "invalid_file_type": "Type de fichier invalide. Veuillez choisir un fichier dans un format pris en charge.", - "select_category": "Sélectionner une catégorie" + "select_category": "Sélectionner une catégorie", + "logout_failed": "Une erreur s'est produite lors de la déconnexion" }, "menu": { "terms": "Conditions d'utilisation", diff --git a/frontend/src/contexts/auth.context.tsx b/frontend/src/contexts/auth.context.tsx index dfdc1089..a2faf186 100644 --- a/frontend/src/contexts/auth.context.tsx +++ b/frontend/src/contexts/auth.context.tsx @@ -6,7 +6,6 @@ * 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 getConfig from "next/config"; import { useRouter } from "next/router"; import { createContext, ReactNode, useEffect, useState } from "react"; @@ -22,7 +21,6 @@ import { Progress } from "@/app-components/displays/Progress"; import { useLogout } from "@/hooks/entities/auth-hooks"; import { useApiClient } from "@/hooks/useApiClient"; import { CURRENT_USER_KEY, PUBLIC_PATHS } from "@/hooks/useAuth"; -import { useToast } from "@/hooks/useToast"; import { useTranslate } from "@/hooks/useTranslate"; import { RouterType } from "@/services/types"; import { IUser } from "@/types/user.types"; @@ -54,18 +52,16 @@ export const AuthProvider = ({ children }: AuthProviderProps): JSX.Element => { const router = useRouter(); const [search, setSearch] = useState(""); const hasPublicPath = PUBLIC_PATHS.includes(router.pathname); - const { i18n, t } = useTranslate(); - const { toast } = useToast(); + const { i18n } = useTranslate(); const [isReady, setIsReady] = useState(false); const queryClient = useQueryClient(); const updateLanguage = (lang: string) => { i18n.changeLanguage(lang); }; - const { mutateAsync: logoutSession } = useLogout(); + const { mutate: logoutSession } = useLogout(); const logout = async () => { updateLanguage(publicRuntimeConfig.lang.default); - await logoutSession(); - toast.success(t("message.logout_success")); + logoutSession(); }; const authRedirection = async (isAuthenticated: boolean) => { if (isAuthenticated) { diff --git a/frontend/src/hooks/entities/auth-hooks.ts b/frontend/src/hooks/entities/auth-hooks.ts index c1fa01a1..e3a1b07b 100755 --- a/frontend/src/hooks/entities/auth-hooks.ts +++ b/frontend/src/hooks/entities/auth-hooks.ts @@ -22,6 +22,8 @@ import { useSocket } from "@/websocket/socket-hooks"; import { useFind } from "../crud/useFind"; import { useApiClient } from "../useApiClient"; import { CURRENT_USER_KEY, useAuth, useLogoutRedirection } from "../useAuth"; +import { useToast } from "../useToast"; +import { useTranslate } from "../useTranslate"; export const useLogin = ( options?: Omit< @@ -54,6 +56,8 @@ export const useLogout = ( const { apiClient } = useApiClient(); const { socket } = useSocket(); const { logoutRedirection } = useLogoutRedirection(); + const { toast } = useToast(); + const { t } = useTranslate(); return useMutation({ ...options, @@ -65,6 +69,10 @@ export const useLogout = ( onSuccess: async () => { queryClient.removeQueries([CURRENT_USER_KEY]); await logoutRedirection(); + toast.success(t("message.logout_success")); + }, + onError: () => { + toast.error(t("message.logout_failed")); }, }); }; From 60fc21f329c4416b88d2b2fcbc59204987eeceef Mon Sep 17 00:00:00 2001 From: Med Marrouchi Date: Wed, 22 Jan 2025 17:25:06 +0100 Subject: [PATCH 3/6] Update frontend/public/locales/en/translation.json --- frontend/public/locales/en/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/public/locales/en/translation.json b/frontend/public/locales/en/translation.json index 50e41ba0..dd9bad2a 100644 --- a/frontend/public/locales/en/translation.json +++ b/frontend/public/locales/en/translation.json @@ -110,7 +110,7 @@ "text_is_required": "Text is required", "invalid_file_type": "Invalid file type. Please select a file in the supported format.", "select_category": "Select a flow", - "logout_failed": "Something went wrong while disconnecting" + "logout_failed": "Something went wrong during logout" }, "menu": { "terms": "Terms of Use", From 2b433ef7559eeffb4cd5e703fd24d83377ff6dc7 Mon Sep 17 00:00:00 2001 From: Mohamed Marrouchi Date: Wed, 22 Jan 2025 19:34:11 +0100 Subject: [PATCH 4/6] fix: re-subscribe on ws reconnect --- widget/src/components/UserSubscription.tsx | 5 ++-- widget/src/providers/ChatProvider.tsx | 33 +++++++++++++++++++--- widget/src/utils/SocketIoClient.ts | 9 +++++- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/widget/src/components/UserSubscription.tsx b/widget/src/components/UserSubscription.tsx index 4e78e65e..487be242 100644 --- a/widget/src/components/UserSubscription.tsx +++ b/widget/src/components/UserSubscription.tsx @@ -1,11 +1,12 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 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, @@ -75,7 +76,7 @@ const UserSubscription: React.FC = () => { }); setMessages(messages); setParticipants([ - ...participants, + participants[0], { id: profile.foreign_id, foreign_id: profile.foreign_id, diff --git a/widget/src/providers/ChatProvider.tsx b/widget/src/providers/ChatProvider.tsx index 3b903ddd..f24795cd 100644 --- a/widget/src/providers/ChatProvider.tsx +++ b/widget/src/providers/ChatProvider.tsx @@ -1,11 +1,12 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 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, { createContext, ReactNode, @@ -268,7 +269,7 @@ const ChatProvider: React.FC<{ content_type: QuickReplyType.text, text: qr.title, payload: qr.payload, - }) as ISuggestion, + } as ISuggestion), ), ); } else { @@ -308,11 +309,17 @@ const ChatProvider: React.FC<{ async (firstName?: string, lastName?: string) => { try { setConnectionState(2); + const queryParams: Record = + firstName && lastName + ? { first_name: firstName, last_name: lastName } + : {}; const { body } = await socketCtx.socket.get<{ messages: TMessage[]; profile: ISubscriber; }>( - `/webhook/${config.channel}/?first_name=${firstName}&last_name=${lastName}`, + `/webhook/${config.channel}/?${new URLSearchParams( + queryParams, + ).toString()}`, ); localStorage.setItem("profile", JSON.stringify(body.profile)); @@ -332,7 +339,7 @@ const ChatProvider: React.FC<{ }), ); setParticipants([ - ...participants, + participants[0], { id: body.profile.foreign_id, foreign_id: body.profile.foreign_id, @@ -382,6 +389,24 @@ const ChatProvider: React.FC<{ if (screen === "chat" && connectionState === ConnectionState.connected) { handleSubscription(); } + + // When user loses internet connection, on reconnect + // we will need to subscribe him again (join the io room) + const reSubscribe = () => { + const item = localStorage.getItem("profile"); + + if (item) { + const profile = JSON.parse(item) as ISubscriber; + + handleSubscription(profile.first_name, profile.last_name); + } + }; + + socketCtx.socket.io.on("reconnect", reSubscribe); + + return () => { + socketCtx.socket.io.off("reconnect", reSubscribe); + }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); diff --git a/widget/src/utils/SocketIoClient.ts b/widget/src/utils/SocketIoClient.ts index 538754b6..b8f3d36a 100644 --- a/widget/src/utils/SocketIoClient.ts +++ b/widget/src/utils/SocketIoClient.ts @@ -1,11 +1,12 @@ /* - * Copyright © 2024 Hexastack. All rights reserved. + * Copyright © 2025 Hexastack. All rights reserved. * * Licensed under the GNU Affero General Public License v3.0 (AGPLv3) with the following additional terms: * 1. The name "Hexabot" is a trademark of Hexastack. You may not use this name in derivative works without express written permission. * 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 { io, ManagerOptions, Socket, SocketOptions } from "socket.io-client"; import { Config } from "../types/config.types"; @@ -56,6 +57,8 @@ export class SocketIoClient { private config: SocketIoClientConfig; + public id: string; + constructor( apiUrl: string, socketConfig: SocketIoClientConfig, @@ -72,6 +75,10 @@ export class SocketIoClient { this.init(handlers); } + get io() { + return this.socket.io; + } + /** * Initializes the socket client and sets up event handlers. * @param handlers Event handlers for connection, disconnection, and connection errors From fcacd626329acbb51a252d5c2cb4c1e7e15bf3a4 Mon Sep 17 00:00:00 2001 From: Mohamed Marrouchi Date: Wed, 22 Jan 2025 19:40:03 +0100 Subject: [PATCH 5/6] fix: remove extra attr --- widget/src/utils/SocketIoClient.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/widget/src/utils/SocketIoClient.ts b/widget/src/utils/SocketIoClient.ts index b8f3d36a..9a9f55a0 100644 --- a/widget/src/utils/SocketIoClient.ts +++ b/widget/src/utils/SocketIoClient.ts @@ -57,8 +57,6 @@ export class SocketIoClient { private config: SocketIoClientConfig; - public id: string; - constructor( apiUrl: string, socketConfig: SocketIoClientConfig, From 60434a6e8a48187a165ec1c240963c7e588ca480 Mon Sep 17 00:00:00 2001 From: hexastack Date: Thu, 23 Jan 2025 10:20:40 +0100 Subject: [PATCH 6/6] fix: remove unused snippet --- frontend/src/app-components/inputs/PasswordInput.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/frontend/src/app-components/inputs/PasswordInput.tsx b/frontend/src/app-components/inputs/PasswordInput.tsx index 98bcf871..35206a5e 100644 --- a/frontend/src/app-components/inputs/PasswordInput.tsx +++ b/frontend/src/app-components/inputs/PasswordInput.tsx @@ -6,7 +6,6 @@ * 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 VisibilityOffOutlinedIcon from "@mui/icons-material/VisibilityOffOutlined"; import VisibilityOutlinedIcon from "@mui/icons-material/VisibilityOutlined"; import { IconButton, InputAdornment, TextFieldProps } from "@mui/material"; @@ -15,7 +14,7 @@ import { forwardRef, useState } from "react"; import { Input } from "./Input"; export const PasswordInput = forwardRef( - ({ onChange, InputProps, value, ...rest }, ref) => { + ({ onChange, InputProps, ...rest }, ref) => { const [showPassword, setShowPassword] = useState(false); const handleTogglePasswordVisibility = () => { setShowPassword(!showPassword); @@ -26,7 +25,6 @@ export const PasswordInput = forwardRef( ref={ref} type={showPassword ? "text" : "password"} {...rest} - defaultValue={value} onChange={onChange} InputProps={{ ...InputProps,