@getwidgets/live-chat-widget 1.0.7 → 1.0.8

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.
@@ -19710,14 +19710,16 @@
19710
19710
  function LuSend(props) {
19711
19711
  return GenIcon({ "attr": { "viewBox": "0 0 24 24", "fill": "none", "stroke": "currentColor", "strokeWidth": "2", "strokeLinecap": "round", "strokeLinejoin": "round" }, "child": [{ "tag": "path", "attr": { "d": "m22 2-7 20-4-9-9-4Z" } }, { "tag": "path", "attr": { "d": "M22 2 11 13" } }] })(props);
19712
19712
  }
19713
+ const API_BASE_URL = "https://api.getwidgets.app";
19714
+ const WS_BASE_URL = "wss://api.getwidgets.app";
19713
19715
  const fetchWidgetConfig = async (widgetId) => {
19714
- const res = await fetch(`https://api.getwidgets.app/api/widgets/user-widgets/${widgetId}/config`);
19716
+ const res = await fetch(`${API_BASE_URL}/api/widgets/user-widgets/${widgetId}/config`);
19715
19717
  if (!res.ok) throw new Error("Failed to fetch widget config");
19716
19718
  return res.json();
19717
19719
  };
19718
19720
  const checkSessionStatus = async (widgetId, sessionId) => {
19719
19721
  try {
19720
- const url = `https://api.getwidgets.app/api/widgets/livechat/${widgetId}/status/?session_id=${sessionId}`;
19722
+ const url = `${API_BASE_URL}/api/widgets/livechat/${widgetId}/status/?session_id=${sessionId}`;
19721
19723
  const res = await fetch(url);
19722
19724
  if (!res.ok) {
19723
19725
  const txt = await res.text().catch(() => null);
@@ -19748,7 +19750,7 @@
19748
19750
  }
19749
19751
  return sessionId;
19750
19752
  };
19751
- const LiveChatWidget = ({ widgetId, apiKey }) => {
19753
+ const LiveChatWidget = ({ widgetId, apiKey, name: name2 = null, email = null, unique_id = null }) => {
19752
19754
  var _a, _b;
19753
19755
  const [config, setConfig] = reactExports.useState(null);
19754
19756
  const [messages, setMessages] = reactExports.useState([]);
@@ -19773,6 +19775,23 @@
19773
19775
  const [windowWidth, setWindowWidth] = reactExports.useState(typeof window !== "undefined" ? window.innerWidth : 1024);
19774
19776
  const isOpenRef = reactExports.useRef(false);
19775
19777
  const ICON_MAP = { IoIosSend, IoSendOutline, MdOutlineSend, LuSend };
19778
+ const [authToken, setAuthToken] = reactExports.useState(null);
19779
+ const [localStorageToken, setLocalStorageToken] = reactExports.useState(null);
19780
+ const [contactName, setContactName] = reactExports.useState("");
19781
+ const [contactEmail, setContactEmail] = reactExports.useState("");
19782
+ const [showContactFields, setShowContactFields] = reactExports.useState(false);
19783
+ const [showContactErrors, setShowContactErrors] = reactExports.useState(false);
19784
+ const base64UrlEncode = (value) => {
19785
+ const json = typeof value === "string" ? value : JSON.stringify(value);
19786
+ const base64 = btoa(unescape(encodeURIComponent(json)));
19787
+ return base64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
19788
+ };
19789
+ const buildUnsignedJwt = (payload) => {
19790
+ const header2 = { alg: "none", typ: "JWT" };
19791
+ const encodedHeader = base64UrlEncode(header2);
19792
+ const encodedPayload = base64UrlEncode(payload);
19793
+ return `${encodedHeader}.${encodedPayload}`;
19794
+ };
19776
19795
  const normalizeIncomingMessage = (m2) => {
19777
19796
  if (!m2) return { id: void 0, role: "agent", content: "", timestamp: (/* @__PURE__ */ new Date()).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }), created_at: Date.now() };
19778
19797
  if (m2.sent_by) {
@@ -19818,6 +19837,54 @@
19818
19837
  const id2 = getSessionId();
19819
19838
  setSessionId(id2);
19820
19839
  }, [widgetId]);
19840
+ reactExports.useEffect(() => {
19841
+ if (typeof window === "undefined") return;
19842
+ try {
19843
+ const stored = localStorage.getItem("widgetkraft_livechat_authtoken");
19844
+ setLocalStorageToken(stored);
19845
+ if (stored && !authToken) setAuthToken(stored);
19846
+ } catch (e) {
19847
+ }
19848
+ const handleStorageChange = (e) => {
19849
+ if (e.key === "widgetkraft_livechat_authtoken" || e.key === null) {
19850
+ try {
19851
+ const stored = localStorage.getItem("widgetkraft_livechat_authtoken");
19852
+ setLocalStorageToken(stored);
19853
+ if (stored && !authToken) setAuthToken(stored);
19854
+ } catch (err) {
19855
+ }
19856
+ }
19857
+ };
19858
+ window.addEventListener("storage", handleStorageChange);
19859
+ return () => window.removeEventListener("storage", handleStorageChange);
19860
+ }, [authToken]);
19861
+ reactExports.useEffect(() => {
19862
+ const buildAuthToken = async () => {
19863
+ const hasIdentity = Boolean(name2 || email || unique_id);
19864
+ if (!hasIdentity || typeof window === "undefined") {
19865
+ return;
19866
+ }
19867
+ try {
19868
+ const payload = { name: name2, email, unique_id };
19869
+ const token = buildUnsignedJwt(payload);
19870
+ if (token) {
19871
+ try {
19872
+ localStorage.setItem("widgetkraft_livechat_authtoken", token);
19873
+ } catch (e) {
19874
+ }
19875
+ setAuthToken(token);
19876
+ }
19877
+ } catch (e) {
19878
+ console.warn("Failed to create auth token", e);
19879
+ try {
19880
+ localStorage.removeItem("widgetkraft_livechat_authtoken");
19881
+ } catch (err) {
19882
+ }
19883
+ setAuthToken(null);
19884
+ }
19885
+ };
19886
+ buildAuthToken();
19887
+ }, [name2, email, unique_id]);
19821
19888
  reactExports.useEffect(() => {
19822
19889
  const dismissalKey = `live-chat-notification-dismissed/${widgetId}`;
19823
19890
  const dismissalTime = localStorage.getItem(dismissalKey);
@@ -19877,11 +19944,12 @@
19877
19944
  }, []);
19878
19945
  const startLiveSession = async (widgetUuid, currentSessionId) => {
19879
19946
  try {
19880
- const url = `https://api.getwidgets.app/api/widgets/livechat/start/${widgetUuid}/`;
19947
+ const url = `${API_BASE_URL}/api/widgets/livechat/start/${widgetUuid}/`;
19948
+ const storedAuthToken = authToken || (typeof window !== "undefined" ? localStorage.getItem("widgetkraft_livechat_authtoken") : null);
19881
19949
  const res = await fetch(url, {
19882
19950
  method: "POST",
19883
19951
  headers: { "Content-Type": "application/json" },
19884
- body: JSON.stringify({ session_id: currentSessionId })
19952
+ body: JSON.stringify({ session_id: currentSessionId, auth_token: storedAuthToken || null })
19885
19953
  });
19886
19954
  if (!res.ok) {
19887
19955
  const txt = await res.text().catch(() => null);
@@ -19904,7 +19972,7 @@
19904
19972
  }
19905
19973
  wsRef.current = null;
19906
19974
  }
19907
- const url = `wss://api.getwidgets.app/ws/livechat/${sid}`;
19975
+ const url = `${WS_BASE_URL}/ws/livechat/${sid}`;
19908
19976
  const ws = new WebSocket(url);
19909
19977
  wsRef.current = ws;
19910
19978
  ws.onopen = () => {
@@ -20141,8 +20209,8 @@
20141
20209
  localStorage.setItem(dismissalKey, Date.now().toString());
20142
20210
  setShowNotificationPreview(false);
20143
20211
  };
20144
- if (!config) return null;
20145
- const { header, chat_area, input_area, appearance } = ((_a = config.config) == null ? void 0 : _a.chat_widget) || {};
20212
+ const widgetConfig = ((_a = config == null ? void 0 : config.config) == null ? void 0 : _a.chat_widget) || {};
20213
+ const { header, chat_area, input_area, appearance } = widgetConfig;
20146
20214
  const headerConfig = header || {};
20147
20215
  const appearanceConfig = appearance || {};
20148
20216
  const chatAreaConfig = chat_area || {};
@@ -20229,6 +20297,39 @@
20229
20297
  };
20230
20298
  const timeTextColor = getContrastColor(appearanceConfig.background_color || "#F9FAFB");
20231
20299
  const isMobile = windowWidth < 768;
20300
+ reactExports.useEffect(() => {
20301
+ if (inputAreaConfig == null ? void 0 : inputAreaConfig.allow_chat_with_email) {
20302
+ const hasToken = authToken || localStorageToken;
20303
+ setShowContactFields(!hasToken);
20304
+ } else {
20305
+ setShowContactFields(false);
20306
+ }
20307
+ }, [inputAreaConfig == null ? void 0 : inputAreaConfig.allow_chat_with_email, authToken, localStorageToken, isOpen]);
20308
+ const isContactValid = !(inputAreaConfig == null ? void 0 : inputAreaConfig.allow_chat_with_email) || (!showContactFields ? true : contactName.trim() && contactEmail.trim());
20309
+ const createAndStoreAuthToken = () => {
20310
+ const payload = { name: contactName.trim(), email: contactEmail.trim(), unique_id };
20311
+ const token = buildUnsignedJwt(payload);
20312
+ try {
20313
+ localStorage.setItem("widgetkraft_livechat_authtoken", token);
20314
+ } catch (e) {
20315
+ }
20316
+ setAuthToken(token);
20317
+ return token;
20318
+ };
20319
+ const performSendWithContact = async () => {
20320
+ if ((inputAreaConfig == null ? void 0 : inputAreaConfig.allow_chat_with_email) && !authToken) {
20321
+ const missingName = !contactName.trim();
20322
+ const missingEmail = !contactEmail.trim();
20323
+ if (missingName || missingEmail) {
20324
+ setShowContactErrors(true);
20325
+ return;
20326
+ }
20327
+ createAndStoreAuthToken();
20328
+ setShowContactFields(false);
20329
+ }
20330
+ await performSend();
20331
+ };
20332
+ if (!config) return null;
20232
20333
  return /* @__PURE__ */ React.createElement("div", { style: getPositionStyles() }, !isOpen && showNotificationPreview && /* @__PURE__ */ React.createElement(
20233
20334
  "div",
20234
20335
  {
@@ -20481,7 +20582,7 @@
20481
20582
  /* @__PURE__ */ React.createElement(
20482
20583
  "div",
20483
20584
  {
20484
- className: "flex items-center gap-2 border-t flex-shrink-0",
20585
+ className: "flex flex-col gap-2 border-t flex-shrink-0",
20485
20586
  style: {
20486
20587
  backgroundColor: inputAreaConfig.background_color || "#FFF",
20487
20588
  borderColor: inputAreaConfig.border_color || "#D1D5DB",
@@ -20491,7 +20592,7 @@
20491
20592
  gap: isMobile ? "8px" : "12px"
20492
20593
  }
20493
20594
  },
20494
- /* @__PURE__ */ React.createElement(
20595
+ /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-2 w-full" }, /* @__PURE__ */ React.createElement(
20495
20596
  "input",
20496
20597
  {
20497
20598
  ref: inputRef,
@@ -20511,11 +20612,10 @@
20511
20612
  },
20512
20613
  placeholder: inputAreaConfig.placeholder_text || "Type your message..."
20513
20614
  }
20514
- ),
20515
- /* @__PURE__ */ React.createElement(
20615
+ ), /* @__PURE__ */ React.createElement(
20516
20616
  "button",
20517
20617
  {
20518
- onClick: performSend,
20618
+ onClick: performSendWithContact,
20519
20619
  disabled: localLoading || !inputMessage.trim(),
20520
20620
  className: "rounded-full flex items-center justify-center flex-shrink-0 hover:opacity-90 transition-opacity",
20521
20621
  style: {
@@ -20524,7 +20624,7 @@
20524
20624
  height: `${sendButtonSize}px`,
20525
20625
  minWidth: `${sendButtonSize}px`,
20526
20626
  minHeight: `${sendButtonSize}px`,
20527
- opacity: localLoading || !inputMessage.trim() ? 0.5 : 1,
20627
+ opacity: localLoading || !inputMessage.trim() || !isContactValid ? 0.5 : 1,
20528
20628
  transition: "opacity 0.2s",
20529
20629
  border: "none",
20530
20630
  cursor: "pointer"
@@ -20553,7 +20653,47 @@
20553
20653
  if (iconUrl) return /* @__PURE__ */ React.createElement("img", { src: iconUrl, alt: "send", style: { width: "60%", height: "60%" } });
20554
20654
  return /* @__PURE__ */ React.createElement(Send, { className: "w-4 h-4", style: { color: "#FFFFFF" } });
20555
20655
  })()
20556
- )
20656
+ )),
20657
+ showContactFields && /* @__PURE__ */ React.createElement("div", { className: "p-3 border rounded-lg mt-2", style: {
20658
+ borderColor: showContactErrors && (!contactName.trim() || !contactEmail.trim()) ? "#EF4444" : inputAreaConfig.border_color || "#D1D5DB",
20659
+ backgroundColor: inputAreaConfig.background_color || "#FFF"
20660
+ } }, /* @__PURE__ */ React.createElement("div", { className: "text-sm font-semibold mb-2", style: { color: inputAreaConfig.text_color || "#111827" } }, "How should we reach you?"), /* @__PURE__ */ React.createElement("div", { className: "space-y-2" }, /* @__PURE__ */ React.createElement(
20661
+ "input",
20662
+ {
20663
+ type: "text",
20664
+ placeholder: "Name",
20665
+ value: contactName,
20666
+ onChange: (e) => {
20667
+ setContactName(e.target.value);
20668
+ if (showContactErrors) setShowContactErrors(false);
20669
+ },
20670
+ className: "w-full px-3 py-1.5 border rounded-md",
20671
+ style: {
20672
+ borderColor: showContactErrors && !contactName.trim() ? "#EF4444" : inputAreaConfig.border_color || "#D1D5DB",
20673
+ color: inputAreaConfig.text_color || "#111827",
20674
+ fontSize: "13px",
20675
+ backgroundColor: inputAreaConfig.background_color || "#FFF"
20676
+ }
20677
+ }
20678
+ ), /* @__PURE__ */ React.createElement(
20679
+ "input",
20680
+ {
20681
+ type: "email",
20682
+ placeholder: "Email address",
20683
+ value: contactEmail,
20684
+ onChange: (e) => {
20685
+ setContactEmail(e.target.value);
20686
+ if (showContactErrors) setShowContactErrors(false);
20687
+ },
20688
+ className: "w-full px-3 py-1.5 border rounded-md",
20689
+ style: {
20690
+ borderColor: showContactErrors && !contactEmail.trim() ? "#EF4444" : inputAreaConfig.border_color || "#D1D5DB",
20691
+ color: inputAreaConfig.text_color || "#111827",
20692
+ fontSize: "13px",
20693
+ backgroundColor: inputAreaConfig.background_color || "#FFF"
20694
+ }
20695
+ }
20696
+ ), showContactErrors && (!contactName.trim() || !contactEmail.trim()) && /* @__PURE__ */ React.createElement("div", { className: "text-xs", style: { color: "#EF4444" } }, `${!contactName.trim() ? "Name is required. " : ""}${!contactEmail.trim() ? "Email is required." : ""}`)))
20557
20697
  ),
20558
20698
  inputAreaConfig.show_branding !== false && /* @__PURE__ */ React.createElement(
20559
20699
  "div",
@@ -20649,7 +20789,7 @@
20649
20789
  }
20650
20790
  `));
20651
20791
  };
20652
- function init({ widgetId, apiKey, mode = "inline" }) {
20792
+ function init({ widgetId, apiKey, mode = "inline", name: name2 = null, email = null, unique_id = null }) {
20653
20793
  const container = document.getElementById("livechat-root");
20654
20794
  if (!container) {
20655
20795
  console.error("Live Chat Widget root element (#livechat-root) not found.");
@@ -20669,7 +20809,16 @@
20669
20809
  const root2 = client.createRoot(mountPoint);
20670
20810
  try {
20671
20811
  root2.render(
20672
- /* @__PURE__ */ React.createElement(React.StrictMode, null, /* @__PURE__ */ React.createElement(LiveChatWidget, { widgetId, apiKey }))
20812
+ /* @__PURE__ */ React.createElement(React.StrictMode, null, /* @__PURE__ */ React.createElement(
20813
+ LiveChatWidget,
20814
+ {
20815
+ widgetId,
20816
+ apiKey,
20817
+ name: name2,
20818
+ email,
20819
+ unique_id
20820
+ }
20821
+ ))
20673
20822
  );
20674
20823
  container.__livechat_mounted = true;
20675
20824
  } catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getwidgets/live-chat-widget",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "main": "dist/live-chat-widget.umd.js",
5
5
  "unpkg": "dist/live-chat-widget.umd.js",
6
6
  "type": "module",