|
|
@@ -1,6 +1,7 @@
|
|
1
|
1
|
/* global $, __filename */
|
|
2
|
2
|
|
|
3
|
3
|
import { getLogger } from 'jitsi-meet-logger';
|
|
|
4
|
+import isEqual from 'lodash.isequal';
|
|
4
|
5
|
import { $iq, $msg, $pres, Strophe } from 'strophe.js';
|
|
5
|
6
|
|
|
6
|
7
|
import * as JitsiTranscriptionStatus from '../../JitsiTranscriptionStatus';
|
|
|
@@ -181,13 +182,10 @@ export default class ChatRoom extends Listenable {
|
|
181
|
182
|
/**
|
|
182
|
183
|
* Joins the chat room.
|
|
183
|
184
|
* @param {string} password - Password to unlock room on joining.
|
|
184
|
|
- * @param {Object} customJoinPresenceExtensions - Key values object to be used
|
|
185
|
|
- * for the initial presence, they key will be an xmpp node and its text is the value,
|
|
186
|
|
- * and those will be added to the initial <x xmlns='http://jabber.org/protocol/muc'/>
|
|
187
|
185
|
* @returns {Promise} - resolved when join completes. At the time of this
|
|
188
|
186
|
* writing it's never rejected.
|
|
189
|
187
|
*/
|
|
190
|
|
- join(password, customJoinPresenceExtensions) {
|
|
|
188
|
+ join(password) {
|
|
191
|
189
|
this.password = password;
|
|
192
|
190
|
|
|
193
|
191
|
return new Promise(resolve => {
|
|
|
@@ -200,7 +198,7 @@ export default class ChatRoom extends Listenable {
|
|
200
|
198
|
: this.moderator.allocateConferenceFocus();
|
|
201
|
199
|
|
|
202
|
200
|
preJoin.then(() => {
|
|
203
|
|
- this.sendPresence(true, customJoinPresenceExtensions);
|
|
|
201
|
+ this.sendPresence(true);
|
|
204
|
202
|
this._removeConnListeners.push(
|
|
205
|
203
|
this.connection.addEventListener(
|
|
206
|
204
|
XmppConnection.Events.CONN_STATUS_CHANGED,
|
|
|
@@ -214,9 +212,8 @@ export default class ChatRoom extends Listenable {
|
|
214
|
212
|
/**
|
|
215
|
213
|
*
|
|
216
|
214
|
* @param fromJoin - Whether this is initial presence to join the room.
|
|
217
|
|
- * @param customJoinPresenceExtensions - Object of key values to be added to the initial presence only.
|
|
218
|
215
|
*/
|
|
219
|
|
- sendPresence(fromJoin, customJoinPresenceExtensions) {
|
|
|
216
|
+ sendPresence(fromJoin) {
|
|
220
|
217
|
const to = this.presMap.to;
|
|
221
|
218
|
|
|
222
|
219
|
if (!this.connection || !this.connection.connected || !to || (!this.joined && !fromJoin)) {
|
|
|
@@ -237,11 +234,6 @@ export default class ChatRoom extends Listenable {
|
|
237
|
234
|
if (this.password) {
|
|
238
|
235
|
pres.c('password').t(this.password).up();
|
|
239
|
236
|
}
|
|
240
|
|
- if (customJoinPresenceExtensions) {
|
|
241
|
|
- Object.keys(customJoinPresenceExtensions).forEach(key => {
|
|
242
|
|
- pres.c(key).t(customJoinPresenceExtensions[key]).up();
|
|
243
|
|
- });
|
|
244
|
|
- }
|
|
245
|
237
|
pres.up();
|
|
246
|
238
|
}
|
|
247
|
239
|
|
|
|
@@ -524,6 +516,10 @@ export default class ChatRoom extends Listenable {
|
|
524
|
516
|
case 'identity':
|
|
525
|
517
|
member.identity = extractIdentityInformation(node);
|
|
526
|
518
|
break;
|
|
|
519
|
+ case 'features': {
|
|
|
520
|
+ member.features = this._extractFeatures(node);
|
|
|
521
|
+ break;
|
|
|
522
|
+ }
|
|
527
|
523
|
case 'stat': {
|
|
528
|
524
|
const { attributes } = node;
|
|
529
|
525
|
|
|
|
@@ -584,7 +580,7 @@ export default class ChatRoom extends Listenable {
|
|
584
|
580
|
hasStatusUpdate = member.status !== undefined;
|
|
585
|
581
|
hasVersionUpdate = member.version !== undefined;
|
|
586
|
582
|
if (member.isFocus) {
|
|
587
|
|
- this._initFocus(from, jid);
|
|
|
583
|
+ this._initFocus(from, member.features);
|
|
588
|
584
|
} else {
|
|
589
|
585
|
// identity is being added to member joined, so external
|
|
590
|
586
|
// services can be notified for that (currently identity is
|
|
|
@@ -599,7 +595,8 @@ export default class ChatRoom extends Listenable {
|
|
599
|
595
|
member.status,
|
|
600
|
596
|
member.identity,
|
|
601
|
597
|
member.botType,
|
|
602
|
|
- member.jid);
|
|
|
598
|
+ member.jid,
|
|
|
599
|
+ member.features);
|
|
603
|
600
|
|
|
604
|
601
|
// we are reporting the status with the join
|
|
605
|
602
|
// so we do not want a second event about status update
|
|
|
@@ -646,7 +643,7 @@ export default class ChatRoom extends Listenable {
|
|
646
|
643
|
// so this case should not happen, if public jid is turned off we will receive the jid
|
|
647
|
644
|
// when we become moderator in the room
|
|
648
|
645
|
memberOfThis.isFocus = true;
|
|
649
|
|
- this._initFocus(from, jid);
|
|
|
646
|
+ this._initFocus(from, member.features);
|
|
650
|
647
|
}
|
|
651
|
648
|
|
|
652
|
649
|
// store the new display name
|
|
|
@@ -664,6 +661,11 @@ export default class ChatRoom extends Listenable {
|
|
664
|
661
|
hasVersionUpdate = true;
|
|
665
|
662
|
memberOfThis.version = member.version;
|
|
666
|
663
|
}
|
|
|
664
|
+
|
|
|
665
|
+ if (!isEqual(memberOfThis.features, member.features)) {
|
|
|
666
|
+ memberOfThis.features = member.features;
|
|
|
667
|
+ this.eventEmitter.emit(XMPPEvents.PARTICIPANT_FEATURES_CHANGED, from, member.features);
|
|
|
668
|
+ }
|
|
667
|
669
|
}
|
|
668
|
670
|
|
|
669
|
671
|
// after we had fired member or room joined events, lets fire events
|
|
|
@@ -705,6 +707,9 @@ export default class ChatRoom extends Listenable {
|
|
705
|
707
|
|
|
706
|
708
|
this.eventEmitter.emit(
|
|
707
|
709
|
XMPPEvents.CONFERENCE_PROPERTIES_CHANGED, properties);
|
|
|
710
|
+
|
|
|
711
|
+ this.restartByTerminateSupported = properties['support-terminate-restart'] === 'true';
|
|
|
712
|
+ logger.info(`Jicofo supports restart by terminate: ${this.supportsRestartByTerminate()}`);
|
|
708
|
713
|
}
|
|
709
|
714
|
break;
|
|
710
|
715
|
case 'transcription-status': {
|
|
|
@@ -757,25 +762,33 @@ export default class ChatRoom extends Listenable {
|
|
757
|
762
|
}
|
|
758
|
763
|
|
|
759
|
764
|
/**
|
|
760
|
|
- * Initialize some properties when the focus participant is verified.
|
|
761
|
|
- * @param from jid of the focus
|
|
762
|
|
- * @param mucJid the jid of the focus in the muc
|
|
|
765
|
+ * Extracts the features from the presence.
|
|
|
766
|
+ * @param node the node to process.
|
|
|
767
|
+ * @return features the Set of features where extracted data is added.
|
|
|
768
|
+ * @private
|
|
763
|
769
|
*/
|
|
764
|
|
- _initFocus(from, mucJid) {
|
|
765
|
|
- // skip if we have queried jicofo already, it will not change
|
|
766
|
|
- if (this.focusFeatures) {
|
|
767
|
|
- return;
|
|
|
770
|
+ _extractFeatures(node) {
|
|
|
771
|
+ const features = new Set();
|
|
|
772
|
+
|
|
|
773
|
+ for (let j = 0; j < node.children.length; j++) {
|
|
|
774
|
+ const { attributes } = node.children[j];
|
|
|
775
|
+
|
|
|
776
|
+ if (attributes && attributes.var) {
|
|
|
777
|
+ features.add(attributes.var);
|
|
|
778
|
+ }
|
|
768
|
779
|
}
|
|
769
|
780
|
|
|
770
|
|
- this.focusMucJid = from;
|
|
|
781
|
+ return features;
|
|
|
782
|
+ }
|
|
771
|
783
|
|
|
772
|
|
- logger.info(`Ignore focus: ${from}, real JID: ${mucJid}`);
|
|
773
|
|
- this.xmpp.caps.getFeatures(mucJid, 15000).then(features => {
|
|
774
|
|
- this.focusFeatures = features;
|
|
775
|
|
- logger.info(`Jicofo supports restart by terminate: ${this.supportsRestartByTerminate()}`);
|
|
776
|
|
- }, error => {
|
|
777
|
|
- logger.error('Failed to discover Jicofo features', error && error.message);
|
|
778
|
|
- });
|
|
|
784
|
+ /**
|
|
|
785
|
+ * Initialize some properties when the focus participant is verified.
|
|
|
786
|
+ * @param from jid of the focus
|
|
|
787
|
+ * @param features the features reported in jicofo presence
|
|
|
788
|
+ */
|
|
|
789
|
+ _initFocus(from, features) {
|
|
|
790
|
+ this.focusMucJid = from;
|
|
|
791
|
+ this.focusFeatures = features;
|
|
779
|
792
|
}
|
|
780
|
793
|
|
|
781
|
794
|
/**
|
|
|
@@ -791,9 +804,7 @@ export default class ChatRoom extends Listenable {
|
|
791
|
804
|
* @returns {boolean}
|
|
792
|
805
|
*/
|
|
793
|
806
|
supportsRestartByTerminate() {
|
|
794
|
|
- return this.focusFeatures
|
|
795
|
|
- ? this.focusFeatures.has('https://jitsi.org/meet/jicofo/terminate-restart')
|
|
796
|
|
- : false;
|
|
|
807
|
+ return this.restartByTerminateSupported;
|
|
797
|
808
|
}
|
|
798
|
809
|
|
|
799
|
810
|
/**
|