diff --git a/client/src/components/chat-affix/chat-affix.container.jsx b/client/src/components/chat-affix/chat-affix.container.jsx
index af99d4719..21a4ede7e 100644
--- a/client/src/components/chat-affix/chat-affix.container.jsx
+++ b/client/src/components/chat-affix/chat-affix.container.jsx
@@ -12,11 +12,16 @@ export function ChatAffixContainer({ bodyshop, chatVisible, currentUser }) {
const client = useApolloClient();
const { socket } = useSocket();
- // 1) FCM subscription (independent of socket handler registration)
- useEffect(() => {
- if (!bodyshop?.messagingservicesid) return;
+ const messagingServicesId = bodyshop?.messagingservicesid;
+ const bodyshopId = bodyshop?.id;
+ const imexshopid = bodyshop?.imexshopid;
- async function subscribeToTopicForFCMNotification() {
+ const messagingEnabled = Boolean(messagingServicesId);
+
+ useEffect(() => {
+ if (!messagingEnabled) return;
+
+ (async () => {
try {
await requestForToken();
await axios.post("/notifications/subscribe", {
@@ -24,23 +29,19 @@ export function ChatAffixContainer({ bodyshop, chatVisible, currentUser }) {
vapidKey: import.meta.env.VITE_APP_FIREBASE_PUBLIC_VAPID_KEY
}),
type: "messaging",
- imexshopid: bodyshop.imexshopid
+ imexshopid
});
} catch (error) {
console.log("Error attempting to subscribe to messaging topic: ", error);
}
- }
+ })();
+ }, [messagingEnabled, imexshopid]);
- subscribeToTopicForFCMNotification();
- }, [bodyshop?.messagingservicesid, bodyshop?.imexshopid]);
-
- // 2) Register socket handlers as soon as socket is connected (regardless of chatVisible)
useEffect(() => {
if (!socket) return;
- if (!bodyshop?.messagingservicesid) return;
- if (!bodyshop?.id) return;
+ if (!messagingEnabled) return;
+ if (!bodyshopId) return;
- // If socket isn't connected yet, ensure no stale handlers remain.
if (!socket.connected) {
unregisterMessagingHandlers({ socket });
return;
@@ -56,16 +57,14 @@ export function ChatAffixContainer({ bodyshop, chatVisible, currentUser }) {
bodyshop
});
- return () => {
- unregisterMessagingHandlers({ socket });
- };
- }, [socket, socket?.connected, bodyshop?.id, bodyshop?.messagingservicesid, client, currentUser?.email]);
+ return () => unregisterMessagingHandlers({ socket });
+ }, [socket, messagingEnabled, bodyshopId, client, currentUser?.email, bodyshop]);
- if (!bodyshop?.messagingservicesid) return <>>;
+ if (!messagingEnabled) return null;
return (
- {bodyshop?.messagingservicesid ? : null}
+ {messagingEnabled ? : null}
);
}
diff --git a/client/src/components/chat-open-button/chat-open-button.component.jsx b/client/src/components/chat-open-button/chat-open-button.component.jsx
index 5c6781edd..4d58f7a58 100644
--- a/client/src/components/chat-open-button/chat-open-button.component.jsx
+++ b/client/src/components/chat-open-button/chat-open-button.component.jsx
@@ -1,22 +1,23 @@
+import { Button } from "antd";
import parsePhoneNumber from "libphonenumber-js";
+import { useCallback, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
-import { openChatByPhone } from "../../redux/messaging/messaging.actions";
-import PhoneNumberFormatter from "../../utils/PhoneFormatter";
-
import { createStructuredSelector } from "reselect";
+import { openChatByPhone } from "../../redux/messaging/messaging.actions";
import { selectBodyshop } from "../../redux/user/user.selectors";
import { searchingForConversation } from "../../redux/messaging/messaging.selectors";
import { useSocket } from "../../contexts/SocketIO/useSocket.js";
import { useNotification } from "../../contexts/Notifications/notificationContext.jsx";
+import PhoneNumberFormatter from "../../utils/PhoneFormatter";
const mapStateToProps = createStructuredSelector({
bodyshop: selectBodyshop,
- searchingForConversation: searchingForConversation
+ searchingForConversation
});
const mapDispatchToProps = (dispatch) => ({
- openChatByPhone: (phone) => dispatch(openChatByPhone(phone))
+ openChatByPhone: (payload) => dispatch(openChatByPhone(payload))
});
export function ChatOpenButton({ bodyshop, searchingForConversation, phone, type, jobid, openChatByPhone }) {
@@ -24,31 +25,59 @@ export function ChatOpenButton({ bodyshop, searchingForConversation, phone, type
const { socket } = useSocket();
const notification = useNotification();
- if (!phone) return <>>;
+ if (!phone) return null;
- if (!bodyshop.messagingservicesid) {
- return {phone};
- }
+ const messagingEnabled = Boolean(bodyshop?.messagingservicesid);
+ const parsed = useMemo(() => {
+ if (!messagingEnabled) return null;
+ try {
+ return parsePhoneNumber(phone, "CA") || null;
+ } catch {
+ return null;
+ }
+ }, [messagingEnabled, phone]);
+
+ const isValid = Boolean(parsed?.isValid?.() && parsed.isValid());
+ const clickable = messagingEnabled && !searchingForConversation && isValid;
+
+ const onClick = useCallback(
+ (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (!messagingEnabled) return;
+ if (searchingForConversation) return;
+
+ if (!isValid) {
+ notification.error({ title: t("messaging.error.invalidphone") });
+ return;
+ }
+
+ openChatByPhone({
+ phone_num: parsed.formatInternational(),
+ jobid,
+ socket
+ });
+ },
+ [messagingEnabled, searchingForConversation, isValid, parsed, jobid, socket, openChatByPhone, notification, t]
+ );
+
+ const content = {phone};
+
+ // If not clickable, render plain formatted text (no link styling)
+ if (!clickable) return content;
+
+ // Clickable: render as a link-styled button (best for a “command”)
return (
- {
- e.preventDefault();
- e.stopPropagation();
-
- if (searchingForConversation) return; // Prevent finding the same thing twice.
-
- const p = parsePhoneNumber(phone, "CA");
- if (p && p.isValid()) {
- openChatByPhone({ phone_num: p.formatInternational(), jobid, socket });
- } else {
- notification.error({ title: t("messaging.error.invalidphone") });
- }
- }}
+
+ {content}
+
);
}
diff --git a/client/src/translations/en_us/common.json b/client/src/translations/en_us/common.json
index f08c77f21..363c53308 100644
--- a/client/src/translations/en_us/common.json
+++ b/client/src/translations/en_us/common.json
@@ -2431,7 +2431,8 @@
"messaging": {
"actions": {
"link": "Link to Job",
- "new": "New Conversation"
+ "new": "New Conversation",
+ "openchat": "Open Chat"
},
"errors": {
"invalidphone": "The phone number is invalid. Unable to open conversation. ",
diff --git a/client/src/translations/es/common.json b/client/src/translations/es/common.json
index a8441a160..287f76346 100644
--- a/client/src/translations/es/common.json
+++ b/client/src/translations/es/common.json
@@ -2428,7 +2428,8 @@
"messaging": {
"actions": {
"link": "",
- "new": ""
+ "new": "",
+ "openchat": ""
},
"errors": {
"invalidphone": "",
diff --git a/client/src/translations/fr/common.json b/client/src/translations/fr/common.json
index 97a04bf3b..0a56716bf 100644
--- a/client/src/translations/fr/common.json
+++ b/client/src/translations/fr/common.json
@@ -2428,7 +2428,9 @@
"messaging": {
"actions": {
"link": "",
- "new": ""
+ "new": "",
+
+ "openchat": ""
},
"errors": {
"invalidphone": "",
diff --git a/client/src/utils/PhoneFormatter.jsx b/client/src/utils/PhoneFormatter.jsx
index 90fc49e50..cfd63b786 100644
--- a/client/src/utils/PhoneFormatter.jsx
+++ b/client/src/utils/PhoneFormatter.jsx
@@ -11,13 +11,8 @@ export default function PhoneNumberFormatter({ children, type }) {
return (
- {phone}
- {type ? (
- <>
- {" "}
- ({type})
- >
- ) : null}
+ {phone}
+ {type ? ({type}) : null}
);
}