Browse Source

feat(replace-participant): Replace participant with same jwt in the conf

- update lib-jitsi-meet to version with support for replacing participant
master
hmuresan 4 years ago
parent
commit
342dd4ceca

+ 22
- 3
conference.js View File

304
 
304
 
305
         // not enough rights to create conference
305
         // not enough rights to create conference
306
         case JitsiConferenceErrors.AUTHENTICATION_REQUIRED: {
306
         case JitsiConferenceErrors.AUTHENTICATION_REQUIRED: {
307
+            const { replaceParticipant }
308
+                = APP.store.getState()['features/base/config'];
309
+
310
+
307
             // Schedule reconnect to check if someone else created the room.
311
             // Schedule reconnect to check if someone else created the room.
308
             this.reconnectTimeout = setTimeout(() => {
312
             this.reconnectTimeout = setTimeout(() => {
309
                 APP.store.dispatch(conferenceWillJoin(room));
313
                 APP.store.dispatch(conferenceWillJoin(room));
310
-                room.join();
314
+                room.join(null, replaceParticipant);
311
             }, 5000);
315
             }, 5000);
312
 
316
 
313
             const { password }
317
             const { password }
393
      *
397
      *
394
      */
398
      */
395
     connect() {
399
     connect() {
400
+        const { replaceParticipant } = APP.store.getState()['features/base/config'];
401
+
396
         // the local storage overrides here and in connection.js can be used by jibri
402
         // the local storage overrides here and in connection.js can be used by jibri
397
-        room.join(jitsiLocalStorage.getItem('xmpp_conference_password_override'));
403
+        room.join(jitsiLocalStorage.getItem('xmpp_conference_password_override'), replaceParticipant);
398
     }
404
     }
399
 }
405
 }
400
 
406
 
2167
             JitsiConferenceEvents.LOCK_STATE_CHANGED,
2173
             JitsiConferenceEvents.LOCK_STATE_CHANGED,
2168
             (...args) => APP.store.dispatch(lockStateChanged(room, ...args)));
2174
             (...args) => APP.store.dispatch(lockStateChanged(room, ...args)));
2169
 
2175
 
2170
-        room.on(JitsiConferenceEvents.KICKED, participant => {
2176
+        room.on(JitsiConferenceEvents.KICKED, (participant, reason, isReplaced) => {
2177
+            if (isReplaced) {
2178
+                // this event triggers when the local participant is kicked, `participant`
2179
+                // is the kicker. In replace participant case, kicker is undefined,
2180
+                // as the server initiated it. We mark in store the local participant
2181
+                // as being replaced based on jwt.
2182
+                const localParticipant = getLocalParticipant(APP.store.getState());
2183
+
2184
+                APP.store.dispatch(participantUpdated({
2185
+                    conference: room,
2186
+                    id: localParticipant.id,
2187
+                    isReplaced
2188
+                }));
2189
+            }
2171
             APP.store.dispatch(kickedOut(room, participant));
2190
             APP.store.dispatch(kickedOut(room, participant));
2172
         });
2191
         });
2173
 
2192
 

+ 4
- 1
modules/UI/authentication/AuthHandler.js View File

209
     }).then(url => {
209
     }).then(url => {
210
         // de-authenticate conference on the fly
210
         // de-authenticate conference on the fly
211
         if (room.isJoined()) {
211
         if (room.isJoined()) {
212
-            room.join();
212
+            const { replaceParticipant }
213
+                = APP.store.getState()['features/base/config'];
214
+
215
+            room.join(null, replaceParticipant);
213
         }
216
         }
214
 
217
 
215
         return url;
218
         return url;

+ 2
- 2
package-lock.json View File

11071
       }
11071
       }
11072
     },
11072
     },
11073
     "lib-jitsi-meet": {
11073
     "lib-jitsi-meet": {
11074
-      "version": "github:jitsi/lib-jitsi-meet#2259d4418574841a782e98df27c44370bb84df45",
11075
-      "from": "github:jitsi/lib-jitsi-meet#2259d4418574841a782e98df27c44370bb84df45",
11074
+      "version": "github:jitsi/lib-jitsi-meet#fad985e95a9e8a4fb8a1b8b1ad2cfef75370c866",
11075
+      "from": "github:jitsi/lib-jitsi-meet#fad985e95a9e8a4fb8a1b8b1ad2cfef75370c866",
11076
       "requires": {
11076
       "requires": {
11077
         "@jitsi/js-utils": "1.0.2",
11077
         "@jitsi/js-utils": "1.0.2",
11078
         "@jitsi/sdp-interop": "github:jitsi/sdp-interop#5fc4af6dcf8a6e6af9fedbcd654412fd47b1b4ae",
11078
         "@jitsi/sdp-interop": "github:jitsi/sdp-interop#5fc4af6dcf8a6e6af9fedbcd654412fd47b1b4ae",

+ 1
- 1
package.json View File

55
     "jquery-i18next": "1.2.1",
55
     "jquery-i18next": "1.2.1",
56
     "js-md5": "0.6.1",
56
     "js-md5": "0.6.1",
57
     "jwt-decode": "2.2.0",
57
     "jwt-decode": "2.2.0",
58
-    "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#2259d4418574841a782e98df27c44370bb84df45",
58
+    "lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#fad985e95a9e8a4fb8a1b8b1ad2cfef75370c866",
59
     "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
59
     "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
60
     "lodash": "4.17.21",
60
     "lodash": "4.17.21",
61
     "moment": "2.29.1",
61
     "moment": "2.29.1",

+ 5
- 2
react/features/base/conference/actions.js View File

460
 
460
 
461
         sendLocalParticipant(state, conference);
461
         sendLocalParticipant(state, conference);
462
 
462
 
463
-        conference.join(password);
463
+        conference.join(password, config.replaceParticipant);
464
     };
464
     };
465
 }
465
 }
466
 
466
 
477
         const { authRequired, password }
477
         const { authRequired, password }
478
             = getState()['features/base/conference'];
478
             = getState()['features/base/conference'];
479
 
479
 
480
+        const { replaceParticipant }
481
+            = getState()['features/base/config'];
482
+
480
         authRequired && dispatch(_conferenceWillJoin(authRequired));
483
         authRequired && dispatch(_conferenceWillJoin(authRequired));
481
-        authRequired && authRequired.join(password);
484
+        authRequired && authRequired.join(password, replaceParticipant);
482
     };
485
     };
483
 }
486
 }
484
 
487
 

+ 7
- 2
react/features/base/conference/functions.js View File

87
     if (user.isHidden()) {
87
     if (user.isHidden()) {
88
         dispatch(hiddenParticipantJoined(id, displayName));
88
         dispatch(hiddenParticipantJoined(id, displayName));
89
     } else {
89
     } else {
90
+        const isReplacing = user.isReplacing && user.isReplacing();
91
+
90
         dispatch(participantJoined({
92
         dispatch(participantJoined({
91
             botType: user.getBotType(),
93
             botType: user.getBotType(),
92
             connectionStatus: user.getConnectionStatus(),
94
             connectionStatus: user.getConnectionStatus(),
94
             id,
96
             id,
95
             name: displayName,
97
             name: displayName,
96
             presence: user.getStatus(),
98
             presence: user.getStatus(),
97
-            role: user.getRole()
99
+            role: user.getRole(),
100
+            isReplacing
98
         }));
101
         }));
99
     }
102
     }
100
 }
103
 }
119
     if (user.isHidden()) {
122
     if (user.isHidden()) {
120
         dispatch(hiddenParticipantLeft(id));
123
         dispatch(hiddenParticipantLeft(id));
121
     } else {
124
     } else {
122
-        dispatch(participantLeft(id, conference));
125
+        const isReplaced = user.isReplaced && user.isReplaced();
126
+
127
+        dispatch(participantLeft(id, conference, isReplaced));
123
     }
128
     }
124
 }
129
 }
125
 
130
 

+ 1
- 0
react/features/base/config/configWhitelist.js View File

152
     'requireDisplayName',
152
     'requireDisplayName',
153
     'remoteVideoMenu',
153
     'remoteVideoMenu',
154
     'roomPasswordNumberOfDigits',
154
     'roomPasswordNumberOfDigits',
155
+    'replaceParticipant',
155
     'resolution',
156
     'resolution',
156
     'startAudioMuted',
157
     'startAudioMuted',
157
     'startAudioOnly',
158
     'startAudioOnly',

+ 9
- 3
react/features/base/participants/actions.js View File

366
  * with the participant identified by the specified {@code id}. Only the local
366
  * with the participant identified by the specified {@code id}. Only the local
367
  * participant is allowed to not specify an associated {@code JitsiConference}
367
  * participant is allowed to not specify an associated {@code JitsiConference}
368
  * instance.
368
  * instance.
369
+ * @param {boolean} isReplaced - Whether the participant is to be replaced in the meeting.
369
  * @returns {{
370
  * @returns {{
370
  *     type: PARTICIPANT_LEFT,
371
  *     type: PARTICIPANT_LEFT,
371
  *     participant: {
372
  *     participant: {
374
  *     }
375
  *     }
375
  * }}
376
  * }}
376
  */
377
  */
377
-export function participantLeft(id, conference) {
378
+export function participantLeft(id, conference, isReplaced) {
378
     return {
379
     return {
379
         type: PARTICIPANT_LEFT,
380
         type: PARTICIPANT_LEFT,
380
         participant: {
381
         participant: {
381
             conference,
382
             conference,
382
-            id
383
+            id,
384
+            isReplaced
383
         }
385
         }
384
     };
386
     };
385
 }
387
 }
490
         dispatch({
492
         dispatch({
491
             type: PARTICIPANT_KICKED,
493
             type: PARTICIPANT_KICKED,
492
             kicked: kicked.getId(),
494
             kicked: kicked.getId(),
493
-            kicker: kicker.getId()
495
+            kicker: kicker?.getId()
494
         });
496
         });
495
 
497
 
498
+        if (kicked.isReplaced && kicked.isReplaced()) {
499
+            return;
500
+        }
501
+
496
         dispatch(showNotification({
502
         dispatch(showNotification({
497
             titleArguments: {
503
             titleArguments: {
498
                 kicked:
504
                 kicked:

+ 5
- 3
react/features/base/participants/middleware.js View File

161
         for (const p of getState()['features/base/participants']) {
161
         for (const p of getState()['features/base/participants']) {
162
             !p.local
162
             !p.local
163
                 && (!conference || p.conference !== conference)
163
                 && (!conference || p.conference !== conference)
164
-                && dispatch(participantLeft(p.id, p.conference));
164
+                && dispatch(participantLeft(p.id, p.conference, p.isReplaced));
165
         }
165
         }
166
     });
166
     });
167
 
167
 
356
     if (!action.participant.local
356
     if (!action.participant.local
357
             && (!startAudioMuted
357
             && (!startAudioMuted
358
                 || getParticipantCount(state) < startAudioMuted)) {
358
                 || getParticipantCount(state) < startAudioMuted)) {
359
+        const { isReplacing, isReplaced } = action.participant;
360
+
359
         if (action.type === PARTICIPANT_JOINED) {
361
         if (action.type === PARTICIPANT_JOINED) {
360
             const { presence } = action.participant;
362
             const { presence } = action.participant;
361
 
363
 
362
             // The sounds for the poltergeist are handled by features/invite.
364
             // The sounds for the poltergeist are handled by features/invite.
363
-            if (presence !== INVITED && presence !== CALLING) {
365
+            if (presence !== INVITED && presence !== CALLING && !isReplacing) {
364
                 dispatch(playSound(PARTICIPANT_JOINED_SOUND_ID));
366
                 dispatch(playSound(PARTICIPANT_JOINED_SOUND_ID));
365
             }
367
             }
366
-        } else if (action.type === PARTICIPANT_LEFT) {
368
+        } else if (action.type === PARTICIPANT_LEFT && !isReplaced) {
367
             dispatch(playSound(PARTICIPANT_LEFT_SOUND_ID));
369
             dispatch(playSound(PARTICIPANT_LEFT_SOUND_ID));
368
         }
370
         }
369
     }
371
     }

+ 2
- 0
react/features/base/participants/reducer.js View File

187
         dominantSpeaker,
187
         dominantSpeaker,
188
         email,
188
         email,
189
         isFakeParticipant,
189
         isFakeParticipant,
190
+        isReplacing,
190
         isJigasi,
191
         isJigasi,
191
         loadableAvatarUrl,
192
         loadableAvatarUrl,
192
         local,
193
         local,
218
         email,
219
         email,
219
         id,
220
         id,
220
         isFakeParticipant,
221
         isFakeParticipant,
222
+        isReplacing,
221
         isJigasi,
223
         isJigasi,
222
         loadableAvatarUrl,
224
         loadableAvatarUrl,
223
         local: local || false,
225
         local: local || false,

+ 6
- 0
react/features/conference/actions.native.js View File

18
  */
18
  */
19
 export function notifyKickedOut(participant: Object, submit: ?Function) {
19
 export function notifyKickedOut(participant: Object, submit: ?Function) {
20
     return (dispatch: Dispatch<any>, getState: Function) => {
20
     return (dispatch: Dispatch<any>, getState: Function) => {
21
+        if (!participant || (participant.isReplaced && participant.isReplaced())) {
22
+            submit && submit();
23
+
24
+            return;
25
+        }
26
+
21
         dispatch(openDialog(AlertDialog, {
27
         dispatch(openDialog(AlertDialog, {
22
             contentKey: {
28
             contentKey: {
23
                 key: 'dialog.kickTitle',
29
                 key: 'dialog.kickTitle',

+ 4
- 0
react/features/conference/actions.web.js View File

18
  */
18
  */
19
 export function notifyKickedOut(participant: Object, _: ?Function) { // eslint-disable-line no-unused-vars
19
 export function notifyKickedOut(participant: Object, _: ?Function) { // eslint-disable-line no-unused-vars
20
     return (dispatch: Dispatch<any>, getState: Function) => {
20
     return (dispatch: Dispatch<any>, getState: Function) => {
21
+        if (!participant || (participant.isReplaced && participant.isReplaced())) {
22
+            return;
23
+        }
24
+
21
         const args = {
25
         const args = {
22
             participantDisplayName:
26
             participantDisplayName:
23
                 getParticipantDisplayName(getState, participant.getId())
27
                 getParticipantDisplayName(getState, participant.getId())

+ 1
- 1
react/features/external-api/middleware.js View File

113
                 id: getLocalParticipant(store.getState()).id,
113
                 id: getLocalParticipant(store.getState()).id,
114
                 local: true
114
                 local: true
115
             },
115
             },
116
-            { id: action.participant.getId() }
116
+            { id: action.participant ? action.participant.getId() : undefined }
117
         );
117
         );
118
         break;
118
         break;
119
 
119
 

+ 2
- 1
react/features/lobby/components/AbstractKnockingParticipantList.js View File

71
 
71
 
72
     return {
72
     return {
73
         _participants: knockingParticipants,
73
         _participants: knockingParticipants,
74
-        _visible: lobbyEnabled && isLocalParticipantModerator(state) && Boolean(knockingParticipants.length)
74
+        _visible: lobbyEnabled && isLocalParticipantModerator(state)
75
+          && Boolean(knockingParticipants && knockingParticipants.length)
75
     };
76
     };
76
 }
77
 }

+ 12
- 9
react/features/notifications/middleware.js View File

34
         const { participant: p } = action;
34
         const { participant: p } = action;
35
         const { dispatch, getState } = store;
35
         const { dispatch, getState } = store;
36
 
36
 
37
-        if (!p.local && !joinLeaveNotificationsDisabled()) {
37
+        if (!p.local && !joinLeaveNotificationsDisabled() && !p.isReplacing) {
38
             dispatch(showParticipantJoinedNotification(
38
             dispatch(showParticipantJoinedNotification(
39
                 getParticipantDisplayName(getState, p.id)
39
                 getParticipantDisplayName(getState, p.id)
40
             ));
40
             ));
45
             // Do not show the notification for mobile and also when the focus indicator is disabled.
45
             // Do not show the notification for mobile and also when the focus indicator is disabled.
46
             const displayName = getParticipantDisplayName(getState, p.id);
46
             const displayName = getParticipantDisplayName(getState, p.id);
47
 
47
 
48
-            dispatch(showNotification({
49
-                descriptionArguments: { to: displayName || '$t(notify.somebody)' },
50
-                descriptionKey: 'notify.grantedTo',
51
-                titleKey: 'notify.somebody',
52
-                title: displayName
53
-            },
54
-            NOTIFICATION_TIMEOUT));
48
+            if (!p.isReplacing) {
49
+                dispatch(showNotification({
50
+                    descriptionArguments: { to: displayName || '$t(notify.somebody)' },
51
+                    descriptionKey: 'notify.grantedTo',
52
+                    titleKey: 'notify.somebody',
53
+                    title: displayName
54
+                },
55
+                NOTIFICATION_TIMEOUT));
56
+            }
55
         }
57
         }
56
 
58
 
57
         return result;
59
         return result;
65
 
67
 
66
             if (typeof interfaceConfig === 'object'
68
             if (typeof interfaceConfig === 'object'
67
                 && participant
69
                 && participant
68
-                && !participant.local) {
70
+                && !participant.local
71
+                && !action.participant.isReplaced) {
69
                 store.dispatch(showNotification({
72
                 store.dispatch(showNotification({
70
                     descriptionKey: 'notify.disconnected',
73
                     descriptionKey: 'notify.disconnected',
71
                     titleKey: 'notify.somebody',
74
                     titleKey: 'notify.somebody',

Loading…
Cancel
Save