quickblox 2.24.0 → 2.24.1-alpha.1
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/package.json +1 -1
- package/quickblox.d.ts +34 -1
- package/quickblox.js +218 -67
- package/quickblox.min.js +1 -1
- package/src/modules/chat/qbChat.js +214 -64
- package/src/modules/chat/qbNoticeConsts.js +2 -1
- package/src/qbConfig.js +2 -2
|
@@ -11,6 +11,13 @@ var chatUtils = require('./qbChatHelpers'),
|
|
|
11
11
|
|
|
12
12
|
var unsupportedError = 'This function isn\'t supported outside of the browser (...yet)';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* [CROS-1061] How long to wait for the server to answer a Notice enable/disable
|
|
16
|
+
* IQ before reporting an error to the caller. Without a bound the callback is
|
|
17
|
+
* never invoked when the server stays silent.
|
|
18
|
+
*/
|
|
19
|
+
var NOTICE_IQ_TIMEOUT_MS = 10000;
|
|
20
|
+
|
|
14
21
|
var XMPP;
|
|
15
22
|
|
|
16
23
|
/** create StropheJS or NodeXMPP connection object */
|
|
@@ -44,6 +51,7 @@ NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.UPDATED_MESSAGE] =
|
|
|
44
51
|
NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.DELETED_MESSAGE] = true;
|
|
45
52
|
NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.UPDATED_DIALOG] = true;
|
|
46
53
|
NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.DELETED_DIALOG] = true;
|
|
54
|
+
NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.CREATED_DIALOG] = true;
|
|
47
55
|
|
|
48
56
|
/**
|
|
49
57
|
* Cross-env: get all direct child elements of `parent` whose tag name equals
|
|
@@ -327,6 +335,91 @@ function _parseCustomData(extraParams) {
|
|
|
327
335
|
return out;
|
|
328
336
|
}
|
|
329
337
|
|
|
338
|
+
/**
|
|
339
|
+
* Build a partial QBChatDialog object from a dialog-notice <extraParams>.
|
|
340
|
+
* Shared by NoticeUpdatedDialog and NoticeCreatedDialog (SR-2952) — the server
|
|
341
|
+
* sends a full dialog snapshot in both cases. Fields with null/empty values are
|
|
342
|
+
* omitted by the server, so every read is guarded.
|
|
343
|
+
*
|
|
344
|
+
* CreatedDialog additionally carries created_at/updated_at/user_id (creator) and
|
|
345
|
+
* has no last_message* fields (dialog just created); UpdatedDialog carries
|
|
346
|
+
* last_message* and (per server contract) may also carry created_at/updated_at/
|
|
347
|
+
* user_id. Reading them here for both is safe — absent fields are simply skipped.
|
|
348
|
+
*
|
|
349
|
+
* @param {Element} extraParams
|
|
350
|
+
* @param {String} dialogId - already-resolved dialog_id text.
|
|
351
|
+
* @return {Object} partial dialog snapshot.
|
|
352
|
+
*/
|
|
353
|
+
function _parseDialogSnapshot(extraParams, dialogId) {
|
|
354
|
+
var dlg = {};
|
|
355
|
+
dlg._id = dialogId;
|
|
356
|
+
|
|
357
|
+
var name = chatUtils.getElementText(extraParams, 'name');
|
|
358
|
+
if (name !== undefined && name !== null && name !== '') {
|
|
359
|
+
dlg.name = name;
|
|
360
|
+
}
|
|
361
|
+
var photo = chatUtils.getElementText(extraParams, 'photo');
|
|
362
|
+
if (photo !== undefined && photo !== null && photo !== '') {
|
|
363
|
+
dlg.photo = photo;
|
|
364
|
+
}
|
|
365
|
+
var typeText = chatUtils.getElementText(extraParams, 'type');
|
|
366
|
+
var typeNum = parseInt(typeText, 10);
|
|
367
|
+
if (!isNaN(typeNum)) {
|
|
368
|
+
dlg.type = typeNum;
|
|
369
|
+
}
|
|
370
|
+
var occupants = _parseNumericIdsCsv(chatUtils.getElementText(extraParams, 'occupants_ids'));
|
|
371
|
+
if (occupants.length > 0) {
|
|
372
|
+
dlg.occupants_ids = occupants;
|
|
373
|
+
}
|
|
374
|
+
var admins = _parseNumericIdsCsv(chatUtils.getElementText(extraParams, 'admin_ids'));
|
|
375
|
+
if (admins.length > 0) {
|
|
376
|
+
dlg.admin_ids = admins;
|
|
377
|
+
}
|
|
378
|
+
var isJoinReqText = chatUtils.getElementText(extraParams, 'is_join_required');
|
|
379
|
+
if (isJoinReqText !== undefined && isJoinReqText !== null && isJoinReqText !== '') {
|
|
380
|
+
var isJoinReq = parseInt(isJoinReqText, 10);
|
|
381
|
+
dlg.is_join_required = isNaN(isJoinReq) ? isJoinReqText : isJoinReq;
|
|
382
|
+
}
|
|
383
|
+
var roomJid = chatUtils.getElementText(extraParams, 'xmpp_room_jid');
|
|
384
|
+
if (roomJid !== undefined && roomJid !== null && roomJid !== '') {
|
|
385
|
+
// Server may pretty-print xmpp_room_jid with surrounding whitespace.
|
|
386
|
+
dlg.xmpp_room_jid = roomJid.trim();
|
|
387
|
+
}
|
|
388
|
+
var customData = _parseCustomData(extraParams);
|
|
389
|
+
if (customData !== undefined) {
|
|
390
|
+
dlg.custom_data = customData;
|
|
391
|
+
}
|
|
392
|
+
// Creation / modification metadata (present on CreatedDialog; also valid on
|
|
393
|
+
// UpdatedDialog per server contract). Kept as ISO-8601 strings verbatim.
|
|
394
|
+
var createdAt = chatUtils.getElementText(extraParams, 'created_at');
|
|
395
|
+
if (createdAt !== undefined && createdAt !== null && createdAt !== '') {
|
|
396
|
+
dlg.created_at = createdAt;
|
|
397
|
+
}
|
|
398
|
+
var updatedAt = chatUtils.getElementText(extraParams, 'updated_at');
|
|
399
|
+
if (updatedAt !== undefined && updatedAt !== null && updatedAt !== '') {
|
|
400
|
+
dlg.updated_at = updatedAt;
|
|
401
|
+
}
|
|
402
|
+
var userIdText = chatUtils.getElementText(extraParams, 'user_id');
|
|
403
|
+
var userId = parseInt(userIdText, 10);
|
|
404
|
+
if (!isNaN(userId)) {
|
|
405
|
+
dlg.user_id = userId;
|
|
406
|
+
}
|
|
407
|
+
// Last message fields (present on UpdatedDialog; absent on a freshly
|
|
408
|
+
// CreatedDialog — the guards below simply skip them then).
|
|
409
|
+
var lmText = chatUtils.getElementText(extraParams, 'last_message');
|
|
410
|
+
if (lmText) { dlg.last_message = lmText; }
|
|
411
|
+
var lmId = chatUtils.getElementText(extraParams, 'last_message_id');
|
|
412
|
+
if (lmId) { dlg.last_message_id = lmId; }
|
|
413
|
+
var lmDsText = chatUtils.getElementText(extraParams, 'last_message_date_sent');
|
|
414
|
+
var lmDs = parseInt(lmDsText, 10);
|
|
415
|
+
if (!isNaN(lmDs)) { dlg.last_message_date_sent = lmDs; }
|
|
416
|
+
var lmUidText = chatUtils.getElementText(extraParams, 'last_message_user_id');
|
|
417
|
+
var lmUid = parseInt(lmUidText, 10);
|
|
418
|
+
if (!isNaN(lmUid)) { dlg.last_message_user_id = lmUid; }
|
|
419
|
+
|
|
420
|
+
return dlg;
|
|
421
|
+
}
|
|
422
|
+
|
|
330
423
|
/**
|
|
331
424
|
* Parse a Notice headline stanza into {type, payload} ready for routing.
|
|
332
425
|
* Returns null if stanza is not a Notice (no urn:xmpp:notice:0 namespace, or
|
|
@@ -422,59 +515,12 @@ function _parseNoticeStanza(stanza) {
|
|
|
422
515
|
message: msg
|
|
423
516
|
};
|
|
424
517
|
}
|
|
425
|
-
} else if (type === IDS.UPDATED_DIALOG) {
|
|
426
|
-
//
|
|
427
|
-
|
|
428
|
-
dlg._id = dialogId;
|
|
429
|
-
var name = chatUtils.getElementText(extraParams, 'name');
|
|
430
|
-
if (name !== undefined && name !== null && name !== '') {
|
|
431
|
-
dlg.name = name;
|
|
432
|
-
}
|
|
433
|
-
var photo = chatUtils.getElementText(extraParams, 'photo');
|
|
434
|
-
if (photo !== undefined && photo !== null && photo !== '') {
|
|
435
|
-
dlg.photo = photo;
|
|
436
|
-
}
|
|
437
|
-
var typeText = chatUtils.getElementText(extraParams, 'type');
|
|
438
|
-
var typeNum = parseInt(typeText, 10);
|
|
439
|
-
if (!isNaN(typeNum)) {
|
|
440
|
-
dlg.type = typeNum;
|
|
441
|
-
}
|
|
442
|
-
var occupants = _parseNumericIdsCsv(chatUtils.getElementText(extraParams, 'occupants_ids'));
|
|
443
|
-
if (occupants.length > 0) {
|
|
444
|
-
dlg.occupants_ids = occupants;
|
|
445
|
-
}
|
|
446
|
-
var admins = _parseNumericIdsCsv(chatUtils.getElementText(extraParams, 'admin_ids'));
|
|
447
|
-
if (admins.length > 0) {
|
|
448
|
-
dlg.admin_ids = admins;
|
|
449
|
-
}
|
|
450
|
-
var isJoinReqText = chatUtils.getElementText(extraParams, 'is_join_required');
|
|
451
|
-
if (isJoinReqText !== undefined && isJoinReqText !== null && isJoinReqText !== '') {
|
|
452
|
-
var isJoinReq = parseInt(isJoinReqText, 10);
|
|
453
|
-
dlg.is_join_required = isNaN(isJoinReq) ? isJoinReqText : isJoinReq;
|
|
454
|
-
}
|
|
455
|
-
var roomJid = chatUtils.getElementText(extraParams, 'xmpp_room_jid');
|
|
456
|
-
if (roomJid !== undefined && roomJid !== null && roomJid !== '') {
|
|
457
|
-
dlg.xmpp_room_jid = roomJid;
|
|
458
|
-
}
|
|
459
|
-
var customData = _parseCustomData(extraParams);
|
|
460
|
-
if (customData !== undefined) {
|
|
461
|
-
dlg.custom_data = customData;
|
|
462
|
-
}
|
|
463
|
-
// Last message fields.
|
|
464
|
-
var lmText = chatUtils.getElementText(extraParams, 'last_message');
|
|
465
|
-
if (lmText) { dlg.last_message = lmText; }
|
|
466
|
-
var lmId = chatUtils.getElementText(extraParams, 'last_message_id');
|
|
467
|
-
if (lmId) { dlg.last_message_id = lmId; }
|
|
468
|
-
var lmDsText = chatUtils.getElementText(extraParams, 'last_message_date_sent');
|
|
469
|
-
var lmDs = parseInt(lmDsText, 10);
|
|
470
|
-
if (!isNaN(lmDs)) { dlg.last_message_date_sent = lmDs; }
|
|
471
|
-
var lmUidText = chatUtils.getElementText(extraParams, 'last_message_user_id');
|
|
472
|
-
var lmUid = parseInt(lmUidText, 10);
|
|
473
|
-
if (!isNaN(lmUid)) { dlg.last_message_user_id = lmUid; }
|
|
474
|
-
|
|
518
|
+
} else if (type === IDS.UPDATED_DIALOG || type === IDS.CREATED_DIALOG) {
|
|
519
|
+
// Full server dialog snapshot. UpdatedDialog and CreatedDialog (SR-2952)
|
|
520
|
+
// share the same shape; they differ only by moduleIdentifier (routing).
|
|
475
521
|
payload = {
|
|
476
522
|
kind: 'dialog',
|
|
477
|
-
dialog:
|
|
523
|
+
dialog: _parseDialogSnapshot(extraParams, dialogId)
|
|
478
524
|
};
|
|
479
525
|
} else {
|
|
480
526
|
return null;
|
|
@@ -521,6 +567,10 @@ function _routeNoticeEvent(chatProxy, parsed) {
|
|
|
521
567
|
if (typeof chatProxy.onDialogUpdatedListener === 'function') {
|
|
522
568
|
Utils.safeCallbackCall(chatProxy.onDialogUpdatedListener, p.dialog);
|
|
523
569
|
}
|
|
570
|
+
} else if (type === IDS.CREATED_DIALOG) {
|
|
571
|
+
if (typeof chatProxy.onDialogCreatedListener === 'function') {
|
|
572
|
+
Utils.safeCallbackCall(chatProxy.onDialogCreatedListener, p.dialog);
|
|
573
|
+
}
|
|
524
574
|
}
|
|
525
575
|
}
|
|
526
576
|
|
|
@@ -621,6 +671,15 @@ function ChatProxy(service) {
|
|
|
621
671
|
this.onMessageReactionChangedListener = null;
|
|
622
672
|
this.onDialogDeletedListener = null;
|
|
623
673
|
this.onDialogUpdatedListener = null;
|
|
674
|
+
/**
|
|
675
|
+
* NoticeCreatedDialog listener (SR-2952). Fired when a Group or Private
|
|
676
|
+
* dialog is created and the current user has the Notice feature enabled.
|
|
677
|
+
* Signature: onDialogCreatedListener(dialog). The dialog is a partial
|
|
678
|
+
* snapshot from the creation notice — it carries created_at/updated_at/
|
|
679
|
+
* user_id (creator) but no last_message* fields (the dialog is brand new),
|
|
680
|
+
* and for a private dialog no xmpp_room_jid. Not fired for Public dialogs.
|
|
681
|
+
*/
|
|
682
|
+
this.onDialogCreatedListener = null;
|
|
624
683
|
|
|
625
684
|
// [QC-1550] XMPP connection is considered "verified" only after the first
|
|
626
685
|
// successful pong response. Strophe emits Status.CONNECTED at the transport
|
|
@@ -1800,6 +1859,22 @@ ChatProxy.prototype = {
|
|
|
1800
1859
|
self._isConnecting = false;
|
|
1801
1860
|
self._sessionHasExpired = false;
|
|
1802
1861
|
|
|
1862
|
+
// [CROS-1061] A new XMPP session never carries the previous session's
|
|
1863
|
+
// Notice subscription: the server binds <enable xmlns="urn:xmpp:notice:0"/>
|
|
1864
|
+
// to the stream, so it is gone once the stream is gone. Without this
|
|
1865
|
+
// reset the flag stays `true` after a transport reconnect while no
|
|
1866
|
+
// notices are delivered any more, and isNoticesEnabled() misleads the
|
|
1867
|
+
// application into skipping the re-enable (verified against a live
|
|
1868
|
+
// server: socket break -> reconnect -> flag true, zero notice stanzas;
|
|
1869
|
+
// clearing the flag and re-sending <enable> restored delivery).
|
|
1870
|
+
//
|
|
1871
|
+
// Placed in the shared preamble, above the isInitialConnect branch, and
|
|
1872
|
+
// above the onReconnectListener call below: with
|
|
1873
|
+
// config.pingLocalhostTimeInterval === 0 that listener fires
|
|
1874
|
+
// synchronously from this function, and a consumer re-enabling notices
|
|
1875
|
+
// from it must not observe a stale `true`.
|
|
1876
|
+
self._isNoticesEnabled = false;
|
|
1877
|
+
|
|
1803
1878
|
self._enableCarbons();
|
|
1804
1879
|
|
|
1805
1880
|
if (isInitialConnect) {
|
|
@@ -1810,7 +1885,6 @@ ChatProxy.prototype = {
|
|
|
1810
1885
|
self._isConnectionVerified = true;
|
|
1811
1886
|
self._isReconnectListenerPending = false;
|
|
1812
1887
|
|
|
1813
|
-
// TODO(2.25.0): auto-enable Notice Feature here (see qbChat.js enableNotices JSDoc).
|
|
1814
1888
|
self.roster.get(function (contacts) {
|
|
1815
1889
|
xmppClient.send(presence);
|
|
1816
1890
|
|
|
@@ -1818,7 +1892,24 @@ ChatProxy.prototype = {
|
|
|
1818
1892
|
callback(self.roster.contacts);
|
|
1819
1893
|
});
|
|
1820
1894
|
} else {
|
|
1895
|
+
// [CROS-1061] Union of the rooms still tracked on this instance and
|
|
1896
|
+
// the ones an explicit reconnect() had to drop. Consumed once —
|
|
1897
|
+
// clearing it here keeps a later session (or another user after
|
|
1898
|
+
// logout) from resurrecting a stale list.
|
|
1899
|
+
//
|
|
1900
|
+
// Safe to build the join stanzas at this point: setUserCurrentJid()
|
|
1901
|
+
// above has already installed the new JID, and muc.join() derives
|
|
1902
|
+
// the presence `from` out of it. Moving this block above that call
|
|
1903
|
+
// would send from="".
|
|
1821
1904
|
var rooms = Object.keys(self.muc.joinedRooms);
|
|
1905
|
+
var remembered = self.muc._roomsToRejoin || [];
|
|
1906
|
+
|
|
1907
|
+
for (var r = 0; r < remembered.length; r++) {
|
|
1908
|
+
if (rooms.indexOf(remembered[r]) === -1) {
|
|
1909
|
+
rooms.push(remembered[r]);
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
self.muc._roomsToRejoin = [];
|
|
1822
1913
|
|
|
1823
1914
|
xmppClient.send(presence);
|
|
1824
1915
|
|
|
@@ -2041,6 +2132,11 @@ ChatProxy.prototype = {
|
|
|
2041
2132
|
// connection had a chance to verify itself.
|
|
2042
2133
|
this._isConnectionVerified = false;
|
|
2043
2134
|
this._isReconnectListenerPending = false;
|
|
2135
|
+
// [CROS-1061] Remember the rooms before dropping them, so the re-join
|
|
2136
|
+
// loop in _postConnectActions has something to restore. Without this the
|
|
2137
|
+
// list is lost and group dialogs stop receiving message-level notices
|
|
2138
|
+
// until the page is reloaded.
|
|
2139
|
+
this.muc._roomsToRejoin = Object.keys(this.muc.joinedRooms);
|
|
2044
2140
|
this.muc.joinedRooms = {};
|
|
2045
2141
|
this.helpers.setUserCurrentJid('');
|
|
2046
2142
|
|
|
@@ -2434,6 +2530,9 @@ ChatProxy.prototype = {
|
|
|
2434
2530
|
this._checkConnectionTimer = undefined;
|
|
2435
2531
|
this._checkExpiredSessionTimer = undefined;
|
|
2436
2532
|
this.muc.joinedRooms = {};
|
|
2533
|
+
// [CROS-1061] Never carry rooms across a logout: re-joining the previous
|
|
2534
|
+
// user's rooms from a new JID is answered with 403/407 by the server.
|
|
2535
|
+
this.muc._roomsToRejoin = [];
|
|
2437
2536
|
// [QC-1550] Reset XMPP verification state on explicit disconnect so the
|
|
2438
2537
|
// next connect() starts from a clean slate. _isLogout guard below also
|
|
2439
2538
|
// prevents firing of deferred listeners if a pong arrives in flight,
|
|
@@ -2465,19 +2564,29 @@ ChatProxy.prototype = {
|
|
|
2465
2564
|
* Subscribe the current XMPP session to Notice Feature stanzas
|
|
2466
2565
|
* (urn:xmpp:notice:0). After a successful IQ result the server starts
|
|
2467
2566
|
* delivering NoticeUpdatedMessage / NoticeDeletedMessage /
|
|
2468
|
-
* NoticeUpdatedDialog / NoticeDeletedDialog
|
|
2567
|
+
* NoticeCreatedDialog / NoticeUpdatedDialog / NoticeDeletedDialog
|
|
2568
|
+
* headline stanzas.
|
|
2469
2569
|
*
|
|
2470
|
-
* The
|
|
2471
|
-
*
|
|
2472
|
-
*
|
|
2570
|
+
* The subscription belongs to the XMPP session, so it does not survive a
|
|
2571
|
+
* reconnect: the local enabled flag is reset to false whenever a new session
|
|
2572
|
+
* is established (initial connect and every reconnect alike) as well as on
|
|
2573
|
+
* disconnect. Consumers must call enableNotices() again after a reconnect —
|
|
2574
|
+
* an intentional divergence from the Android SDK, which auto-restores the
|
|
2575
|
+
* subscription. Reacting to onReconnectListener is the supported way to do
|
|
2576
|
+
* that.
|
|
2473
2577
|
*
|
|
2474
|
-
*
|
|
2475
|
-
*
|
|
2476
|
-
*
|
|
2477
|
-
*
|
|
2578
|
+
* Concurrent calls are safe: each IQ carries its own unique id, responses
|
|
2579
|
+
* are dispatched per id, and the flag is set independently for each result.
|
|
2580
|
+
* Calling enableNotices() again before the previous callback fires is
|
|
2581
|
+
* therefore allowed — for example when a second reconnect arrives while the
|
|
2582
|
+
* first enable is still in flight.
|
|
2478
2583
|
*
|
|
2479
|
-
*
|
|
2480
|
-
*
|
|
2584
|
+
* If the server does not answer, the callback receives an error after a
|
|
2585
|
+
* timeout instead of never being called.
|
|
2586
|
+
*
|
|
2587
|
+
* TODO(2.25.0): make auto-enable SDK-internal — fire on initial connect and
|
|
2588
|
+
* on every reconnect. Spec & three-state matrix are tracked in the internal
|
|
2589
|
+
* 2.25.0 backlog (auto-enable notices).
|
|
2481
2590
|
*
|
|
2482
2591
|
* @memberof QB.chat
|
|
2483
2592
|
* @param {enableNoticesCallback} callback - Called with (error, result) on IQ result.
|
|
@@ -2516,7 +2625,10 @@ ChatProxy.prototype = {
|
|
|
2516
2625
|
* Returns the local Notice Feature subscription flag.
|
|
2517
2626
|
* @memberof QB.chat
|
|
2518
2627
|
* @return {Boolean} true after a successful enableNotices(), false otherwise.
|
|
2519
|
-
* Reset to false on
|
|
2628
|
+
* Reset to false on disconnect and whenever a new XMPP session is
|
|
2629
|
+
* established, so after a reconnect it reports false until the consumer
|
|
2630
|
+
* enables notices again. Not synchronized with the server: it reflects
|
|
2631
|
+
* what this client last requested, not the server's own state.
|
|
2520
2632
|
* @since 2.24.0
|
|
2521
2633
|
*/
|
|
2522
2634
|
isNoticesEnabled: function () {
|
|
@@ -2569,10 +2681,34 @@ ChatProxy.prototype = {
|
|
|
2569
2681
|
}
|
|
2570
2682
|
|
|
2571
2683
|
if (Utils.getEnv().browser) {
|
|
2572
|
-
|
|
2684
|
+
// [CROS-1061] Without the 4th argument Strophe never times the IQ out,
|
|
2685
|
+
// so a server that stays silent leaves the caller without any callback
|
|
2686
|
+
// at all — the application spins forever. On timeout Strophe calls
|
|
2687
|
+
// _onError(null), which reports a 'cancel' error and leaves the flag
|
|
2688
|
+
// untouched.
|
|
2689
|
+
//
|
|
2690
|
+
// Note: this covers "server does not answer". It does NOT cover
|
|
2691
|
+
// "socket died", because the timeout is an addTimedHandler and
|
|
2692
|
+
// connection.reset() (called from the DISCONNECTED branch) clears
|
|
2693
|
+
// timedHandlers. That case is covered by the consumer-side timeout.
|
|
2694
|
+
self.connection.sendIQ(iq, _onSuccess, _onError, NOTICE_IQ_TIMEOUT_MS);
|
|
2573
2695
|
} else {
|
|
2574
2696
|
// Node/NativeScript path: register callback in nodeStanzasCallbacks map.
|
|
2697
|
+
// This path does not go through sendIQ, so the timeout is ours to run.
|
|
2698
|
+
var noticeIqTimer = setTimeout(function () {
|
|
2699
|
+
// Drop the pending entry first: _onIQ only dispatches while the
|
|
2700
|
+
// entry exists, so a late answer after this point is ignored
|
|
2701
|
+
// instead of invoking the callback a second time.
|
|
2702
|
+
delete self.nodeStanzasCallbacks[iqParams.id];
|
|
2703
|
+
_onError(null);
|
|
2704
|
+
}, NOTICE_IQ_TIMEOUT_MS);
|
|
2705
|
+
|
|
2575
2706
|
self.nodeStanzasCallbacks[iqParams.id] = function (stanza) {
|
|
2707
|
+
// Must be first: otherwise a timely answer still leaves the timer
|
|
2708
|
+
// armed, and it would fire later and call the callback a second
|
|
2709
|
+
// time with an error.
|
|
2710
|
+
clearTimeout(noticeIqTimer);
|
|
2711
|
+
|
|
2576
2712
|
// The map handler receives the result/error stanza. Inspect type to dispatch.
|
|
2577
2713
|
var iqType = chatUtils.getAttr(stanza, 'type');
|
|
2578
2714
|
if (iqType === 'result') {
|
|
@@ -2901,6 +3037,20 @@ function MucProxy(options) {
|
|
|
2901
3037
|
this.nodeStanzasCallbacks = options.nodeStanzasCallbacks;
|
|
2902
3038
|
//
|
|
2903
3039
|
this.joinedRooms = {};
|
|
3040
|
+
|
|
3041
|
+
/**
|
|
3042
|
+
* [CROS-1061] Rooms carried across an explicit QB.chat.reconnect().
|
|
3043
|
+
*
|
|
3044
|
+
* reconnect() has to clear joinedRooms — the new session holds no presence
|
|
3045
|
+
* yet — but until now it dropped the list without keeping a copy, so the
|
|
3046
|
+
* re-join loop in _postConnectActions found nothing to restore and group
|
|
3047
|
+
* dialogs silently stopped receiving message-level notices.
|
|
3048
|
+
*
|
|
3049
|
+
* Written only by reconnect(), read and cleared once by _postConnectActions,
|
|
3050
|
+
* and cleared by disconnect() so one user's rooms can never be re-joined
|
|
3051
|
+
* from another user's JID.
|
|
3052
|
+
*/
|
|
3053
|
+
this._roomsToRejoin = [];
|
|
2904
3054
|
}
|
|
2905
3055
|
|
|
2906
3056
|
MucProxy.prototype = {
|
|
@@ -23,7 +23,8 @@ var NOTICE_MODULE_IDENTIFIER = {
|
|
|
23
23
|
UPDATED_MESSAGE: 'NoticeUpdatedMessage',
|
|
24
24
|
DELETED_MESSAGE: 'NoticeDeletedMessage',
|
|
25
25
|
UPDATED_DIALOG: 'NoticeUpdatedDialog',
|
|
26
|
-
DELETED_DIALOG: 'NoticeDeletedDialog'
|
|
26
|
+
DELETED_DIALOG: 'NoticeDeletedDialog',
|
|
27
|
+
CREATED_DIALOG: 'NoticeCreatedDialog'
|
|
27
28
|
};
|
|
28
29
|
|
|
29
30
|
module.exports = {
|