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

- update lib-jitsi-meet to version with support for replacing participant
This commit is contained in:
hmuresan 2021-06-11 11:58:45 +03:00 committed by Horatiu Muresan
parent 60188794b5
commit 342dd4ceca
15 changed files with 83 additions and 28 deletions

View File

@ -304,10 +304,14 @@ class ConferenceConnector {
// not enough rights to create conference
case JitsiConferenceErrors.AUTHENTICATION_REQUIRED: {
const { replaceParticipant }
= APP.store.getState()['features/base/config'];
// Schedule reconnect to check if someone else created the room.
this.reconnectTimeout = setTimeout(() => {
APP.store.dispatch(conferenceWillJoin(room));
room.join();
room.join(null, replaceParticipant);
}, 5000);
const { password }
@ -393,8 +397,10 @@ class ConferenceConnector {
*
*/
connect() {
const { replaceParticipant } = APP.store.getState()['features/base/config'];
// the local storage overrides here and in connection.js can be used by jibri
room.join(jitsiLocalStorage.getItem('xmpp_conference_password_override'));
room.join(jitsiLocalStorage.getItem('xmpp_conference_password_override'), replaceParticipant);
}
}
@ -2167,7 +2173,20 @@ export default {
JitsiConferenceEvents.LOCK_STATE_CHANGED,
(...args) => APP.store.dispatch(lockStateChanged(room, ...args)));
room.on(JitsiConferenceEvents.KICKED, participant => {
room.on(JitsiConferenceEvents.KICKED, (participant, reason, isReplaced) => {
if (isReplaced) {
// this event triggers when the local participant is kicked, `participant`
// is the kicker. In replace participant case, kicker is undefined,
// as the server initiated it. We mark in store the local participant
// as being replaced based on jwt.
const localParticipant = getLocalParticipant(APP.store.getState());
APP.store.dispatch(participantUpdated({
conference: room,
id: localParticipant.id,
isReplaced
}));
}
APP.store.dispatch(kickedOut(room, participant));
});

View File

@ -209,7 +209,10 @@ function logout(room: Object) {
}).then(url => {
// de-authenticate conference on the fly
if (room.isJoined()) {
room.join();
const { replaceParticipant }
= APP.store.getState()['features/base/config'];
room.join(null, replaceParticipant);
}
return url;

4
package-lock.json generated
View File

@ -11071,8 +11071,8 @@
}
},
"lib-jitsi-meet": {
"version": "github:jitsi/lib-jitsi-meet#2259d4418574841a782e98df27c44370bb84df45",
"from": "github:jitsi/lib-jitsi-meet#2259d4418574841a782e98df27c44370bb84df45",
"version": "github:jitsi/lib-jitsi-meet#fad985e95a9e8a4fb8a1b8b1ad2cfef75370c866",
"from": "github:jitsi/lib-jitsi-meet#fad985e95a9e8a4fb8a1b8b1ad2cfef75370c866",
"requires": {
"@jitsi/js-utils": "1.0.2",
"@jitsi/sdp-interop": "github:jitsi/sdp-interop#5fc4af6dcf8a6e6af9fedbcd654412fd47b1b4ae",

View File

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

View File

@ -460,7 +460,7 @@ export function createConference() {
sendLocalParticipant(state, conference);
conference.join(password);
conference.join(password, config.replaceParticipant);
};
}
@ -477,8 +477,11 @@ export function checkIfCanJoin() {
const { authRequired, password }
= getState()['features/base/conference'];
const { replaceParticipant }
= getState()['features/base/config'];
authRequired && dispatch(_conferenceWillJoin(authRequired));
authRequired && authRequired.join(password);
authRequired && authRequired.join(password, replaceParticipant);
};
}

View File

@ -87,6 +87,8 @@ export function commonUserJoinedHandling(
if (user.isHidden()) {
dispatch(hiddenParticipantJoined(id, displayName));
} else {
const isReplacing = user.isReplacing && user.isReplacing();
dispatch(participantJoined({
botType: user.getBotType(),
connectionStatus: user.getConnectionStatus(),
@ -94,7 +96,8 @@ export function commonUserJoinedHandling(
id,
name: displayName,
presence: user.getStatus(),
role: user.getRole()
role: user.getRole(),
isReplacing
}));
}
}
@ -119,7 +122,9 @@ export function commonUserLeftHandling(
if (user.isHidden()) {
dispatch(hiddenParticipantLeft(id));
} else {
dispatch(participantLeft(id, conference));
const isReplaced = user.isReplaced && user.isReplaced();
dispatch(participantLeft(id, conference, isReplaced));
}
}

View File

@ -152,6 +152,7 @@ export default [
'requireDisplayName',
'remoteVideoMenu',
'roomPasswordNumberOfDigits',
'replaceParticipant',
'resolution',
'startAudioMuted',
'startAudioOnly',

View File

@ -366,6 +366,7 @@ export function hiddenParticipantLeft(id) {
* with the participant identified by the specified {@code id}. Only the local
* participant is allowed to not specify an associated {@code JitsiConference}
* instance.
* @param {boolean} isReplaced - Whether the participant is to be replaced in the meeting.
* @returns {{
* type: PARTICIPANT_LEFT,
* participant: {
@ -374,12 +375,13 @@ export function hiddenParticipantLeft(id) {
* }
* }}
*/
export function participantLeft(id, conference) {
export function participantLeft(id, conference, isReplaced) {
return {
type: PARTICIPANT_LEFT,
participant: {
conference,
id
id,
isReplaced
}
};
}
@ -490,9 +492,13 @@ export function participantKicked(kicker, kicked) {
dispatch({
type: PARTICIPANT_KICKED,
kicked: kicked.getId(),
kicker: kicker.getId()
kicker: kicker?.getId()
});
if (kicked.isReplaced && kicked.isReplaced()) {
return;
}
dispatch(showNotification({
titleArguments: {
kicked:

View File

@ -161,7 +161,7 @@ StateListenerRegistry.register(
for (const p of getState()['features/base/participants']) {
!p.local
&& (!conference || p.conference !== conference)
&& dispatch(participantLeft(p.id, p.conference));
&& dispatch(participantLeft(p.id, p.conference, p.isReplaced));
}
});
@ -356,14 +356,16 @@ function _maybePlaySounds({ getState, dispatch }, action) {
if (!action.participant.local
&& (!startAudioMuted
|| getParticipantCount(state) < startAudioMuted)) {
const { isReplacing, isReplaced } = action.participant;
if (action.type === PARTICIPANT_JOINED) {
const { presence } = action.participant;
// The sounds for the poltergeist are handled by features/invite.
if (presence !== INVITED && presence !== CALLING) {
if (presence !== INVITED && presence !== CALLING && !isReplacing) {
dispatch(playSound(PARTICIPANT_JOINED_SOUND_ID));
}
} else if (action.type === PARTICIPANT_LEFT) {
} else if (action.type === PARTICIPANT_LEFT && !isReplaced) {
dispatch(playSound(PARTICIPANT_LEFT_SOUND_ID));
}
}

View File

@ -187,6 +187,7 @@ function _participantJoined({ participant }) {
dominantSpeaker,
email,
isFakeParticipant,
isReplacing,
isJigasi,
loadableAvatarUrl,
local,
@ -218,6 +219,7 @@ function _participantJoined({ participant }) {
email,
id,
isFakeParticipant,
isReplacing,
isJigasi,
loadableAvatarUrl,
local: local || false,

View File

@ -18,6 +18,12 @@ import { getParticipantDisplayName } from '../base/participants';
*/
export function notifyKickedOut(participant: Object, submit: ?Function) {
return (dispatch: Dispatch<any>, getState: Function) => {
if (!participant || (participant.isReplaced && participant.isReplaced())) {
submit && submit();
return;
}
dispatch(openDialog(AlertDialog, {
contentKey: {
key: 'dialog.kickTitle',

View File

@ -18,6 +18,10 @@ import {
*/
export function notifyKickedOut(participant: Object, _: ?Function) { // eslint-disable-line no-unused-vars
return (dispatch: Dispatch<any>, getState: Function) => {
if (!participant || (participant.isReplaced && participant.isReplaced())) {
return;
}
const args = {
participantDisplayName:
getParticipantDisplayName(getState, participant.getId())

View File

@ -113,7 +113,7 @@ MiddlewareRegistry.register(store => next => action => {
id: getLocalParticipant(store.getState()).id,
local: true
},
{ id: action.participant.getId() }
{ id: action.participant ? action.participant.getId() : undefined }
);
break;

View File

@ -71,6 +71,7 @@ export function mapStateToProps(state: Object): $Shape<Props> {
return {
_participants: knockingParticipants,
_visible: lobbyEnabled && isLocalParticipantModerator(state) && Boolean(knockingParticipants.length)
_visible: lobbyEnabled && isLocalParticipantModerator(state)
&& Boolean(knockingParticipants && knockingParticipants.length)
};
}

View File

@ -34,7 +34,7 @@ MiddlewareRegistry.register(store => next => action => {
const { participant: p } = action;
const { dispatch, getState } = store;
if (!p.local && !joinLeaveNotificationsDisabled()) {
if (!p.local && !joinLeaveNotificationsDisabled() && !p.isReplacing) {
dispatch(showParticipantJoinedNotification(
getParticipantDisplayName(getState, p.id)
));
@ -45,13 +45,15 @@ MiddlewareRegistry.register(store => next => action => {
// Do not show the notification for mobile and also when the focus indicator is disabled.
const displayName = getParticipantDisplayName(getState, p.id);
dispatch(showNotification({
descriptionArguments: { to: displayName || '$t(notify.somebody)' },
descriptionKey: 'notify.grantedTo',
titleKey: 'notify.somebody',
title: displayName
},
NOTIFICATION_TIMEOUT));
if (!p.isReplacing) {
dispatch(showNotification({
descriptionArguments: { to: displayName || '$t(notify.somebody)' },
descriptionKey: 'notify.grantedTo',
titleKey: 'notify.somebody',
title: displayName
},
NOTIFICATION_TIMEOUT));
}
}
return result;
@ -65,7 +67,8 @@ MiddlewareRegistry.register(store => next => action => {
if (typeof interfaceConfig === 'object'
&& participant
&& !participant.local) {
&& !participant.local
&& !action.participant.isReplaced) {
store.dispatch(showNotification({
descriptionKey: 'notify.disconnected',
titleKey: 'notify.somebody',