diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java index 0238390b3..8ba9c2e5a 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetView.java @@ -416,20 +416,20 @@ public class JitsiMeetView extends FrameLayout { } /** - * Handler for focus changes which the window where this view is attached to - * is experiencing. Here we call into the Immersive mode plugin, so it - * triggers an event. + * Called when the window containing this view gains or loses focus. * - * @param hasFocus - Whether the window / view has focus or not. + * @param hasFocus If the window of this view now has focus, {@code true}; + * otherwise, {@code false}. */ @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); - RNImmersiveModule module = RNImmersiveModule.getInstance(); + // https://github.com/mockingbot/react-native-immersive#restore-immersive-state + RNImmersiveModule immersive = RNImmersiveModule.getInstance(); - if (hasFocus && module != null) { - module.emitImmersiveStateChangeEvent(); + if (hasFocus && immersive != null) { + immersive.emitImmersiveStateChangeEvent(); } } diff --git a/react/features/base/jwt/reducer.js b/react/features/base/jwt/reducer.js index adefdd46e..11d8d5a93 100644 --- a/react/features/base/jwt/reducer.js +++ b/react/features/base/jwt/reducer.js @@ -5,7 +5,7 @@ import { equals, set, ReducerRegistry } from '../redux'; import { SET_CALLEE_INFO_VISIBLE, SET_JWT } from './actionTypes'; /** - * The initial redux state of the feature jwt. + * The default/initial redux state of the feature jwt. * * @private * @type {{ @@ -13,7 +13,7 @@ import { SET_CALLEE_INFO_VISIBLE, SET_JWT } from './actionTypes'; * isGuest: boolean * }} */ -const _INITIAL_STATE = { +const DEFAULT_STATE = { /** * The indicator which determines whether (the) {@code CalleeInfo} is * visible. @@ -42,7 +42,7 @@ const _INITIAL_STATE = { */ ReducerRegistry.register( 'features/base/jwt', - (state = _INITIAL_STATE, action) => { + (state = DEFAULT_STATE, action) => { switch (action.type) { case SET_CALLEE_INFO_VISIBLE: return set(state, 'calleeInfoVisible', action.calleeInfoVisible); @@ -51,7 +51,7 @@ ReducerRegistry.register( // eslint-disable-next-line no-unused-vars const { type, ...payload } = action; const nextState = { - ..._INITIAL_STATE, + ...DEFAULT_STATE, ...payload }; diff --git a/react/features/base/lib-jitsi-meet/reducer.js b/react/features/base/lib-jitsi-meet/reducer.js index f4768ac62..5825d620d 100644 --- a/react/features/base/lib-jitsi-meet/reducer.js +++ b/react/features/base/lib-jitsi-meet/reducer.js @@ -1,3 +1,5 @@ +// @flow + import { ReducerRegistry } from '../redux'; import { @@ -8,18 +10,18 @@ import { } from './actionTypes'; /** - * The initial state of the feature base/lib-jitsi-meet. + * The default/initial redux state of the feature base/lib-jitsi-meet. * * @type {Object} */ -const INITIAL_STATE = {}; +const DEFAULT_STATE = {}; ReducerRegistry.register( 'features/base/lib-jitsi-meet', - (state = INITIAL_STATE, action) => { + (state = DEFAULT_STATE, action) => { switch (action.type) { case LIB_DID_DISPOSE: - return INITIAL_STATE; + return DEFAULT_STATE; case LIB_DID_INIT: return { diff --git a/react/features/base/logging/reducer.js b/react/features/base/logging/reducer.js index e408661bd..4b97b7539 100644 --- a/react/features/base/logging/reducer.js +++ b/react/features/base/logging/reducer.js @@ -1,18 +1,20 @@ +// @flow + import { equals, ReducerRegistry } from '../redux'; import { SET_LOGGING_CONFIG } from './actionTypes'; /** - * The initial state of the feature base/logging. + * The default/initial redux state of the feature base/logging. * - * XXX When making any changes to the INITIAL_STATE make sure to also update + * XXX When making any changes to the DEFAULT_STATE make sure to also update * logging_config.js file located in the root directory of this project !!! * * @type {{ * config: Object * }} */ -const INITIAL_STATE = { +const DEFAULT_STATE = { config: { defaultLogLevel: 'trace', @@ -26,7 +28,7 @@ const INITIAL_STATE = { ReducerRegistry.register( 'features/base/logging', - (state = INITIAL_STATE, action) => { + (state = DEFAULT_STATE, action) => { switch (action.type) { case SET_LOGGING_CONFIG: return _setLoggingConfig(state, action); @@ -48,9 +50,9 @@ ReducerRegistry.register( */ function _setLoggingConfig(state, action) { const config = { - // The config of INITIAL_STATE is the default configuration of the + // The config of DEFAULT_STATE is the default configuration of the // feature base/logging. - ...INITIAL_STATE.config, + ...DEFAULT_STATE.config, ...action.config }; diff --git a/react/features/base/responsive-ui/reducer.js b/react/features/base/responsive-ui/reducer.js index d614df196..6c530fe43 100644 --- a/react/features/base/responsive-ui/reducer.js +++ b/react/features/base/responsive-ui/reducer.js @@ -1,19 +1,21 @@ +// @flow + import { ReducerRegistry, set } from '../redux'; import { SET_ASPECT_RATIO, SET_REDUCED_UI } from './actionTypes'; import { ASPECT_RATIO_NARROW } from './constants'; /** - * The initial redux state of the feature base/responsive-ui. + * The default/initial redux state of the feature base/responsive-ui. */ -const _INITIAL_STATE = { +const DEFAULT_STATE = { aspectRatio: ASPECT_RATIO_NARROW, reducedUI: false }; ReducerRegistry.register( 'features/base/responsive-ui', - (state = _INITIAL_STATE, action) => { + (state = DEFAULT_STATE, action) => { switch (action.type) { case SET_ASPECT_RATIO: return set(state, 'aspectRatio', action.aspectRatio); diff --git a/react/features/mobile/background/middleware.js b/react/features/mobile/background/middleware.js index 3af31adaa..17ec4aaae 100644 --- a/react/features/mobile/background/middleware.js +++ b/react/features/mobile/background/middleware.js @@ -1,16 +1,13 @@ -/* @flow */ +// @flow import { AppState } from 'react-native'; import type { Dispatch } from 'redux'; -import { - APP_WILL_MOUNT, - APP_WILL_UNMOUNT -} from '../../app'; +import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../app'; import { MiddlewareRegistry } from '../../base/redux'; import { - _setAppStateListener, + _setAppStateListener as _setAppStateListenerA, _setBackgroundVideoMuted, appStateChanged } from './actions'; @@ -25,39 +22,29 @@ import { * required to mute or unmute the local video in case the application goes to * the background or comes back from it. * - * @param {Store} store - Redux store. + * @param {Store} store - The redux store. * @returns {Function} * @see {@link https://facebook.github.io/react-native/docs/appstate.html} */ MiddlewareRegistry.register(store => next => action => { switch (action.type) { - case _SET_APP_STATE_LISTENER: { - // Remove the current/old AppState listener. - const { appStateListener } = store.getState()['features/background']; - - if (appStateListener) { - AppState.removeEventListener('change', appStateListener); - } - - // Add the new AppState listener. - if (action.listener) { - AppState.addEventListener('change', action.listener); - } - break; - } + case _SET_APP_STATE_LISTENER: + return _setAppStateListenerF(store, next, action); case APP_STATE_CHANGED: _appStateChanged(store.dispatch, action.appState); break; - case APP_WILL_MOUNT: - store.dispatch( - _setAppStateListener( - _onAppStateChange.bind(undefined, store.dispatch))); + case APP_WILL_MOUNT: { + const { dispatch } = store; + + dispatch( + _setAppStateListenerA(_onAppStateChange.bind(undefined, dispatch))); break; + } case APP_WILL_UNMOUNT: - store.dispatch(_setAppStateListener(null)); + store.dispatch(_setAppStateListenerA(undefined)); break; } @@ -65,11 +52,11 @@ MiddlewareRegistry.register(store => next => action => { }); /** - * Handles app state changes. Dispatches the necessary Redux actions for the + * Handles app state changes. Dispatches the necessary redux actions for the * local video to be muted when the app goes to the background, and to be * unmuted when the app comes back. * - * @param {Dispatch} dispatch - Redux dispatch function. + * @param {Dispatch} dispatch - The redux {@code dispatch} function. * @param {string} appState - The current app state. * @private * @returns {void} @@ -97,9 +84,9 @@ function _appStateChanged(dispatch: Function, appState: string) { /** * Called by React Native's AppState API to notify that the application state - * has changed. Dispatches the change within the (associated) Redux store. + * has changed. Dispatches the change within the (associated) redux store. * - * @param {Dispatch} dispatch - Redux dispatch function. + * @param {Dispatch} dispatch - The redux {@code dispatch} function. * @param {string} appState - The current application execution state. * @private * @returns {void} @@ -107,3 +94,31 @@ function _appStateChanged(dispatch: Function, appState: string) { function _onAppStateChange(dispatch: Dispatch<*>, appState: string) { dispatch(appStateChanged(appState)); } + +/** + * Notifies the feature filmstrip that the action + * {@link _SET_IMMERSIVE_LISTENER} is being dispatched within a specific redux + * store. + * + * @param {Store} store - The redux store in which the specified action is being + * dispatched. + * @param {Dispatch} next - The redux dispatch function to dispatch the + * specified action to the specified store. + * @param {Action} action - The redux action {@code _SET_IMMERSIVE_LISTENER} + * which is being dispatched in the specified store. + * @private + * @returns {Object} The value returned by {@code next(action)}. + */ +function _setAppStateListenerF({ getState }, next, action) { + // Remove the old AppState listener and add the new one. + const { appStateListener: oldListener } = getState()['features/background']; + const result = next(action); + const { appStateListener: newListener } = getState()['features/background']; + + if (oldListener !== newListener) { + oldListener && AppState.removeEventListener('change', oldListener); + newListener && AppState.addEventListener('change', newListener); + } + + return result; +} diff --git a/react/features/mobile/background/reducer.js b/react/features/mobile/background/reducer.js index 041abdba3..1b04112b4 100644 --- a/react/features/mobile/background/reducer.js +++ b/react/features/mobile/background/reducer.js @@ -1,3 +1,5 @@ +// @flow + import { ReducerRegistry } from '../../base/redux'; import { @@ -5,13 +7,16 @@ import { APP_STATE_CHANGED } from './actionTypes'; -const INITIAL_STATE = { +/** + * The default/initial redux state of the feature background. + */ +const DEFAULT_STATE = { appState: 'active' }; ReducerRegistry.register( 'features/background', - (state = INITIAL_STATE, action) => { + (state = DEFAULT_STATE, action) => { switch (action.type) { case _SET_APP_STATE_LISTENER: return { diff --git a/react/features/mobile/full-screen/actionTypes.js b/react/features/mobile/full-screen/actionTypes.js index e9aadbee4..cf273dd54 100644 --- a/react/features/mobile/full-screen/actionTypes.js +++ b/react/features/mobile/full-screen/actionTypes.js @@ -1,5 +1,6 @@ /** - * The type of redux action to set the Immersive change event listener. + * The type of (redux) action to set the react-native-immersive's change event + * listener. * * { * type: _SET_IMMERSIVE_LISTENER, diff --git a/react/features/mobile/full-screen/actions.js b/react/features/mobile/full-screen/actions.js index 52b7bd7b7..20450046e 100644 --- a/react/features/mobile/full-screen/actions.js +++ b/react/features/mobile/full-screen/actions.js @@ -3,13 +3,14 @@ import { _SET_IMMERSIVE_LISTENER } from './actionTypes'; /** - * Sets the listener to be used with React Native's Immersive API. + * Sets the change event listener to be used with react-native-immersive's API. * - * @param {Function} listener - Function to be set as the change event listener. + * @param {Function} [listener] - The function to be used with + * react-native-immersive's API as the change event listener. * @protected * @returns {{ * type: _SET_IMMERSIVE_LISTENER, - * listener: Function + * listener: ?Function * }} */ export function _setImmersiveListener(listener: ?Function) { diff --git a/react/features/mobile/full-screen/middleware.js b/react/features/mobile/full-screen/middleware.js index 3974a517c..3f2471646 100644 --- a/react/features/mobile/full-screen/middleware.js +++ b/react/features/mobile/full-screen/middleware.js @@ -14,7 +14,7 @@ import { import { Platform } from '../../base/react'; import { MiddlewareRegistry } from '../../base/redux'; -import { _setImmersiveListener } from './actions'; +import { _setImmersiveListener as _setImmersiveListenerA } from './actions'; import { _SET_IMMERSIVE_LISTENER } from './actionTypes'; /** @@ -28,59 +28,47 @@ import { _SET_IMMERSIVE_LISTENER } from './actionTypes'; * @param {Store} store - The redux store. * @returns {Function} */ -MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { - const result = next(action); - - let fullScreen = null; - +MiddlewareRegistry.register(store => next => action => { switch (action.type) { case _SET_IMMERSIVE_LISTENER: - // XXX The React Native module Immersive is only implemented on Android - // and throws on other platforms. - if (Platform.OS === 'android') { - // Remove the current/old Immersive listener. - const { listener } = getState()['features/full-screen']; - - listener && Immersive.removeImmersiveListener(listener); - - // Add the new listener. - action.listener && Immersive.addImmersiveListener(action.listener); - } - break; + return _setImmersiveListenerF(store, next, action); case APP_WILL_MOUNT: { - const context = { - dispatch, - getState - }; + const result = next(action); - dispatch( - _setImmersiveListener(_onImmersiveChange.bind(undefined, context))); - break; + store.dispatch( + _setImmersiveListenerA(_onImmersiveChange.bind(undefined, store))); + + return result; } + case APP_WILL_UNMOUNT: - _setImmersiveListener(undefined); + store.dispatch(_setImmersiveListenerA(undefined)); break; case CONFERENCE_WILL_JOIN: case CONFERENCE_JOINED: case SET_AUDIO_ONLY: { + const result = next(action); const { audioOnly, conference, joining } - = getState()['features/base/conference']; + = store.getState()['features/base/conference']; - fullScreen = conference || joining ? !audioOnly : false; - break; + _setFullScreen(conference || joining ? !audioOnly : false); + + return result; } case CONFERENCE_FAILED: - case CONFERENCE_LEFT: - fullScreen = false; - break; + case CONFERENCE_LEFT: { + const result = next(action); + + _setFullScreen(false); + + return result; + } } - fullScreen !== null && _setFullScreen(fullScreen); - - return result; + return next(action); }); /** @@ -119,11 +107,43 @@ function _setFullScreen(fullScreen: boolean) { // throws on other platforms. if (Platform.OS === 'android') { fullScreen ? Immersive.on() : Immersive.off(); + } else { + // On platforms other than Android go with whatever React Native itself + // supports. + StatusBar.setHidden(fullScreen, 'slide'); + } +} - return; +/** + * Notifies the feature filmstrip that the action + * {@link _SET_IMMERSIVE_LISTENER} is being dispatched within a specific redux + * store. + * + * @param {Store} store - The redux store in which the specified action is being + * dispatched. + * @param {Dispatch} next - The redux dispatch function to dispatch the + * specified action to the specified store. + * @param {Action} action - The redux action {@code _SET_IMMERSIVE_LISTENER} + * which is being dispatched in the specified store. + * @private + * @returns {Object} The value returned by {@code next(action)}. + */ +function _setImmersiveListenerF({ getState }, next, action) { + // XXX The React Native module Immersive is only implemented on Android and + // throws on other platforms. + if (Platform.OS === 'android') { + // Remove the old Immersive listener and add the new one. + const { listener: oldListener } = getState()['features/full-screen']; + const result = next(action); + const { listener: newListener } = getState()['features/full-screen']; + + if (oldListener !== newListener) { + oldListener && Immersive.removeImmersiveListener(oldListener); + newListener && Immersive.addImmersiveListener(newListener); + } + + return result; } - // On platforms other than Android go with whatever React Native itself - // supports. - StatusBar.setHidden(fullScreen, 'slide'); + return next(action); } diff --git a/react/features/mobile/full-screen/reducer.js b/react/features/mobile/full-screen/reducer.js index 1e04f9692..2091da7df 100644 --- a/react/features/mobile/full-screen/reducer.js +++ b/react/features/mobile/full-screen/reducer.js @@ -1,21 +1,17 @@ +// @flow + import { ReducerRegistry } from '../../base/redux'; import { _SET_IMMERSIVE_LISTENER } from './actionTypes'; -const INITIAL_STATE = { - listener: undefined -}; +ReducerRegistry.register('features/full-screen', (state = {}, action) => { + switch (action.type) { + case _SET_IMMERSIVE_LISTENER: + return { + ...state, + listener: action.listener + }; + } -ReducerRegistry.register( - 'features/full-screen', - (state = INITIAL_STATE, action) => { - switch (action.type) { - case _SET_IMMERSIVE_LISTENER: - return { - ...state, - listener: action.listener - }; - } - - return state; - }); + return state; +}); diff --git a/react/features/mobile/network-activity/reducer.js b/react/features/mobile/network-activity/reducer.js index 13fba2b7f..93b2f5502 100644 --- a/react/features/mobile/network-activity/reducer.js +++ b/react/features/mobile/network-activity/reducer.js @@ -1,4 +1,4 @@ -/* @flow */ +// @flow import { ReducerRegistry, set } from '../../base/redux'; @@ -9,13 +9,13 @@ import { } from './actionTypes'; /** - * The initial redux state of the feature network-activity. + * The default/initial redux state of the feature network-activity. * * @type {{ * requests: Map * }} */ -const _INITIAL_STATE = { +const DEFAULT_STATE = { /** * The ongoing network requests i.e. the network request which have been * added to the redux store/state and have not been removed. @@ -27,7 +27,7 @@ const _INITIAL_STATE = { ReducerRegistry.register( 'features/network-activity', - (state = _INITIAL_STATE, action) => { + (state = DEFAULT_STATE, action) => { switch (action.type) { case _ADD_NETWORK_REQUEST: { const { @@ -44,7 +44,7 @@ ReducerRegistry.register( } case _REMOVE_ALL_NETWORK_REQUESTS: - return set(state, 'requests', _INITIAL_STATE.requests); + return set(state, 'requests', DEFAULT_STATE.requests); case _REMOVE_NETWORK_REQUEST: { const { request: key } = action;