@diffsome/react 1.2.9 → 1.2.11

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
@@ -246,6 +246,23 @@
246
246
  color: #10b981;
247
247
  }
248
248
 
249
+ .diffsome-chat-message-pending {
250
+ opacity: 0.7;
251
+ }
252
+
253
+ .diffsome-chat-message-failed {
254
+ opacity: 0.7;
255
+ border: 1px solid #ef4444;
256
+ }
257
+
258
+ .diffsome-chat-message-status {
259
+ font-style: italic;
260
+ }
261
+
262
+ .diffsome-chat-message-error {
263
+ color: #ef4444;
264
+ }
265
+
249
266
  /* System Message */
250
267
  .diffsome-chat-message-system {
251
268
  align-self: center;
package/dist/index.d.mts CHANGED
@@ -876,6 +876,8 @@ interface Message {
876
876
  sender_name?: string;
877
877
  created_at?: string;
878
878
  is_read?: boolean;
879
+ is_pending?: boolean;
880
+ is_failed?: boolean;
879
881
  }
880
882
  interface ChatWidgetProps {
881
883
  messages: Message[];
@@ -897,6 +899,8 @@ interface ChatMessageProps {
897
899
  senderName?: string;
898
900
  timestamp?: string;
899
901
  isRead?: boolean;
902
+ isPending?: boolean;
903
+ isFailed?: boolean;
900
904
  className?: string;
901
905
  }
902
906
  declare const ChatMessage: React__default.FC<ChatMessageProps>;
package/dist/index.d.ts CHANGED
@@ -876,6 +876,8 @@ interface Message {
876
876
  sender_name?: string;
877
877
  created_at?: string;
878
878
  is_read?: boolean;
879
+ is_pending?: boolean;
880
+ is_failed?: boolean;
879
881
  }
880
882
  interface ChatWidgetProps {
881
883
  messages: Message[];
@@ -897,6 +899,8 @@ interface ChatMessageProps {
897
899
  senderName?: string;
898
900
  timestamp?: string;
899
901
  isRead?: boolean;
902
+ isPending?: boolean;
903
+ isFailed?: boolean;
900
904
  className?: string;
901
905
  }
902
906
  declare const ChatMessage: React__default.FC<ChatMessageProps>;
package/dist/index.js CHANGED
@@ -2595,6 +2595,8 @@ var ChatMessage = ({
2595
2595
  senderName,
2596
2596
  timestamp,
2597
2597
  isRead,
2598
+ isPending,
2599
+ isFailed,
2598
2600
  className = ""
2599
2601
  }) => {
2600
2602
  const isVisitor = senderType === "visitor";
@@ -2608,12 +2610,12 @@ var ChatMessage = ({
2608
2610
  className: `diffsome-chat-message ${isVisitor ? "diffsome-chat-message-visitor" : "diffsome-chat-message-agent"} ${className}`,
2609
2611
  children: [
2610
2612
  !isVisitor && senderName && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "diffsome-chat-message-sender", children: senderName }),
2611
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "diffsome-chat-message-bubble", children: [
2613
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `diffsome-chat-message-bubble ${isPending ? "diffsome-chat-message-pending" : ""} ${isFailed ? "diffsome-chat-message-failed" : ""}`, children: [
2612
2614
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "diffsome-chat-message-content", children: content }),
2613
- timestamp && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "diffsome-chat-message-time", children: [
2615
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "diffsome-chat-message-time", children: isPending ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "diffsome-chat-message-status", children: "Sending..." }) : isFailed ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "diffsome-chat-message-status diffsome-chat-message-error", children: "Failed" }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
2614
2616
  timestamp,
2615
2617
  isVisitor && isRead && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "diffsome-chat-message-read", children: " \u2713" })
2616
- ] })
2618
+ ] }) })
2617
2619
  ] })
2618
2620
  ]
2619
2621
  }
@@ -2773,7 +2775,9 @@ var ChatWidget = ({
2773
2775
  senderType: msg.sender_type,
2774
2776
  senderName: msg.sender_name,
2775
2777
  timestamp: formatTime(msg.created_at),
2776
- isRead: msg.is_read
2778
+ isRead: msg.is_read,
2779
+ isPending: msg.is_pending,
2780
+ isFailed: msg.is_failed
2777
2781
  },
2778
2782
  msg.id
2779
2783
  )),
@@ -2819,49 +2823,80 @@ var ChatBubble = ({
2819
2823
  }) => {
2820
2824
  const [isOpen, setIsOpen] = (0, import_react24.useState)(false);
2821
2825
  const [messages, setMessages] = (0, import_react24.useState)([]);
2822
- const [conversationId, setConversationId] = (0, import_react24.useState)(null);
2826
+ const [conversationId, setConversationId] = (0, import_react24.useState)(() => {
2827
+ if (typeof localStorage !== "undefined") {
2828
+ const saved = localStorage.getItem("diffsome_conversation_id");
2829
+ return saved ? parseInt(saved, 10) : null;
2830
+ }
2831
+ return null;
2832
+ });
2823
2833
  const [connection, setConnection] = (0, import_react24.useState)(null);
2824
2834
  const [loading, setLoading] = (0, import_react24.useState)(false);
2825
2835
  const [typing, setTyping] = (0, import_react24.useState)(false);
2826
2836
  const [unreadCount, setUnreadCount] = (0, import_react24.useState)(0);
2827
2837
  const seenMessageIds = (0, import_react24.useRef)(/* @__PURE__ */ new Set());
2838
+ const isRestoringRef = (0, import_react24.useRef)(false);
2839
+ const connectToConversation = (0, import_react24.useCallback)((convId) => {
2840
+ const conn = client.chat.connect(convId);
2841
+ setConnection(conn);
2842
+ conn.onMessage((msg) => {
2843
+ if (seenMessageIds.current.has(msg.id)) {
2844
+ return;
2845
+ }
2846
+ seenMessageIds.current.add(msg.id);
2847
+ setMessages((prev) => [...prev, msg]);
2848
+ if (!isOpen && msg.sender_type !== "visitor") {
2849
+ setUnreadCount((c) => c + 1);
2850
+ }
2851
+ });
2852
+ conn.onTyping((senderType) => {
2853
+ if (senderType === "agent") {
2854
+ setTyping(true);
2855
+ setTimeout(() => setTyping(false), 3e3);
2856
+ }
2857
+ });
2858
+ return conn;
2859
+ }, [client.chat, isOpen]);
2828
2860
  const initChat = (0, import_react24.useCallback)(async () => {
2829
- if (conversationId) return;
2861
+ if (connection) return;
2830
2862
  setLoading(true);
2831
2863
  try {
2864
+ let convId = conversationId;
2865
+ if (convId && !isRestoringRef.current) {
2866
+ isRestoringRef.current = true;
2867
+ try {
2868
+ const conn2 = connectToConversation(convId);
2869
+ const existingMessages2 = await conn2.getMessages();
2870
+ existingMessages2.forEach((m) => seenMessageIds.current.add(m.id));
2871
+ setMessages(existingMessages2);
2872
+ return;
2873
+ } catch (error) {
2874
+ console.log("Could not restore conversation, starting new one");
2875
+ convId = null;
2876
+ setConversationId(null);
2877
+ localStorage.removeItem("diffsome_conversation_id");
2878
+ }
2879
+ }
2832
2880
  const result = await client.chat.start({
2833
2881
  visitor_name: visitorName,
2834
2882
  visitor_email: visitorEmail,
2835
2883
  initial_message: greeting
2836
2884
  });
2837
- setConversationId(result.conversation_id);
2838
- const conn = client.chat.connect(result.conversation_id);
2839
- setConnection(conn);
2885
+ convId = result.conversation_id;
2886
+ setConversationId(convId);
2887
+ if (typeof localStorage !== "undefined") {
2888
+ localStorage.setItem("diffsome_conversation_id", convId.toString());
2889
+ }
2890
+ const conn = connectToConversation(convId);
2840
2891
  const existingMessages = await conn.getMessages();
2841
2892
  existingMessages.forEach((m) => seenMessageIds.current.add(m.id));
2842
2893
  setMessages(existingMessages);
2843
- conn.onMessage((msg) => {
2844
- if (seenMessageIds.current.has(msg.id)) {
2845
- return;
2846
- }
2847
- seenMessageIds.current.add(msg.id);
2848
- setMessages((prev) => [...prev, msg]);
2849
- if (!isOpen && msg.sender_type !== "visitor") {
2850
- setUnreadCount((c) => c + 1);
2851
- }
2852
- });
2853
- conn.onTyping((senderType) => {
2854
- if (senderType === "agent") {
2855
- setTyping(true);
2856
- setTimeout(() => setTyping(false), 3e3);
2857
- }
2858
- });
2859
2894
  } catch (error) {
2860
2895
  console.error("Failed to initialize chat:", error);
2861
2896
  } finally {
2862
2897
  setLoading(false);
2863
2898
  }
2864
- }, [client.chat, conversationId, visitorName, visitorEmail, greeting, isOpen]);
2899
+ }, [client.chat, connection, conversationId, connectToConversation, visitorName, visitorEmail, greeting]);
2865
2900
  const handleOpen = () => {
2866
2901
  setIsOpen(true);
2867
2902
  setUnreadCount(0);
@@ -2876,10 +2911,28 @@ var ChatBubble = ({
2876
2911
  };
2877
2912
  const handleSend = async (content) => {
2878
2913
  if (!connection) return;
2879
- try {
2880
- await connection.send(content);
2914
+ const tempId = -Date.now();
2915
+ const pendingMessage = {
2916
+ id: tempId,
2917
+ content,
2918
+ sender_type: "visitor",
2919
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
2920
+ is_pending: true
2921
+ };
2922
+ setMessages((prev) => [...prev, pendingMessage]);
2923
+ try {
2924
+ const sentMessage = await connection.send(content);
2925
+ if (sentMessage) {
2926
+ seenMessageIds.current.add(sentMessage.id);
2927
+ setMessages(
2928
+ (prev) => prev.map((m) => m.id === tempId ? { ...sentMessage, is_pending: false } : m)
2929
+ );
2930
+ }
2881
2931
  } catch (error) {
2882
2932
  console.error("Failed to send message:", error);
2933
+ setMessages(
2934
+ (prev) => prev.map((m) => m.id === tempId ? { ...m, is_pending: false, is_failed: true } : m)
2935
+ );
2883
2936
  }
2884
2937
  };
2885
2938
  const handleTyping = () => {
package/dist/index.mjs CHANGED
@@ -2496,13 +2496,15 @@ import { useState as useState22, useEffect as useEffect21, useCallback as useCal
2496
2496
  import { useRef as useRef2, useEffect as useEffect20 } from "react";
2497
2497
 
2498
2498
  // src/components/chat/ChatMessage.tsx
2499
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
2499
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
2500
2500
  var ChatMessage = ({
2501
2501
  content,
2502
2502
  senderType,
2503
2503
  senderName,
2504
2504
  timestamp,
2505
2505
  isRead,
2506
+ isPending,
2507
+ isFailed,
2506
2508
  className = ""
2507
2509
  }) => {
2508
2510
  const isVisitor = senderType === "visitor";
@@ -2516,12 +2518,12 @@ var ChatMessage = ({
2516
2518
  className: `diffsome-chat-message ${isVisitor ? "diffsome-chat-message-visitor" : "diffsome-chat-message-agent"} ${className}`,
2517
2519
  children: [
2518
2520
  !isVisitor && senderName && /* @__PURE__ */ jsx3("div", { className: "diffsome-chat-message-sender", children: senderName }),
2519
- /* @__PURE__ */ jsxs2("div", { className: "diffsome-chat-message-bubble", children: [
2521
+ /* @__PURE__ */ jsxs2("div", { className: `diffsome-chat-message-bubble ${isPending ? "diffsome-chat-message-pending" : ""} ${isFailed ? "diffsome-chat-message-failed" : ""}`, children: [
2520
2522
  /* @__PURE__ */ jsx3("div", { className: "diffsome-chat-message-content", children: content }),
2521
- timestamp && /* @__PURE__ */ jsxs2("div", { className: "diffsome-chat-message-time", children: [
2523
+ /* @__PURE__ */ jsx3("div", { className: "diffsome-chat-message-time", children: isPending ? /* @__PURE__ */ jsx3("span", { className: "diffsome-chat-message-status", children: "Sending..." }) : isFailed ? /* @__PURE__ */ jsx3("span", { className: "diffsome-chat-message-status diffsome-chat-message-error", children: "Failed" }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
2522
2524
  timestamp,
2523
2525
  isVisitor && isRead && /* @__PURE__ */ jsx3("span", { className: "diffsome-chat-message-read", children: " \u2713" })
2524
- ] })
2526
+ ] }) })
2525
2527
  ] })
2526
2528
  ]
2527
2529
  }
@@ -2619,7 +2621,7 @@ var ChatInput = ({
2619
2621
  };
2620
2622
 
2621
2623
  // src/components/chat/ChatWidget.tsx
2622
- import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2624
+ import { Fragment as Fragment3, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
2623
2625
  var ChatWidget = ({
2624
2626
  messages,
2625
2627
  onSend,
@@ -2673,7 +2675,7 @@ var ChatWidget = ({
2673
2675
  /* @__PURE__ */ jsx5("div", { className: "diffsome-chat-messages", children: loading ? /* @__PURE__ */ jsxs4("div", { className: "diffsome-chat-loading", children: [
2674
2676
  /* @__PURE__ */ jsx5("div", { className: "diffsome-chat-loading-spinner" }),
2675
2677
  /* @__PURE__ */ jsx5("span", { children: "Loading messages..." })
2676
- ] }) : messages.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "diffsome-chat-empty", children: /* @__PURE__ */ jsx5("span", { children: "No messages yet. Start the conversation!" }) }) : /* @__PURE__ */ jsxs4(Fragment2, { children: [
2678
+ ] }) : messages.length === 0 ? /* @__PURE__ */ jsx5("div", { className: "diffsome-chat-empty", children: /* @__PURE__ */ jsx5("span", { children: "No messages yet. Start the conversation!" }) }) : /* @__PURE__ */ jsxs4(Fragment3, { children: [
2677
2679
  messages.map((msg) => /* @__PURE__ */ jsx5(
2678
2680
  ChatMessage,
2679
2681
  {
@@ -2681,7 +2683,9 @@ var ChatWidget = ({
2681
2683
  senderType: msg.sender_type,
2682
2684
  senderName: msg.sender_name,
2683
2685
  timestamp: formatTime(msg.created_at),
2684
- isRead: msg.is_read
2686
+ isRead: msg.is_read,
2687
+ isPending: msg.is_pending,
2688
+ isFailed: msg.is_failed
2685
2689
  },
2686
2690
  msg.id
2687
2691
  )),
@@ -2727,49 +2731,80 @@ var ChatBubble = ({
2727
2731
  }) => {
2728
2732
  const [isOpen, setIsOpen] = useState22(false);
2729
2733
  const [messages, setMessages] = useState22([]);
2730
- const [conversationId, setConversationId] = useState22(null);
2734
+ const [conversationId, setConversationId] = useState22(() => {
2735
+ if (typeof localStorage !== "undefined") {
2736
+ const saved = localStorage.getItem("diffsome_conversation_id");
2737
+ return saved ? parseInt(saved, 10) : null;
2738
+ }
2739
+ return null;
2740
+ });
2731
2741
  const [connection, setConnection] = useState22(null);
2732
2742
  const [loading, setLoading] = useState22(false);
2733
2743
  const [typing, setTyping] = useState22(false);
2734
2744
  const [unreadCount, setUnreadCount] = useState22(0);
2735
2745
  const seenMessageIds = useRef3(/* @__PURE__ */ new Set());
2746
+ const isRestoringRef = useRef3(false);
2747
+ const connectToConversation = useCallback22((convId) => {
2748
+ const conn = client.chat.connect(convId);
2749
+ setConnection(conn);
2750
+ conn.onMessage((msg) => {
2751
+ if (seenMessageIds.current.has(msg.id)) {
2752
+ return;
2753
+ }
2754
+ seenMessageIds.current.add(msg.id);
2755
+ setMessages((prev) => [...prev, msg]);
2756
+ if (!isOpen && msg.sender_type !== "visitor") {
2757
+ setUnreadCount((c) => c + 1);
2758
+ }
2759
+ });
2760
+ conn.onTyping((senderType) => {
2761
+ if (senderType === "agent") {
2762
+ setTyping(true);
2763
+ setTimeout(() => setTyping(false), 3e3);
2764
+ }
2765
+ });
2766
+ return conn;
2767
+ }, [client.chat, isOpen]);
2736
2768
  const initChat = useCallback22(async () => {
2737
- if (conversationId) return;
2769
+ if (connection) return;
2738
2770
  setLoading(true);
2739
2771
  try {
2772
+ let convId = conversationId;
2773
+ if (convId && !isRestoringRef.current) {
2774
+ isRestoringRef.current = true;
2775
+ try {
2776
+ const conn2 = connectToConversation(convId);
2777
+ const existingMessages2 = await conn2.getMessages();
2778
+ existingMessages2.forEach((m) => seenMessageIds.current.add(m.id));
2779
+ setMessages(existingMessages2);
2780
+ return;
2781
+ } catch (error) {
2782
+ console.log("Could not restore conversation, starting new one");
2783
+ convId = null;
2784
+ setConversationId(null);
2785
+ localStorage.removeItem("diffsome_conversation_id");
2786
+ }
2787
+ }
2740
2788
  const result = await client.chat.start({
2741
2789
  visitor_name: visitorName,
2742
2790
  visitor_email: visitorEmail,
2743
2791
  initial_message: greeting
2744
2792
  });
2745
- setConversationId(result.conversation_id);
2746
- const conn = client.chat.connect(result.conversation_id);
2747
- setConnection(conn);
2793
+ convId = result.conversation_id;
2794
+ setConversationId(convId);
2795
+ if (typeof localStorage !== "undefined") {
2796
+ localStorage.setItem("diffsome_conversation_id", convId.toString());
2797
+ }
2798
+ const conn = connectToConversation(convId);
2748
2799
  const existingMessages = await conn.getMessages();
2749
2800
  existingMessages.forEach((m) => seenMessageIds.current.add(m.id));
2750
2801
  setMessages(existingMessages);
2751
- conn.onMessage((msg) => {
2752
- if (seenMessageIds.current.has(msg.id)) {
2753
- return;
2754
- }
2755
- seenMessageIds.current.add(msg.id);
2756
- setMessages((prev) => [...prev, msg]);
2757
- if (!isOpen && msg.sender_type !== "visitor") {
2758
- setUnreadCount((c) => c + 1);
2759
- }
2760
- });
2761
- conn.onTyping((senderType) => {
2762
- if (senderType === "agent") {
2763
- setTyping(true);
2764
- setTimeout(() => setTyping(false), 3e3);
2765
- }
2766
- });
2767
2802
  } catch (error) {
2768
2803
  console.error("Failed to initialize chat:", error);
2769
2804
  } finally {
2770
2805
  setLoading(false);
2771
2806
  }
2772
- }, [client.chat, conversationId, visitorName, visitorEmail, greeting, isOpen]);
2807
+ }, [client.chat, connection, conversationId, connectToConversation, visitorName, visitorEmail, greeting]);
2773
2808
  const handleOpen = () => {
2774
2809
  setIsOpen(true);
2775
2810
  setUnreadCount(0);
@@ -2784,10 +2819,28 @@ var ChatBubble = ({
2784
2819
  };
2785
2820
  const handleSend = async (content) => {
2786
2821
  if (!connection) return;
2787
- try {
2788
- await connection.send(content);
2822
+ const tempId = -Date.now();
2823
+ const pendingMessage = {
2824
+ id: tempId,
2825
+ content,
2826
+ sender_type: "visitor",
2827
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
2828
+ is_pending: true
2829
+ };
2830
+ setMessages((prev) => [...prev, pendingMessage]);
2831
+ try {
2832
+ const sentMessage = await connection.send(content);
2833
+ if (sentMessage) {
2834
+ seenMessageIds.current.add(sentMessage.id);
2835
+ setMessages(
2836
+ (prev) => prev.map((m) => m.id === tempId ? { ...sentMessage, is_pending: false } : m)
2837
+ );
2838
+ }
2789
2839
  } catch (error) {
2790
2840
  console.error("Failed to send message:", error);
2841
+ setMessages(
2842
+ (prev) => prev.map((m) => m.id === tempId ? { ...m, is_pending: false, is_failed: true } : m)
2843
+ );
2791
2844
  }
2792
2845
  };
2793
2846
  const handleTyping = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diffsome/react",
3
- "version": "1.2.9",
3
+ "version": "1.2.11",
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",