Skip to content

Commit

Permalink
fix(conference): require user interaction to unmute in iframe (jitsi#…
Browse files Browse the repository at this point in the history
…4429)

* fix(conference): require user interaction to unmute in iframe

* squash: explicitly whitelist react native
  • Loading branch information
virtuacoplenny authored and damencho committed Jul 10, 2019
1 parent a53a86e commit e7f9e8e
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 2 deletions.
20 changes: 19 additions & 1 deletion conference.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ import {
createLocalTracksF,
destroyLocalTracks,
isLocalTrackMuted,
isUserInteractionRequiredForUnmute,
replaceLocalTrack,
trackAdded,
trackRemoved
Expand Down Expand Up @@ -663,8 +664,11 @@ export default {
options.roomName, {
startAudioOnly: config.startAudioOnly,
startScreenSharing: config.startScreenSharing,
startWithAudioMuted: config.startWithAudioMuted || config.startSilent,
startWithAudioMuted: config.startWithAudioMuted
|| config.startSilent
|| isUserInteractionRequiredForUnmute(APP.store.getState()),
startWithVideoMuted: config.startWithVideoMuted
|| isUserInteractionRequiredForUnmute(APP.store.getState())
}))
.then(([ tracks, con ]) => {
tracks.forEach(track => {
Expand Down Expand Up @@ -765,6 +769,13 @@ export default {
* dialogs in case of media permissions error.
*/
muteAudio(mute, showUI = true) {
if (!mute
&& isUserInteractionRequiredForUnmute(APP.store.getState())) {
logger.error('Unmuting audio requires user interaction');

return;
}

// Not ready to modify track's state yet
if (!this._localTracksInitialized) {
// This will only modify base/media.audio.muted which is then synced
Expand Down Expand Up @@ -828,6 +839,13 @@ export default {
* dialogs in case of media permissions error.
*/
muteVideo(mute, showUI = true) {
if (!mute
&& isUserInteractionRequiredForUnmute(APP.store.getState())) {
logger.error('Unmuting video requires user interaction');

return;
}

// If not ready to modify track's state yet adjust the base/media
if (!this._localTracksInitialized) {
// This will only modify base/media.video.muted which is then synced
Expand Down
1 change: 1 addition & 0 deletions react/features/app/components/App.web.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { AtlasKitThemeProvider } from '@atlaskit/theme';
import React from 'react';

import { DialogContainer } from '../../base/dialog';
import '../../base/user-interaction';
import '../../base/responsive-ui';
import '../../chat';
import '../../external-api';
Expand Down
19 changes: 19 additions & 0 deletions react/features/base/tracks/functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,25 @@ export function isRemoteTrackMuted(tracks, mediaType, participantId) {
return !track || track.muted;
}

/**
* Returns whether or not the current environment needs a user interaction with
* the page before any unmute can occur.
*
* @param {Object} state - The redux state.
* @returns {boolean}
*/
export function isUserInteractionRequiredForUnmute(state) {
const { browser } = JitsiMeetJS.util;

return !browser.isReactNative()
&& !browser.isChrome()
&& !browser.isChromiumBased()
&& !browser.isElectron()
&& window
&& window.self !== window.top
&& !state['features/base/user-interaction'].interacted;
}

/**
* Mutes or unmutes a specific {@code JitsiLocalTrack}. If the muted state of
* the specified {@code track} is already in accord with the specified
Expand Down
17 changes: 16 additions & 1 deletion react/features/base/tracks/middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,12 @@ import {
TRACK_REMOVED,
TRACK_UPDATED
} from './actionTypes';
import { getLocalTrack, getTrackByJitsiTrack, setTrackMuted } from './functions';
import {
getLocalTrack,
getTrackByJitsiTrack,
isUserInteractionRequiredForUnmute,
setTrackMuted
} from './functions';

declare var APP: Object;

Expand All @@ -50,6 +55,11 @@ MiddlewareRegistry.register(store => next => action => {
break;
}
case SET_AUDIO_MUTED:
if (!action.muted
&& isUserInteractionRequiredForUnmute(store.getState())) {
return;
}

_setMuted(store, action, MEDIA_TYPE.AUDIO);
break;

Expand All @@ -74,6 +84,11 @@ MiddlewareRegistry.register(store => next => action => {
}

case SET_VIDEO_MUTED:
if (!action.muted
&& isUserInteractionRequiredForUnmute(store.getState())) {
return;
}

_setMuted(store, action, MEDIA_TYPE.VIDEO);
break;

Expand Down
20 changes: 20 additions & 0 deletions react/features/base/user-interaction/actionTypes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* The type of (redux) action which signals that an event listener has been
* added to listen for any user interaction with the page.
*
* {
* type: SET_USER_INTERACTION_LISTENER,
* userInteractionListener: Function
* }
*/
export const SET_USER_INTERACTION_LISTENER = 'SET_USER_INTERACTION_LISTENER';

/**
* The type of (redux) action which signals the user has interacted with the
* page.
*
* {
* type: USER_INTERACTION_RECEIVED,
* }
*/
export const USER_INTERACTION_RECEIVED = 'USER_INTERACTION_RECEIVED';
2 changes: 2 additions & 0 deletions react/features/base/user-interaction/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import './middleware';
import './reducer';
79 changes: 79 additions & 0 deletions react/features/base/user-interaction/middleware.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// @flow

import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../app';
import { MiddlewareRegistry } from '../redux';

import {
SET_USER_INTERACTION_LISTENER,
USER_INTERACTION_RECEIVED
} from './actionTypes';

/**
* Implements the entry point of the middleware of the feature base/user-interaction.
*
* @param {Store} store - The redux store.
* @returns {Function}
*/
MiddlewareRegistry.register(store => next => action => {
switch (action.type) {
case APP_WILL_MOUNT:
_startListeningForUserInteraction(store);
break;

case APP_WILL_UNMOUNT:
case USER_INTERACTION_RECEIVED:
_stopListeningForUserInteraction(store);
break;
}

return next(action);
});

/**
* Registers listeners to notify redux of any user interaction with the page.
*
* @param {Object} store - The redux store.
* @private
* @returns {void}
*/
function _startListeningForUserInteraction(store) {
const userInteractionListener = event => {
if (event.isTrusted) {
store.dispatch({
type: USER_INTERACTION_RECEIVED
});

_stopListeningForUserInteraction(store);
}
};

window.addEventListener('mousedown', userInteractionListener);
window.addEventListener('keydown', userInteractionListener);

store.dispatch({
type: SET_USER_INTERACTION_LISTENER,
userInteractionListener
});
}

/**
* Un-registers listeners intended to notify when the user has interacted with
* the page.
*
* @param {Object} store - The redux store.
* @private
* @returns {void}
*/
function _stopListeningForUserInteraction({ getState, dispatch }) {
const { userInteractionListener } = getState()['features/base/app'];

if (userInteractionListener) {
window.removeEventListener('mousedown', userInteractionListener);
window.removeEventListener('keydown', userInteractionListener);

dispatch({
type: SET_USER_INTERACTION_LISTENER,
userInteractionListener: undefined
});
}
}
34 changes: 34 additions & 0 deletions react/features/base/user-interaction/reducer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// @flow

import { ReducerRegistry } from '../redux';

import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../app';
import {
SET_USER_INTERACTION_LISTENER,
USER_INTERACTION_RECEIVED
} from './actionTypes';

ReducerRegistry.register('features/base/user-interaction', (state = {}, action) => {
switch (action.type) {
case APP_WILL_MOUNT:
case APP_WILL_UNMOUNT: {
return {
...state,
interacted: false
};
}
case SET_USER_INTERACTION_LISTENER:
return {
...state,
userInteractionListener: action.userInteractionListener
};

case USER_INTERACTION_RECEIVED:
return {
...state,
interacted: true
};
}

return state;
});

0 comments on commit e7f9e8e

Please sign in to comment.