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 // not enough rights to create conference
case JitsiConferenceErrors.AUTHENTICATION_REQUIRED: { case JitsiConferenceErrors.AUTHENTICATION_REQUIRED: {
const { replaceParticipant }
= APP.store.getState()['features/base/config'];
// Schedule reconnect to check if someone else created the room. // Schedule reconnect to check if someone else created the room.
this.reconnectTimeout = setTimeout(() => { this.reconnectTimeout = setTimeout(() => {
APP.store.dispatch(conferenceWillJoin(room)); APP.store.dispatch(conferenceWillJoin(room));
room.join(); room.join(null, replaceParticipant);
}, 5000); }, 5000);
const { password } const { password }
@ -393,8 +397,10 @@ class ConferenceConnector {
* *
*/ */
connect() { connect() {
const { replaceParticipant } = APP.store.getState()['features/base/config'];
// the local storage overrides here and in connection.js can be used by jibri // 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, JitsiConferenceEvents.LOCK_STATE_CHANGED,
(...args) => APP.store.dispatch(lockStateChanged(room, ...args))); (...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)); APP.store.dispatch(kickedOut(room, participant));
}); });

View File

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

4
package-lock.json generated
View File

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

View File

@ -55,7 +55,7 @@
"jquery-i18next": "1.2.1", "jquery-i18next": "1.2.1",
"js-md5": "0.6.1", "js-md5": "0.6.1",
"jwt-decode": "2.2.0", "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", "libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.21", "lodash": "4.17.21",
"moment": "2.29.1", "moment": "2.29.1",

View File

@ -460,7 +460,7 @@ export function createConference() {
sendLocalParticipant(state, conference); sendLocalParticipant(state, conference);
conference.join(password); conference.join(password, config.replaceParticipant);
}; };
} }
@ -477,8 +477,11 @@ export function checkIfCanJoin() {
const { authRequired, password } const { authRequired, password }
= getState()['features/base/conference']; = getState()['features/base/conference'];
const { replaceParticipant }
= getState()['features/base/config'];
authRequired && dispatch(_conferenceWillJoin(authRequired)); 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()) { if (user.isHidden()) {
dispatch(hiddenParticipantJoined(id, displayName)); dispatch(hiddenParticipantJoined(id, displayName));
} else { } else {
const isReplacing = user.isReplacing && user.isReplacing();
dispatch(participantJoined({ dispatch(participantJoined({
botType: user.getBotType(), botType: user.getBotType(),
connectionStatus: user.getConnectionStatus(), connectionStatus: user.getConnectionStatus(),
@ -94,7 +96,8 @@ export function commonUserJoinedHandling(
id, id,
name: displayName, name: displayName,
presence: user.getStatus(), presence: user.getStatus(),
role: user.getRole() role: user.getRole(),
isReplacing
})); }));
} }
} }
@ -119,7 +122,9 @@ export function commonUserLeftHandling(
if (user.isHidden()) { if (user.isHidden()) {
dispatch(hiddenParticipantLeft(id)); dispatch(hiddenParticipantLeft(id));
} else { } 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', 'requireDisplayName',
'remoteVideoMenu', 'remoteVideoMenu',
'roomPasswordNumberOfDigits', 'roomPasswordNumberOfDigits',
'replaceParticipant',
'resolution', 'resolution',
'startAudioMuted', 'startAudioMuted',
'startAudioOnly', 'startAudioOnly',

View File

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

View File

@ -161,7 +161,7 @@ StateListenerRegistry.register(
for (const p of getState()['features/base/participants']) { for (const p of getState()['features/base/participants']) {
!p.local !p.local
&& (!conference || p.conference !== conference) && (!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 if (!action.participant.local
&& (!startAudioMuted && (!startAudioMuted
|| getParticipantCount(state) < startAudioMuted)) { || getParticipantCount(state) < startAudioMuted)) {
const { isReplacing, isReplaced } = action.participant;
if (action.type === PARTICIPANT_JOINED) { if (action.type === PARTICIPANT_JOINED) {
const { presence } = action.participant; const { presence } = action.participant;
// The sounds for the poltergeist are handled by features/invite. // 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)); 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)); dispatch(playSound(PARTICIPANT_LEFT_SOUND_ID));
} }
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -71,6 +71,7 @@ export function mapStateToProps(state: Object): $Shape<Props> {
return { return {
_participants: knockingParticipants, _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 { participant: p } = action;
const { dispatch, getState } = store; const { dispatch, getState } = store;
if (!p.local && !joinLeaveNotificationsDisabled()) { if (!p.local && !joinLeaveNotificationsDisabled() && !p.isReplacing) {
dispatch(showParticipantJoinedNotification( dispatch(showParticipantJoinedNotification(
getParticipantDisplayName(getState, p.id) getParticipantDisplayName(getState, p.id)
)); ));
@ -45,6 +45,7 @@ MiddlewareRegistry.register(store => next => action => {
// Do not show the notification for mobile and also when the focus indicator is disabled. // Do not show the notification for mobile and also when the focus indicator is disabled.
const displayName = getParticipantDisplayName(getState, p.id); const displayName = getParticipantDisplayName(getState, p.id);
if (!p.isReplacing) {
dispatch(showNotification({ dispatch(showNotification({
descriptionArguments: { to: displayName || '$t(notify.somebody)' }, descriptionArguments: { to: displayName || '$t(notify.somebody)' },
descriptionKey: 'notify.grantedTo', descriptionKey: 'notify.grantedTo',
@ -53,6 +54,7 @@ MiddlewareRegistry.register(store => next => action => {
}, },
NOTIFICATION_TIMEOUT)); NOTIFICATION_TIMEOUT));
} }
}
return result; return result;
} }
@ -65,7 +67,8 @@ MiddlewareRegistry.register(store => next => action => {
if (typeof interfaceConfig === 'object' if (typeof interfaceConfig === 'object'
&& participant && participant
&& !participant.local) { && !participant.local
&& !action.participant.isReplaced) {
store.dispatch(showNotification({ store.dispatch(showNotification({
descriptionKey: 'notify.disconnected', descriptionKey: 'notify.disconnected',
titleKey: 'notify.somebody', titleKey: 'notify.somebody',