@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.
- package/dist/firebase-messaging-sw.js +127 -19
- package/dist/index.cjs +363 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +35 -1
- package/dist/index.d.ts +35 -1
- package/dist/index.mjs +346 -9
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
17679
|
+
toast2.toast.success(message, options);
|
|
17393
17680
|
break;
|
|
17394
17681
|
case "error":
|
|
17395
|
-
|
|
17682
|
+
toast2.toast.error(message, options);
|
|
17396
17683
|
break;
|
|
17397
17684
|
case "info":
|
|
17398
|
-
|
|
17685
|
+
toast2.toast(message, options);
|
|
17399
17686
|
break;
|
|
17400
17687
|
case "warning":
|
|
17401
|
-
|
|
17688
|
+
toast2.toast(message, { ...options, icon: "\u26A0\uFE0F" });
|
|
17402
17689
|
break;
|
|
17403
17690
|
default:
|
|
17404
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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;
|