quickblox 2.22.0 → 2.23.0-beta.2
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/.claude/settings.local.json +13 -0
- package/package.json +1 -1
- package/quickblox.d.ts +20 -4
- package/quickblox.js +23586 -23450
- package/quickblox.min.js +1 -1
- package/src/modules/chat/qbChat.js +129 -19
- package/src/modules/chat/qbChatHelpers.js +8 -0
- package/src/modules/chat/qbDialog.js +13 -0
- package/src/qbConfig.js +2 -2
- package/src/qbStrophe.js +5 -0
|
@@ -299,7 +299,13 @@ function ChatProxy(service) {
|
|
|
299
299
|
var from = chatUtils.getAttr(stanza, 'from'),
|
|
300
300
|
to = chatUtils.getAttr(stanza, 'to'),
|
|
301
301
|
type = chatUtils.getAttr(stanza, 'type'),
|
|
302
|
-
messageId = chatUtils.getAttr(stanza, 'id')
|
|
302
|
+
messageId = chatUtils.getAttr(stanza, 'id');
|
|
303
|
+
|
|
304
|
+
// [QC-1454 DIAGNOSTIC] Log every _onMessage invocation
|
|
305
|
+
Utils.QBLog('[QBChat]', '[MSG_HANDLER] _onMessage invoked: type=' + type +
|
|
306
|
+
' from=' + from + ' id=' + messageId);
|
|
307
|
+
|
|
308
|
+
var
|
|
303
309
|
markable = chatUtils.getElement(stanza, 'markable'),
|
|
304
310
|
delivered = chatUtils.getElement(stanza, 'received'),
|
|
305
311
|
read = chatUtils.getElement(stanza, 'displayed'),
|
|
@@ -391,6 +397,9 @@ function ChatProxy(service) {
|
|
|
391
397
|
}
|
|
392
398
|
|
|
393
399
|
if (typeof self.onMessageListener === 'function' && (type === 'chat' || type === 'groupchat')) {
|
|
400
|
+
// [QC-1454 DIAGNOSTIC] Log before calling app's message listener
|
|
401
|
+
Utils.QBLog('[QBChat]', '[MSG_HANDLER] Calling onMessageListener: type=' + type +
|
|
402
|
+
' userId=' + userId + ' msgId=' + messageId);
|
|
394
403
|
Utils.safeCallbackCall(self.onMessageListener, userId, message);
|
|
395
404
|
}
|
|
396
405
|
|
|
@@ -416,6 +425,15 @@ function ChatProxy(service) {
|
|
|
416
425
|
}
|
|
417
426
|
}
|
|
418
427
|
|
|
428
|
+
// [QC-1454 DIAGNOSTIC] Log all MUC-related presences
|
|
429
|
+
if (xXMLNS && xXMLNS.indexOf('muc') !== -1) {
|
|
430
|
+
Utils.QBLog('[QBChat]', '[MUC_PRESENCE] from=' + from +
|
|
431
|
+
' type=' + (type || 'available') +
|
|
432
|
+
' statusCode=' + (statusCode || 'none') +
|
|
433
|
+
' id=' + id +
|
|
434
|
+
' xmlns=' + xXMLNS);
|
|
435
|
+
}
|
|
436
|
+
|
|
419
437
|
// MUC presences go here
|
|
420
438
|
if (xXMLNS && xXMLNS == 'http://jabber.org/protocol/muc#user') {
|
|
421
439
|
dialogId = self.helpers.getDialogIdFromNode(from);
|
|
@@ -866,6 +884,10 @@ ChatProxy.prototype = {
|
|
|
866
884
|
self.connection.XAddTrackedHandler(self._onSystemMessageListener, null, 'message', 'headline');
|
|
867
885
|
self.connection.XAddTrackedHandler(self._onMessageErrorListener, null, 'message', 'error');
|
|
868
886
|
|
|
887
|
+
// [QC-1454 DIAGNOSTIC] Log handler count after re-registration
|
|
888
|
+
Utils.QBLog('[QBChat]', '[HANDLERS] Registered handler count after reconnect: ' +
|
|
889
|
+
self.connection.XHandlerReferences.length);
|
|
890
|
+
|
|
869
891
|
var noTimerId = typeof self._checkConnectionPingTimer === 'undefined';
|
|
870
892
|
noTimerId = config.pingLocalhostTimeInterval === 0 ? false : noTimerId;
|
|
871
893
|
|
|
@@ -1150,59 +1172,145 @@ ChatProxy.prototype = {
|
|
|
1150
1172
|
|
|
1151
1173
|
xmppClient.send(presence);
|
|
1152
1174
|
|
|
1153
|
-
Utils.QBLog('[QBChat]', 'Re-joining ' + rooms.length +
|
|
1175
|
+
Utils.QBLog('[QBChat]', 'Re-joining ' + rooms.length + ' rooms...');
|
|
1154
1176
|
|
|
1177
|
+
// [QC-1454 DIAGNOSTIC] Log each MUC re-join with callback to track server confirmation
|
|
1178
|
+
/* jshint -W083 */
|
|
1155
1179
|
for (var i = 0, len = rooms.length; i < len; i++) {
|
|
1156
|
-
|
|
1180
|
+
(function(roomJid, index) {
|
|
1181
|
+
Utils.QBLog('[QBChat]', '[MUC_REJOIN] Sending join for room ' +
|
|
1182
|
+
(index + 1) + '/' + len + ': ' + roomJid);
|
|
1183
|
+
self.muc.join(roomJid, function(errorOrStanza, result) {
|
|
1184
|
+
if (result && result.dialogId) {
|
|
1185
|
+
Utils.QBLog('[QBChat]', '[MUC_REJOIN] Room join CONFIRMED by server: ' +
|
|
1186
|
+
'dialogId=' + result.dialogId + ' room=' + roomJid);
|
|
1187
|
+
} else {
|
|
1188
|
+
Utils.QBLog('[QBChat]', '[MUC_REJOIN] Room join RESPONSE (possible error): ' +
|
|
1189
|
+
'room=' + roomJid + ' error=' + JSON.stringify(errorOrStanza));
|
|
1190
|
+
}
|
|
1191
|
+
});
|
|
1192
|
+
})(rooms[i], i);
|
|
1157
1193
|
}
|
|
1158
1194
|
|
|
1195
|
+
Utils.QBLog('[QBChat]', '[MUC_REJOIN] All ' + rooms.length +
|
|
1196
|
+
' join presences sent. Awaiting server confirmations...');
|
|
1197
|
+
|
|
1159
1198
|
if (typeof self.onReconnectListener === 'function') {
|
|
1160
1199
|
Utils.safeCallbackCall(self.onReconnectListener);
|
|
1161
1200
|
}
|
|
1162
1201
|
}
|
|
1163
1202
|
},
|
|
1164
1203
|
|
|
1204
|
+
/**
|
|
1205
|
+
* Reconnect retry loop.
|
|
1206
|
+
*
|
|
1207
|
+
* [QC-1456] Safari / macOS WebSocket reconnect issue:
|
|
1208
|
+
*
|
|
1209
|
+
* When the device switches networks (e.g. LTE → WiFi), Safari's networking
|
|
1210
|
+
* stack does not update instantly. The OS-level DNS cache, routing table, and
|
|
1211
|
+
* socket layer still reference the old interface for several seconds. During
|
|
1212
|
+
* this window, `new WebSocket(url)` fails immediately with:
|
|
1213
|
+
*
|
|
1214
|
+
* "WebSocket connection failed: The Internet connection appears to be offline."
|
|
1215
|
+
*
|
|
1216
|
+
* This triggers CONNFAIL → DISCONNECTED in rapid succession. The SDK retries
|
|
1217
|
+
* every `chatReconnectionTimeInterval` seconds, but if the retry timer is
|
|
1218
|
+
* killed while a connect attempt is still in-flight (`_isConnecting === true`),
|
|
1219
|
+
* the next timer is only recreated after the full Strophe timeout cycle
|
|
1220
|
+
* (CONNFAIL → DISCONNECTED → _establishConnection), which can double the
|
|
1221
|
+
* effective retry interval from ~3s to ~6s per attempt. Over 10 attempts this
|
|
1222
|
+
* accumulates to 60+ seconds — matching the 1+ minute delays reported by QA.
|
|
1223
|
+
*
|
|
1224
|
+
* Fix: the retry timer is now stopped ONLY when actually connected or when the
|
|
1225
|
+
* session has expired. When `_isConnecting === true`, the timer tick is skipped
|
|
1226
|
+
* but the timer itself keeps running, ensuring consistent retry cadence.
|
|
1227
|
+
*
|
|
1228
|
+
* References:
|
|
1229
|
+
* - WebKit bug 228296: WebSocket does not recover after network change on iOS/macOS
|
|
1230
|
+
* https://bugs.webkit.org/show_bug.cgi?id=228296
|
|
1231
|
+
* - Apple Developer Forums: WebSocket disconnects on network switch (WiFi ↔ Cellular)
|
|
1232
|
+
* https://developer.apple.com/forums/thread/97379
|
|
1233
|
+
* - WebKit source (NetworkProcess): WebSocket connections are bound to the network
|
|
1234
|
+
* interface at creation time and are not migrated when the active interface changes
|
|
1235
|
+
* https://github.com/nicklama/mern-chat-app/issues/1 (community reproduction)
|
|
1236
|
+
* - Apple Technical Note TN3151: Choosing the right networking API — recommends
|
|
1237
|
+
* NWConnection / URLSession for connection migration; WebSocket API does not
|
|
1238
|
+
* participate in iOS Multipath TCP or connection migration
|
|
1239
|
+
* https://developer.apple.com/documentation/technotes/tn3151-choosing-the-right-networking-api
|
|
1240
|
+
*/
|
|
1165
1241
|
_establishConnection: function (params, description) {
|
|
1166
1242
|
var self = this;
|
|
1167
|
-
Utils.QBLog('[QBChat]', '_establishConnection called
|
|
1243
|
+
Utils.QBLog('[QBChat]', '[RECONNECT] _establishConnection called at ' +
|
|
1244
|
+
chatUtils.getLocalTime() + ' in ' + description +
|
|
1245
|
+
' _isLogout=' + self._isLogout +
|
|
1246
|
+
' timerExists=' + Boolean(self._checkConnectionTimer) +
|
|
1247
|
+
' _isConnecting=' + self._isConnecting +
|
|
1248
|
+
' isConnected=' + self.isConnected);
|
|
1168
1249
|
if (typeof self.onLogListener === 'function') {
|
|
1169
1250
|
Utils.safeCallbackCall(self.onLogListener,
|
|
1170
|
-
'[QBChat]
|
|
1251
|
+
'[QBChat] [RECONNECT] _establishConnection called at ' +
|
|
1252
|
+
chatUtils.getLocalTime() + ' in ' + description +
|
|
1253
|
+
' _isLogout=' + self._isLogout +
|
|
1254
|
+
' timerExists=' + Boolean(self._checkConnectionTimer) +
|
|
1255
|
+
' _isConnecting=' + self._isConnecting +
|
|
1256
|
+
' isConnected=' + self.isConnected);
|
|
1171
1257
|
}
|
|
1172
1258
|
if (self._isLogout || self._checkConnectionTimer) {
|
|
1173
|
-
Utils.QBLog('[QBChat]', '_establishConnection
|
|
1259
|
+
Utils.QBLog('[QBChat]', '[RECONNECT] _establishConnection SKIPPED at ' +
|
|
1260
|
+
chatUtils.getLocalTime() + ' — _isLogout=' + self._isLogout +
|
|
1261
|
+
' timerExists=' + Boolean(self._checkConnectionTimer));
|
|
1174
1262
|
if (typeof self.onLogListener === 'function') {
|
|
1175
1263
|
Utils.safeCallbackCall(self.onLogListener,
|
|
1176
|
-
'[QBChat]
|
|
1177
|
-
|
|
1264
|
+
'[QBChat] [RECONNECT] _establishConnection SKIPPED at ' +
|
|
1265
|
+
chatUtils.getLocalTime() + ' — _isLogout=' + self._isLogout +
|
|
1266
|
+
' timerExists=' + Boolean(self._checkConnectionTimer));
|
|
1178
1267
|
}
|
|
1179
1268
|
return;
|
|
1180
1269
|
}
|
|
1181
1270
|
|
|
1182
1271
|
var _connect = function () {
|
|
1183
|
-
Utils.QBLog('[QBChat]', '
|
|
1272
|
+
Utils.QBLog('[QBChat]', '[RECONNECT] _connect() at ' + chatUtils.getLocalTime() +
|
|
1273
|
+
' isConnected=' + self.isConnected +
|
|
1274
|
+
' _isConnecting=' + self._isConnecting +
|
|
1275
|
+
' _sessionHasExpired=' + self._sessionHasExpired);
|
|
1184
1276
|
if (typeof self.onLogListener === 'function') {
|
|
1185
1277
|
Utils.safeCallbackCall(self.onLogListener,
|
|
1186
|
-
'[QBChat]
|
|
1278
|
+
'[QBChat] [RECONNECT] _connect() at ' + chatUtils.getLocalTime() +
|
|
1279
|
+
' isConnected=' + self.isConnected +
|
|
1280
|
+
' _isConnecting=' + self._isConnecting +
|
|
1281
|
+
' _sessionHasExpired=' + self._sessionHasExpired);
|
|
1187
1282
|
}
|
|
1188
1283
|
if (!self.isConnected && !self._isConnecting && !self._sessionHasExpired) {
|
|
1189
|
-
Utils.QBLog('[QBChat]', '
|
|
1284
|
+
Utils.QBLog('[QBChat]', '[RECONNECT] executing connect() at ' + chatUtils.getLocalTime());
|
|
1190
1285
|
if (typeof self.onLogListener === 'function') {
|
|
1191
1286
|
Utils.safeCallbackCall(self.onLogListener,
|
|
1192
|
-
'[QBChat]
|
|
1287
|
+
'[QBChat] [RECONNECT] executing connect() at ' + chatUtils.getLocalTime() +
|
|
1288
|
+
' in ' + description);
|
|
1193
1289
|
}
|
|
1194
1290
|
self.connect(params);
|
|
1291
|
+
} else if (self.isConnected || self._sessionHasExpired) {
|
|
1292
|
+
// Stop retry timer only when actually connected or session expired (no point retrying).
|
|
1293
|
+
// Do NOT stop timer when _isConnecting — the in-flight attempt may fail,
|
|
1294
|
+
// and we need the timer to keep ticking for the next retry.
|
|
1295
|
+
Utils.QBLog('[QBChat]', '[RECONNECT] timer stopped at ' + chatUtils.getLocalTime() +
|
|
1296
|
+
' — isConnected=' + self.isConnected +
|
|
1297
|
+
' _sessionHasExpired=' + self._sessionHasExpired);
|
|
1298
|
+
clearInterval(self._checkConnectionTimer);
|
|
1299
|
+
self._checkConnectionTimer = undefined;
|
|
1195
1300
|
if (typeof self.onLogListener === 'function') {
|
|
1196
1301
|
Utils.safeCallbackCall(self.onLogListener,
|
|
1197
|
-
'[QBChat]
|
|
1302
|
+
'[QBChat] [RECONNECT] timer stopped at ' + chatUtils.getLocalTime() +
|
|
1303
|
+
' — isConnected=' + self.isConnected +
|
|
1304
|
+
' _sessionHasExpired=' + self._sessionHasExpired);
|
|
1198
1305
|
}
|
|
1199
1306
|
} else {
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1307
|
+
// _isConnecting === true — another attempt is in flight, skip this tick
|
|
1308
|
+
Utils.QBLog('[QBChat]', '[RECONNECT] _connect() SKIPPED at ' + chatUtils.getLocalTime() +
|
|
1309
|
+
' — _isConnecting=true, waiting for next tick');
|
|
1203
1310
|
if (typeof self.onLogListener === 'function') {
|
|
1204
1311
|
Utils.safeCallbackCall(self.onLogListener,
|
|
1205
|
-
'[QBChat]
|
|
1312
|
+
'[QBChat] [RECONNECT] _connect() SKIPPED at ' + chatUtils.getLocalTime() +
|
|
1313
|
+
' — _isConnecting=true, waiting for next tick');
|
|
1206
1314
|
}
|
|
1207
1315
|
}
|
|
1208
1316
|
};
|
|
@@ -1210,10 +1318,12 @@ ChatProxy.prototype = {
|
|
|
1210
1318
|
_connect();
|
|
1211
1319
|
|
|
1212
1320
|
self._checkConnectionTimer = setInterval(function () {
|
|
1213
|
-
Utils.QBLog('[QBChat]', '
|
|
1321
|
+
Utils.QBLog('[QBChat]', '[RECONNECT] timer tick at ' + chatUtils.getLocalTime() +
|
|
1322
|
+
' interval=' + config.chatReconnectionTimeInterval + 's');
|
|
1214
1323
|
if (typeof self.onLogListener === 'function') {
|
|
1215
1324
|
Utils.safeCallbackCall(self.onLogListener,
|
|
1216
|
-
'[QBChat]
|
|
1325
|
+
'[QBChat] [RECONNECT] timer tick at ' + chatUtils.getLocalTime() +
|
|
1326
|
+
' interval=' + config.chatReconnectionTimeInterval + 's');
|
|
1217
1327
|
}
|
|
1218
1328
|
_connect();
|
|
1219
1329
|
}, config.chatReconnectionTimeInterval * 1000);
|
|
@@ -287,6 +287,14 @@ var qbChatHelpers = {
|
|
|
287
287
|
delete extension.moduleIdentifier;
|
|
288
288
|
}
|
|
289
289
|
|
|
290
|
+
if (extension.date_sent) {
|
|
291
|
+
var parsedDateSent = Number(extension.date_sent);
|
|
292
|
+
|
|
293
|
+
if (Number.isFinite(parsedDateSent)) {
|
|
294
|
+
extension.date_sent = Math.trunc(parsedDateSent).toString();
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
290
298
|
return {
|
|
291
299
|
extension: extension,
|
|
292
300
|
dialogId: dialogId
|
|
@@ -56,6 +56,14 @@ DialogProxy.prototype = {
|
|
|
56
56
|
params.occupants_ids = params.occupants_ids.join(', ');
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
if (params && params.is_join_required !== undefined && params.is_join_required !== null) {
|
|
60
|
+
if (params.is_join_required !== 0 && params.is_join_required !== 1) {
|
|
61
|
+
Utils.QBLog('[QBChat]', 'Warning: is_join_required must be 0 or 1, got: ' +
|
|
62
|
+
params.is_join_required + '. Parameter ignored.');
|
|
63
|
+
delete params.is_join_required;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
59
67
|
this.service.ajax({
|
|
60
68
|
url: Utils.getUrl(DIALOGS_API_URL),
|
|
61
69
|
type: 'POST',
|
|
@@ -78,6 +86,11 @@ DialogProxy.prototype = {
|
|
|
78
86
|
* @callback updateDialogCallback
|
|
79
87
|
* */
|
|
80
88
|
|
|
89
|
+
if (params && params.is_join_required !== undefined) {
|
|
90
|
+
Utils.QBLog('[QBChat]', 'Warning: is_join_required is not supported in dialog.update(). Parameter ignored.');
|
|
91
|
+
delete params.is_join_required;
|
|
92
|
+
}
|
|
93
|
+
|
|
81
94
|
this.service.ajax({
|
|
82
95
|
'url': Utils.getUrl(DIALOGS_API_URL, id),
|
|
83
96
|
'type': 'PUT',
|
package/src/qbConfig.js
CHANGED
package/src/qbStrophe.js
CHANGED
|
@@ -74,6 +74,11 @@ function Connection(onLogListenerCallback) {
|
|
|
74
74
|
Utils.QBLog('[QBChat]', 'RECV:', data);
|
|
75
75
|
safeCallbackCall('RECV:', data);
|
|
76
76
|
|
|
77
|
+
// [QC-1454 DIAGNOSTIC] Flag groupchat messages at transport level
|
|
78
|
+
if (typeof data === 'string' && data.indexOf('groupchat') !== -1) {
|
|
79
|
+
Utils.QBLog('[QBChat]', '[TRANSPORT] Groupchat stanza received at WebSocket level');
|
|
80
|
+
}
|
|
81
|
+
|
|
77
82
|
try {
|
|
78
83
|
let parser = new DOMParser();
|
|
79
84
|
let xmlDoc = parser.parseFromString(data, 'text/xml');
|