@realtimexsco/live-chat 1.4.7 → 1.4.9

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.
@@ -4,15 +4,9 @@
4
4
  * /firebase-messaging-sw.js:
5
5
  *
6
6
  * cp node_modules/@realtimexsco/live-chat/dist/firebase-messaging-sw.js public/
7
- *
8
- * Do NOT edit the Firebase config into this file — the chat package passes it
9
- * via the registration URL (?config=...), so the copy stays generic.
10
- *
11
- * If your app already has its own service worker at the root scope, merge the
12
- * contents of this file into it instead (two workers cannot share a scope).
13
7
  */
14
8
 
15
- /* global firebase, importScripts */
9
+ /* global firebase, importScripts, BroadcastChannel */
16
10
 
17
11
  importScripts(
18
12
  "https://www.gstatic.com/firebasejs/10.14.1/firebase-app-compat.js",
@@ -21,18 +15,37 @@ importScripts(
21
15
  "https://www.gstatic.com/firebasejs/10.14.1/firebase-messaging-compat.js",
22
16
  );
23
17
 
18
+ var PUSH_BROADCAST_CHANNEL = "realtimex-push";
19
+ var NOTIFICATION_CLICK_SW_TYPE = "realtimex:notification-click";
20
+
24
21
  var params = new URL(self.location.href).searchParams;
25
22
  var rawConfig = params.get("config");
26
23
 
27
24
  if (rawConfig) {
28
25
  try {
29
26
  var firebaseConfig = JSON.parse(rawConfig);
30
-
31
27
  firebase.initializeApp(firebaseConfig);
28
+ var messaging = firebase.messaging();
29
+
30
+ messaging.onBackgroundMessage(function (payload) {
31
+ console.log("[realtimex-push] FCM background payload (raw):", payload);
32
+ console.log("[realtimex-push] FCM background data:", payload && payload.data);
32
33
 
33
- // Background messages that carry a `notification` payload are displayed
34
- // by FCM automatically; initializing messaging here is what enables it.
35
- firebase.messaging();
34
+ var title =
35
+ (payload.notification && payload.notification.title) || "New message";
36
+ var body = (payload.notification && payload.notification.body) || "";
37
+ var data = payload.data || {};
38
+ var tag = "realtimex-" + (data.conversationId || data.messageId || "push");
39
+
40
+ // Show notification ourselves so click always carries `data` for redirect.
41
+ // Same tag replaces FCM auto-notification when both fire.
42
+ return self.registration.showNotification(title, {
43
+ body: body,
44
+ data: data,
45
+ tag: tag,
46
+ renotify: true,
47
+ });
48
+ });
36
49
  } catch (error) {
37
50
  console.error("[realtimex] Invalid firebase config in SW URL:", error);
38
51
  }
@@ -43,24 +56,119 @@ if (rawConfig) {
43
56
  );
44
57
  }
45
58
 
46
- // Focus (or open) the chat when a notification is clicked.
59
+ function parseMaybeJson(value) {
60
+ if (typeof value !== "string") return value;
61
+ try {
62
+ return JSON.parse(value);
63
+ } catch (e) {
64
+ return value;
65
+ }
66
+ }
67
+
68
+ function extractNotificationData(notification) {
69
+ var raw = (notification && notification.data) || {};
70
+ var fcmMsg = parseMaybeJson(raw.FCM_MSG);
71
+ var nested =
72
+ fcmMsg && fcmMsg.data && typeof fcmMsg.data === "object" ? fcmMsg.data : null;
73
+ var merged = {};
74
+
75
+ if (nested) {
76
+ for (var key in nested) {
77
+ if (Object.prototype.hasOwnProperty.call(nested, key)) {
78
+ merged[key] = nested[key];
79
+ }
80
+ }
81
+ }
82
+
83
+ for (var key2 in raw) {
84
+ if (Object.prototype.hasOwnProperty.call(raw, key2) && key2 !== "FCM_MSG") {
85
+ merged[key2] = raw[key2];
86
+ }
87
+ }
88
+
89
+ return merged;
90
+ }
91
+
92
+ function resolveConversationId(data) {
93
+ if (!data) return "";
94
+ return String(
95
+ data.conversationId ||
96
+ data.conversation_id ||
97
+ data.conversationID ||
98
+ "",
99
+ ).trim();
100
+ }
101
+
102
+ function buildTargetUrl(data, conversationId) {
103
+ if (data && data.link) return String(data.link);
104
+ if (conversationId) {
105
+ return "/chat?conversationId=" + encodeURIComponent(conversationId);
106
+ }
107
+ return "/chat";
108
+ }
109
+
110
+ function isChatClient(client) {
111
+ try {
112
+ var pathname = new URL(client.url).pathname || "";
113
+ return pathname === "/chat" || pathname.indexOf("/chat/") === 0;
114
+ } catch (e) {
115
+ return false;
116
+ }
117
+ }
118
+
119
+ function notifyPage(message) {
120
+ try {
121
+ var bc = new BroadcastChannel(PUSH_BROADCAST_CHANNEL);
122
+ bc.postMessage(message);
123
+ bc.close();
124
+ } catch (e) {
125
+ // ignore
126
+ }
127
+ }
128
+
47
129
  self.addEventListener("notificationclick", function (event) {
48
130
  event.notification.close();
49
131
 
50
- var targetUrl =
51
- (event.notification.data &&
52
- event.notification.data.FCM_MSG &&
53
- event.notification.data.FCM_MSG.data &&
54
- event.notification.data.FCM_MSG.data.link) ||
55
- "/";
132
+ console.log(
133
+ "[realtimex-push] notificationclick raw notification.data:",
134
+ event.notification && event.notification.data,
135
+ );
136
+
137
+ var data = extractNotificationData(event.notification);
138
+ var conversationId = resolveConversationId(data);
139
+ var targetUrl = buildTargetUrl(data, conversationId);
140
+ var message = {
141
+ type: NOTIFICATION_CLICK_SW_TYPE,
142
+ conversationId: conversationId,
143
+ data: data,
144
+ };
145
+
146
+ console.log("[realtimex-push] notificationclick extracted data:", data);
147
+ console.log("[realtimex-push] notificationclick conversationId:", conversationId);
148
+ console.log("[realtimex-push] notificationclick targetUrl:", targetUrl);
56
149
 
57
150
  event.waitUntil(
58
151
  clients
59
152
  .matchAll({ type: "window", includeUncontrolled: true })
60
153
  .then(function (windowClients) {
154
+ var chatClient = null;
155
+ var anyClient = null;
156
+
61
157
  for (var i = 0; i < windowClients.length; i++) {
62
- if ("focus" in windowClients[i]) return windowClients[i].focus();
158
+ var client = windowClients[i];
159
+ if (!anyClient) anyClient = client;
160
+ if (!chatClient && isChatClient(client)) chatClient = client;
161
+ }
162
+
163
+ var target = chatClient || anyClient;
164
+ if (target && "focus" in target) {
165
+ return target.focus().then(function () {
166
+ notifyPage(message);
167
+ if (target.postMessage) target.postMessage(message);
168
+ });
63
169
  }
170
+
171
+ notifyPage(message);
64
172
  if (clients.openWindow) return clients.openWindow(targetUrl);
65
173
  return undefined;
66
174
  }),
package/dist/index.cjs CHANGED
@@ -16,7 +16,7 @@ var lucideReact = require('lucide-react');
16
16
  var classVarianceAuthority = require('class-variance-authority');
17
17
  var EmojiPicker = require('emoji-picker-react');
18
18
  var reactDayPicker = require('react-day-picker');
19
- var reactHotToast = require('react-hot-toast');
19
+ var toast2 = require('react-hot-toast');
20
20
 
21
21
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
22
 
@@ -41,6 +41,7 @@ function _interopNamespace(e) {
41
41
  var axios__default = /*#__PURE__*/_interopDefault(axios);
42
42
  var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
43
43
  var EmojiPicker__default = /*#__PURE__*/_interopDefault(EmojiPicker);
44
+ var toast2__default = /*#__PURE__*/_interopDefault(toast2);
44
45
 
45
46
  var __defProp = Object.defineProperty;
46
47
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -4653,6 +4654,205 @@ var Chat = ({
4653
4654
  // src/hooks/useChatController.ts
4654
4655
  init_useStore();
4655
4656
  init_store_helpers();
4657
+
4658
+ // src/notifications/open-conversation.ts
4659
+ init_store_helpers();
4660
+ var OPEN_CONVERSATION_EVENT = "realtimex:open-conversation";
4661
+ var NOTIFICATION_CLICK_SW_TYPE = "realtimex:notification-click";
4662
+ var PUSH_BROADCAST_CHANNEL = "realtimex-push";
4663
+ var PENDING_STORAGE_KEY = "realtimex:pending-open-conversation";
4664
+ var resolveConversationIdFromPushData = (data) => {
4665
+ if (!data || typeof data !== "object") return "";
4666
+ let fcmMsg = data.FCM_MSG;
4667
+ if (typeof fcmMsg === "string") {
4668
+ try {
4669
+ fcmMsg = JSON.parse(fcmMsg);
4670
+ } catch {
4671
+ fcmMsg = void 0;
4672
+ }
4673
+ }
4674
+ const nested = fcmMsg && typeof fcmMsg === "object" ? fcmMsg.data : void 0;
4675
+ const source = { ...nested || {}, ...data };
4676
+ return String(
4677
+ source.conversationId || source.conversation_id || source.conversationID || ""
4678
+ ).trim();
4679
+ };
4680
+ var normalizePushMeta = (meta) => {
4681
+ if (!meta || typeof meta !== "object") return {};
4682
+ return {
4683
+ senderId: String(
4684
+ meta.senderId || meta.sender_id || ""
4685
+ ).trim() || void 0,
4686
+ kind: String(meta.kind || "").trim() || void 0,
4687
+ messageId: String(
4688
+ meta.messageId || meta.message_id || ""
4689
+ ).trim() || void 0,
4690
+ title: String(meta.title || "").trim() || void 0
4691
+ };
4692
+ };
4693
+ var parsePendingRaw = (raw) => {
4694
+ if (!raw) return null;
4695
+ try {
4696
+ const parsed = JSON.parse(raw);
4697
+ if (parsed?.conversationId) {
4698
+ return {
4699
+ conversationId: String(parsed.conversationId).trim(),
4700
+ ...normalizePushMeta(parsed)
4701
+ };
4702
+ }
4703
+ } catch {
4704
+ }
4705
+ const id = String(raw).trim();
4706
+ return id ? { conversationId: id } : null;
4707
+ };
4708
+ var peekPendingOpenConversation = () => peekPendingOpenRequest()?.conversationId ?? null;
4709
+ var peekPendingOpenRequest = () => {
4710
+ try {
4711
+ return parsePendingRaw(sessionStorage.getItem(PENDING_STORAGE_KEY));
4712
+ } catch {
4713
+ return null;
4714
+ }
4715
+ };
4716
+ var consumePendingOpenConversation = () => consumePendingOpenRequest()?.conversationId ?? null;
4717
+ var consumePendingOpenRequest = () => {
4718
+ const request = peekPendingOpenRequest();
4719
+ try {
4720
+ sessionStorage.removeItem(PENDING_STORAGE_KEY);
4721
+ } catch {
4722
+ }
4723
+ return request;
4724
+ };
4725
+ var requestOpenConversation = (conversationId, meta) => {
4726
+ const id = String(conversationId || "").trim();
4727
+ if (!id) return;
4728
+ const request = {
4729
+ conversationId: id,
4730
+ ...normalizePushMeta(meta)
4731
+ };
4732
+ console.log("[realtimex-push] requestOpenConversation:", request);
4733
+ try {
4734
+ sessionStorage.setItem(PENDING_STORAGE_KEY, JSON.stringify(request));
4735
+ } catch {
4736
+ }
4737
+ try {
4738
+ window.dispatchEvent(
4739
+ new CustomEvent(OPEN_CONVERSATION_EVENT, { detail: request })
4740
+ );
4741
+ } catch {
4742
+ }
4743
+ };
4744
+ var buildChannelFromPushRequest = (request, loggedUserId, allUsers) => {
4745
+ const conversationId = request.conversationId;
4746
+ const isGroup = request.kind === "group";
4747
+ const senderId = String(request.senderId || "").trim();
4748
+ if (isGroup) {
4749
+ const rawName2 = request.title || "Group Chat";
4750
+ return {
4751
+ id: conversationId,
4752
+ conversationId,
4753
+ name: rawName2.charAt(0).toUpperCase() + rawName2.slice(1),
4754
+ isGroup: true
4755
+ };
4756
+ }
4757
+ const senderUser = senderId ? (allUsers || []).find((user) => String(user._id) === senderId) : null;
4758
+ const rawName = senderUser?.name || request.title || "New message";
4759
+ return {
4760
+ id: senderId || conversationId,
4761
+ conversationId,
4762
+ name: rawName.charAt(0).toUpperCase() + rawName.slice(1),
4763
+ image: senderUser?.image || void 0,
4764
+ email: senderUser?.email,
4765
+ isGroup: false
4766
+ };
4767
+ };
4768
+ var mapConversationToChannel = (conversation, loggedUserId, allUsers) => {
4769
+ const conversationId = String(conversation?._id || conversation?.id || "");
4770
+ const isGroup = isGroupConversation(conversation);
4771
+ if (isGroup) {
4772
+ const rawName2 = conversation.groupName || conversation.name || "Group Chat";
4773
+ return {
4774
+ id: conversationId,
4775
+ conversationId,
4776
+ name: rawName2.charAt(0).toUpperCase() + rawName2.slice(1),
4777
+ image: resolveGroupImage(conversation),
4778
+ isGroup: true,
4779
+ isLeft: !!conversation.isLeft,
4780
+ isBlocked: !!conversation.isBlocked,
4781
+ pinnedCount: conversation.pinnedCount,
4782
+ starredCount: conversation.starredCount
4783
+ };
4784
+ }
4785
+ const { id: otherId, user } = resolveOtherParticipant(
4786
+ conversation,
4787
+ loggedUserId,
4788
+ allUsers
4789
+ );
4790
+ const rawName = user?.name || "Unknown User";
4791
+ return {
4792
+ id: otherId || conversationId,
4793
+ conversationId,
4794
+ name: rawName.charAt(0).toUpperCase() + rawName.slice(1),
4795
+ image: user?.image || void 0,
4796
+ email: user?.email,
4797
+ isGroup: false,
4798
+ isLeft: !!conversation.isLeft,
4799
+ isBlocked: !!conversation.isBlocked,
4800
+ pinnedCount: conversation.pinnedCount,
4801
+ starredCount: conversation.starredCount
4802
+ };
4803
+ };
4804
+ var readConversationIdFromLocation = () => {
4805
+ try {
4806
+ return new URLSearchParams(window.location.search).get("conversationId") || "";
4807
+ } catch {
4808
+ return "";
4809
+ }
4810
+ };
4811
+ var consumedUrlConversationIds = /* @__PURE__ */ new Set();
4812
+ var handleNotificationOpen = (conversationId, pushData, options) => {
4813
+ const id = String(conversationId || "").trim();
4814
+ if (!id) {
4815
+ console.warn("[realtimex-push] notification open ignored \u2014 no conversationId", pushData);
4816
+ return;
4817
+ }
4818
+ requestOpenConversation(id, pushData);
4819
+ const chatPath = options.chatPath || "/chat";
4820
+ const pathWithQuery = `${chatPath}?conversationId=${encodeURIComponent(id)}`;
4821
+ options.onNavigate?.(id, pathWithQuery);
4822
+ };
4823
+ var installNotificationClickBridge = (options = {}) => {
4824
+ if (typeof window === "undefined") return () => void 0;
4825
+ const onSwMessage = (event) => {
4826
+ const data = event.data;
4827
+ console.log("[realtimex-push] SW \u2192 page message:", data);
4828
+ if (!data || data.type !== NOTIFICATION_CLICK_SW_TYPE) return;
4829
+ const pushData = data.data && typeof data.data === "object" ? data.data : void 0;
4830
+ const conversationId = String(data.conversationId || "") || resolveConversationIdFromPushData(pushData);
4831
+ console.log("[realtimex-push] notification click \u2192 open:", {
4832
+ conversationId,
4833
+ pushData
4834
+ });
4835
+ handleNotificationOpen(conversationId, pushData, options);
4836
+ };
4837
+ navigator.serviceWorker?.addEventListener("message", onSwMessage);
4838
+ let broadcast = null;
4839
+ try {
4840
+ broadcast = new BroadcastChannel(PUSH_BROADCAST_CHANNEL);
4841
+ broadcast.onmessage = onSwMessage;
4842
+ } catch {
4843
+ }
4844
+ const fromUrl = readConversationIdFromLocation();
4845
+ if (fromUrl && !consumedUrlConversationIds.has(fromUrl)) {
4846
+ consumedUrlConversationIds.add(fromUrl);
4847
+ handleNotificationOpen(fromUrl, void 0, options);
4848
+ }
4849
+ return () => {
4850
+ navigator.serviceWorker?.removeEventListener("message", onSwMessage);
4851
+ broadcast?.close();
4852
+ };
4853
+ };
4854
+
4855
+ // src/hooks/useChatController.ts
4656
4856
  function resolveMessageType(content, attachments) {
4657
4857
  if (attachments.length === 0) return "text";
4658
4858
  if (!content.trim() && attachments.length === 1) {
@@ -4725,6 +4925,10 @@ var useChatController = () => {
4725
4925
  const lastDM = exports.useChatStore((s) => s.lastDM);
4726
4926
  const loggedUserDetails = exports.useChatStore((s) => s.loggedUserDetails);
4727
4927
  const rateLimitResetAt2 = exports.useChatStore((s) => s.rateLimitResetAt);
4928
+ const allUsers = exports.useChatStore((s) => s.allUsers);
4929
+ const isFetchingConversations = exports.useChatStore((s) => s.isFetchingConversations);
4930
+ const conversations = exports.useChatStore((s) => s.conversations);
4931
+ const [pendingOpenRequest, setPendingOpenRequest] = React2.useState(() => peekPendingOpenRequest());
4728
4932
  const conversationsLength = exports.useChatStore(
4729
4933
  (s) => state.activeChannel?.conversationId ? 0 : s.conversations.length
4730
4934
  );
@@ -4753,6 +4957,89 @@ var useChatController = () => {
4753
4957
  isGroupConversation(latestInfo)
4754
4958
  ].join("|");
4755
4959
  });
4960
+ React2.useEffect(() => {
4961
+ const onOpen = (event) => {
4962
+ const detail = event.detail;
4963
+ if (detail?.conversationId) {
4964
+ console.log("[realtimex-push] OPEN_CONVERSATION_EVENT:", detail);
4965
+ setPendingOpenRequest({
4966
+ conversationId: String(detail.conversationId),
4967
+ senderId: detail.senderId,
4968
+ kind: detail.kind,
4969
+ messageId: detail.messageId,
4970
+ title: detail.title
4971
+ });
4972
+ }
4973
+ };
4974
+ window.addEventListener(OPEN_CONVERSATION_EVENT, onOpen);
4975
+ return () => window.removeEventListener(OPEN_CONVERSATION_EVENT, onOpen);
4976
+ }, []);
4977
+ React2.useEffect(() => {
4978
+ if (!pendingOpenRequest?.conversationId || !loggedUserDetails) return;
4979
+ let cancelled = false;
4980
+ const openPending = async () => {
4981
+ const targetId = String(pendingOpenRequest.conversationId);
4982
+ console.log("[realtimex-push] opening conversation:", pendingOpenRequest);
4983
+ let conversation = conversations.find(
4984
+ (c) => String(c._id || c.id) === targetId
4985
+ ) || null;
4986
+ if (!conversation) {
4987
+ conversation = conversationInfos[targetId] || null;
4988
+ }
4989
+ if (!conversation) {
4990
+ if (isFetchingConversations) {
4991
+ console.log("[realtimex-push] waiting for conversations list\u2026");
4992
+ return;
4993
+ }
4994
+ try {
4995
+ await fetchConversationInfo2(targetId);
4996
+ } catch (error) {
4997
+ console.warn("[realtimex-push] fetchConversationInfo failed:", error);
4998
+ }
4999
+ if (cancelled) return;
5000
+ const stateNow = exports.useChatStore.getState();
5001
+ conversation = stateNow.conversations.find(
5002
+ (c) => String(c._id || c.id) === targetId
5003
+ ) || stateNow.conversationInfos[targetId] || null;
5004
+ }
5005
+ const channel = conversation ? mapConversationToChannel(
5006
+ conversation,
5007
+ loggedUserDetails._id,
5008
+ allUsers
5009
+ ) : buildChannelFromPushRequest(
5010
+ pendingOpenRequest,
5011
+ loggedUserDetails._id,
5012
+ allUsers
5013
+ );
5014
+ console.log("[realtimex-push] selected channel:", channel);
5015
+ if (cancelled) return;
5016
+ setState((prev) => {
5017
+ if (prev.activeChannel?.conversationId && String(prev.activeChannel.conversationId) === targetId) {
5018
+ return prev;
5019
+ }
5020
+ return {
5021
+ ...prev,
5022
+ activeChannel: channel,
5023
+ activeConversationId: targetId
5024
+ };
5025
+ });
5026
+ exports.useChatStore.getState().setActiveConversationId(targetId);
5027
+ consumePendingOpenRequest();
5028
+ setPendingOpenRequest(null);
5029
+ };
5030
+ void openPending();
5031
+ return () => {
5032
+ cancelled = true;
5033
+ };
5034
+ }, [
5035
+ pendingOpenRequest,
5036
+ loggedUserDetails,
5037
+ conversations,
5038
+ conversationInfos,
5039
+ isFetchingConversations,
5040
+ allUsers,
5041
+ fetchConversationInfo2
5042
+ ]);
4756
5043
  React2.useEffect(() => {
4757
5044
  const handler = setTimeout(() => {
4758
5045
  updateState({ debouncedSearch: state.searchQuery });
@@ -17389,19 +17676,19 @@ var showNotification = (message, type = "info", icon, duration) => {
17389
17676
  };
17390
17677
  switch (type) {
17391
17678
  case "success":
17392
- reactHotToast.toast.success(message, options);
17679
+ toast2.toast.success(message, options);
17393
17680
  break;
17394
17681
  case "error":
17395
- reactHotToast.toast.error(message, options);
17682
+ toast2.toast.error(message, options);
17396
17683
  break;
17397
17684
  case "info":
17398
- reactHotToast.toast(message, options);
17685
+ toast2.toast(message, options);
17399
17686
  break;
17400
17687
  case "warning":
17401
- reactHotToast.toast(message, { ...options, icon: "\u26A0\uFE0F" });
17688
+ toast2.toast(message, { ...options, icon: "\u26A0\uFE0F" });
17402
17689
  break;
17403
17690
  default:
17404
- reactHotToast.toast(message, options);
17691
+ toast2.toast(message, options);
17405
17692
  }
17406
17693
  };
17407
17694
  var downloadFile = async (src, name) => {
@@ -17610,6 +17897,9 @@ var onForegroundPush = async (callback) => {
17610
17897
  }
17611
17898
  const { fb, messaging } = await getFirebaseMessaging(config.firebaseConfig);
17612
17899
  return fb.onMessage(messaging, (payload) => {
17900
+ console.log("[realtimex-push] FCM foreground payload (raw):", payload);
17901
+ console.log("[realtimex-push] FCM foreground data:", payload?.data);
17902
+ console.log("[realtimex-push] FCM foreground notification:", payload?.notification);
17613
17903
  callback({
17614
17904
  title: payload?.notification?.title,
17615
17905
  body: payload?.notification?.body,
@@ -17630,9 +17920,6 @@ var rememberToastId = (id) => {
17630
17920
  }
17631
17921
  return true;
17632
17922
  };
17633
- var resolveIncomingConversationId = (data) => String(
17634
- data?.conversationId || data?.conversation_id || data?.conversationID || ""
17635
- );
17636
17923
  var resolveToastText = (title, body) => {
17637
17924
  if (title && body) return `${title}: ${body}`;
17638
17925
  return title || body || "";
@@ -17642,7 +17929,39 @@ var emitToast = (payload, onPush) => {
17642
17929
  const title = payload.title || payload.data?.title;
17643
17930
  const body = payload.body || payload.data?.body || payload.data?.message;
17644
17931
  const text = resolveToastText(title, body);
17645
- if (text) showNotification(text, "info");
17932
+ const conversationId = resolveConversationIdFromPushData(payload.data);
17933
+ if (!text) return;
17934
+ if (conversationId) {
17935
+ toast2__default.default.custom(
17936
+ (t) => /* @__PURE__ */ jsxRuntime.jsx(
17937
+ "button",
17938
+ {
17939
+ type: "button",
17940
+ onClick: () => {
17941
+ toast2__default.default.dismiss(t.id);
17942
+ requestOpenConversation(conversationId, payload.data);
17943
+ },
17944
+ style: {
17945
+ display: "block",
17946
+ width: "100%",
17947
+ textAlign: "left",
17948
+ cursor: "pointer",
17949
+ background: "var(--background, #fff)",
17950
+ color: "var(--foreground, #111)",
17951
+ border: "1px solid rgba(0,0,0,0.08)",
17952
+ borderRadius: 8,
17953
+ padding: "10px 14px",
17954
+ boxShadow: "0 4px 12px rgba(0,0,0,0.12)",
17955
+ font: "inherit"
17956
+ },
17957
+ children: text
17958
+ }
17959
+ ),
17960
+ { duration: 5e3 }
17961
+ );
17962
+ return;
17963
+ }
17964
+ showNotification(text, "info");
17646
17965
  };
17647
17966
  var ForegroundPushBridge = ({ onPush }) => {
17648
17967
  const lastDM = exports.useChatStore((s) => s.lastDM);
@@ -17693,22 +18012,29 @@ var ForegroundPushBridge = ({ onPush }) => {
17693
18012
  let retryTimer = null;
17694
18013
  let cancelled = false;
17695
18014
  const handlePayload = (payload) => {
17696
- const incoming = resolveIncomingConversationId(payload.data);
18015
+ console.log("[realtimex-push] ForegroundPushBridge payload:", payload);
18016
+ console.log("[realtimex-push] ForegroundPushBridge data keys:", payload.data);
18017
+ const incoming = resolveConversationIdFromPushData(payload.data);
17697
18018
  const activeId = String(
17698
18019
  exports.useChatStore.getState().activeConversationId || ""
17699
18020
  );
17700
18021
  const messageId2 = String(
17701
18022
  payload.data?.messageId || payload.data?.message_id || payload.data?.messageID || ""
17702
18023
  );
18024
+ console.log("[realtimex-push] resolved from backend data:", {
18025
+ conversationId: incoming,
18026
+ messageId: messageId2,
18027
+ activeConversationId: activeId
18028
+ });
17703
18029
  if (incoming && activeId && incoming === activeId) {
17704
- console.debug(
18030
+ console.log(
17705
18031
  "[realtimex-push] foreground push suppressed (conversation open):",
17706
18032
  incoming
17707
18033
  );
17708
18034
  return;
17709
18035
  }
17710
18036
  if (!rememberToastId(messageId2)) return;
17711
- console.debug("[realtimex-push] foreground push \u2192 toast:", {
18037
+ console.log("[realtimex-push] foreground push \u2192 toast:", {
17712
18038
  title: payload.title,
17713
18039
  conversationId: incoming,
17714
18040
  activeConversationId: activeId
@@ -17757,8 +18083,20 @@ var ForegroundPushBridge = ({ onPush }) => {
17757
18083
  window.removeEventListener(PUSH_CHANGED_EVENT, onPushChanged);
17758
18084
  };
17759
18085
  }, []);
18086
+ React2.useEffect(() => {
18087
+ const onOpen = (event) => {
18088
+ const id = String(
18089
+ event.detail?.conversationId || ""
18090
+ );
18091
+ if (id) {
18092
+ console.debug("[realtimex-push] open-conversation event:", id);
18093
+ }
18094
+ };
18095
+ window.addEventListener(OPEN_CONVERSATION_EVENT, onOpen);
18096
+ return () => window.removeEventListener(OPEN_CONVERSATION_EVENT, onOpen);
18097
+ }, []);
17760
18098
  return /* @__PURE__ */ jsxRuntime.jsx(
17761
- reactHotToast.Toaster,
18099
+ toast2.Toaster,
17762
18100
  {
17763
18101
  position: "top-right",
17764
18102
  toastOptions: { duration: 4e3 },
@@ -18251,6 +18589,9 @@ exports.LoggedUserDetails = LoggedUserDetails_default;
18251
18589
  exports.MessageInput = ChannelMessageBoxView_default;
18252
18590
  exports.MessageItem = MessageItemView;
18253
18591
  exports.MessageList = ActiveChannelMessagesView_default;
18592
+ exports.NOTIFICATION_CLICK_SW_TYPE = NOTIFICATION_CLICK_SW_TYPE;
18593
+ exports.OPEN_CONVERSATION_EVENT = OPEN_CONVERSATION_EVENT;
18594
+ exports.PUSH_BROADCAST_CHANNEL = PUSH_BROADCAST_CHANNEL;
18254
18595
  exports.PUSH_CHANGED_EVENT = PUSH_CHANGED_EVENT;
18255
18596
  exports.PushNotificationToggle = PushNotificationToggle;
18256
18597
  exports.apiGetPushConfig = apiGetPushConfig;
@@ -18258,8 +18599,11 @@ exports.apiGetPushPreference = apiGetPushPreference;
18258
18599
  exports.apiRegisterPushToken = apiRegisterPushToken;
18259
18600
  exports.apiRemovePushToken = apiRemovePushToken;
18260
18601
  exports.apiUpdatePushPreference = apiUpdatePushPreference;
18602
+ exports.buildChannelFromPushRequest = buildChannelFromPushRequest;
18261
18603
  exports.capitalizeFirst = capitalizeFirst;
18262
18604
  exports.clampJumpDate = clampJumpDate;
18605
+ exports.consumePendingOpenConversation = consumePendingOpenConversation;
18606
+ exports.consumePendingOpenRequest = consumePendingOpenRequest;
18263
18607
  exports.default = index_default;
18264
18608
  exports.defaultChatClassNames = defaultChatClassNames;
18265
18609
  exports.defaultChatComponents = defaultChatComponents;
@@ -18272,14 +18616,19 @@ exports.getFirstAndLastNameOneCharacter = getFirstAndLastNameOneCharacter;
18272
18616
  exports.getResponsiveWidth = getResponsiveWidth;
18273
18617
  exports.getSocketConfig = getSocketConfig;
18274
18618
  exports.getStoredPushToken = getStoredPushToken;
18619
+ exports.installNotificationClickBridge = installNotificationClickBridge;
18275
18620
  exports.isEmpty = isEmpty;
18276
18621
  exports.isPushSupported = isPushSupported;
18277
18622
  exports.mergeChatCustomization = mergeChatCustomization;
18278
18623
  exports.mergeChatLayoutConfig = mergeChatLayoutConfig;
18279
18624
  exports.onForegroundPush = onForegroundPush;
18280
18625
  exports.packageDefaultComponents = packageDefaultComponents;
18626
+ exports.peekPendingOpenConversation = peekPendingOpenConversation;
18627
+ exports.peekPendingOpenRequest = peekPendingOpenRequest;
18628
+ exports.requestOpenConversation = requestOpenConversation;
18281
18629
  exports.resolveApiUrl = resolveApiUrl;
18282
18630
  exports.resolveChatApiKey = resolveChatApiKey;
18631
+ exports.resolveConversationIdFromPushData = resolveConversationIdFromPushData;
18283
18632
  exports.resolveJumpDateBounds = resolveJumpDateBounds;
18284
18633
  exports.resolveJumpPreset = resolveJumpPreset;
18285
18634
  exports.resolveJumpStep = resolveJumpStep;