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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "quickblox",
3
3
  "description": "QuickBlox JavaScript SDK",
4
- "version": "2.24.0",
4
+ "version": "2.24.1-alpha.1",
5
5
  "homepage": "https://quickblox.com/developers/Javascript",
6
6
  "main": "src/qbMain.js",
7
7
  "types": "quickblox.d.ts",
package/quickblox.d.ts CHANGED
@@ -418,6 +418,26 @@ export declare interface QBChatMessage {
418
418
  * Available since SDK 2.24.0.
419
419
  */
420
420
  reactions?: QBReaction[]
421
+ /**
422
+ * Message editing flag (SR-2955). The server sets `1` when the message was
423
+ * edited, `0`/`null` otherwise. Delivered on the message object in REST
424
+ * responses (`message.list` / `message.getById`). Note: the server also carries it
425
+ * in `NoticeUpdatedMessage` (server release 2.94.0), but the SDK's real-time
426
+ * `onMessageUpdatedListener` object does not currently include it — read it from
427
+ * a REST fetch of the message.
428
+ * Available since SDK 2.24.0.
429
+ */
430
+ is_edited?: 0 | 1 | boolean | null
431
+ /**
432
+ * ID of the user who last edited the message (SR-2955). Omitted/`null` when the
433
+ * message was never edited. Available since SDK 2.24.0.
434
+ */
435
+ editor_id?: number | null
436
+ /**
437
+ * ISO timestamp of the last edit (SR-2955). Omitted/`null` when the message was
438
+ * never edited. Available since SDK 2.24.0.
439
+ */
440
+ edited_at?: string | null
421
441
  /**
422
442
  * Name of the custom field.
423
443
  * Chat message can be extended with additional fields and contain any other user key-value custom parameters.
@@ -683,7 +703,8 @@ interface QBChatModule {
683
703
  /**
684
704
  * Subscribe the current XMPP session to Notice Feature stanzas (urn:xmpp:notice:0).
685
705
  * After a successful IQ result the server starts delivering NoticeUpdatedMessage /
686
- * NoticeDeletedMessage / NoticeUpdatedDialog / NoticeDeletedDialog headline stanzas.
706
+ * NoticeDeletedMessage / NoticeCreatedDialog / NoticeUpdatedDialog / NoticeDeletedDialog
707
+ * headline stanzas.
687
708
  * The local enabled flag is reset on chat disconnect — call enableNotices() again after reconnect.
688
709
  * @since 2.24.0
689
710
  */
@@ -749,6 +770,18 @@ interface QBChatModule {
749
770
  * @since 2.24.0
750
771
  */
751
772
  onDialogUpdatedListener?: (dialog: QBChatDialog) => void
773
+ /**
774
+ * Notice Feature: a Group or Private dialog was created (SR-2952). Delivered via the
775
+ * XMPP `NoticeCreatedDialog` headline stanza after `enableNotices()`, only to users
776
+ * with the Notice feature enabled. NOT sent for Public dialogs.
777
+ *
778
+ * The dialog object is a PARTIAL snapshot captured at creation time: it carries
779
+ * `created_at` / `updated_at` / `user_id` (creator) and the base fields, but has no
780
+ * `last_message*` fields (the dialog is brand new) and, for a private dialog, no
781
+ * `xmpp_room_jid`. Preserves the existing QBChatDialog snake_case shape.
782
+ * @since 2.24.1
783
+ */
784
+ onDialogCreatedListener?: (dialog: QBChatDialog) => void
752
785
  /** Blocked entities receive an error when try to chat with a user in a 1-1 chat and receivie nothing in a group chat. */
753
786
  onMessageErrorListener?: (messageId: QBChatMessage['_id'], error: any) => void
754
787
  /**
package/quickblox.js CHANGED
@@ -30507,6 +30507,13 @@ var chatUtils = require('./qbChatHelpers'),
30507
30507
 
30508
30508
  var unsupportedError = 'This function isn\'t supported outside of the browser (...yet)';
30509
30509
 
30510
+ /**
30511
+ * [CROS-1061] How long to wait for the server to answer a Notice enable/disable
30512
+ * IQ before reporting an error to the caller. Without a bound the callback is
30513
+ * never invoked when the server stays silent.
30514
+ */
30515
+ var NOTICE_IQ_TIMEOUT_MS = 10000;
30516
+
30510
30517
  var XMPP;
30511
30518
 
30512
30519
  /** create StropheJS or NodeXMPP connection object */
@@ -30540,6 +30547,7 @@ NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.UPDATED_MESSAGE] =
30540
30547
  NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.DELETED_MESSAGE] = true;
30541
30548
  NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.UPDATED_DIALOG] = true;
30542
30549
  NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.DELETED_DIALOG] = true;
30550
+ NOTICE_KNOWN_MODULE_IDS[noticeConsts.NOTICE_MODULE_IDENTIFIER.CREATED_DIALOG] = true;
30543
30551
 
30544
30552
  /**
30545
30553
  * Cross-env: get all direct child elements of `parent` whose tag name equals
@@ -30823,6 +30831,91 @@ function _parseCustomData(extraParams) {
30823
30831
  return out;
30824
30832
  }
30825
30833
 
30834
+ /**
30835
+ * Build a partial QBChatDialog object from a dialog-notice <extraParams>.
30836
+ * Shared by NoticeUpdatedDialog and NoticeCreatedDialog (SR-2952) — the server
30837
+ * sends a full dialog snapshot in both cases. Fields with null/empty values are
30838
+ * omitted by the server, so every read is guarded.
30839
+ *
30840
+ * CreatedDialog additionally carries created_at/updated_at/user_id (creator) and
30841
+ * has no last_message* fields (dialog just created); UpdatedDialog carries
30842
+ * last_message* and (per server contract) may also carry created_at/updated_at/
30843
+ * user_id. Reading them here for both is safe — absent fields are simply skipped.
30844
+ *
30845
+ * @param {Element} extraParams
30846
+ * @param {String} dialogId - already-resolved dialog_id text.
30847
+ * @return {Object} partial dialog snapshot.
30848
+ */
30849
+ function _parseDialogSnapshot(extraParams, dialogId) {
30850
+ var dlg = {};
30851
+ dlg._id = dialogId;
30852
+
30853
+ var name = chatUtils.getElementText(extraParams, 'name');
30854
+ if (name !== undefined && name !== null && name !== '') {
30855
+ dlg.name = name;
30856
+ }
30857
+ var photo = chatUtils.getElementText(extraParams, 'photo');
30858
+ if (photo !== undefined && photo !== null && photo !== '') {
30859
+ dlg.photo = photo;
30860
+ }
30861
+ var typeText = chatUtils.getElementText(extraParams, 'type');
30862
+ var typeNum = parseInt(typeText, 10);
30863
+ if (!isNaN(typeNum)) {
30864
+ dlg.type = typeNum;
30865
+ }
30866
+ var occupants = _parseNumericIdsCsv(chatUtils.getElementText(extraParams, 'occupants_ids'));
30867
+ if (occupants.length > 0) {
30868
+ dlg.occupants_ids = occupants;
30869
+ }
30870
+ var admins = _parseNumericIdsCsv(chatUtils.getElementText(extraParams, 'admin_ids'));
30871
+ if (admins.length > 0) {
30872
+ dlg.admin_ids = admins;
30873
+ }
30874
+ var isJoinReqText = chatUtils.getElementText(extraParams, 'is_join_required');
30875
+ if (isJoinReqText !== undefined && isJoinReqText !== null && isJoinReqText !== '') {
30876
+ var isJoinReq = parseInt(isJoinReqText, 10);
30877
+ dlg.is_join_required = isNaN(isJoinReq) ? isJoinReqText : isJoinReq;
30878
+ }
30879
+ var roomJid = chatUtils.getElementText(extraParams, 'xmpp_room_jid');
30880
+ if (roomJid !== undefined && roomJid !== null && roomJid !== '') {
30881
+ // Server may pretty-print xmpp_room_jid with surrounding whitespace.
30882
+ dlg.xmpp_room_jid = roomJid.trim();
30883
+ }
30884
+ var customData = _parseCustomData(extraParams);
30885
+ if (customData !== undefined) {
30886
+ dlg.custom_data = customData;
30887
+ }
30888
+ // Creation / modification metadata (present on CreatedDialog; also valid on
30889
+ // UpdatedDialog per server contract). Kept as ISO-8601 strings verbatim.
30890
+ var createdAt = chatUtils.getElementText(extraParams, 'created_at');
30891
+ if (createdAt !== undefined && createdAt !== null && createdAt !== '') {
30892
+ dlg.created_at = createdAt;
30893
+ }
30894
+ var updatedAt = chatUtils.getElementText(extraParams, 'updated_at');
30895
+ if (updatedAt !== undefined && updatedAt !== null && updatedAt !== '') {
30896
+ dlg.updated_at = updatedAt;
30897
+ }
30898
+ var userIdText = chatUtils.getElementText(extraParams, 'user_id');
30899
+ var userId = parseInt(userIdText, 10);
30900
+ if (!isNaN(userId)) {
30901
+ dlg.user_id = userId;
30902
+ }
30903
+ // Last message fields (present on UpdatedDialog; absent on a freshly
30904
+ // CreatedDialog — the guards below simply skip them then).
30905
+ var lmText = chatUtils.getElementText(extraParams, 'last_message');
30906
+ if (lmText) { dlg.last_message = lmText; }
30907
+ var lmId = chatUtils.getElementText(extraParams, 'last_message_id');
30908
+ if (lmId) { dlg.last_message_id = lmId; }
30909
+ var lmDsText = chatUtils.getElementText(extraParams, 'last_message_date_sent');
30910
+ var lmDs = parseInt(lmDsText, 10);
30911
+ if (!isNaN(lmDs)) { dlg.last_message_date_sent = lmDs; }
30912
+ var lmUidText = chatUtils.getElementText(extraParams, 'last_message_user_id');
30913
+ var lmUid = parseInt(lmUidText, 10);
30914
+ if (!isNaN(lmUid)) { dlg.last_message_user_id = lmUid; }
30915
+
30916
+ return dlg;
30917
+ }
30918
+
30826
30919
  /**
30827
30920
  * Parse a Notice headline stanza into {type, payload} ready for routing.
30828
30921
  * Returns null if stanza is not a Notice (no urn:xmpp:notice:0 namespace, or
@@ -30918,59 +31011,12 @@ function _parseNoticeStanza(stanza) {
30918
31011
  message: msg
30919
31012
  };
30920
31013
  }
30921
- } else if (type === IDS.UPDATED_DIALOG) {
30922
- // Build a partial QBChatDialog object from extraParams (full server snapshot).
30923
- var dlg = {};
30924
- dlg._id = dialogId;
30925
- var name = chatUtils.getElementText(extraParams, 'name');
30926
- if (name !== undefined && name !== null && name !== '') {
30927
- dlg.name = name;
30928
- }
30929
- var photo = chatUtils.getElementText(extraParams, 'photo');
30930
- if (photo !== undefined && photo !== null && photo !== '') {
30931
- dlg.photo = photo;
30932
- }
30933
- var typeText = chatUtils.getElementText(extraParams, 'type');
30934
- var typeNum = parseInt(typeText, 10);
30935
- if (!isNaN(typeNum)) {
30936
- dlg.type = typeNum;
30937
- }
30938
- var occupants = _parseNumericIdsCsv(chatUtils.getElementText(extraParams, 'occupants_ids'));
30939
- if (occupants.length > 0) {
30940
- dlg.occupants_ids = occupants;
30941
- }
30942
- var admins = _parseNumericIdsCsv(chatUtils.getElementText(extraParams, 'admin_ids'));
30943
- if (admins.length > 0) {
30944
- dlg.admin_ids = admins;
30945
- }
30946
- var isJoinReqText = chatUtils.getElementText(extraParams, 'is_join_required');
30947
- if (isJoinReqText !== undefined && isJoinReqText !== null && isJoinReqText !== '') {
30948
- var isJoinReq = parseInt(isJoinReqText, 10);
30949
- dlg.is_join_required = isNaN(isJoinReq) ? isJoinReqText : isJoinReq;
30950
- }
30951
- var roomJid = chatUtils.getElementText(extraParams, 'xmpp_room_jid');
30952
- if (roomJid !== undefined && roomJid !== null && roomJid !== '') {
30953
- dlg.xmpp_room_jid = roomJid;
30954
- }
30955
- var customData = _parseCustomData(extraParams);
30956
- if (customData !== undefined) {
30957
- dlg.custom_data = customData;
30958
- }
30959
- // Last message fields.
30960
- var lmText = chatUtils.getElementText(extraParams, 'last_message');
30961
- if (lmText) { dlg.last_message = lmText; }
30962
- var lmId = chatUtils.getElementText(extraParams, 'last_message_id');
30963
- if (lmId) { dlg.last_message_id = lmId; }
30964
- var lmDsText = chatUtils.getElementText(extraParams, 'last_message_date_sent');
30965
- var lmDs = parseInt(lmDsText, 10);
30966
- if (!isNaN(lmDs)) { dlg.last_message_date_sent = lmDs; }
30967
- var lmUidText = chatUtils.getElementText(extraParams, 'last_message_user_id');
30968
- var lmUid = parseInt(lmUidText, 10);
30969
- if (!isNaN(lmUid)) { dlg.last_message_user_id = lmUid; }
30970
-
31014
+ } else if (type === IDS.UPDATED_DIALOG || type === IDS.CREATED_DIALOG) {
31015
+ // Full server dialog snapshot. UpdatedDialog and CreatedDialog (SR-2952)
31016
+ // share the same shape; they differ only by moduleIdentifier (routing).
30971
31017
  payload = {
30972
31018
  kind: 'dialog',
30973
- dialog: dlg
31019
+ dialog: _parseDialogSnapshot(extraParams, dialogId)
30974
31020
  };
30975
31021
  } else {
30976
31022
  return null;
@@ -31017,6 +31063,10 @@ function _routeNoticeEvent(chatProxy, parsed) {
31017
31063
  if (typeof chatProxy.onDialogUpdatedListener === 'function') {
31018
31064
  Utils.safeCallbackCall(chatProxy.onDialogUpdatedListener, p.dialog);
31019
31065
  }
31066
+ } else if (type === IDS.CREATED_DIALOG) {
31067
+ if (typeof chatProxy.onDialogCreatedListener === 'function') {
31068
+ Utils.safeCallbackCall(chatProxy.onDialogCreatedListener, p.dialog);
31069
+ }
31020
31070
  }
31021
31071
  }
31022
31072
 
@@ -31117,6 +31167,15 @@ function ChatProxy(service) {
31117
31167
  this.onMessageReactionChangedListener = null;
31118
31168
  this.onDialogDeletedListener = null;
31119
31169
  this.onDialogUpdatedListener = null;
31170
+ /**
31171
+ * NoticeCreatedDialog listener (SR-2952). Fired when a Group or Private
31172
+ * dialog is created and the current user has the Notice feature enabled.
31173
+ * Signature: onDialogCreatedListener(dialog). The dialog is a partial
31174
+ * snapshot from the creation notice — it carries created_at/updated_at/
31175
+ * user_id (creator) but no last_message* fields (the dialog is brand new),
31176
+ * and for a private dialog no xmpp_room_jid. Not fired for Public dialogs.
31177
+ */
31178
+ this.onDialogCreatedListener = null;
31120
31179
 
31121
31180
  // [QC-1550] XMPP connection is considered "verified" only after the first
31122
31181
  // successful pong response. Strophe emits Status.CONNECTED at the transport
@@ -32296,6 +32355,22 @@ ChatProxy.prototype = {
32296
32355
  self._isConnecting = false;
32297
32356
  self._sessionHasExpired = false;
32298
32357
 
32358
+ // [CROS-1061] A new XMPP session never carries the previous session's
32359
+ // Notice subscription: the server binds <enable xmlns="urn:xmpp:notice:0"/>
32360
+ // to the stream, so it is gone once the stream is gone. Without this
32361
+ // reset the flag stays `true` after a transport reconnect while no
32362
+ // notices are delivered any more, and isNoticesEnabled() misleads the
32363
+ // application into skipping the re-enable (verified against a live
32364
+ // server: socket break -> reconnect -> flag true, zero notice stanzas;
32365
+ // clearing the flag and re-sending <enable> restored delivery).
32366
+ //
32367
+ // Placed in the shared preamble, above the isInitialConnect branch, and
32368
+ // above the onReconnectListener call below: with
32369
+ // config.pingLocalhostTimeInterval === 0 that listener fires
32370
+ // synchronously from this function, and a consumer re-enabling notices
32371
+ // from it must not observe a stale `true`.
32372
+ self._isNoticesEnabled = false;
32373
+
32299
32374
  self._enableCarbons();
32300
32375
 
32301
32376
  if (isInitialConnect) {
@@ -32306,7 +32381,6 @@ ChatProxy.prototype = {
32306
32381
  self._isConnectionVerified = true;
32307
32382
  self._isReconnectListenerPending = false;
32308
32383
 
32309
- // TODO(2.25.0): auto-enable Notice Feature here (see qbChat.js enableNotices JSDoc).
32310
32384
  self.roster.get(function (contacts) {
32311
32385
  xmppClient.send(presence);
32312
32386
 
@@ -32314,7 +32388,24 @@ ChatProxy.prototype = {
32314
32388
  callback(self.roster.contacts);
32315
32389
  });
32316
32390
  } else {
32391
+ // [CROS-1061] Union of the rooms still tracked on this instance and
32392
+ // the ones an explicit reconnect() had to drop. Consumed once —
32393
+ // clearing it here keeps a later session (or another user after
32394
+ // logout) from resurrecting a stale list.
32395
+ //
32396
+ // Safe to build the join stanzas at this point: setUserCurrentJid()
32397
+ // above has already installed the new JID, and muc.join() derives
32398
+ // the presence `from` out of it. Moving this block above that call
32399
+ // would send from="".
32317
32400
  var rooms = Object.keys(self.muc.joinedRooms);
32401
+ var remembered = self.muc._roomsToRejoin || [];
32402
+
32403
+ for (var r = 0; r < remembered.length; r++) {
32404
+ if (rooms.indexOf(remembered[r]) === -1) {
32405
+ rooms.push(remembered[r]);
32406
+ }
32407
+ }
32408
+ self.muc._roomsToRejoin = [];
32318
32409
 
32319
32410
  xmppClient.send(presence);
32320
32411
 
@@ -32537,6 +32628,11 @@ ChatProxy.prototype = {
32537
32628
  // connection had a chance to verify itself.
32538
32629
  this._isConnectionVerified = false;
32539
32630
  this._isReconnectListenerPending = false;
32631
+ // [CROS-1061] Remember the rooms before dropping them, so the re-join
32632
+ // loop in _postConnectActions has something to restore. Without this the
32633
+ // list is lost and group dialogs stop receiving message-level notices
32634
+ // until the page is reloaded.
32635
+ this.muc._roomsToRejoin = Object.keys(this.muc.joinedRooms);
32540
32636
  this.muc.joinedRooms = {};
32541
32637
  this.helpers.setUserCurrentJid('');
32542
32638
 
@@ -32930,6 +33026,9 @@ ChatProxy.prototype = {
32930
33026
  this._checkConnectionTimer = undefined;
32931
33027
  this._checkExpiredSessionTimer = undefined;
32932
33028
  this.muc.joinedRooms = {};
33029
+ // [CROS-1061] Never carry rooms across a logout: re-joining the previous
33030
+ // user's rooms from a new JID is answered with 403/407 by the server.
33031
+ this.muc._roomsToRejoin = [];
32933
33032
  // [QC-1550] Reset XMPP verification state on explicit disconnect so the
32934
33033
  // next connect() starts from a clean slate. _isLogout guard below also
32935
33034
  // prevents firing of deferred listeners if a pong arrives in flight,
@@ -32961,19 +33060,29 @@ ChatProxy.prototype = {
32961
33060
  * Subscribe the current XMPP session to Notice Feature stanzas
32962
33061
  * (urn:xmpp:notice:0). After a successful IQ result the server starts
32963
33062
  * delivering NoticeUpdatedMessage / NoticeDeletedMessage /
32964
- * NoticeUpdatedDialog / NoticeDeletedDialog headline stanzas.
33063
+ * NoticeCreatedDialog / NoticeUpdatedDialog / NoticeDeletedDialog
33064
+ * headline stanzas.
32965
33065
  *
32966
- * The local enabled flag is reset to false on chat disconnect consumers
32967
- * must call enableNotices() again after a reconnect (intentional divergence
32968
- * from the Android SDK, which auto-restores the subscription).
33066
+ * The subscription belongs to the XMPP session, so it does not survive a
33067
+ * reconnect: the local enabled flag is reset to false whenever a new session
33068
+ * is established (initial connect and every reconnect alike) as well as on
33069
+ * disconnect. Consumers must call enableNotices() again after a reconnect —
33070
+ * an intentional divergence from the Android SDK, which auto-restores the
33071
+ * subscription. Reacting to onReconnectListener is the supported way to do
33072
+ * that.
32969
33073
  *
32970
- * TODO(2.25.0): make auto-enable SDK-internal fire on initial connect and
32971
- * on relogin after session-expired, skip on Stream-Management reconnect.
32972
- * Spec & three-state matrix are tracked in the internal 2.25.0 backlog
32973
- * (auto-enable notices).
33074
+ * Concurrent calls are safe: each IQ carries its own unique id, responses
33075
+ * are dispatched per id, and the flag is set independently for each result.
33076
+ * Calling enableNotices() again before the previous callback fires is
33077
+ * therefore allowed — for example when a second reconnect arrives while the
33078
+ * first enable is still in flight.
33079
+ *
33080
+ * If the server does not answer, the callback receives an error after a
33081
+ * timeout instead of never being called.
32974
33082
  *
32975
- * Do not call enableNotices again before the previous callback fires —
32976
- * concurrent enable calls are not specified.
33083
+ * TODO(2.25.0): make auto-enable SDK-internal fire on initial connect and
33084
+ * on every reconnect. Spec & three-state matrix are tracked in the internal
33085
+ * 2.25.0 backlog (auto-enable notices).
32977
33086
  *
32978
33087
  * @memberof QB.chat
32979
33088
  * @param {enableNoticesCallback} callback - Called with (error, result) on IQ result.
@@ -33012,7 +33121,10 @@ ChatProxy.prototype = {
33012
33121
  * Returns the local Notice Feature subscription flag.
33013
33122
  * @memberof QB.chat
33014
33123
  * @return {Boolean} true after a successful enableNotices(), false otherwise.
33015
- * Reset to false on chat disconnect; not synchronized with the server.
33124
+ * Reset to false on disconnect and whenever a new XMPP session is
33125
+ * established, so after a reconnect it reports false until the consumer
33126
+ * enables notices again. Not synchronized with the server: it reflects
33127
+ * what this client last requested, not the server's own state.
33016
33128
  * @since 2.24.0
33017
33129
  */
33018
33130
  isNoticesEnabled: function () {
@@ -33065,10 +33177,34 @@ ChatProxy.prototype = {
33065
33177
  }
33066
33178
 
33067
33179
  if (Utils.getEnv().browser) {
33068
- self.connection.sendIQ(iq, _onSuccess, _onError);
33180
+ // [CROS-1061] Without the 4th argument Strophe never times the IQ out,
33181
+ // so a server that stays silent leaves the caller without any callback
33182
+ // at all — the application spins forever. On timeout Strophe calls
33183
+ // _onError(null), which reports a 'cancel' error and leaves the flag
33184
+ // untouched.
33185
+ //
33186
+ // Note: this covers "server does not answer". It does NOT cover
33187
+ // "socket died", because the timeout is an addTimedHandler and
33188
+ // connection.reset() (called from the DISCONNECTED branch) clears
33189
+ // timedHandlers. That case is covered by the consumer-side timeout.
33190
+ self.connection.sendIQ(iq, _onSuccess, _onError, NOTICE_IQ_TIMEOUT_MS);
33069
33191
  } else {
33070
33192
  // Node/NativeScript path: register callback in nodeStanzasCallbacks map.
33193
+ // This path does not go through sendIQ, so the timeout is ours to run.
33194
+ var noticeIqTimer = setTimeout(function () {
33195
+ // Drop the pending entry first: _onIQ only dispatches while the
33196
+ // entry exists, so a late answer after this point is ignored
33197
+ // instead of invoking the callback a second time.
33198
+ delete self.nodeStanzasCallbacks[iqParams.id];
33199
+ _onError(null);
33200
+ }, NOTICE_IQ_TIMEOUT_MS);
33201
+
33071
33202
  self.nodeStanzasCallbacks[iqParams.id] = function (stanza) {
33203
+ // Must be first: otherwise a timely answer still leaves the timer
33204
+ // armed, and it would fire later and call the callback a second
33205
+ // time with an error.
33206
+ clearTimeout(noticeIqTimer);
33207
+
33072
33208
  // The map handler receives the result/error stanza. Inspect type to dispatch.
33073
33209
  var iqType = chatUtils.getAttr(stanza, 'type');
33074
33210
  if (iqType === 'result') {
@@ -33397,6 +33533,20 @@ function MucProxy(options) {
33397
33533
  this.nodeStanzasCallbacks = options.nodeStanzasCallbacks;
33398
33534
  //
33399
33535
  this.joinedRooms = {};
33536
+
33537
+ /**
33538
+ * [CROS-1061] Rooms carried across an explicit QB.chat.reconnect().
33539
+ *
33540
+ * reconnect() has to clear joinedRooms — the new session holds no presence
33541
+ * yet — but until now it dropped the list without keeping a copy, so the
33542
+ * re-join loop in _postConnectActions found nothing to restore and group
33543
+ * dialogs silently stopped receiving message-level notices.
33544
+ *
33545
+ * Written only by reconnect(), read and cleared once by _postConnectActions,
33546
+ * and cleared by disconnect() so one user's rooms can never be re-joined
33547
+ * from another user's JID.
33548
+ */
33549
+ this._roomsToRejoin = [];
33400
33550
  }
33401
33551
 
33402
33552
  MucProxy.prototype = {
@@ -35156,7 +35306,8 @@ var NOTICE_MODULE_IDENTIFIER = {
35156
35306
  UPDATED_MESSAGE: 'NoticeUpdatedMessage',
35157
35307
  DELETED_MESSAGE: 'NoticeDeletedMessage',
35158
35308
  UPDATED_DIALOG: 'NoticeUpdatedDialog',
35159
- DELETED_DIALOG: 'NoticeDeletedDialog'
35309
+ DELETED_DIALOG: 'NoticeDeletedDialog',
35310
+ CREATED_DIALOG: 'NoticeCreatedDialog'
35160
35311
  };
35161
35312
 
35162
35313
  module.exports = {
@@ -40569,8 +40720,8 @@ module.exports = StreamManagement;
40569
40720
  */
40570
40721
 
40571
40722
  var config = {
40572
- version: '2.24.0',
40573
- buildNumber: '1180',
40723
+ version: '2.24.1-alpha.1',
40724
+ buildNumber: '1181',
40574
40725
  creds: {
40575
40726
  'appId': 0,
40576
40727
  'authKey': '',