fix: leaking listeners while waiting on auth dialog (#11288)

* fix: Drop duplicate call of wait for owner.

* fix: Fixes leaking listeners while waiting for host to join.

While waiting for the host to join on the dialog we attempt to join over and over again till we are admitted to enter the meeting. While doing that authRequired flag is on, and we were adding listeners on and on.

* feat: Introduces conference join in progress action.

This event is coming from lib-jitsi-meet and is fired when we receive the first presence of series when joining. It is always fired before joined event.

* fix: Moves testing middleware to use CONFERENCE_JOIN_IN_PROGRESS.

* fix: Moves follow-me middleware to use CONFERENCE_JOIN_IN_PROGRESS.

* fix: Moves some polls logic to middleware and use CONFERENCE_JOIN_IN_PROGRESS.

* fix: Moves reactions middleware to use CONFERENCE_JOIN_IN_PROGRESS.

* fix: Moves recordings middleware to use CONFERENCE_JOIN_IN_PROGRESS.

* fix: Moves shared-video middleware to use CONFERENCE_JOIN_IN_PROGRESS.

* fix: Moves videosipgw middleware to use CONFERENCE_JOIN_IN_PROGRESS.

* squash: Fix comments.

* fix: Fixes join in progress on web.

* fix: Moves variable extraction inside handlers.

* fix: Moves variable extraction inside handlers again.

* fix: Moves etherpad middleware to use CONFERENCE_JOIN_IN_PROGRESS.
This commit is contained in:
Дамян Минков 2022-04-05 21:13:39 -05:00 committed by GitHub
parent 33db511d93
commit a99532b0d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 311 additions and 250 deletions

View File

@ -37,6 +37,7 @@ import {
commonUserLeftHandling,
conferenceFailed,
conferenceJoined,
conferenceJoinInProgress,
conferenceLeft,
conferenceSubjectChanged,
conferenceTimestampChanged,
@ -142,8 +143,7 @@ import {
initPrejoin,
isPrejoinPageVisible,
makePrecallTest,
setJoiningInProgress,
setPrejoinPageVisibility
setJoiningInProgress
} from './react/features/prejoin';
import { disableReceiver, stopReceiver } from './react/features/remote-control';
import { setScreenAudioShareState, isScreenAudioShared } from './react/features/screen-share/';
@ -2068,9 +2068,9 @@ export default {
room.on(JitsiConferenceEvents.CONFERENCE_JOINED, () => {
this._onConferenceJoined();
});
room.on(JitsiConferenceEvents.CONFERENCE_JOIN_IN_PROGRESS, () => {
APP.store.dispatch(setPrejoinPageVisibility(false));
});
room.on(
JitsiConferenceEvents.CONFERENCE_JOIN_IN_PROGRESS,
() => APP.store.dispatch(conferenceJoinInProgress(room)));
room.on(
JitsiConferenceEvents.CONFERENCE_LEFT,

View File

@ -6,7 +6,10 @@ import { openConnection } from '../../../connection';
import {
openAuthDialog,
openLoginDialog } from '../../../react/features/authentication/actions.web';
import { WaitForOwnerDialog } from '../../../react/features/authentication/components';
import {
LoginDialog,
WaitForOwnerDialog
} from '../../../react/features/authentication/components';
import {
isTokenAuthEnabled,
getTokenAuthUrl
@ -16,7 +19,7 @@ import { isDialogOpen } from '../../../react/features/base/dialog';
import { setJWT } from '../../../react/features/base/jwt';
import UIUtil from '../util/UIUtil';
import LoginDialog from './LoginDialog';
import ExternalLoginDialog from './LoginDialog';
let externalAuthWindow;
@ -51,7 +54,7 @@ function doExternalAuth(room, lockPassword) {
getUrl = room.getExternalAuthUrl(true);
}
getUrl.then(url => {
externalAuthWindow = LoginDialog.showExternalAuthDialog(
externalAuthWindow = ExternalLoginDialog.showExternalAuthDialog(
url,
() => {
externalAuthWindow = null;
@ -187,7 +190,7 @@ function authenticate(room: Object, lockPassword: string) {
* @param {string} [lockPassword] password to use if the conference is locked
*/
function requireAuth(room: Object, lockPassword: string) {
if (!isDialogOpen(APP.store, WaitForOwnerDialog)) {
if (isDialogOpen(APP.store, WaitForOwnerDialog) || isDialogOpen(APP.store, LoginDialog)) {
return;
}

View File

@ -37,7 +37,6 @@ import '../lobby/middleware';
import '../notifications/middleware';
import '../overlay/middleware';
import '../polls/middleware';
import '../polls/subscriber';
import '../reactions/middleware';
import '../recent-list/middleware';
import '../recording/middleware';

View File

@ -22,8 +22,7 @@ import {
import {
hideLoginDialog,
openWaitForOwnerDialog,
stopWaitForOwner,
waitForOwner
stopWaitForOwner
} from './actions.web';
import { LoginDialog, WaitForOwnerDialog } from './components';
@ -72,7 +71,11 @@ MiddlewareRegistry.register(store => next => action => {
recoverable = error.recoverable;
}
if (recoverable) {
store.dispatch(waitForOwner());
// we haven't migrated all the code from AuthHandler, and we need for now conference.js to trigger
// the dialog to pass all required parameters to WaitForOwnerDialog
// keep it commented, so we do not trigger sending iqs to jicofo twice
// and showing the broken dialog with no handler
// store.dispatch(waitForOwner());
} else {
store.dispatch(stopWaitForOwner());
}

View File

@ -32,6 +32,17 @@ export const CONFERENCE_FAILED = 'CONFERENCE_FAILED';
*/
export const CONFERENCE_JOINED = 'CONFERENCE_JOINED';
/**
* The type of (redux) action which signals that a specific conference joining is in progress.
* A CONFERENCE_JOINED is guaranteed to follow.
*
* {
* type: CONFERENCE_JOIN_IN_PROGRESS,
* conference: JitsiConference
* }
*/
export const CONFERENCE_JOIN_IN_PROGRESS = 'CONFERENCE_JOIN_IN_PROGRESS';
/**
* The type of (redux) action which signals that a specific conference was left.
*

View File

@ -39,6 +39,7 @@ import { getBackendSafeRoomName } from '../util';
import {
AUTH_STATUS_CHANGED,
CONFERENCE_FAILED,
CONFERENCE_JOIN_IN_PROGRESS,
CONFERENCE_JOINED,
CONFERENCE_LEFT,
CONFERENCE_LOCAL_SUBJECT_CHANGED,
@ -105,6 +106,9 @@ function _addConferenceListeners(conference, dispatch, state) {
conference.on(
JitsiConferenceEvents.CONFERENCE_JOINED,
(...args) => dispatch(conferenceJoined(conference, ...args)));
conference.on(
JitsiConferenceEvents.CONFERENCE_JOIN_IN_PROGRESS,
(...args) => dispatch(conferenceJoinInProgress(conference, ...args)));
conference.on(
JitsiConferenceEvents.CONFERENCE_LEFT,
(...args) => {
@ -350,6 +354,23 @@ export function conferenceJoined(conference: Object) {
};
}
/**
* Signals that a specific conference join is in progress.
*
* @param {JitsiConference} conference - The JitsiConference instance for which join by the local participant
* is in progress.
* @returns {{
* type: CONFERENCE_JOIN_IN_PROGRESS,
* conference: JitsiConference
* }}
*/
export function conferenceJoinInProgress(conference: Object) {
return {
type: CONFERENCE_JOIN_IN_PROGRESS,
conference
};
}
/**
* Signals that a specific conference has been left.
*

View File

@ -2,7 +2,10 @@
import { AUDIO_ONLY_SCREEN_SHARE_NO_TRACK } from '../../../../modules/UI/UIErrors';
import { showNotification, NOTIFICATION_TIMEOUT_TYPE } from '../../notifications';
import { setSkipPrejoinOnReload } from '../../prejoin';
import {
setPrejoinPageVisibility,
setSkipPrejoinOnReload
} from '../../prejoin';
import { setScreenAudioShareState, setScreenshareAudioTrack } from '../../screen-share';
import { AudioMixerEffect } from '../../stream-effects/audio-mixer/AudioMixerEffect';
import { setAudioOnly } from '../audio-only';
@ -19,7 +22,7 @@ import {
TOGGLE_SCREENSHARING
} from '../tracks';
import { CONFERENCE_FAILED, CONFERENCE_JOINED } from './actionTypes';
import { CONFERENCE_FAILED, CONFERENCE_JOIN_IN_PROGRESS, CONFERENCE_JOINED } from './actionTypes';
import { getCurrentConference } from './functions';
import './middleware.any';
@ -28,6 +31,11 @@ MiddlewareRegistry.register(store => next => action => {
const { enableForcedReload } = getState()['features/base/config'];
switch (action.type) {
case CONFERENCE_JOIN_IN_PROGRESS: {
dispatch(setPrejoinPageVisibility(false));
break;
}
case CONFERENCE_JOINED: {
if (enableForcedReload) {
dispatch(setSkipPrejoinOnReload(false));

View File

@ -1,6 +1,6 @@
// @flow
import { CONFERENCE_WILL_JOIN } from '../conference';
import { CONFERENCE_JOIN_IN_PROGRESS } from '../conference/actionTypes';
import { SET_CONFIG } from '../config';
import { JitsiConferenceEvents } from '../lib-jitsi-meet';
import { MiddlewareRegistry } from '../redux';
@ -24,7 +24,7 @@ import logger from './logger';
*/
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case CONFERENCE_WILL_JOIN:
case CONFERENCE_JOIN_IN_PROGRESS:
_bindConferenceConnectionListener(action.conference, store);
break;
case SET_CONFIG: {

View File

@ -2,6 +2,7 @@
import UIEvents from '../../../service/UI/UIEvents';
import { getCurrentConference } from '../base/conference';
import { CONFERENCE_JOIN_IN_PROGRESS } from '../base/conference/actionTypes';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import { TOGGLE_DOCUMENT_EDITING } from './actionTypes';
@ -21,6 +22,23 @@ const ETHERPAD_COMMAND = 'etherpad';
// eslint-disable-next-line no-unused-vars
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
switch (action.type) {
case CONFERENCE_JOIN_IN_PROGRESS: {
const { conference } = action;
conference.addCommandListener(ETHERPAD_COMMAND,
({ value }) => {
let url;
const { etherpad_base: etherpadBase } = getState()['features/base/config'];
if (etherpadBase) {
url = new URL(value, etherpadBase).toString();
}
dispatch(setDocumentUrl(url));
}
);
break;
}
case TOGGLE_DOCUMENT_EDITING: {
if (typeof APP !== 'undefined') {
APP.UI.emitEvent(UIEvents.ETHERPAD_CLICKED);
@ -39,24 +57,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
*/
StateListenerRegistry.register(
state => getCurrentConference(state),
(conference, { dispatch, getState }, previousConference) => {
if (conference) {
conference.addCommandListener(ETHERPAD_COMMAND,
({ value }) => {
let url;
const { etherpad_base: etherpadBase } = getState()['features/base/config'];
if (etherpadBase) {
const u = new URL(value, etherpadBase);
url = u.toString();
}
dispatch(setDocumentUrl(url));
}
);
}
(conference, { dispatch }, previousConference) => {
if (previousConference) {
dispatch(setDocumentUrl(undefined));
}

View File

@ -2,7 +2,7 @@
import _ from 'lodash';
import { CONFERENCE_WILL_JOIN } from '../base/conference/actionTypes';
import { CONFERENCE_JOIN_IN_PROGRESS } from '../base/conference/actionTypes';
import {
getParticipantById,
getPinnedParticipant,
@ -61,7 +61,7 @@ let nextOnStageTimer = 0;
*/
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case CONFERENCE_WILL_JOIN: {
case CONFERENCE_JOIN_IN_PROGRESS: {
const { conference } = action;
conference.addCommandListener(

View File

@ -1,16 +1,95 @@
// @flow
import { MiddlewareRegistry } from '../base/redux';
import { getCurrentConference } from '../base/conference';
import { CONFERENCE_JOIN_IN_PROGRESS } from '../base/conference/actionTypes';
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import { playSound } from '../base/sounds';
import { INCOMING_MSG_SOUND_ID } from '../chat/constants';
import {
NOTIFICATION_TIMEOUT_TYPE,
NOTIFICATION_TYPE,
showNotification
} from '../notifications';
import { RECEIVE_POLL } from './actionTypes';
import { clearPolls, receiveAnswer, receivePoll } from './actions';
import {
COMMAND_ANSWER_POLL,
COMMAND_NEW_POLL,
COMMAND_OLD_POLLS
} from './constants';
import type { Answer, Poll } from './types';
/**
* Set up state change listener to perform maintenance tasks when the conference
* is left or failed, e.g. Clear messages or close the chat modal if it's left
* open.
*/
StateListenerRegistry.register(
state => getCurrentConference(state),
(conference, { dispatch }, previousConference) => {
if (conference !== previousConference) {
// conference changed, left or failed...
// clean old polls
dispatch(clearPolls());
}
});
const parsePollData = (pollData): Poll | null => {
if (typeof pollData !== 'object' || pollData === null) {
return null;
}
const { id, senderId, senderName, question, answers } = pollData;
if (typeof id !== 'string' || typeof senderId !== 'string' || typeof senderName !== 'string'
|| typeof question !== 'string' || !(answers instanceof Array)) {
return null;
}
const answersParsed = [];
for (const answer of answers) {
const voters = new Map();
for (const [ voterId, voter ] of Object.entries(answer.voters)) {
if (typeof voter !== 'string') {
return null;
}
voters.set(voterId, voter);
}
answersParsed.push({
name: answer.name,
voters
});
}
return {
changingVote: false,
senderId,
senderName,
question,
showResults: true,
lastVote: null,
answers: answersParsed
};
};
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const result = next(action);
switch (action.type) {
case CONFERENCE_JOIN_IN_PROGRESS: {
const { conference } = action;
conference.on(JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
(_, data) => _handleReceivePollsMessage(data, dispatch));
conference.on(JitsiConferenceEvents.NON_PARTICIPANT_MESSAGE_RECEIVED,
(_, data) => _handleReceivePollsMessage(data, dispatch));
break;
}
// Middleware triggered when a poll is received
case RECEIVE_POLL: {
@ -30,3 +109,73 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
return result;
});
/**
* Handles receiving of polls message command.
*
* @param {Object} data - The json data carried by the polls message.
* @param {Function} dispatch - The dispatch function.
*
* @returns {void}
*/
function _handleReceivePollsMessage(data, dispatch) {
switch (data.type) {
case COMMAND_NEW_POLL: {
const { question, answers, pollId, senderId, senderName } = data;
const poll = {
changingVote: false,
senderId,
senderName,
showResults: false,
lastVote: null,
question,
answers: answers.map(answer => {
return {
name: answer,
voters: new Map()
};
})
};
dispatch(receivePoll(pollId, poll, true));
dispatch(showNotification({
appearance: NOTIFICATION_TYPE.NORMAL,
titleKey: 'polls.notification.title',
descriptionKey: 'polls.notification.description'
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
break;
}
case COMMAND_ANSWER_POLL: {
const { pollId, answers, voterId, voterName } = data;
const receivedAnswer: Answer = {
voterId,
voterName,
pollId,
answers
};
dispatch(receiveAnswer(pollId, receivedAnswer));
break;
}
case COMMAND_OLD_POLLS: {
const { polls } = data;
for (const pollData of polls) {
const poll = parsePollData(pollData);
if (poll === null) {
console.warn('[features/polls] Invalid old poll data');
} else {
dispatch(receivePoll(pollData.id, poll, false));
}
}
break;
}
}
}

View File

@ -1,130 +0,0 @@
// @flow
import { getCurrentConference } from '../base/conference';
import { JitsiConferenceEvents } from '../base/lib-jitsi-meet';
import { StateListenerRegistry } from '../base/redux';
import {
NOTIFICATION_TIMEOUT_TYPE,
NOTIFICATION_TYPE,
showNotification
} from '../notifications';
import { clearPolls, receiveAnswer, receivePoll } from './actions';
import { COMMAND_NEW_POLL, COMMAND_ANSWER_POLL, COMMAND_OLD_POLLS } from './constants';
import type { Answer, Poll } from './types';
const parsePollData = (pollData): Poll | null => {
if (typeof pollData !== 'object' || pollData === null) {
return null;
}
const { id, senderId, senderName, question, answers } = pollData;
if (typeof id !== 'string' || typeof senderId !== 'string' || typeof senderName !== 'string'
|| typeof question !== 'string' || !(answers instanceof Array)) {
return null;
}
const answersParsed = [];
for (const answer of answers) {
const voters = new Map();
for (const [ voterId, voter ] of Object.entries(answer.voters)) {
if (typeof voter !== 'string') {
return null;
}
voters.set(voterId, voter);
}
answersParsed.push({
name: answer.name,
voters
});
}
return {
changingVote: false,
senderId,
senderName,
question,
showResults: true,
lastVote: null,
answers: answersParsed
};
};
StateListenerRegistry.register(
state => getCurrentConference(state),
(conference, store, previousConference) => {
if (conference && conference !== previousConference) {
const receiveMessage = (_, data) => {
switch (data.type) {
case COMMAND_NEW_POLL: {
const { question, answers, pollId, senderId, senderName } = data;
const poll = {
changingVote: false,
senderId,
senderName,
showResults: false,
lastVote: null,
question,
answers: answers.map(answer => {
return {
name: answer,
voters: new Map()
};
})
};
store.dispatch(receivePoll(pollId, poll, true));
store.dispatch(showNotification({
appearance: NOTIFICATION_TYPE.NORMAL,
titleKey: 'polls.notification.title',
descriptionKey: 'polls.notification.description'
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
break;
}
case COMMAND_ANSWER_POLL: {
const { pollId, answers, voterId, voterName } = data;
const receivedAnswer: Answer = {
voterId,
voterName,
pollId,
answers
};
store.dispatch(receiveAnswer(pollId, receivedAnswer));
break;
}
case COMMAND_OLD_POLLS: {
const { polls } = data;
for (const pollData of polls) {
const poll = parsePollData(pollData);
if (poll === null) {
console.warn('[features/polls] Invalid old poll data');
} else {
store.dispatch(receivePoll(pollData.id, poll, false));
}
}
break;
}
}
};
conference.on(JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED, receiveMessage);
conference.on(JitsiConferenceEvents.NON_PARTICIPANT_MESSAGE_RECEIVED, receiveMessage);
// clean old polls
store.dispatch(clearPolls());
}
}
);

View File

@ -5,7 +5,7 @@ import { batch } from 'react-redux';
import { createReactionSoundsDisabledEvent, sendAnalytics } from '../analytics';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
import {
CONFERENCE_WILL_JOIN,
CONFERENCE_JOIN_IN_PROGRESS,
SET_START_REACTIONS_MUTED,
setStartReactionsMuted
} from '../base/conference';
@ -104,7 +104,7 @@ MiddlewareRegistry.register(store => next => action => {
break;
}
case CONFERENCE_WILL_JOIN: {
case CONFERENCE_JOIN_IN_PROGRESS: {
const { conference } = action;
conference.addCommandListener(

View File

@ -6,7 +6,7 @@ import {
sendAnalytics
} from '../analytics';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
import { CONFERENCE_WILL_JOIN, getCurrentConference } from '../base/conference';
import { CONFERENCE_JOIN_IN_PROGRESS, getCurrentConference } from '../base/conference';
import JitsiMeetJS, {
JitsiConferenceEvents,
JitsiRecordingConstants
@ -106,21 +106,15 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
break;
case CONFERENCE_WILL_JOIN: {
case CONFERENCE_JOIN_IN_PROGRESS: {
const { conference } = action;
conference.on(
JitsiConferenceEvents.RECORDER_STATE_CHANGED,
recorderSession => {
if (recorderSession) {
recorderSession.getID()
&& dispatch(
updateRecordingSessionData(recorderSession));
recorderSession.getError()
&& _showRecordingErrorNotification(
recorderSession, dispatch);
recorderSession.getID() && dispatch(updateRecordingSessionData(recorderSession));
recorderSession.getError() && _showRecordingErrorNotification(recorderSession, dispatch);
}
return;

View File

@ -2,7 +2,8 @@
import { batch } from 'react-redux';
import { CONFERENCE_LEFT, getCurrentConference } from '../base/conference';
import { CONFERENCE_JOIN_IN_PROGRESS, CONFERENCE_LEFT } from '../base/conference/actionTypes';
import { getCurrentConference } from '../base/conference/functions';
import {
PARTICIPANT_LEFT,
getLocalParticipant,
@ -10,7 +11,7 @@ import {
participantLeft,
pinParticipant
} from '../base/participants';
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
import { MiddlewareRegistry } from '../base/redux';
import { SET_SHARED_VIDEO_STATUS, RESET_SHARED_VIDEO_STATUS } from './actionTypes';
import {
@ -30,16 +31,37 @@ import { isSharingStatus } from './functions';
MiddlewareRegistry.register(store => next => action => {
const { dispatch, getState } = store;
const state = getState();
const conference = getCurrentConference(state);
const localParticipantId = getLocalParticipant(state)?.id;
const { videoUrl, status, ownerId, time, muted, volume } = action;
const { ownerId: stateOwnerId, videoUrl: statevideoUrl } = state['features/shared-video'];
switch (action.type) {
case CONFERENCE_JOIN_IN_PROGRESS: {
const { conference } = action;
const localParticipantId = getLocalParticipant(state)?.id;
conference.addCommandListener(SHARED_VIDEO,
({ value, attributes }) => {
const { from } = attributes;
const sharedVideoStatus = attributes.state;
if (isSharingStatus(sharedVideoStatus)) {
handleSharingVideoStatus(store, value, attributes, conference);
} else if (sharedVideoStatus === 'stop') {
dispatch(participantLeft(value, conference));
if (localParticipantId !== from) {
dispatch(resetSharedVideoStatus());
}
}
}
);
break;
}
case CONFERENCE_LEFT:
dispatch(resetSharedVideoStatus());
break;
case PARTICIPANT_LEFT:
case PARTICIPANT_LEFT: {
const conference = getCurrentConference(state);
const { ownerId: stateOwnerId, videoUrl: statevideoUrl } = state['features/shared-video'];
if (action.participant.id === stateOwnerId) {
batch(() => {
dispatch(resetSharedVideoStatus());
@ -47,7 +69,12 @@ MiddlewareRegistry.register(store => next => action => {
});
}
break;
case SET_SHARED_VIDEO_STATUS:
}
case SET_SHARED_VIDEO_STATUS: {
const conference = getCurrentConference(state);
const localParticipantId = getLocalParticipant(state)?.id;
const { videoUrl, status, ownerId, time, muted, volume } = action;
if (localParticipantId === ownerId) {
sendShareVideoCommand({
conference,
@ -60,8 +87,14 @@ MiddlewareRegistry.register(store => next => action => {
});
}
break;
case RESET_SHARED_VIDEO_STATUS:
}
case RESET_SHARED_VIDEO_STATUS: {
const localParticipantId = getLocalParticipant(state)?.id;
const { ownerId: stateOwnerId, videoUrl: statevideoUrl } = state['features/shared-video'];
if (localParticipantId === stateOwnerId) {
const conference = getCurrentConference(state);
sendShareVideoCommand({
conference,
id: statevideoUrl,
@ -74,41 +107,11 @@ MiddlewareRegistry.register(store => next => action => {
}
break;
}
}
return next(action);
});
/**
* Set up state change listener to perform maintenance tasks when the conference
* is left or failed, e.g. Clear messages or close the chat modal if it's left
* open.
*/
StateListenerRegistry.register(
state => getCurrentConference(state),
(conference, store, previousConference) => {
if (conference && conference !== previousConference) {
conference.addCommandListener(SHARED_VIDEO,
({ value, attributes }) => {
const { dispatch, getState } = store;
const { from } = attributes;
const localParticipantId = getLocalParticipant(getState()).id;
const status = attributes.state;
if (isSharingStatus(status)) {
handleSharingVideoStatus(store, value, attributes, conference);
} else if (status === 'stop') {
dispatch(participantLeft(value, conference));
if (localParticipantId !== from) {
dispatch(resetSharedVideoStatus());
}
}
}
);
}
}
);
/**
* Handles the playing, pause and start statuses for the shared video.
* Dispatches participantJoined event and, if necessary, pins it.

View File

@ -1,38 +1,37 @@
// @flow
import { getCurrentConference } from '../base/conference';
import { CONFERENCE_JOIN_IN_PROGRESS } from '../base/conference/actionTypes';
import { getLocalParticipant } from '../base/participants';
import { StateListenerRegistry } from '../base/redux';
import { MiddlewareRegistry } from '../base/redux';
import { setDisableButton } from './actions.web';
import { SHARED_VIDEO } from './constants';
import './middleware.any';
/**
* Set up state change listener to disable or enable the share video button in
* the toolbar menu.
*/
StateListenerRegistry.register(
state => getCurrentConference(state),
(conference, store, previousConference) => {
if (conference && conference !== previousConference) {
conference.addCommandListener(SHARED_VIDEO,
({ attributes }) => {
const { dispatch, getState } = store;
const { from } = attributes;
const localParticipantId = getLocalParticipant(getState()).id;
const status = attributes.state;
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const state = getState();
const localParticipantId = getLocalParticipant(state)?.id;
if (status === 'playing') {
if (localParticipantId !== from) {
dispatch(setDisableButton(true));
}
} else if (status === 'stop') {
dispatch(setDisableButton(false));
}
switch (action.type) {
case CONFERENCE_JOIN_IN_PROGRESS: {
const { conference } = action;
conference.addCommandListener(SHARED_VIDEO, ({ attributes }) => {
const { from } = attributes;
const status = attributes.state;
if (status === 'playing') {
if (localParticipantId !== from) {
dispatch(setDisableButton(true));
}
);
}
} else if (status === 'stop') {
dispatch(setDisableButton(false));
}
});
break;
}
);
}
return next(action);
});

View File

@ -1,6 +1,6 @@
// @flow
import { CONFERENCE_WILL_JOIN } from '../base/conference';
import { CONFERENCE_JOIN_IN_PROGRESS } from '../base/conference/actionTypes';
import {
JitsiConferenceEvents,
JitsiSIPVideoGWStatus
@ -29,12 +29,12 @@ import logger from './logger';
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
MiddlewareRegistry.register(({ dispatch }) => next => action => {
const result = next(action);
switch (action.type) {
case CONFERENCE_WILL_JOIN: {
const conference = getState()['features/base/conference'].joining;
case CONFERENCE_JOIN_IN_PROGRESS: {
const { conference } = action;
conference.on(
JitsiConferenceEvents.VIDEO_SIP_GW_AVAILABILITY_CHANGED,