feat(tiles): Add responsive behaviour.

* Enforce fixed column number at various width breakpoints.
* Bring back the filmstrip at small sizes but hide it.
* Change default maximum columns to 7.
This commit is contained in:
Mihai-Andrei Uscat 2021-01-18 12:17:23 +02:00 committed by Mihai-Andrei Uscat
parent 6ca3c6e43a
commit db84889143
6 changed files with 156 additions and 19 deletions

View File

@ -143,16 +143,6 @@
@media only screen and (max-width: $verySmallScreen) {
@include very-small-button-size();
#videoResolutionLabel {
display: none;
}
.vertical-filmstrip .filmstrip {
display: none;
}
.chrome-extension-banner {
display: none;
}
}
&.shift-right {

View File

@ -100,6 +100,15 @@
video {
object-fit: contain;
}
/**
* Max-width corresponding to the ASPECT_RATIO_BREAKPOINT from features/filmstrip/constants.
*/
@media only screen and (max-width: 500px) {
video {
object-fit: cover;
}
}
}
.has-overflow#filmstripRemoteVideosContainer {

View File

@ -10,7 +10,40 @@ export const FILMSTRIP_SIZE = 90;
*/
export const TILE_ASPECT_RATIO = 16 / 9;
/**
* The aspect ratio of a square tile in tile view.
*/
export const SQUARE_TILE_ASPECT_RATIO = 1;
/**
* Width below which the overflow menu(s) will be displayed as drawer(s).
*/
export const DISPLAY_DRAWER_THRESHOLD = 512;
/**
* Breakpoint past which a single column view is enforced in tile view.
*/
export const SINGLE_COLUMN_BREAKPOINT = 300;
/**
* Breakpoint past which a two column view is enforced in tile view.
*/
export const TWO_COLUMN_BREAKPOINT = 1000;
/**
* Breakpoint past which the aspect ratio is switched in tile view.
* Also, past this breakpoint, if there are two participants in the conference, we enforce
* single column view.
* If this is to be modified, please also change the related media query from the tile_view scss file.
*/
export const ASPECT_RATIO_BREAKPOINT = 500;
/**
* The default number of columns for tile view.
*/
export const DEFAULT_MAX_COLUMNS = 5;
/**
* An extended number of columns for tile view.
*/
export const ABSOLUTE_MAX_COLUMNS = 7;

View File

@ -16,7 +16,7 @@ import {
isRemoteTrackMuted
} from '../base/tracks/functions';
import { TILE_ASPECT_RATIO } from './constants';
import { ASPECT_RATIO_BREAKPOINT, SQUARE_TILE_ASPECT_RATIO, TILE_ASPECT_RATIO } from './constants';
declare var interfaceConfig: Object;
@ -142,12 +142,13 @@ export function calculateThumbnailSizeForTileView({
clientWidth,
clientHeight
}: Object) {
const aspectRatio = clientWidth < ASPECT_RATIO_BREAKPOINT ? SQUARE_TILE_ASPECT_RATIO : TILE_ASPECT_RATIO;
const viewWidth = clientWidth - TILE_VIEW_SIDE_MARGINS;
const viewHeight = clientHeight - TILE_VIEW_SIDE_MARGINS;
const initialWidth = viewWidth / columns;
const aspectRatioHeight = initialWidth / TILE_ASPECT_RATIO;
const aspectRatioHeight = initialWidth / aspectRatio;
const height = Math.floor(Math.min(aspectRatioHeight, viewHeight / visibleRows));
const width = Math.floor(TILE_ASPECT_RATIO * height);
const width = Math.floor(aspectRatio * height);
return {
height,

View File

@ -3,11 +3,17 @@
import Filmstrip from '../../../modules/UI/videolayout/Filmstrip';
import VideoLayout from '../../../modules/UI/videolayout/VideoLayout';
import { StateListenerRegistry, equals } from '../base/redux';
import { setFilmstripVisible } from '../filmstrip/actions';
import { setOverflowDrawer } from '../toolbox/actions.web';
import { getCurrentLayout, getTileViewGridDimensions, shouldDisplayTileView, LAYOUTS } from '../video-layout';
import { setHorizontalViewDimensions, setTileViewDimensions } from './actions.web';
import { DISPLAY_DRAWER_THRESHOLD } from './constants';
import {
ASPECT_RATIO_BREAKPOINT,
DISPLAY_DRAWER_THRESHOLD,
SINGLE_COLUMN_BREAKPOINT,
TWO_COLUMN_BREAKPOINT
} from './constants';
/**
* Listens for changes in the number of participants to calculate the dimensions of the tile view grid and the tiles.
@ -134,3 +140,67 @@ StateListenerRegistry.register(
/* listener */ (widthBelowThreshold, store) => {
store.dispatch(setOverflowDrawer(widthBelowThreshold));
});
/**
* Gracefully hide/show the filmstrip when going past threshold.
*/
StateListenerRegistry.register(
/* selector */ state => state['features/base/responsive-ui'].clientWidth < ASPECT_RATIO_BREAKPOINT,
/* listener */ (widthBelowThreshold, store) => {
store.dispatch(setFilmstripVisible(!widthBelowThreshold));
});
/**
* Symbol mapping used for the tile view responsiveness computation.
*/
const responsiveColumnMapping = {
singleColumn: Symbol('singleColumn'),
twoColumns: Symbol('twoColumns'),
twoParticipantsSingleColumn: Symbol('twoParticipantsSingleColumn')
};
/**
* Listens for changes in the screen size to recompute
* the dimensions of the tile view grid and the tiles for responsiveness.
*/
StateListenerRegistry.register(
/* selector */ state => {
const { clientWidth } = state['features/base/responsive-ui'];
if (clientWidth < TWO_COLUMN_BREAKPOINT && clientWidth >= ASPECT_RATIO_BREAKPOINT) {
// Forcing the recomputation of tiles when screen switches in or out of
// the (TWO_COLUMN_BREAKPOINT, ASPECT_RATIO_BREAKPOINT] interval.
return responsiveColumnMapping.twoColumns;
} else if (clientWidth < ASPECT_RATIO_BREAKPOINT && clientWidth >= SINGLE_COLUMN_BREAKPOINT) {
// Forcing the recomputation of tiles when screen switches in or out of
// the (ASPECT_RATIO_BREAKPOINT, SINGLE_COLUMN_BREAKPOINT] interval.
return responsiveColumnMapping.twoParticipantsSingleColumn;
}
/**
* This gets called either when the width of the screen is above {@code TWO_COLUMN_BREAKPOINT}
* or below {@CODE SINGLE_COLUMN_BREAKPOINT}, however, the internal logic from {@code getMaxColumnCount}
* only takes the second case into consideration.
*/
return responsiveColumnMapping.singleColumn;
},
/* listener */ (_, store) => {
const state = store.getState();
if (shouldDisplayTileView(state)) {
const gridDimensions = getTileViewGridDimensions(state);
const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
const { isOpen } = state['features/chat'];
store.dispatch(
setTileViewDimensions(
gridDimensions,
{
clientHeight,
clientWidth
},
isOpen
)
);
}
});

View File

@ -2,6 +2,14 @@
import { getFeatureFlag, TILE_VIEW_ENABLED } from '../base/flags';
import { getPinnedParticipant, getParticipantCount } from '../base/participants';
import { CHAT_SIZE } from '../chat/constants';
import {
ASPECT_RATIO_BREAKPOINT,
DEFAULT_MAX_COLUMNS,
ABSOLUTE_MAX_COLUMNS,
SINGLE_COLUMN_BREAKPOINT,
TWO_COLUMN_BREAKPOINT
} from '../filmstrip/constants';
import { isYoutubeVideoPlaying } from '../youtube-player/functions';
import { LAYOUTS } from './constants';
@ -27,14 +35,38 @@ export function getCurrentLayout(state: Object) {
/**
* Returns how many columns should be displayed in tile view. The number
* returned will be between 1 and 5, inclusive.
* returned will be between 1 and 7, inclusive.
*
* @param {Object} state - The redux store state.
* @returns {number}
*/
export function getMaxColumnCount() {
const configuredMax = interfaceConfig.TILE_VIEW_MAX_COLUMNS || 5;
export function getMaxColumnCount(state: Object) {
const configuredMax = interfaceConfig.TILE_VIEW_MAX_COLUMNS || DEFAULT_MAX_COLUMNS;
const { clientWidth } = state['features/base/responsive-ui'];
let availableWidth = clientWidth;
const participantCount = getParticipantCount(state);
const { isOpen } = state['features/chat'];
return Math.min(Math.max(configuredMax, 1), 5);
if (isOpen) {
availableWidth -= CHAT_SIZE;
}
// If there are just two participants in a conference, enforce single-column view for mobile size.
if (participantCount === 2 && availableWidth < ASPECT_RATIO_BREAKPOINT) {
return Math.min(1, Math.max(configuredMax, 1));
}
// Enforce single column view at very small screen widths.
if (availableWidth < SINGLE_COLUMN_BREAKPOINT) {
return Math.min(1, Math.max(configuredMax, 1));
}
// Enforce two column view below breakpoint.
if (availableWidth < TWO_COLUMN_BREAKPOINT) {
return Math.min(2, Math.max(configuredMax, 1));
}
return Math.min(Math.max(configuredMax, 1), ABSOLUTE_MAX_COLUMNS);
}
/**
@ -48,7 +80,9 @@ export function getMaxColumnCount() {
* @returns {Object} An object is return with the desired number of columns,
* rows, and visible rows (the rest should overflow) for the tile view layout.
*/
export function getTileViewGridDimensions(state: Object, maxColumns: number = getMaxColumnCount()) {
export function getTileViewGridDimensions(state: Object) {
const maxColumns = getMaxColumnCount(state);
// When in tile view mode, we must discount ourselves (the local participant) because our
// tile is not visible.
const { iAmRecorder } = state['features/base/config'];