feat(RN-filmtrip) stop reordering small meetings
This commit is contained in:
parent
61abf0d882
commit
adef5095da
|
@ -1,6 +1,7 @@
|
|||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import SplashScreen from 'react-native-splash-screen';
|
||||
|
||||
import { DialogContainer } from '../../base/dialog';
|
||||
|
@ -8,7 +9,7 @@ import { updateFlags } from '../../base/flags/actions';
|
|||
import { CALL_INTEGRATION_ENABLED, SERVER_URL_CHANGE_ENABLED } from '../../base/flags/constants';
|
||||
import { getFeatureFlag } from '../../base/flags/functions';
|
||||
import { Platform } from '../../base/react';
|
||||
import { DimensionsDetector, clientResized } from '../../base/responsive-ui';
|
||||
import { DimensionsDetector, clientResized, setSafeAreaInsets } from '../../base/responsive-ui';
|
||||
import { updateSettings } from '../../base/settings';
|
||||
import { _getRouteToRender } from '../getRouteToRender.native';
|
||||
import logger from '../logger';
|
||||
|
@ -76,6 +77,7 @@ export class App extends AbstractApp {
|
|||
|
||||
// Bind event handler so it is only bound once per instance.
|
||||
this._onDimensionsChanged = this._onDimensionsChanged.bind(this);
|
||||
this._onSafeAreaInsetsChanged = this._onSafeAreaInsetsChanged.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -138,10 +140,13 @@ export class App extends AbstractApp {
|
|||
*/
|
||||
_createMainElement(component, props) {
|
||||
return (
|
||||
<DimensionsDetector
|
||||
onDimensionsChanged = { this._onDimensionsChanged }>
|
||||
{ super._createMainElement(component, props) }
|
||||
</DimensionsDetector>
|
||||
<SafeAreaProvider>
|
||||
<DimensionsDetector
|
||||
onDimensionsChanged = { this._onDimensionsChanged }
|
||||
onSafeAreaInsetsChanged = { this._onSafeAreaInsetsChanged }>
|
||||
{ super._createMainElement(component, props) }
|
||||
</DimensionsDetector>
|
||||
</SafeAreaProvider>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -198,6 +203,23 @@ export class App extends AbstractApp {
|
|||
dispatch(clientResized(width, height));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the safe are insets values.
|
||||
*
|
||||
* @param {Object} insets - The insets.
|
||||
* @param {number} insets.top - The top inset.
|
||||
* @param {number} insets.right - The right inset.
|
||||
* @param {number} insets.bottom - The bottom inset.
|
||||
* @param {number} insets.left - The left inset.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onSafeAreaInsetsChanged(insets) {
|
||||
const { dispatch } = this.state.store;
|
||||
|
||||
dispatch(setSafeAreaInsets(insets));
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the platform specific dialog container.
|
||||
*
|
||||
|
|
|
@ -7,6 +7,16 @@
|
|||
*/
|
||||
export const CLIENT_RESIZED = 'CLIENT_RESIZED';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which indicates that the insets from the SafeAreaProvider have changed.
|
||||
*
|
||||
* {
|
||||
* type: SAFE_AREA_INSETS_CHANGED,
|
||||
* insets: Object
|
||||
* }
|
||||
*/
|
||||
export const SAFE_AREA_INSETS_CHANGED = 'SAFE_AREA_INSETS_CHANGED';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which sets the aspect ratio of the app's user
|
||||
* interface.
|
||||
|
|
|
@ -7,7 +7,13 @@ import { CHAT_SIZE } from '../../chat/constants';
|
|||
import { getParticipantsPaneOpen } from '../../participants-pane/functions';
|
||||
import theme from '../components/themes/participantsPaneTheme.json';
|
||||
|
||||
import { CLIENT_RESIZED, SET_ASPECT_RATIO, SET_CONTEXT_MENU_OPEN, SET_REDUCED_UI } from './actionTypes';
|
||||
import {
|
||||
CLIENT_RESIZED,
|
||||
SAFE_AREA_INSETS_CHANGED,
|
||||
SET_ASPECT_RATIO,
|
||||
SET_CONTEXT_MENU_OPEN,
|
||||
SET_REDUCED_UI
|
||||
} from './actionTypes';
|
||||
import { ASPECT_RATIO_NARROW, ASPECT_RATIO_WIDE } from './constants';
|
||||
|
||||
/**
|
||||
|
@ -123,3 +129,19 @@ export function setParticipantContextMenuOpen(isOpen: boolean) {
|
|||
isOpen
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the insets from the SafeAreaProvider.
|
||||
*
|
||||
* @param {Object} insets - The new insets to be set.
|
||||
* @returns {{
|
||||
* type: SAFE_AREA_INSETS_CHANGED,
|
||||
* insets: Object
|
||||
* }}
|
||||
*/
|
||||
export function setSafeAreaInsets(insets) {
|
||||
return {
|
||||
type: SAFE_AREA_INSETS_CHANGED,
|
||||
insets
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import React, { useCallback, useEffect } from 'react';
|
||||
import { StyleSheet, View } from 'react-native';
|
||||
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
type Props = {
|
||||
|
||||
|
@ -11,6 +11,11 @@ type Props = {
|
|||
*/
|
||||
onDimensionsChanged: Function,
|
||||
|
||||
/**
|
||||
* The safe are insets handler.
|
||||
*/
|
||||
onSafeAreaInsetsChanged: Function,
|
||||
|
||||
/**
|
||||
* Any nested components.
|
||||
*/
|
||||
|
@ -20,21 +25,23 @@ type Props = {
|
|||
/**
|
||||
* A {@link View} which captures the 'onLayout' event and calls a prop with the
|
||||
* component size.
|
||||
*
|
||||
* @param {Props} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
* @returns {Component} - Renders the root view and it's children.
|
||||
*/
|
||||
export default class DimensionsDetector extends PureComponent<Props> {
|
||||
/**
|
||||
* Initializes a new DimensionsDetector instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props: Object) {
|
||||
super(props);
|
||||
export default function DimensionsDetector(props: Props) {
|
||||
const { top = 0, right = 0, bottom = 0, left = 0 } = useSafeAreaInsets();
|
||||
const { children, onDimensionsChanged, onSafeAreaInsetsChanged } = props;
|
||||
|
||||
this._onLayout = this._onLayout.bind(this);
|
||||
}
|
||||
|
||||
_onLayout: (Object) => void;
|
||||
useEffect(() => {
|
||||
onSafeAreaInsetsChanged && onSafeAreaInsetsChanged({
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
left
|
||||
});
|
||||
}, [ onSafeAreaInsetsChanged, top, right, bottom, left ]);
|
||||
|
||||
/**
|
||||
* Handles the "on layout" View's event and calls the onDimensionsChanged
|
||||
|
@ -45,24 +52,15 @@ export default class DimensionsDetector extends PureComponent<Props> {
|
|||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onLayout({ nativeEvent: { layout: { height, width } } }) {
|
||||
const { onDimensionsChanged } = this.props;
|
||||
|
||||
const onLayout = useCallback(({ nativeEvent: { layout: { height, width } } }) => {
|
||||
onDimensionsChanged && onDimensionsChanged(width, height);
|
||||
}
|
||||
}, [ onDimensionsChanged ]);
|
||||
|
||||
/**
|
||||
* Renders the root view and it's children.
|
||||
*
|
||||
* @returns {Component}
|
||||
*/
|
||||
render() {
|
||||
return (
|
||||
<View
|
||||
onLayout = { this._onLayout }
|
||||
style = { StyleSheet.absoluteFillObject } >
|
||||
{ this.props.children }
|
||||
</View>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<View
|
||||
onLayout = { onLayout }
|
||||
style = { StyleSheet.absoluteFillObject } >
|
||||
{ children }
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -2,7 +2,13 @@
|
|||
|
||||
import { ReducerRegistry, set } from '../redux';
|
||||
|
||||
import { CLIENT_RESIZED, SET_ASPECT_RATIO, SET_CONTEXT_MENU_OPEN, SET_REDUCED_UI } from './actionTypes';
|
||||
import {
|
||||
CLIENT_RESIZED,
|
||||
SAFE_AREA_INSETS_CHANGED,
|
||||
SET_ASPECT_RATIO,
|
||||
SET_CONTEXT_MENU_OPEN,
|
||||
SET_REDUCED_UI
|
||||
} from './actionTypes';
|
||||
import { ASPECT_RATIO_NARROW } from './constants';
|
||||
|
||||
const {
|
||||
|
@ -30,6 +36,13 @@ ReducerRegistry.register('features/base/responsive-ui', (state = DEFAULT_STATE,
|
|||
clientHeight: action.clientHeight
|
||||
};
|
||||
}
|
||||
|
||||
case SAFE_AREA_INSETS_CHANGED:
|
||||
return {
|
||||
...state,
|
||||
safeAreaInsets: action.insets
|
||||
};
|
||||
|
||||
case SET_ASPECT_RATIO:
|
||||
return set(state, 'aspectRatio', action.aspectRatio);
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
// @flow
|
||||
|
||||
import { getParticipantCountWithFake } from '../base/participants';
|
||||
import conferenceStyles from '../conference/components/native/styles';
|
||||
|
||||
import { SET_TILE_VIEW_DIMENSIONS } from './actionTypes';
|
||||
import { styles } from './components';
|
||||
import { SQUARE_TILE_ASPECT_RATIO, TILE_MARGIN } from './constants';
|
||||
import { getColumnCount } from './functions.native';
|
||||
import { getColumnCount } from './functions';
|
||||
import { getTileViewParticipantCount } from './functions.native';
|
||||
|
||||
export * from './actions.any';
|
||||
|
||||
|
@ -18,16 +20,19 @@ export * from './actions.any';
|
|||
export function setTileViewDimensions() {
|
||||
return (dispatch: Function, getState: Function) => {
|
||||
const state = getState();
|
||||
const participantCount = getParticipantCountWithFake(state);
|
||||
const { clientHeight: height, clientWidth: width } = state['features/base/responsive-ui'];
|
||||
const participantCount = getTileViewParticipantCount(state);
|
||||
const { clientHeight: height, clientWidth: width, safeAreaInsets = {} } = state['features/base/responsive-ui'];
|
||||
const { left = 0, right = 0, top = 0, bottom = 0 } = safeAreaInsets;
|
||||
const columns = getColumnCount(state);
|
||||
const heightToUse = height - (TILE_MARGIN * 2);
|
||||
const widthToUse = width - (TILE_MARGIN * 2);
|
||||
const rows = Math.ceil(participantCount / columns);
|
||||
const conferenceBorder = conferenceStyles.conference.borderWidth || 0;
|
||||
const heightToUse = height - top - (2 * conferenceBorder);
|
||||
const widthToUse = width - (TILE_MARGIN * 2) - left - right - (2 * conferenceBorder);
|
||||
let tileWidth;
|
||||
|
||||
// If there is going to be at least two rows, ensure that at least two
|
||||
// rows display fully on screen.
|
||||
if (participantCount / columns > 1) {
|
||||
if (rows / columns > 1) {
|
||||
tileWidth = Math.min(widthToUse / columns, heightToUse / 2);
|
||||
} else {
|
||||
tileWidth = Math.min(widthToUse / columns, heightToUse);
|
||||
|
@ -37,6 +42,9 @@ export function setTileViewDimensions() {
|
|||
|
||||
tileWidth = Math.floor(tileWidth);
|
||||
|
||||
// Adding safeAreaInsets.bottom to the total height of all thumbnails because we add it as a padding to the
|
||||
// thumbnails container.
|
||||
const hasScroll = heightToUse < ((tileHeight + (2 * styles.thumbnail.margin)) * rows) + bottom;
|
||||
|
||||
dispatch({
|
||||
type: SET_TILE_VIEW_DIMENSIONS,
|
||||
|
@ -45,7 +53,8 @@ export function setTileViewDimensions() {
|
|||
thumbnailSize: {
|
||||
height: tileHeight,
|
||||
width: tileWidth
|
||||
}
|
||||
},
|
||||
hasScroll
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { FlatList } from 'react-native';
|
||||
import { SafeAreaView } from 'react-native-safe-area-context';
|
||||
import { SafeAreaView, withSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
import { getLocalParticipant } from '../../../base/participants';
|
||||
import { Platform } from '../../../base/react';
|
||||
|
@ -11,7 +11,12 @@ import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
|
|||
import { shouldHideSelfView } from '../../../base/settings/functions.any';
|
||||
import { isToolboxVisible } from '../../../toolbox/functions';
|
||||
import { setVisibleRemoteParticipants } from '../../actions';
|
||||
import { isFilmstripVisible, shouldRemoteVideosBeVisible } from '../../functions';
|
||||
import {
|
||||
getFilmstripDimensions,
|
||||
isFilmstripVisible,
|
||||
shouldDisplayLocalThumbnailSeparately,
|
||||
shouldRemoteVideosBeVisible
|
||||
} from '../../functions';
|
||||
|
||||
import LocalThumbnail from './LocalThumbnail';
|
||||
import Thumbnail from './Thumbnail';
|
||||
|
@ -60,6 +65,11 @@ type Props = {
|
|||
* Invoked to trigger state changes in Redux.
|
||||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* Object containing the safe area insets.
|
||||
*/
|
||||
insets: Object,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -105,7 +115,7 @@ class Filmstrip extends PureComponent<Props> {
|
|||
// indicators such as moderator, audio and video muted, etc. For now we
|
||||
// do not have much of a choice but to continue rendering LocalThumbnail
|
||||
// as any other remote Thumbnail on Android.
|
||||
this._separateLocalThumbnail = Platform.OS !== 'android';
|
||||
this._separateLocalThumbnail = shouldDisplayLocalThumbnailSeparately();
|
||||
|
||||
this._viewabilityConfig = {
|
||||
itemVisiblePercentThreshold: 30,
|
||||
|
@ -136,20 +146,23 @@ class Filmstrip extends PureComponent<Props> {
|
|||
* @returns {Object} - The width and the height.
|
||||
*/
|
||||
_getDimensions() {
|
||||
const { _aspectRatio, _clientWidth, _clientHeight } = this.props;
|
||||
const { height, width, margin } = styles.thumbnail;
|
||||
const {
|
||||
_aspectRatio,
|
||||
_clientWidth,
|
||||
_clientHeight,
|
||||
_disableSelfView,
|
||||
_localParticipantId,
|
||||
insets
|
||||
} = this.props;
|
||||
const localParticipantVisible = Boolean(_localParticipantId) && !_disableSelfView;
|
||||
|
||||
if (_aspectRatio === ASPECT_RATIO_NARROW) {
|
||||
return {
|
||||
height,
|
||||
width: this._separateLocalThumbnail ? _clientWidth - width - (margin * 2) : _clientWidth
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
height: this._separateLocalThumbnail ? _clientHeight - height - (margin * 2) : _clientHeight,
|
||||
width
|
||||
};
|
||||
return getFilmstripDimensions({
|
||||
aspectRatio: _aspectRatio,
|
||||
clientHeight: _clientHeight,
|
||||
clientWidth: _clientWidth,
|
||||
insets,
|
||||
localParticipantVisible
|
||||
});
|
||||
}
|
||||
|
||||
_getItemLayout: (?Array<string>, number) => {length: number, offset: number, index: number};
|
||||
|
@ -312,7 +325,7 @@ function _mapStateToProps(state) {
|
|||
const responsiveUI = state['features/base/responsive-ui'];
|
||||
|
||||
return {
|
||||
_aspectRatio: state['features/base/responsive-ui'].aspectRatio,
|
||||
_aspectRatio: responsiveUI.aspectRatio,
|
||||
_clientHeight: responsiveUI.clientHeight,
|
||||
_clientWidth: responsiveUI.clientWidth,
|
||||
_disableSelfView: disableSelfView,
|
||||
|
@ -323,4 +336,4 @@ function _mapStateToProps(state) {
|
|||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(Filmstrip);
|
||||
export default withSafeAreaInsets(connect(_mapStateToProps)(Filmstrip));
|
||||
|
|
|
@ -218,7 +218,7 @@ export const SHOW_TOOLBAR_CONTEXT_MENU_AFTER = 600;
|
|||
|
||||
/**
|
||||
* The margin for each side of the tile view. Taken away from the available
|
||||
* height and width for the tile container to display in.
|
||||
* width for the tile container to display in.
|
||||
*
|
||||
* NOTE: Mobile specific.
|
||||
*
|
||||
|
|
|
@ -4,7 +4,7 @@ import { getSourceNameSignalingFeatureFlag } from '../base/config';
|
|||
import { getVirtualScreenshareParticipantOwnerId } from '../base/participants';
|
||||
|
||||
import { setRemoteParticipants } from './actions';
|
||||
import { isReorderingEnabled } from './functions';
|
||||
import { isFilmstripScrollVisible } from './functions';
|
||||
|
||||
/**
|
||||
* Computes the reorderd list of the remote participants.
|
||||
|
@ -118,3 +118,17 @@ export function updateRemoteParticipantsOnLeave(store: Object, participantId: ?s
|
|||
reorderedParticipants.delete(participantId)
|
||||
&& store.dispatch(setRemoteParticipants(Array.from(reorderedParticipants)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if thumbnail reordering is enabled and false otherwise.
|
||||
* Note: The function will return false if all participants are displayed on the screen.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {boolean} - True if thumbnail reordering is enabled and false otherwise.
|
||||
*/
|
||||
export function isReorderingEnabled(state) {
|
||||
const { testing = {} } = state['features/base/config'];
|
||||
const enableThumbnailReordering = testing.enableThumbnailReordering ?? true;
|
||||
|
||||
return enableThumbnailReordering && isFilmstripScrollVisible(state);
|
||||
}
|
||||
|
|
|
@ -1,9 +1,19 @@
|
|||
// @flow
|
||||
|
||||
import { getFeatureFlag, FILMSTRIP_ENABLED } from '../base/flags';
|
||||
import { getParticipantCountWithFake, getPinnedParticipant } from '../base/participants';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getParticipantCountWithFake,
|
||||
getPinnedParticipant
|
||||
} from '../base/participants';
|
||||
import { Platform } from '../base/react';
|
||||
import { toState } from '../base/redux';
|
||||
import { ASPECT_RATIO_NARROW } from '../base/responsive-ui/constants';
|
||||
import { shouldHideSelfView } from '../base/settings/functions.any';
|
||||
import conferenceStyles from '../conference/components/native/styles';
|
||||
import { shouldDisplayTileView } from '../video-layout';
|
||||
|
||||
import { styles } from './components';
|
||||
|
||||
export * from './functions.any';
|
||||
|
||||
|
@ -61,6 +71,22 @@ export function shouldRemoteVideosBeVisible(state: Object) {
|
|||
|| disable1On1Mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of participants displayed in tile view.
|
||||
*
|
||||
* @param {Object | Function} stateful - The Object or Function that can be
|
||||
* resolved to a Redux state object with the toState function.
|
||||
* @returns {number} - The number of participants displayed in tile view.
|
||||
*/
|
||||
export function getTileViewParticipantCount(stateful: Object | Function) {
|
||||
const state = toState(stateful);
|
||||
const disableSelfView = shouldHideSelfView(state);
|
||||
const localParticipant = getLocalParticipant(state);
|
||||
const participantCount = getParticipantCountWithFake(state) - (disableSelfView && localParticipant ? 1 : 0);
|
||||
|
||||
return participantCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns how many columns should be displayed for tile view.
|
||||
*
|
||||
|
@ -71,7 +97,7 @@ export function shouldRemoteVideosBeVisible(state: Object) {
|
|||
*/
|
||||
export function getColumnCount(stateful: Object | Function) {
|
||||
const state = toState(stateful);
|
||||
const participantCount = getParticipantCountWithFake(state);
|
||||
const participantCount = getTileViewParticipantCount(state);
|
||||
const { aspectRatio } = state['features/base/responsive-ui'];
|
||||
|
||||
// For narrow view, tiles should stack on top of each other for a lonely
|
||||
|
@ -90,16 +116,38 @@ export function getColumnCount(stateful: Object | Function) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Returns true if thumbnail reordering is enabled and false otherwise.
|
||||
* Returns true if the filmstrip has a scroll and false otherwise.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {boolean} - True if thumbnail reordering is enabled and false otherwise.
|
||||
* @returns {boolean} - True if the scroll is displayed and false otherwise.
|
||||
*/
|
||||
export function isReorderingEnabled(state) {
|
||||
const { testing = {} } = state['features/base/config'];
|
||||
const enableThumbnailReordering = testing.enableThumbnailReordering ?? true;
|
||||
export function isFilmstripScrollVisible(state) {
|
||||
if (shouldDisplayTileView(state)) {
|
||||
return state['features/filmstrip']?.tileViewDimensions?.hasScroll;
|
||||
}
|
||||
|
||||
return enableThumbnailReordering;
|
||||
const { aspectRatio, clientWidth, clientHeight, safeAreaInsets = {} } = state['features/base/responsive-ui'];
|
||||
const isNarrowAspectRatio = aspectRatio === ASPECT_RATIO_NARROW;
|
||||
const disableSelfView = shouldHideSelfView(state);
|
||||
const localParticipant = Boolean(getLocalParticipant(state));
|
||||
const localParticipantVisible = localParticipant && !disableSelfView;
|
||||
const participantCount
|
||||
= getParticipantCountWithFake(state)
|
||||
- (localParticipant && (shouldDisplayLocalThumbnailSeparately() || disableSelfView) ? 1 : 0);
|
||||
const { height: thumbnailHeight, width: thumbnailWidth, margin } = styles.thumbnail;
|
||||
const { height, width } = getFilmstripDimensions({
|
||||
aspectRatio,
|
||||
clientWidth,
|
||||
clientHeight,
|
||||
insets: safeAreaInsets,
|
||||
localParticipantVisible
|
||||
});
|
||||
|
||||
if (isNarrowAspectRatio) {
|
||||
return width < (thumbnailWidth + (2 * margin)) * participantCount;
|
||||
}
|
||||
|
||||
return height < (thumbnailHeight + (2 * margin)) * participantCount;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,3 +168,68 @@ export function isStageFilmstripAvailable() {
|
|||
export function isStageFilmstripEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the width and height of the filmstrip based on the screen size and aspect ratio.
|
||||
*
|
||||
* @param {Object} options - The screen aspect ratio, width, height and safe are insets.
|
||||
* @returns {Object} - The width and the height.
|
||||
*/
|
||||
export function getFilmstripDimensions({
|
||||
aspectRatio,
|
||||
clientWidth,
|
||||
clientHeight,
|
||||
insets = {},
|
||||
localParticipantVisible = true
|
||||
}) {
|
||||
const { height, width, margin } = styles.thumbnail;
|
||||
const conferenceBorder = conferenceStyles.conference.borderWidth || 0;
|
||||
const { left = 0, right = 0, top = 0, bottom = 0 } = insets;
|
||||
|
||||
if (aspectRatio === ASPECT_RATIO_NARROW) {
|
||||
return {
|
||||
height,
|
||||
width:
|
||||
(shouldDisplayLocalThumbnailSeparately() && localParticipantVisible
|
||||
? clientWidth - width - (margin * 2) : clientWidth)
|
||||
- left - right - (styles.filmstripNarrow.margin * 2) - (conferenceBorder * 2)
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
height:
|
||||
(shouldDisplayLocalThumbnailSeparately() && localParticipantVisible
|
||||
? clientHeight - height - (margin * 2) : clientHeight)
|
||||
- top - bottom - (conferenceBorder * 2),
|
||||
width
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the local thumbnail should be displayed separately and false otherwise.
|
||||
*
|
||||
* @returns {boolean} - True if the local thumbnail should be displayed separately and flase otherwise.
|
||||
*/
|
||||
export function shouldDisplayLocalThumbnailSeparately() {
|
||||
// XXX Our current design is to have the local participant separate from
|
||||
// the remote participants. Unfortunately, Android's Video
|
||||
// implementation cannot accommodate that because remote participants'
|
||||
// videos appear on top of the local participant's video at times.
|
||||
// That's because Android's Video utilizes EGL and EGL gives us only two
|
||||
// practical layers in which we can place our participants' videos:
|
||||
// layer #0 sits behind the window, creates a hole in the window, and
|
||||
// there we render the LargeVideo; layer #1 is known as media overlay in
|
||||
// EGL terms, renders on top of layer #0, and, consequently, is for the
|
||||
// Filmstrip. With the separate LocalThumbnail, we should have left the
|
||||
// remote participants' Thumbnails in layer #1 and utilized layer #2 for
|
||||
// LocalThumbnail. Unfortunately, layer #2 is not practical (that's why
|
||||
// I said we had two practical layers only) because it renders on top of
|
||||
// everything which in our case means on top of participant-related
|
||||
// indicators such as moderator, audio and video muted, etc. For now we
|
||||
// do not have much of a choice but to continue rendering LocalThumbnail
|
||||
// as any other remote Thumbnail on Android.
|
||||
return Platform.OS !== 'android';
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -652,20 +652,6 @@ export function getVerticalViewMaxWidth(state) {
|
|||
return maxWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if thumbnail reordering is enabled and false otherwise.
|
||||
* Note: The function will return false if all participants are displayed on the screen.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {boolean} - True if thumbnail reordering is enabled and false otherwise.
|
||||
*/
|
||||
export function isReorderingEnabled(state) {
|
||||
const { testing = {} } = state['features/base/config'];
|
||||
const enableThumbnailReordering = testing.enableThumbnailReordering ?? true;
|
||||
|
||||
return enableThumbnailReordering && isFilmstripScrollVisible(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the scroll is displayed and false otherwise.
|
||||
*
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { PARTICIPANT_JOINED, PARTICIPANT_LEFT } from '../base/participants';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
import { CLIENT_RESIZED, SET_ASPECT_RATIO } from '../base/responsive-ui';
|
||||
import { CLIENT_RESIZED, SAFE_AREA_INSETS_CHANGED, SET_ASPECT_RATIO } from '../base/responsive-ui';
|
||||
|
||||
import { setTileViewDimensions } from './actions';
|
||||
import { updateRemoteParticipants, updateRemoteParticipantsOnLeave } from './functions';
|
||||
|
@ -25,6 +25,7 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
|
||||
switch (action.type) {
|
||||
case CLIENT_RESIZED:
|
||||
case SAFE_AREA_INSETS_CHANGED:
|
||||
case SET_ASPECT_RATIO:
|
||||
store.dispatch(setTileViewDimensions());
|
||||
break;
|
||||
|
|
|
@ -204,12 +204,15 @@ ReducerRegistry.register(
|
|||
}
|
||||
};
|
||||
case SET_VISIBLE_REMOTE_PARTICIPANTS: {
|
||||
const { endIndex, startIndex } = action;
|
||||
const { remoteParticipants } = state;
|
||||
const visibleRemoteParticipants = new Set(remoteParticipants.slice(startIndex, endIndex + 1));
|
||||
|
||||
return {
|
||||
...state,
|
||||
visibleParticipantsStartIndex: action.startIndex,
|
||||
visibleParticipantsEndIndex: action.endIndex,
|
||||
visibleRemoteParticipants:
|
||||
new Set(state.remoteParticipants.slice(action.startIndex, action.endIndex + 1))
|
||||
visibleParticipantsStartIndex: startIndex,
|
||||
visibleParticipantsEndIndex: endIndex,
|
||||
visibleRemoteParticipants
|
||||
};
|
||||
}
|
||||
case PARTICIPANT_LEFT: {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { StateListenerRegistry } from '../base/redux';
|
||||
|
||||
import { updateRemoteParticipants } from './functions';
|
||||
import { isFilmstripScrollVisible, updateRemoteParticipants } from './functions';
|
||||
|
||||
/**
|
||||
* Listens for changes to the screensharing status of the remote participants to recompute the reordered list of the
|
||||
|
@ -25,3 +25,10 @@ StateListenerRegistry.register(
|
|||
StateListenerRegistry.register(
|
||||
/* selector */ state => state['features/base/participants'].dominantSpeaker,
|
||||
/* listener */ (dominantSpeaker, store) => updateRemoteParticipants(store));
|
||||
|
||||
/**
|
||||
* Listens for changes in the filmstrip scroll visibility.
|
||||
*/
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => isFilmstripScrollVisible(state),
|
||||
/* listener */ (_, store) => updateRemoteParticipants(store));
|
||||
|
|
|
@ -1,25 +1,17 @@
|
|||
// @flow
|
||||
|
||||
import { getParticipantCountWithFake } from '../base/participants';
|
||||
import { StateListenerRegistry } from '../base/redux';
|
||||
import { shouldDisplayTileView } from '../video-layout';
|
||||
|
||||
import { setTileViewDimensions } from './actions';
|
||||
import { getTileViewParticipantCount } from './functions.native';
|
||||
import './subscriber.any';
|
||||
|
||||
/**
|
||||
* Listens for changes in the number of participants to calculate the dimensions of the tile view grid and the tiles.
|
||||
*/
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => {
|
||||
const participantCount = getParticipantCountWithFake(state);
|
||||
|
||||
if (participantCount < 6) { // the dimensions are updated only when the participant count is lower than 6.
|
||||
return participantCount;
|
||||
}
|
||||
|
||||
return 5; // make sure we don't update the dimensions.
|
||||
},
|
||||
/* selector */ state => getTileViewParticipantCount(state),
|
||||
/* listener */ (_, store) => {
|
||||
const state = store.getState();
|
||||
|
||||
|
|
|
@ -23,9 +23,7 @@ import {
|
|||
DISPLAY_DRAWER_THRESHOLD
|
||||
} from './constants';
|
||||
import {
|
||||
isFilmstripResizable,
|
||||
isFilmstripScrollVisible,
|
||||
updateRemoteParticipants
|
||||
isFilmstripResizable
|
||||
} from './functions';
|
||||
|
||||
import './subscriber.any';
|
||||
|
@ -166,13 +164,6 @@ StateListenerRegistry.register(
|
|||
store.dispatch(setVerticalViewDimensions());
|
||||
});
|
||||
|
||||
/**
|
||||
* Listens for changes in the filmstrip scroll visibility.
|
||||
*/
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => isFilmstripScrollVisible(state),
|
||||
/* listener */ (_, store) => updateRemoteParticipants(store));
|
||||
|
||||
/**
|
||||
* Listens for changes to determine the size of the stage filmstrip tiles.
|
||||
*/
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import { NavigationContainer } from '@react-navigation/native';
|
||||
import { createStackNavigator } from '@react-navigation/stack';
|
||||
import React from 'react';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
|
||||
import { connect } from '../../../base/redux';
|
||||
import { DialInSummary } from '../../../invite';
|
||||
|
@ -36,43 +35,41 @@ const RootNavigationContainer = ({ isWelcomePageAvailable }: Props) => {
|
|||
? screen.root : screen.connecting;
|
||||
|
||||
return (
|
||||
<SafeAreaProvider>
|
||||
<NavigationContainer
|
||||
independent = { true }
|
||||
ref = { rootNavigationRef }
|
||||
theme = { navigationContainerTheme }>
|
||||
<RootStack.Navigator
|
||||
initialRouteName = { initialRouteName }>
|
||||
{
|
||||
isWelcomePageAvailable
|
||||
&& <>
|
||||
<RootStack.Screen
|
||||
component = { WelcomePageNavigationContainer }
|
||||
name = { screen.root }
|
||||
options = { drawerNavigatorScreenOptions } />
|
||||
<RootStack.Screen
|
||||
component = { DialInSummary }
|
||||
name = { screen.dialInSummary }
|
||||
options = { dialInSummaryScreenOptions } />
|
||||
</>
|
||||
}
|
||||
<RootStack.Screen
|
||||
component = { ConnectingPage }
|
||||
name = { screen.connecting }
|
||||
options = {{
|
||||
gestureEnabled: false,
|
||||
headerShown: false
|
||||
}} />
|
||||
<RootStack.Screen
|
||||
component = { ConferenceNavigationContainer }
|
||||
name = { screen.conference.root }
|
||||
options = {{
|
||||
gestureEnabled: false,
|
||||
headerShown: false
|
||||
}} />
|
||||
</RootStack.Navigator>
|
||||
</NavigationContainer>
|
||||
</SafeAreaProvider>
|
||||
<NavigationContainer
|
||||
independent = { true }
|
||||
ref = { rootNavigationRef }
|
||||
theme = { navigationContainerTheme }>
|
||||
<RootStack.Navigator
|
||||
initialRouteName = { initialRouteName }>
|
||||
{
|
||||
isWelcomePageAvailable
|
||||
&& <>
|
||||
<RootStack.Screen
|
||||
component = { WelcomePageNavigationContainer }
|
||||
name = { screen.root }
|
||||
options = { drawerNavigatorScreenOptions } />
|
||||
<RootStack.Screen
|
||||
component = { DialInSummary }
|
||||
name = { screen.dialInSummary }
|
||||
options = { dialInSummaryScreenOptions } />
|
||||
</>
|
||||
}
|
||||
<RootStack.Screen
|
||||
component = { ConnectingPage }
|
||||
name = { screen.connecting }
|
||||
options = {{
|
||||
gestureEnabled: false,
|
||||
headerShown: false
|
||||
}} />
|
||||
<RootStack.Screen
|
||||
component = { ConferenceNavigationContainer }
|
||||
name = { screen.conference.root }
|
||||
options = {{
|
||||
gestureEnabled: false,
|
||||
headerShown: false
|
||||
}} />
|
||||
</RootStack.Navigator>
|
||||
</NavigationContainer>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue