Bläddra i källkod

feat: display filmstrip on the short side

Adds the ability to detect app area's aspect ratio on react-native
through the features/base/aspect-ratio.

Makes conference, filmstrip and toolbox react to the aspect ratio
changes and display filmstrip on the shorter side of the screen.
master
paweldomas 7 år sedan
förälder
incheckning
c0a7d6144a

+ 15
- 0
react/features/app/components/App.native.js Visa fil

1
 /* global __DEV__ */
1
 /* global __DEV__ */
2
 
2
 
3
 import PropTypes from 'prop-types';
3
 import PropTypes from 'prop-types';
4
+import React from 'react';
4
 import { Linking } from 'react-native';
5
 import { Linking } from 'react-native';
5
 
6
 
6
 import '../../analytics';
7
 import '../../analytics';
7
 import '../../authentication';
8
 import '../../authentication';
9
+import { AspectRatioDetector } from '../../base/aspect-ratio';
8
 import { Platform } from '../../base/react';
10
 import { Platform } from '../../base/react';
9
 import '../../mobile/audio-mode';
11
 import '../../mobile/audio-mode';
10
 import '../../mobile/background';
12
 import '../../mobile/background';
86
         super.componentWillUnmount();
88
         super.componentWillUnmount();
87
     }
89
     }
88
 
90
 
91
+    /**
92
+     * Overrides the super method to inject {@link AspectRatioDetector} as
93
+     * the top most component.
94
+     *
95
+     * @override
96
+     */
97
+    _createElement(component, props) {
98
+        return (
99
+            <AspectRatioDetector>
100
+                {super._createElement(component, props)}
101
+            </AspectRatioDetector>);
102
+    }
103
+
89
     /**
104
     /**
90
      * Attempts to disable the use of React Native
105
      * Attempts to disable the use of React Native
91
      * {@link ExceptionsManager#handleException} on platforms and in
106
      * {@link ExceptionsManager#handleException} on platforms and in

+ 9
- 0
react/features/base/aspect-ratio/actionTypes.js Visa fil

1
+/**
2
+ * The type of (redux) action which signals that a new aspect ratio has been
3
+ * detected by the app.
4
+ * {
5
+ *      type: SET_ASPECT_RATIO,
6
+ *      aspectRatio: Symbol
7
+ * }
8
+ */
9
+export const SET_ASPECT_RATIO = Symbol('SET_ASPECT_RATIO');

+ 22
- 0
react/features/base/aspect-ratio/actions.js Visa fil

1
+/* @flow */
2
+
3
+import { SET_ASPECT_RATIO } from './actionTypes';
4
+import { ASPECT_RATIO_NARROW, ASPECT_RATIO_WIDE } from './constants';
5
+
6
+/**
7
+ * Calculates new aspect ratio for the app based on provided width and height
8
+ * values.
9
+ *
10
+ * @param {number} width - The width of the app's area used on the screen.
11
+ * @param {number} height - The height of the app's area used on the screen.
12
+ * @returns {{
13
+ *      type: SET_ASPECT_RATIO,
14
+ *      aspectRatio: Symbol
15
+ * }}
16
+ */
17
+export function calculateNewAspectRatio(width: number, height: number): Object {
18
+    return {
19
+        type: SET_ASPECT_RATIO,
20
+        aspectRatio: width > height ? ASPECT_RATIO_WIDE : ASPECT_RATIO_NARROW
21
+    };
22
+}

+ 68
- 0
react/features/base/aspect-ratio/components/AspectRatioAware.js Visa fil

1
+// @flow
2
+import PropTypes from 'prop-types';
3
+import React, { Component } from 'react';
4
+import { connect } from 'react-redux';
5
+
6
+import { ASPECT_RATIO_NARROW } from '../constants';
7
+
8
+/**
9
+ * Decorates given React component class into {@link AspectRatioAwareWrapper}
10
+ * which provides the <tt>aspectRatio</tt> property updated on each Redux state
11
+ * change.
12
+ *
13
+ * @param {ReactClass} WrapperComponent - A React component class to be wrapped.
14
+ * @returns {AspectRatioAwareWrapper}
15
+ */
16
+export function AspectRatioAware(
17
+        WrapperComponent: ReactClass<*>): ReactClass<*> {
18
+    return connect(_mapStateToProps)(
19
+        class AspectRatioAwareWrapper extends Component {
20
+            /**
21
+             * Properties of the aspect ratio aware wrapper.
22
+             */
23
+            static propTypes = {
24
+                /**
25
+                 * Either {@link ASPECT_RATIO_NARROW} or
26
+                 * {@link ASPECT_RATIO_WIDE}.
27
+                 */
28
+                aspectRatio: PropTypes.symbol
29
+            }
30
+
31
+            /**
32
+             * Implement's React render method to wrap the nested component.
33
+             *
34
+             * @returns {XML}
35
+             */
36
+            render(): React$Element<*> {
37
+                return <WrapperComponent { ...this.props } />;
38
+            }
39
+        });
40
+}
41
+
42
+/**
43
+ * Maps Redux state to {@link AspectRatioAwareWrapper} properties.
44
+ *
45
+ * @param {Object} state - The Redux whole state.
46
+ * @returns {{
47
+ *      aspectRatio: Symbol
48
+ * }}
49
+ * @private
50
+ */
51
+function _mapStateToProps(state) {
52
+    return {
53
+        aspectRatio: state['features/base/aspect-ratio'].aspectRatio
54
+    };
55
+}
56
+
57
+/**
58
+ * Checks if given React component decorated in {@link AspectRatioAwareWrapper}
59
+ * has currently the {@link ASPECT_RATIO_NARROW} set in the aspect ratio
60
+ * property.
61
+ *
62
+ * @param {AspectRatioAwareWrapper} component - A
63
+ * {@link AspectRatioAwareWrapper} which has <tt>aspectRation</tt> property.
64
+ * @returns {boolean}
65
+ */
66
+export function isNarrowAspectRatio(component: ReactClass<*>) {
67
+    return component.props.aspectRatio === ASPECT_RATIO_NARROW;
68
+}

+ 74
- 0
react/features/base/aspect-ratio/components/AspectRatioDetector.native.js Visa fil

1
+import PropTypes from 'prop-types';
2
+import React, { Component } from 'react';
3
+import { View } from 'react-native';
4
+import { connect } from 'react-redux';
5
+
6
+import { calculateNewAspectRatio } from '../actions';
7
+import styles from './styles';
8
+
9
+/**
10
+ * A root {@link View} which captures the 'onLayout' event and figures out
11
+ * the aspect ratio of the app.
12
+ */
13
+class AspectRatioDetector extends Component {
14
+    /**
15
+     * AspectRatioDetector component's property types.
16
+     *
17
+     * @static
18
+     */
19
+    static propTypes = {
20
+        /**
21
+         * The "onLayout" handler.
22
+         */
23
+        _onLayout: PropTypes.func,
24
+
25
+        /**
26
+         * Any nested components.
27
+         */
28
+        children: PropTypes.object
29
+    };
30
+
31
+    /**
32
+     * Renders the root view and it's children.
33
+     *
34
+     * @returns {Component}
35
+     */
36
+    render() {
37
+        return (
38
+            <View
39
+                onLayout = { this.props._onLayout }
40
+                style = { styles.aspectRatioDetectorStyle } >
41
+                {this.props.children}
42
+            </View>);
43
+    }
44
+}
45
+
46
+/**
47
+ * Maps dispatching of the aspect ratio actions to React component props.
48
+ *
49
+ * @param {Function} dispatch - Redux action dispatcher.
50
+ * @private
51
+ * @returns {{
52
+ *     _onLayout: Function
53
+ * }}
54
+ */
55
+function _mapDispatchToProps(dispatch) {
56
+    return {
57
+        /**
58
+         * Handles the "on layout" View's event and dispatches aspect ratio
59
+         * changed action.
60
+         *
61
+         * @param {{ width: number, height: number }} event - The "on layout"
62
+         * event structure passed by react-native.
63
+         * @returns {void}
64
+         * @private
65
+         */
66
+        _onLayout(event) {
67
+            const { width, height } = event.nativeEvent.layout;
68
+
69
+            dispatch(calculateNewAspectRatio(width, height));
70
+        }
71
+    };
72
+}
73
+
74
+export default connect(undefined, _mapDispatchToProps)(AspectRatioDetector);

+ 2
- 0
react/features/base/aspect-ratio/components/index.js Visa fil

1
+export * from './AspectRatioAware';
2
+export { default as AspectRatioDetector } from './AspectRatioDetector';

+ 14
- 0
react/features/base/aspect-ratio/components/styles.js Visa fil

1
+import { createStyleSheet, fixAndroidViewClipping } from '../../styles/index';
2
+
3
+/**
4
+ * The styles of the feature app.
5
+ */
6
+export default createStyleSheet({
7
+    /**
8
+     * The style for {@link AspectRatioDetector} root view used on react-native.
9
+     */
10
+    aspectRatioDetectorStyle: fixAndroidViewClipping({
11
+        alignSelf: 'stretch',
12
+        flex: 1
13
+    })
14
+});

+ 15
- 0
react/features/base/aspect-ratio/constants.js Visa fil

1
+/**
2
+ * The aspect ratio constant indicates that the app area's width is smaller than
3
+ * the height.
4
+ *
5
+ * @type {Symbol}
6
+ */
7
+export const ASPECT_RATIO_NARROW = Symbol('ASPECT_RATIO_NARROW');
8
+
9
+/**
10
+ * Aspect ratio constant indicates that the app area's width is larger than
11
+ * the height.
12
+ *
13
+ * @type {Symbol}
14
+ */
15
+export const ASPECT_RATIO_WIDE = Symbol('ASPECT_RATIO_WIDE');

+ 6
- 0
react/features/base/aspect-ratio/index.js Visa fil

1
+export * from './actions';
2
+export * from './actionTypes';
3
+export * from './components';
4
+export * from './constants';
5
+
6
+import './reducer';

+ 19
- 0
react/features/base/aspect-ratio/reducer.js Visa fil

1
+import { ReducerRegistry, set } from '../redux';
2
+
3
+import { SET_ASPECT_RATIO } from './actionTypes';
4
+import { ASPECT_RATIO_NARROW } from './constants';
5
+
6
+const INITIAL_STATE = {
7
+    aspectRatio: ASPECT_RATIO_NARROW
8
+};
9
+
10
+ReducerRegistry.register(
11
+'features/base/aspect-ratio',
12
+(state = INITIAL_STATE, action) => {
13
+    switch (action.type) {
14
+    case SET_ASPECT_RATIO:
15
+        return set(state, 'aspectRatio', action.aspectRatio);
16
+    }
17
+
18
+    return state;
19
+});

+ 16
- 13
react/features/conference/components/Conference.native.js Visa fil

184
                   */}
184
                   */}
185
                 <LargeVideo />
185
                 <LargeVideo />
186
 
186
 
187
-                {/*
188
-                  * The Filmstrip is in a stacking layer above the LargeVideo.
189
-                  * The LargeVideo and the Filmstrip form what the Web/React app
190
-                  * calls "videospace". Presumably, the name and grouping stem
191
-                  * from the fact that these two React Components depict the
192
-                  * videos of the conference's participants.
193
-                  */}
194
-                <Filmstrip />
195
-
196
                 {/*
187
                 {/*
197
                   * The overlays need to be bellow the Toolbox so that the user
188
                   * The overlays need to be bellow the Toolbox so that the user
198
                   * may tap the ToolbarButtons.
189
                   * may tap the ToolbarButtons.
209
                         </View>
200
                         </View>
210
                 }
201
                 }
211
 
202
 
212
-                {/*
213
-                  * The Toolbox is in a stacking layer above the Filmstrip.
214
-                  */}
215
-                <Toolbox />
203
+                <View style = { styles.toolboxAndFilmstripContainer } >
204
+                    {/*
205
+                      * The Toolbox is in a stacking layer above the Filmstrip.
206
+                      */}
207
+                    <Toolbox />
208
+                    {/*
209
+                      * The Filmstrip is in a stacking layer above
210
+                      * the LargeVideo.
211
+                      * The LargeVideo and the Filmstrip form what the Web/React
212
+                      * app calls "videospace". Presumably, the name and
213
+                      * grouping stem from the fact that these two React
214
+                      * Components depict the videos of the conference's
215
+                      * participants.
216
+                      */}
217
+                    <Filmstrip />
218
+                </View>
216
 
219
 
217
                 {/*
220
                 {/*
218
                   * The dialogs are in the topmost stacking layers.
221
                   * The dialogs are in the topmost stacking layers.

+ 14
- 0
react/features/conference/components/styles.js Visa fil

38
         // contrast and translucency.
38
         // contrast and translucency.
39
         backgroundColor: ColorPalette.appBackground,
39
         backgroundColor: ColorPalette.appBackground,
40
         opacity: 0.5
40
         opacity: 0.5
41
+    },
42
+
43
+    /**
44
+     * The style of the view which expands over the whole conference area and
45
+     * splits it between both the filmstrip and the toolbox.
46
+     */
47
+    toolboxAndFilmstripContainer: {
48
+        bottom: 0,
49
+        flexDirection: 'column',
50
+        left: 0,
51
+        justifyContent: 'flex-end',
52
+        position: 'absolute',
53
+        right: 0,
54
+        top: 0
41
     }
55
     }
42
 });
56
 });

+ 15
- 11
react/features/filmstrip/components/Filmstrip.native.js Visa fil

5
 import { ScrollView } from 'react-native';
5
 import { ScrollView } from 'react-native';
6
 import { connect } from 'react-redux';
6
 import { connect } from 'react-redux';
7
 
7
 
8
+import { AspectRatioAware, isNarrowAspectRatio } from '../../base/aspect-ratio';
8
 import { Container } from '../../base/react';
9
 import { Container } from '../../base/react';
9
 
10
 
10
 import Thumbnail from './Thumbnail';
11
 import Thumbnail from './Thumbnail';
47
      * @returns {ReactElement}
48
      * @returns {ReactElement}
48
      */
49
      */
49
     render() {
50
     render() {
51
+        const filmstripStyle
52
+            = isNarrowAspectRatio(this)
53
+                ? styles.filmstripNarrow : styles.filmstripWide;
54
+
50
         return (
55
         return (
51
             <Container
56
             <Container
52
-                style = { styles.filmstrip }
53
-                visible = { this.props._visible }>
57
+                style = { filmstripStyle }
58
+                visible = { this.props._visible } >
54
                 <ScrollView
59
                 <ScrollView
55
-
56
-                    contentContainerStyle
57
-                        = { styles.filmstripScrollViewContentContainer }
58
-                    horizontal = { true }
60
+                    horizontal = { isNarrowAspectRatio(this) }
59
                     showsHorizontalScrollIndicator = { false }
61
                     showsHorizontalScrollIndicator = { false }
60
                     showsVerticalScrollIndicator = { false }>
62
                     showsVerticalScrollIndicator = { false }>
61
                     {
63
                     {
121
  * }}
123
  * }}
122
  */
124
  */
123
 function _mapStateToProps(state) {
125
 function _mapStateToProps(state) {
126
+    const participants = state['features/base/participants'];
127
+
124
     return {
128
     return {
125
         /**
129
         /**
126
          * The participants in the conference.
130
          * The participants in the conference.
128
          * @private
132
          * @private
129
          * @type {Participant[]}
133
          * @type {Participant[]}
130
          */
134
          */
131
-        _participants: state['features/base/participants'],
135
+        _participants: participants,
132
 
136
 
133
         /**
137
         /**
134
          * The indicator which determines whether the filmstrip is visible.
138
          * The indicator which determines whether the filmstrip is visible.
135
          *
139
          *
136
          * XXX The React Component Filmstrip is used on mobile only at the time
140
          * XXX The React Component Filmstrip is used on mobile only at the time
137
-         * of this writing and on mobile the filmstrip is visible when the
138
-         * toolbar is not.
141
+         * of this writing and on mobile the filmstrip is when there are at
142
+         * least 2 participants in the conference (including the local one).
139
          *
143
          *
140
          * @private
144
          * @private
141
          * @type {boolean}
145
          * @type {boolean}
142
          */
146
          */
143
-        _visible: !state['features/toolbox'].visible
147
+        _visible: participants.length > 1
144
     };
148
     };
145
 }
149
 }
146
 
150
 
147
-export default connect(_mapStateToProps)(Filmstrip);
151
+export default connect(_mapStateToProps)(AspectRatioAware(Filmstrip));

+ 25
- 16
react/features/filmstrip/components/styles.js Visa fil

1
 import { Platform } from '../../base/react';
1
 import { Platform } from '../../base/react';
2
 import { BoxModel, ColorPalette } from '../../base/styles';
2
 import { BoxModel, ColorPalette } from '../../base/styles';
3
 
3
 
4
+/**
5
+ * The base filmstrip style shared between narrow and wide versions.
6
+ */
7
+const filmstripBaseStyle = {
8
+    flexGrow: 0,
9
+    flexDirection: 'column'
10
+};
11
+
4
 /**
12
 /**
5
  * The styles of the feature filmstrip common to both Web and native.
13
  * The styles of the feature filmstrip common to both Web and native.
6
  */
14
  */
40
     },
48
     },
41
 
49
 
42
     /**
50
     /**
43
-     * The style of the Container which represents the very filmstrip.
51
+     * The style of the narrow filmstrip version which displays thumbnails
52
+     * in a row at the bottom of the screen.
44
      */
53
      */
45
-    filmstrip: {
54
+    filmstripNarrow: {
55
+        ...filmstripBaseStyle,
46
         alignItems: 'flex-end',
56
         alignItems: 'flex-end',
47
-        alignSelf: 'stretch',
48
-        bottom: BoxModel.margin,
49
-        flex: 1,
50
-        flexDirection: 'column',
51
-        left: 0,
52
-        position: 'absolute',
53
-        right: 0
57
+        height: 90,
58
+        marginBottom: BoxModel.margin,
59
+        marginLeft: BoxModel.margin,
60
+        marginRight: BoxModel.margin
54
     },
61
     },
55
 
62
 
56
     /**
63
     /**
57
-     * The style of the content container of the ScrollView which is placed
58
-     * inside filmstrip and which contains the participants' thumbnails in order
59
-     * to allow scrolling through them if they do not fit within the display.
64
+     * The style of the wide version of the filmstrip which appears as a column
65
+     * on the short side of the screen.
60
      */
66
      */
61
-    filmstripScrollViewContentContainer: {
62
-        paddingHorizontal: BoxModel.padding
67
+    filmstripWide: {
68
+        ...filmstripBaseStyle,
69
+        bottom: BoxModel.margin,
70
+        left: BoxModel.margin,
71
+        position: 'absolute',
72
+        top: BoxModel.margin
63
     },
73
     },
64
 
74
 
65
     /**
75
     /**
86
         borderWidth: 1,
96
         borderWidth: 1,
87
         flex: 1,
97
         flex: 1,
88
         justifyContent: 'center',
98
         justifyContent: 'center',
89
-        marginLeft: 2,
90
-        marginRight: 2,
99
+        margin: 2,
91
         overflow: 'hidden',
100
         overflow: 'hidden',
92
         position: 'relative'
101
         position: 'relative'
93
     },
102
     },

+ 17
- 5
react/features/toolbox/components/Toolbox.native.js Visa fil

4
 import { connect } from 'react-redux';
4
 import { connect } from 'react-redux';
5
 
5
 
6
 import { sendAnalyticsEvent } from '../../analytics';
6
 import { sendAnalyticsEvent } from '../../analytics';
7
+import { AspectRatioAware, isNarrowAspectRatio } from '../../base/aspect-ratio';
7
 import { toggleAudioOnly } from '../../base/conference';
8
 import { toggleAudioOnly } from '../../base/conference';
8
 import {
9
 import {
9
     MEDIA_TYPE,
10
     MEDIA_TYPE,
119
      * @returns {ReactElement}
120
      * @returns {ReactElement}
120
      */
121
      */
121
     render() {
122
     render() {
123
+        if (!this.props._visible) {
124
+            return null;
125
+        }
126
+
122
         return (
127
         return (
123
             <Container
128
             <Container
124
-                style = { styles.toolbarContainer }
125
-                visible = { this.props._visible }>
129
+                style = {
130
+                    isNarrowAspectRatio(this)
131
+                        ? styles.toolbarContainerNarrow
132
+                        : styles.toolbarContainerWide } >
126
                 {
133
                 {
127
-                    this._renderPrimaryToolbar()
134
+                    isNarrowAspectRatio(this)
135
+                        ? this._renderSecondaryToolbar()
136
+                        : this._renderPrimaryToolbar()
128
                 }
137
                 }
129
                 {
138
                 {
130
-                    this._renderSecondaryToolbar()
139
+                    isNarrowAspectRatio(this)
140
+                        ? this._renderPrimaryToolbar()
141
+                        : this._renderSecondaryToolbar()
131
                 }
142
                 }
132
             </Container>
143
             </Container>
133
         );
144
         );
420
     };
431
     };
421
 }
432
 }
422
 
433
 
423
-export default connect(_mapStateToProps, _mapDispatchToProps)(Toolbox);
434
+export default connect(_mapStateToProps, _mapDispatchToProps)(
435
+    AspectRatioAware(Toolbox));

+ 18
- 4
react/features/toolbox/components/styles.js Visa fil

6
  * @type {Object}
6
  * @type {Object}
7
  */
7
  */
8
 const _toolbar = {
8
 const _toolbar = {
9
-    flex: 1,
9
+    flex: 0,
10
     position: 'absolute'
10
     position: 'absolute'
11
 };
11
 };
12
 
12
 
86
      */
86
      */
87
     primaryToolbar: {
87
     primaryToolbar: {
88
         ..._toolbar,
88
         ..._toolbar,
89
-        bottom: 3 * BoxModel.margin,
89
+        bottom: 0,
90
         flexDirection: 'row',
90
         flexDirection: 'row',
91
         justifyContent: 'center',
91
         justifyContent: 'center',
92
         left: 0,
92
         left: 0,
135
 
135
 
136
     /**
136
     /**
137
      * The style of the root/top-level {@link Container} of {@link Toolbox}
137
      * The style of the root/top-level {@link Container} of {@link Toolbox}
138
-     * which contains {@link Toolbar}s.
138
+     * which contains {@link Toolbar}s. This is narrow layout version which
139
+     * spans from the top of the screen to the top of the filmstrip located at
140
+     * the bottom of the screen.
141
+     */
142
+    toolbarContainerNarrow: {
143
+        flexDirection: 'column',
144
+        flexGrow: 1
145
+    },
146
+
147
+    /**
148
+     * The style of the root/top-level {@link Container} of {@link Toolbox}
149
+     * which contains {@link Toolbar}s. This is wide layout version which
150
+     * spans from the top to the bottom of the screen and is located to
151
+     * the right of the filmstrip which is displayed as a column on the left
152
+     * side of the screen.
139
      */
153
      */
140
-    toolbarContainer: {
154
+    toolbarContainerWide: {
141
         bottom: 0,
155
         bottom: 0,
142
         left: 0,
156
         left: 0,
143
         position: 'absolute',
157
         position: 'absolute',

Laddar…
Avbryt
Spara