@diffsome/react 1.2.12 → 1.2.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/chat.css CHANGED
@@ -263,6 +263,59 @@
263
263
  color: #ef4444;
264
264
  }
265
265
 
266
+ /* End Chat Button */
267
+ .diffsome-chat-end-container {
268
+ padding: 8px 16px 12px;
269
+ text-align: center;
270
+ }
271
+
272
+ .diffsome-chat-end-btn {
273
+ background: none;
274
+ border: 1px solid var(--diffsome-chat-border);
275
+ color: var(--diffsome-chat-text-muted);
276
+ font-size: 12px;
277
+ padding: 6px 16px;
278
+ border-radius: 16px;
279
+ cursor: pointer;
280
+ transition: all 0.2s ease;
281
+ }
282
+
283
+ .diffsome-chat-end-btn:hover {
284
+ border-color: #ef4444;
285
+ color: #ef4444;
286
+ background: #fef2f2;
287
+ }
288
+
289
+ /* Chat Ended Message */
290
+ .diffsome-chat-ended {
291
+ padding: 16px;
292
+ text-align: center;
293
+ color: var(--diffsome-chat-text-muted);
294
+ font-size: 14px;
295
+ border-top: 1px solid var(--diffsome-chat-border);
296
+ background: var(--diffsome-chat-bg);
297
+ display: flex;
298
+ flex-direction: column;
299
+ align-items: center;
300
+ gap: 12px;
301
+ }
302
+
303
+ .diffsome-chat-new-btn {
304
+ background: var(--diffsome-chat-primary);
305
+ color: white;
306
+ border: none;
307
+ padding: 8px 20px;
308
+ border-radius: 20px;
309
+ font-size: 14px;
310
+ font-weight: 500;
311
+ cursor: pointer;
312
+ transition: background-color 0.2s ease;
313
+ }
314
+
315
+ .diffsome-chat-new-btn:hover {
316
+ background: var(--diffsome-chat-primary-dark);
317
+ }
318
+
266
319
  /* System Message */
267
320
  .diffsome-chat-message-system {
268
321
  align-self: center;
package/dist/index.d.mts CHANGED
@@ -866,6 +866,8 @@ interface ChatBubbleProps {
866
866
  onOpen?: () => void;
867
867
  /** Callback when chat is closed */
868
868
  onClose?: () => void;
869
+ /** Member auth token for logged-in users (optional) */
870
+ memberToken?: string;
869
871
  }
870
872
  declare const ChatBubble: React__default.FC<ChatBubbleProps>;
871
873
 
@@ -884,11 +886,14 @@ interface ChatWidgetProps {
884
886
  onSend: (message: string) => void;
885
887
  onTyping?: () => void;
886
888
  onClose?: () => void;
889
+ onEndChat?: () => void;
890
+ onStartNewChat?: () => void;
887
891
  title?: string;
888
892
  subtitle?: string;
889
893
  placeholder?: string;
890
894
  loading?: boolean;
891
895
  typing?: boolean;
896
+ ended?: boolean;
892
897
  className?: string;
893
898
  }
894
899
  declare const ChatWidget: React__default.FC<ChatWidgetProps>;
package/dist/index.d.ts CHANGED
@@ -866,6 +866,8 @@ interface ChatBubbleProps {
866
866
  onOpen?: () => void;
867
867
  /** Callback when chat is closed */
868
868
  onClose?: () => void;
869
+ /** Member auth token for logged-in users (optional) */
870
+ memberToken?: string;
869
871
  }
870
872
  declare const ChatBubble: React__default.FC<ChatBubbleProps>;
871
873
 
@@ -884,11 +886,14 @@ interface ChatWidgetProps {
884
886
  onSend: (message: string) => void;
885
887
  onTyping?: () => void;
886
888
  onClose?: () => void;
889
+ onEndChat?: () => void;
890
+ onStartNewChat?: () => void;
887
891
  title?: string;
888
892
  subtitle?: string;
889
893
  placeholder?: string;
890
894
  loading?: boolean;
891
895
  typing?: boolean;
896
+ ended?: boolean;
892
897
  className?: string;
893
898
  }
894
899
  declare const ChatWidget: React__default.FC<ChatWidgetProps>;
package/dist/index.js CHANGED
@@ -2719,11 +2719,14 @@ var ChatWidget = ({
2719
2719
  onSend,
2720
2720
  onTyping,
2721
2721
  onClose,
2722
+ onEndChat,
2723
+ onStartNewChat,
2722
2724
  title = "Chat Support",
2723
2725
  subtitle = "We typically reply within a few minutes",
2724
2726
  placeholder = "Type a message...",
2725
2727
  loading = false,
2726
2728
  typing = false,
2729
+ ended = false,
2727
2730
  className = ""
2728
2731
  }) => {
2729
2732
  const messagesEndRef = (0, import_react23.useRef)(null);
@@ -2791,21 +2794,42 @@ var ChatWidget = ({
2791
2794
  ] }),
2792
2795
  /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref: messagesEndRef })
2793
2796
  ] }) }),
2794
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2795
- ChatInput,
2796
- {
2797
- onSend,
2798
- onTyping,
2799
- placeholder,
2800
- disabled: loading
2801
- }
2802
- )
2797
+ ended ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "diffsome-chat-ended", children: [
2798
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { children: "Chat ended. Thank you!" }),
2799
+ onStartNewChat && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2800
+ "button",
2801
+ {
2802
+ className: "diffsome-chat-new-btn",
2803
+ onClick: onStartNewChat,
2804
+ children: "Start New Chat"
2805
+ }
2806
+ )
2807
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
2808
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2809
+ ChatInput,
2810
+ {
2811
+ onSend,
2812
+ onTyping,
2813
+ placeholder,
2814
+ disabled: loading
2815
+ }
2816
+ ),
2817
+ onEndChat && messages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "diffsome-chat-end-container", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
2818
+ "button",
2819
+ {
2820
+ className: "diffsome-chat-end-btn",
2821
+ onClick: onEndChat,
2822
+ children: "End Chat"
2823
+ }
2824
+ ) })
2825
+ ] })
2803
2826
  ] });
2804
2827
  };
2805
2828
 
2806
2829
  // src/components/chat/ChatBubble.tsx
2807
2830
  var import_jsx_runtime6 = require("react/jsx-runtime");
2808
2831
  var STORAGE_KEY = "diffsome_conversation_id";
2832
+ var MEMBER_STORAGE_PREFIX = "diffsome_member_conversation_";
2809
2833
  var ChatBubble = ({
2810
2834
  client,
2811
2835
  title = "Chat Support",
@@ -2820,8 +2844,33 @@ var ChatBubble = ({
2820
2844
  showUnreadBadge = true,
2821
2845
  className = "",
2822
2846
  onOpen,
2823
- onClose
2847
+ onClose,
2848
+ memberToken
2824
2849
  }) => {
2850
+ (0, import_react24.useEffect)(() => {
2851
+ if (memberToken) {
2852
+ client.setToken(memberToken);
2853
+ } else {
2854
+ client.setToken(null);
2855
+ }
2856
+ if (initialized) {
2857
+ connectionRef.current?.disconnect();
2858
+ connectionRef.current = null;
2859
+ setConnection(null);
2860
+ setMessages([]);
2861
+ setConversationId(null);
2862
+ setInitialized(false);
2863
+ setEnded(false);
2864
+ seenMessageIds.current.clear();
2865
+ }
2866
+ }, [client, memberToken]);
2867
+ const getStorageKey = (0, import_react24.useCallback)(() => {
2868
+ if (memberToken) {
2869
+ const hash = memberToken.slice(-8);
2870
+ return `${MEMBER_STORAGE_PREFIX}${hash}`;
2871
+ }
2872
+ return STORAGE_KEY;
2873
+ }, [memberToken]);
2825
2874
  const [isOpen, setIsOpen] = (0, import_react24.useState)(false);
2826
2875
  const [messages, setMessages] = (0, import_react24.useState)([]);
2827
2876
  const [conversationId, setConversationId] = (0, import_react24.useState)(null);
@@ -2830,6 +2879,7 @@ var ChatBubble = ({
2830
2879
  const [typing, setTyping] = (0, import_react24.useState)(false);
2831
2880
  const [unreadCount, setUnreadCount] = (0, import_react24.useState)(0);
2832
2881
  const [initialized, setInitialized] = (0, import_react24.useState)(false);
2882
+ const [ended, setEnded] = (0, import_react24.useState)(false);
2833
2883
  const seenMessageIds = (0, import_react24.useRef)(/* @__PURE__ */ new Set());
2834
2884
  const connectionRef = (0, import_react24.useRef)(null);
2835
2885
  const setupListeners = (0, import_react24.useCallback)((conn) => {
@@ -2853,10 +2903,11 @@ var ChatBubble = ({
2853
2903
  const initChat = (0, import_react24.useCallback)(async () => {
2854
2904
  if (initialized || connectionRef.current) return;
2855
2905
  setLoading(true);
2906
+ const storageKey = getStorageKey();
2856
2907
  try {
2857
2908
  let savedConvId = null;
2858
2909
  if (typeof localStorage !== "undefined") {
2859
- const saved = localStorage.getItem(STORAGE_KEY);
2910
+ const saved = localStorage.getItem(storageKey);
2860
2911
  if (saved) {
2861
2912
  savedConvId = parseInt(saved, 10);
2862
2913
  }
@@ -2875,7 +2926,7 @@ var ChatBubble = ({
2875
2926
  return;
2876
2927
  } catch (error) {
2877
2928
  console.log("Could not restore conversation, starting new one");
2878
- localStorage.removeItem(STORAGE_KEY);
2929
+ localStorage.removeItem(storageKey);
2879
2930
  }
2880
2931
  }
2881
2932
  const result = await client.chat.start({
@@ -2886,7 +2937,7 @@ var ChatBubble = ({
2886
2937
  const newConvId = result.conversation_id;
2887
2938
  setConversationId(newConvId);
2888
2939
  if (typeof localStorage !== "undefined") {
2889
- localStorage.setItem(STORAGE_KEY, newConvId.toString());
2940
+ localStorage.setItem(storageKey, newConvId.toString());
2890
2941
  }
2891
2942
  const conn = client.chat.connect(newConvId);
2892
2943
  connectionRef.current = conn;
@@ -2901,7 +2952,7 @@ var ChatBubble = ({
2901
2952
  } finally {
2902
2953
  setLoading(false);
2903
2954
  }
2904
- }, [client.chat, initialized, setupListeners, visitorName, visitorEmail, greeting]);
2955
+ }, [client.chat, initialized, setupListeners, visitorName, visitorEmail, greeting, getStorageKey]);
2905
2956
  const handleOpen = () => {
2906
2957
  setIsOpen(true);
2907
2958
  setUnreadCount(0);
@@ -2942,6 +2993,30 @@ var ChatBubble = ({
2942
2993
  const handleTyping = () => {
2943
2994
  connectionRef.current?.sendTyping();
2944
2995
  };
2996
+ const handleEndChat = async () => {
2997
+ const conn = connectionRef.current;
2998
+ if (!conn) return;
2999
+ try {
3000
+ await conn.close();
3001
+ } catch (error) {
3002
+ console.error("Failed to close chat:", error);
3003
+ }
3004
+ if (typeof localStorage !== "undefined") {
3005
+ localStorage.removeItem(getStorageKey());
3006
+ }
3007
+ setEnded(true);
3008
+ connectionRef.current?.disconnect();
3009
+ connectionRef.current = null;
3010
+ setConnection(null);
3011
+ setInitialized(false);
3012
+ setConversationId(null);
3013
+ };
3014
+ const handleStartNewChat = () => {
3015
+ setEnded(false);
3016
+ setMessages([]);
3017
+ seenMessageIds.current.clear();
3018
+ initChat();
3019
+ };
2945
3020
  (0, import_react24.useEffect)(() => {
2946
3021
  return () => {
2947
3022
  connectionRef.current?.disconnect();
@@ -2961,11 +3036,14 @@ var ChatBubble = ({
2961
3036
  onSend: handleSend,
2962
3037
  onTyping: handleTyping,
2963
3038
  onClose: handleClose,
3039
+ onEndChat: handleEndChat,
3040
+ onStartNewChat: handleStartNewChat,
2964
3041
  title,
2965
3042
  subtitle,
2966
3043
  placeholder,
2967
3044
  loading,
2968
- typing
3045
+ typing,
3046
+ ended
2969
3047
  }
2970
3048
  ),
2971
3049
  /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
package/dist/index.mjs CHANGED
@@ -2627,11 +2627,14 @@ var ChatWidget = ({
2627
2627
  onSend,
2628
2628
  onTyping,
2629
2629
  onClose,
2630
+ onEndChat,
2631
+ onStartNewChat,
2630
2632
  title = "Chat Support",
2631
2633
  subtitle = "We typically reply within a few minutes",
2632
2634
  placeholder = "Type a message...",
2633
2635
  loading = false,
2634
2636
  typing = false,
2637
+ ended = false,
2635
2638
  className = ""
2636
2639
  }) => {
2637
2640
  const messagesEndRef = useRef2(null);
@@ -2699,21 +2702,42 @@ var ChatWidget = ({
2699
2702
  ] }),
2700
2703
  /* @__PURE__ */ jsx5("div", { ref: messagesEndRef })
2701
2704
  ] }) }),
2702
- /* @__PURE__ */ jsx5(
2703
- ChatInput,
2704
- {
2705
- onSend,
2706
- onTyping,
2707
- placeholder,
2708
- disabled: loading
2709
- }
2710
- )
2705
+ ended ? /* @__PURE__ */ jsxs4("div", { className: "diffsome-chat-ended", children: [
2706
+ /* @__PURE__ */ jsx5("span", { children: "Chat ended. Thank you!" }),
2707
+ onStartNewChat && /* @__PURE__ */ jsx5(
2708
+ "button",
2709
+ {
2710
+ className: "diffsome-chat-new-btn",
2711
+ onClick: onStartNewChat,
2712
+ children: "Start New Chat"
2713
+ }
2714
+ )
2715
+ ] }) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
2716
+ /* @__PURE__ */ jsx5(
2717
+ ChatInput,
2718
+ {
2719
+ onSend,
2720
+ onTyping,
2721
+ placeholder,
2722
+ disabled: loading
2723
+ }
2724
+ ),
2725
+ onEndChat && messages.length > 0 && /* @__PURE__ */ jsx5("div", { className: "diffsome-chat-end-container", children: /* @__PURE__ */ jsx5(
2726
+ "button",
2727
+ {
2728
+ className: "diffsome-chat-end-btn",
2729
+ onClick: onEndChat,
2730
+ children: "End Chat"
2731
+ }
2732
+ ) })
2733
+ ] })
2711
2734
  ] });
2712
2735
  };
2713
2736
 
2714
2737
  // src/components/chat/ChatBubble.tsx
2715
2738
  import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
2716
2739
  var STORAGE_KEY = "diffsome_conversation_id";
2740
+ var MEMBER_STORAGE_PREFIX = "diffsome_member_conversation_";
2717
2741
  var ChatBubble = ({
2718
2742
  client,
2719
2743
  title = "Chat Support",
@@ -2728,8 +2752,33 @@ var ChatBubble = ({
2728
2752
  showUnreadBadge = true,
2729
2753
  className = "",
2730
2754
  onOpen,
2731
- onClose
2755
+ onClose,
2756
+ memberToken
2732
2757
  }) => {
2758
+ useEffect21(() => {
2759
+ if (memberToken) {
2760
+ client.setToken(memberToken);
2761
+ } else {
2762
+ client.setToken(null);
2763
+ }
2764
+ if (initialized) {
2765
+ connectionRef.current?.disconnect();
2766
+ connectionRef.current = null;
2767
+ setConnection(null);
2768
+ setMessages([]);
2769
+ setConversationId(null);
2770
+ setInitialized(false);
2771
+ setEnded(false);
2772
+ seenMessageIds.current.clear();
2773
+ }
2774
+ }, [client, memberToken]);
2775
+ const getStorageKey = useCallback22(() => {
2776
+ if (memberToken) {
2777
+ const hash = memberToken.slice(-8);
2778
+ return `${MEMBER_STORAGE_PREFIX}${hash}`;
2779
+ }
2780
+ return STORAGE_KEY;
2781
+ }, [memberToken]);
2733
2782
  const [isOpen, setIsOpen] = useState22(false);
2734
2783
  const [messages, setMessages] = useState22([]);
2735
2784
  const [conversationId, setConversationId] = useState22(null);
@@ -2738,6 +2787,7 @@ var ChatBubble = ({
2738
2787
  const [typing, setTyping] = useState22(false);
2739
2788
  const [unreadCount, setUnreadCount] = useState22(0);
2740
2789
  const [initialized, setInitialized] = useState22(false);
2790
+ const [ended, setEnded] = useState22(false);
2741
2791
  const seenMessageIds = useRef3(/* @__PURE__ */ new Set());
2742
2792
  const connectionRef = useRef3(null);
2743
2793
  const setupListeners = useCallback22((conn) => {
@@ -2761,10 +2811,11 @@ var ChatBubble = ({
2761
2811
  const initChat = useCallback22(async () => {
2762
2812
  if (initialized || connectionRef.current) return;
2763
2813
  setLoading(true);
2814
+ const storageKey = getStorageKey();
2764
2815
  try {
2765
2816
  let savedConvId = null;
2766
2817
  if (typeof localStorage !== "undefined") {
2767
- const saved = localStorage.getItem(STORAGE_KEY);
2818
+ const saved = localStorage.getItem(storageKey);
2768
2819
  if (saved) {
2769
2820
  savedConvId = parseInt(saved, 10);
2770
2821
  }
@@ -2783,7 +2834,7 @@ var ChatBubble = ({
2783
2834
  return;
2784
2835
  } catch (error) {
2785
2836
  console.log("Could not restore conversation, starting new one");
2786
- localStorage.removeItem(STORAGE_KEY);
2837
+ localStorage.removeItem(storageKey);
2787
2838
  }
2788
2839
  }
2789
2840
  const result = await client.chat.start({
@@ -2794,7 +2845,7 @@ var ChatBubble = ({
2794
2845
  const newConvId = result.conversation_id;
2795
2846
  setConversationId(newConvId);
2796
2847
  if (typeof localStorage !== "undefined") {
2797
- localStorage.setItem(STORAGE_KEY, newConvId.toString());
2848
+ localStorage.setItem(storageKey, newConvId.toString());
2798
2849
  }
2799
2850
  const conn = client.chat.connect(newConvId);
2800
2851
  connectionRef.current = conn;
@@ -2809,7 +2860,7 @@ var ChatBubble = ({
2809
2860
  } finally {
2810
2861
  setLoading(false);
2811
2862
  }
2812
- }, [client.chat, initialized, setupListeners, visitorName, visitorEmail, greeting]);
2863
+ }, [client.chat, initialized, setupListeners, visitorName, visitorEmail, greeting, getStorageKey]);
2813
2864
  const handleOpen = () => {
2814
2865
  setIsOpen(true);
2815
2866
  setUnreadCount(0);
@@ -2850,6 +2901,30 @@ var ChatBubble = ({
2850
2901
  const handleTyping = () => {
2851
2902
  connectionRef.current?.sendTyping();
2852
2903
  };
2904
+ const handleEndChat = async () => {
2905
+ const conn = connectionRef.current;
2906
+ if (!conn) return;
2907
+ try {
2908
+ await conn.close();
2909
+ } catch (error) {
2910
+ console.error("Failed to close chat:", error);
2911
+ }
2912
+ if (typeof localStorage !== "undefined") {
2913
+ localStorage.removeItem(getStorageKey());
2914
+ }
2915
+ setEnded(true);
2916
+ connectionRef.current?.disconnect();
2917
+ connectionRef.current = null;
2918
+ setConnection(null);
2919
+ setInitialized(false);
2920
+ setConversationId(null);
2921
+ };
2922
+ const handleStartNewChat = () => {
2923
+ setEnded(false);
2924
+ setMessages([]);
2925
+ seenMessageIds.current.clear();
2926
+ initChat();
2927
+ };
2853
2928
  useEffect21(() => {
2854
2929
  return () => {
2855
2930
  connectionRef.current?.disconnect();
@@ -2869,11 +2944,14 @@ var ChatBubble = ({
2869
2944
  onSend: handleSend,
2870
2945
  onTyping: handleTyping,
2871
2946
  onClose: handleClose,
2947
+ onEndChat: handleEndChat,
2948
+ onStartNewChat: handleStartNewChat,
2872
2949
  title,
2873
2950
  subtitle,
2874
2951
  placeholder,
2875
2952
  loading,
2876
- typing
2953
+ typing,
2954
+ ended
2877
2955
  }
2878
2956
  ),
2879
2957
  /* @__PURE__ */ jsxs5(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diffsome/react",
3
- "version": "1.2.12",
3
+ "version": "1.2.14",
4
4
  "description": "React hooks and providers for Diffsome SDK - Headless e-commerce & CMS",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",