Browse Source

[RN] Refactor Toolbox

Create standalone components for each feature and move all state to them.
Toolbars are now dummy containers.
master
Saúl Ibarra Corretgé 7 years ago
parent
commit
a2834a2495

+ 3
- 0
lang/main.json View File

@@ -74,6 +74,7 @@
74 74
     "toolbar": {
75 75
         "addPeople": "Add people to your call",
76 76
         "audioonly": "Enable / Disable audio only mode (saves bandwidth)",
77
+        "audioRoute": "Select the audio route",
77 78
         "callQuality": "Manage call quality",
78 79
         "enterFullScreen": "View full screen",
79 80
         "exitFullScreen": "Exit full screen",
@@ -87,6 +88,7 @@
87 88
         "etherpad": "Open / Close shared document",
88 89
         "documentOpen": "Open shared document",
89 90
         "documentClose": "Close shared document",
91
+        "shareRoom": "Share room",
90 92
         "sharedvideo": "Share a YouTube video",
91 93
         "stopSharedVideo": "Stop YouTube video",
92 94
         "fullscreen": "View / Exit full screen",
@@ -102,6 +104,7 @@
102 104
         "cameraDisabled": "Camera is not available",
103 105
         "micDisabled": "Microphone is not available",
104 106
         "filmstrip": "Show / Hide videos",
107
+        "pip": "Enter Picture-in-Picture mode",
105 108
         "profile": "Edit your profile",
106 109
         "raiseHand": "Raise / Lower your hand",
107 110
         "shortcuts": "View shortcuts",

+ 0
- 0
react/features/invite/components/InviteButton.web.js View File


+ 0
- 1
react/features/invite/components/index.js View File

@@ -1,4 +1,3 @@
1 1
 export { default as AddPeopleDialog } from './AddPeopleDialog';
2 2
 export { default as InfoDialogButton } from './InfoDialogButton';
3
-export { default as InviteButton } from './InviteButton';
4 3
 export { DialInSummary } from './dial-in-summary';

+ 0
- 111
react/features/mobile/picture-in-picture/components/EnterPictureInPictureToolbarButton.js View File

@@ -1,111 +0,0 @@
1
-// @flow
2
-
3
-import React, { Component } from 'react';
4
-import { connect } from 'react-redux';
5
-
6
-import { getAppProp } from '../../../app';
7
-import { ToolbarButton } from '../../../toolbox';
8
-
9
-import { enterPictureInPicture } from '../actions';
10
-
11
-/**
12
- * The type of {@link EnterPictureInPictureToobarButton}'s React
13
- * {@code Component} props.
14
- */
15
-type Props = {
16
-
17
-    /**
18
-     * Enters (or rather initiates entering) picture-in-picture.
19
-     *
20
-     * @protected
21
-     */
22
-    _onEnterPictureInPicture: Function,
23
-
24
-    /**
25
-     * The indicator which determines whether Picture-in-Picture is enabled.
26
-     *
27
-     * @protected
28
-     */
29
-    _pictureInPictureEnabled: boolean
30
-};
31
-
32
-/**
33
- * Implements a {@link ToolbarButton} to enter Picture-in-Picture.
34
- */
35
-class EnterPictureInPictureToolbarButton extends Component<Props> {
36
-    /**
37
-     * Implements React's {@link Component#render()}.
38
-     *
39
-     * @inheritdoc
40
-     * @returns {ReactElement}
41
-     */
42
-    render() {
43
-        const {
44
-            _onEnterPictureInPicture,
45
-            _pictureInPictureEnabled,
46
-            ...props
47
-        } = this.props;
48
-
49
-        if (!_pictureInPictureEnabled) {
50
-            return null;
51
-        }
52
-
53
-        return (
54
-            <ToolbarButton
55
-                iconName = { 'menu-down' }
56
-                onClick = { _onEnterPictureInPicture }
57
-                { ...props } />
58
-        );
59
-    }
60
-}
61
-
62
-/**
63
- * Maps redux actions to {@link EnterPictureInPictureToolbarButton}'s React
64
- * {@code Component} props.
65
- *
66
- * @param {Function} dispatch - The redux action {@code dispatch} function.
67
- * @returns {{
68
- * }}
69
- * @private
70
- */
71
-function _mapDispatchToProps(dispatch) {
72
-    return {
73
-
74
-        /**
75
-         * Requests Picture-in-Picture mode.
76
-         *
77
-         * @private
78
-         * @returns {void}
79
-         * @type {Function}
80
-         */
81
-        _onEnterPictureInPicture() {
82
-            dispatch(enterPictureInPicture());
83
-        }
84
-    };
85
-}
86
-
87
-/**
88
- * Maps (parts of) the redux state to
89
- * {@link EnterPictureInPictureToolbarButton}'s React {@code Component} props.
90
- *
91
- * @param {Object} state - The redux store/state.
92
- * @private
93
- * @returns {{
94
- * }}
95
- */
96
-function _mapStateToProps(state) {
97
-    return {
98
-
99
-        /**
100
-         * The indicator which determines whether Picture-in-Picture is enabled.
101
-         *
102
-         * @protected
103
-         * @type {boolean}
104
-         */
105
-        _pictureInPictureEnabled:
106
-            Boolean(getAppProp(state, 'pictureInPictureEnabled'))
107
-    };
108
-}
109
-
110
-export default connect(_mapStateToProps, _mapDispatchToProps)(
111
-    EnterPictureInPictureToolbarButton);

+ 0
- 2
react/features/mobile/picture-in-picture/components/index.js View File

@@ -1,2 +0,0 @@
1
-export { default as EnterPictureInPictureToolbarButton }
2
-    from './EnterPictureInPictureToolbarButton';

+ 0
- 1
react/features/mobile/picture-in-picture/index.js View File

@@ -1,3 +1,2 @@
1 1
 export * from './actions';
2 2
 export * from './actionTypes';
3
-export * from './components';

+ 68
- 229
react/features/toolbox/components/Toolbox.native.js View File

@@ -4,91 +4,75 @@ import React, { Component } from 'react';
4 4
 import { View } from 'react-native';
5 5
 import { connect } from 'react-redux';
6 6
 
7
-import { toggleAudioOnly } from '../../base/conference';
8
-import {
9
-    MEDIA_TYPE,
10
-    toggleCameraFacingMode
11
-} from '../../base/media';
12 7
 import { Container } from '../../base/react';
13 8
 import {
14 9
     isNarrowAspectRatio,
15 10
     makeAspectRatioAware
16 11
 } from '../../base/responsive-ui';
17 12
 import { ColorPalette } from '../../base/styles';
18
-import { InviteButton } from '../../invite';
19
-import {
20
-    EnterPictureInPictureToolbarButton
21
-} from '../../mobile/picture-in-picture';
22
-import { beginRoomLockRequest } from '../../room-lock';
13
+
14
+import styles from './styles';
23 15
 
24 16
 import {
25
-    abstractMapDispatchToProps,
26
-    abstractMapStateToProps
27
-} from '../functions';
17
+    AudioMuteButton,
18
+    AudioOnlyButton,
19
+    AudioRouteButton,
20
+    HangupButton,
21
+    PictureInPictureButton,
22
+    RoomLockButton,
23
+    InviteButton,
24
+    ToggleCameraButton,
25
+    VideoMuteButton
26
+} from './buttons';
28 27
 
29
-import AudioRouteButton from './AudioRouteButton';
30
-import styles from './styles';
31
-import ToolbarButton from './ToolbarButton';
28
+/**
29
+ * Styles for the hangup button.
30
+ */
31
+const hangupButtonStyles = {
32
+    iconStyle: styles.whitePrimaryToolbarButtonIcon,
33
+    style: styles.hangup,
34
+    underlayColor: ColorPalette.buttonUnderlay
35
+};
32 36
 
33
-import { AudioMuteButton, HangupButton, VideoMuteButton } from './buttons';
37
+/**
38
+ * Styles for buttons in the primary toolbar.
39
+ */
40
+const primaryToolbarButtonStyles = {
41
+    iconStyle: styles.primaryToolbarButtonIcon,
42
+    style: styles.primaryToolbarButton
43
+};
34 44
 
35 45
 /**
36
- * The type of {@link Toolbox}'s React {@code Component} props.
46
+ * Styles for buttons in the primary toolbar.
37 47
  */
38
-type Props = {
48
+const primaryToolbarToggledButtonStyles = {
49
+    iconStyle: styles.whitePrimaryToolbarButtonIcon,
50
+    style: styles.whitePrimaryToolbarButton
51
+};
39 52
 
40
-    /**
41
-     * Flag showing that audio is muted.
42
-     */
43
-    _audioMuted: boolean,
53
+/**
54
+ * Styles for buttons in the secondary toolbar.
55
+ */
56
+const secondaryToolbarButtonStyles = {
57
+    iconStyle: styles.secondaryToolbarButtonIcon,
58
+    style: styles.secondaryToolbarButton,
59
+    underlayColor: 'transparent'
60
+};
44 61
 
45
-    /**
46
-     * Flag showing whether the audio-only mode is in use.
47
-     */
48
-    _audioOnly: boolean,
62
+/**
63
+ * The type of {@link Toolbox}'s React {@code Component} props.
64
+ */
65
+type Props = {
49 66
 
50 67
     /**
51 68
      * The indicator which determines whether the toolbox is enabled.
52 69
      */
53 70
     _enabled: boolean,
54 71
 
55
-    /**
56
-     * Flag showing whether room is locked.
57
-     */
58
-    _locked: boolean,
59
-
60
-    /**
61
-     * Handler for hangup.
62
-     */
63
-    _onHangup: Function,
64
-
65
-    /**
66
-     * Sets the lock i.e. password protection of the conference/room.
67
-     */
68
-    _onRoomLock: Function,
69
-
70
-    /**
71
-     * Toggles the audio-only flag of the conference.
72
-     */
73
-    _onToggleAudioOnly: Function,
74
-
75
-    /**
76
-     * Switches between the front/user-facing and back/environment-facing
77
-     * cameras.
78
-     */
79
-    _onToggleCameraFacingMode: Function,
80
-
81
-    /**
82
-     * Flag showing whether video is muted.
83
-     */
84
-    _videoMuted: boolean,
85
-
86 72
     /**
87 73
      * Flag showing whether toolbar is visible.
88 74
      */
89
-    _visible: boolean,
90
-
91
-    dispatch: Function
75
+    _visible: boolean
92 76
 };
93 77
 
94 78
 /**
@@ -120,36 +104,6 @@ class Toolbox extends Component<Props> {
120 104
         );
121 105
     }
122 106
 
123
-    /**
124
-     * Gets the styles for a button that toggles the mute state of a specific
125
-     * media type.
126
-     *
127
-     * @param {string} mediaType - The {@link MEDIA_TYPE} associated with the
128
-     * button to get styles for.
129
-     * @protected
130
-     * @returns {{
131
-     *     iconStyle: Object,
132
-     *     style: Object
133
-     * }}
134
-     */
135
-    _getMuteButtonStyles(mediaType) {
136
-        let iconStyle;
137
-        let style;
138
-
139
-        if (this.props[`_${mediaType}Muted`]) {
140
-            iconStyle = styles.whitePrimaryToolbarButtonIcon;
141
-            style = styles.whitePrimaryToolbarButton;
142
-        } else {
143
-            iconStyle = styles.primaryToolbarButtonIcon;
144
-            style = styles.primaryToolbarButton;
145
-        }
146
-
147
-        return {
148
-            iconStyle,
149
-            style
150
-        };
151
-    }
152
-
153 107
     /**
154 108
      * Renders the toolbar which contains the primary buttons such as hangup,
155 109
      * audio and video mute.
@@ -158,28 +112,20 @@ class Toolbox extends Component<Props> {
158 112
      * @returns {ReactElement}
159 113
      */
160 114
     _renderPrimaryToolbar() {
161
-        const audioButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.AUDIO);
162
-        const videoButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.VIDEO);
163
-        const hangupButtonStyles = {
164
-            iconStyle: styles.whitePrimaryToolbarButtonIcon,
165
-            style: styles.hangup,
166
-            underlayColor: ColorPalette.buttonUnderlay
167
-        };
168
-
169
-        /* eslint-disable react/jsx-handler-names */
170
-
171 115
         return (
172 116
             <View
173 117
                 key = 'primaryToolbar'
174 118
                 pointerEvents = 'box-none'
175 119
                 style = { styles.primaryToolbar }>
176
-                <AudioMuteButton styles = { audioButtonStyles } />
120
+                <AudioMuteButton
121
+                    styles = { primaryToolbarButtonStyles }
122
+                    toggledStyles = { primaryToolbarToggledButtonStyles } />
177 123
                 <HangupButton styles = { hangupButtonStyles } />
178
-                <VideoMuteButton styles = { videoButtonStyles } />
124
+                <VideoMuteButton
125
+                    styles = { primaryToolbarButtonStyles }
126
+                    toggledStyles = { primaryToolbarToggledButtonStyles } />
179 127
             </View>
180 128
         );
181
-
182
-        /* eslint-enable react/jsx-handler-names */
183 129
     }
184 130
 
185 131
     /**
@@ -190,62 +136,20 @@ class Toolbox extends Component<Props> {
190 136
      * @returns {ReactElement}
191 137
      */
192 138
     _renderSecondaryToolbar() {
193
-        const iconStyle = styles.secondaryToolbarButtonIcon;
194
-        const style = styles.secondaryToolbarButton;
195
-        const underlayColor = 'transparent';
196
-        const {
197
-            _audioOnly: audioOnly,
198
-            _videoMuted: videoMuted
199
-        } = this.props;
200
-
201
-        /* eslint-disable react/jsx-curly-spacing,react/jsx-handler-names */
202
-
203 139
         return (
204 140
             <View
205 141
                 key = 'secondaryToolbar'
206 142
                 pointerEvents = 'box-none'
207 143
                 style = { styles.secondaryToolbar }>
208
-                {
209
-                    AudioRouteButton
210
-                        && <AudioRouteButton
211
-                            iconName = { 'volume' }
212
-                            iconStyle = { iconStyle }
213
-                            style = { style }
214
-                            underlayColor = { underlayColor } />
215
-                }
216
-                <ToolbarButton
217
-                    disabled = { audioOnly || videoMuted }
218
-                    iconName = 'switch-camera'
219
-                    iconStyle = { iconStyle }
220
-                    onClick = { this.props._onToggleCameraFacingMode }
221
-                    style = { style }
222
-                    underlayColor = { underlayColor } />
223
-                <ToolbarButton
224
-                    iconName = { audioOnly ? 'visibility-off' : 'visibility' }
225
-                    iconStyle = { iconStyle }
226
-                    onClick = { this.props._onToggleAudioOnly }
227
-                    style = { style }
228
-                    underlayColor = { underlayColor } />
229
-                <ToolbarButton
230
-                    iconName = {
231
-                        this.props._locked ? 'security-locked' : 'security'
232
-                    }
233
-                    iconStyle = { iconStyle }
234
-                    onClick = { this.props._onRoomLock }
235
-                    style = { style }
236
-                    underlayColor = { underlayColor } />
237
-                <InviteButton
238
-                    iconStyle = { iconStyle }
239
-                    style = { style }
240
-                    underlayColor = { underlayColor } />
241
-                <EnterPictureInPictureToolbarButton
242
-                    iconStyle = { iconStyle }
243
-                    style = { style }
244
-                    underlayColor = { underlayColor } />
144
+                <AudioRouteButton styles = { secondaryToolbarButtonStyles } />
145
+                <ToggleCameraButton styles = { secondaryToolbarButtonStyles } />
146
+                <AudioOnlyButton styles = { secondaryToolbarButtonStyles } />
147
+                <RoomLockButton styles = { secondaryToolbarButtonStyles } />
148
+                <InviteButton styles = { secondaryToolbarButtonStyles } />
149
+                <PictureInPictureButton
150
+                    styles = { secondaryToolbarButtonStyles } />
245 151
             </View>
246 152
         );
247
-
248
-        /* eslint-enable react/jsx-curly-spacing,react/jsx-handler-names */
249 153
     }
250 154
 
251 155
     /**
@@ -263,84 +167,21 @@ class Toolbox extends Component<Props> {
263 167
 }
264 168
 
265 169
 /**
266
- * Maps redux actions to {@link Toolbox}'s React {@code Component} props.
267
- *
268
- * @param {Function} dispatch - The redux action {@code dispatch} function.
269
- * @private
270
- * @returns {{
271
- *     _onRoomLock: Function,
272
- *     _onToggleAudioOnly: Function,
273
- *     _onToggleCameraFacingMode: Function,
274
- * }}
275
- */
276
-function _mapDispatchToProps(dispatch) {
277
-    return {
278
-        ...abstractMapDispatchToProps(dispatch),
279
-
280
-        /**
281
-         * Sets the lock i.e. password protection of the conference/room.
282
-         *
283
-         * @private
284
-         * @returns {void}
285
-         * @type {Function}
286
-         */
287
-        _onRoomLock() {
288
-            dispatch(beginRoomLockRequest());
289
-        },
290
-
291
-        /**
292
-         * Toggles the audio-only flag of the conference.
293
-         *
294
-         * @private
295
-         * @returns {void}
296
-         * @type {Function}
297
-         */
298
-        _onToggleAudioOnly() {
299
-            dispatch(toggleAudioOnly());
300
-        },
301
-
302
-        /**
303
-         * Switches between the front/user-facing and back/environment-facing
304
-         * cameras.
305
-         *
306
-         * @private
307
-         * @returns {void}
308
-         * @type {Function}
309
-         */
310
-        _onToggleCameraFacingMode() {
311
-            dispatch(toggleCameraFacingMode());
312
-        }
313
-    };
314
-}
315
-
316
-/**
317
- * Maps (parts of) the redux state to {@link Toolbox}'s React {@code Component}
170
+ * Maps parts of the redux state to {@link Toolbox} (React {@code Component})
318 171
  * props.
319 172
  *
320
- * @param {Object} state - The redux store/state.
321
- * @private
173
+ * @param {Object} state - The redux state of which parts are to be mapped to
174
+ * {@code Toolbox} props.
175
+ * @protected
322 176
  * @returns {{
323
- *     _audioOnly: boolean,
324 177
  *     _enabled: boolean,
325
- *     _locked: boolean
178
+ *     _visible: boolean
326 179
  * }}
327 180
  */
328
-function _mapStateToProps(state) {
329
-    const conference = state['features/base/conference'];
330
-    const { enabled } = state['features/toolbox'];
181
+function _mapStateToProps(state: Object): Object {
182
+    const { enabled, visible } = state['features/toolbox'];
331 183
 
332 184
     return {
333
-        ...abstractMapStateToProps(state),
334
-
335
-        /**
336
-         * The indicator which determines whether the conference is in
337
-         * audio-only mode.
338
-         *
339
-         * @protected
340
-         * @type {boolean}
341
-         */
342
-        _audioOnly: Boolean(conference.audioOnly),
343
-
344 185
         /**
345 186
          * The indicator which determines whether the toolbox is enabled.
346 187
          *
@@ -350,15 +191,13 @@ function _mapStateToProps(state) {
350 191
         _enabled: enabled,
351 192
 
352 193
         /**
353
-         * The indicator which determines whether the conference is
354
-         * locked/password-protected.
194
+         * Flag showing whether toolbox is visible.
355 195
          *
356 196
          * @protected
357 197
          * @type {boolean}
358 198
          */
359
-        _locked: Boolean(conference.locked)
199
+        _visible: visible
360 200
     };
361 201
 }
362 202
 
363
-export default connect(_mapStateToProps, _mapDispatchToProps)(
364
-    makeAspectRatioAware(Toolbox));
203
+export default connect(_mapStateToProps)(makeAspectRatioAware(Toolbox));

+ 3
- 3
react/features/toolbox/components/buttons/AbstractButton.js View File

@@ -37,7 +37,7 @@ export type Props = {
37 37
 /**
38 38
  * An abstract implementation of a button.
39 39
  */
40
-export default class AbstractButton<P: Props, S : *> extends Component<P, S> {
40
+export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
41 41
     static defaultProps = {
42 42
         showLabel: false,
43 43
         styles: undefined,
@@ -173,9 +173,9 @@ export default class AbstractButton<P: Props, S : *> extends Component<P, S> {
173 173
      * Implements React's {@link Component#render()}.
174 174
      *
175 175
      * @inheritdoc
176
-     * @returns {ReactElement}
176
+     * @returns {React$Node}
177 177
      */
178
-    render() {
178
+    render(): React$Node {
179 179
         const props = {
180 180
             ...this.props,
181 181
             accessibilityLabel: this.accessibilityLabel,

+ 84
- 0
react/features/toolbox/components/buttons/native/AudioOnlyButton.js View File

@@ -0,0 +1,84 @@
1
+// @flow
2
+
3
+import { connect } from 'react-redux';
4
+
5
+import { toggleAudioOnly } from '../../../../base/conference';
6
+import { translate } from '../../../../base/i18n';
7
+
8
+import AbstractButton from '../AbstractButton';
9
+import type { Props as AbstractButtonProps } from '../AbstractButton';
10
+
11
+type Props = AbstractButtonProps & {
12
+
13
+    /**
14
+     * Whether the current conference is in audio only mode or not.
15
+     */
16
+    _audioOnly: boolean,
17
+
18
+    /**
19
+     * The redux {@code dispatch} function.
20
+     */
21
+    dispatch: Function
22
+}
23
+
24
+/**
25
+ * An implementation of a button for toggling the audio-only mode.
26
+ */
27
+class AudioOnlyButton extends AbstractButton<Props, *> {
28
+    accessibilityLabel = 'Audio only mode';
29
+    iconName = 'visibility';
30
+    label = 'toolbar.audioonly';
31
+    toggledIconName = 'visibility-off';
32
+
33
+    /**
34
+     * Handles clicking / pressing the button.
35
+     *
36
+     * @private
37
+     * @returns {void}
38
+     */
39
+    _handleClick() {
40
+        this.props.dispatch(toggleAudioOnly());
41
+    }
42
+
43
+    /**
44
+     * Indicates whether this button is disabled or not.
45
+     *
46
+     * @override
47
+     * @private
48
+     * @returns {boolean}
49
+     */
50
+    _isDisabled() {
51
+        return false;
52
+    }
53
+
54
+    /**
55
+     * Indicates whether this button is in toggled state or not.
56
+     *
57
+     * @override
58
+     * @private
59
+     * @returns {boolean}
60
+     */
61
+    _isToggled() {
62
+        return this.props._audioOnly;
63
+    }
64
+}
65
+
66
+/**
67
+ * Maps (parts of) the redux state to the associated props for the
68
+ * {@code AudioOnlyButton} component.
69
+ *
70
+ * @param {Object} state - The Redux state.
71
+ * @private
72
+ * @returns {{
73
+ *     _audioOnly: boolean
74
+ * }}
75
+ */
76
+function _mapStateToProps(state): Object {
77
+    const { audioOnly } = state['features/base/conference'];
78
+
79
+    return {
80
+        _audioOnly: Boolean(audioOnly)
81
+    };
82
+}
83
+
84
+export default translate(connect(_mapStateToProps)(AudioOnlyButton));

react/features/toolbox/components/AudioRouteButton.js → react/features/toolbox/components/buttons/native/AudioRouteButton.js View File

@@ -1,6 +1,6 @@
1 1
 // @flow
2 2
 
3
-import React, { Component } from 'react';
3
+import React from 'react';
4 4
 import {
5 5
     findNodeHandle,
6 6
     NativeModules,
@@ -9,10 +9,13 @@ import {
9 9
 } from 'react-native';
10 10
 import { connect } from 'react-redux';
11 11
 
12
-import { openDialog } from '../../base/dialog';
13
-import { AudioRoutePickerDialog } from '../../mobile/audio-mode';
12
+import { openDialog } from '../../../../base/dialog';
13
+import { translate } from '../../../../base/i18n';
14
+import { AudioRoutePickerDialog } from '../../../../mobile/audio-mode';
15
+
16
+import AbstractButton from '../AbstractButton';
17
+import type { Props as AbstractButtonProps } from '../AbstractButton';
14 18
 
15
-import ToolbarButton from './ToolbarButton';
16 19
 
17 20
 /**
18 21
  * The {@code MPVolumeView} React {@code Component}. It will only be available
@@ -28,48 +31,32 @@ const MPVolumeView
28 31
  */
29 32
 const HIDE_VIEW_STYLE = { display: 'none' };
30 33
 
31
-type Props = {
34
+type Props = AbstractButtonProps & {
32 35
 
33 36
     /**
34 37
      * The redux {@code dispatch} function used to open/show the
35 38
      * {@code AudioRoutePickerDialog}.
36 39
      */
37
-    dispatch: Function,
38
-
39
-    /**
40
-     * The name of the Icon of this {@code AudioRouteButton}.
41
-     */
42
-    iconName: string,
43
-
44
-    /**
45
-     * The style of the Icon of this {@code AudioRouteButton}.
46
-     */
47
-    iconStyle: Object,
48
-
49
-    /**
50
-     * The style(s) of {@code AudioRouteButton}.
51
-     */
52
-    style: Array<*> | Object,
53
-
54
-    /**
55
-     * The color underlaying the button.
56
-     */
57
-    underlayColor: string
40
+    dispatch: Function
58 41
 };
59 42
 
60 43
 /**
61 44
  * A toolbar button which triggers an audio route picker when pressed.
62 45
  */
63
-class AudioRouteButton extends Component<Props> {
46
+class AudioRouteButton extends AbstractButton<Props, *> {
47
+    accessibilityLabel = 'Audio route';
48
+    iconName = 'icon-volume';
49
+    label = 'toolbar.audioRoute';
50
+
64 51
     _volumeComponent: ?Object;
65 52
 
66 53
     /**
67 54
      * Initializes a new {@code AudioRouteButton} instance.
68 55
      *
69
-     * @param {Object} props - The React {@code Component} props to initialize
56
+     * @param {Props} props - The React {@code Component} props to initialize
70 57
      * the new {@code AudioRouteButton} instance with.
71 58
      */
72
-    constructor(props) {
59
+    constructor(props: Props) {
73 60
         super(props);
74 61
 
75 62
         /**
@@ -77,25 +64,21 @@ class AudioRouteButton extends Component<Props> {
77 64
          * showing the volume control view.
78 65
          *
79 66
          * @private
80
-         * @type {ReactComponent}
67
+         * @type {ReactElement}
81 68
          */
82 69
         this._volumeComponent = null;
83 70
 
84 71
         // Bind event handlers so they are only bound once per instance.
85
-        this._onClick = this._onClick.bind(this);
86 72
         this._setVolumeComponent = this._setVolumeComponent.bind(this);
87 73
     }
88 74
 
89
-    _onClick: () => void;
90
-
91 75
     /**
92
-     * Handles clicking/pressing this {@code AudioRouteButton} by showing an
93
-     * audio route picker.
76
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
94 77
      *
95 78
      * @private
96 79
      * @returns {void}
97 80
      */
98
-    _onClick() {
81
+    _handleClick() {
99 82
         if (MPVolumeView) {
100 83
             NativeModules.MPVolumeViewManager.show(
101 84
                 findNodeHandle(this._volumeComponent));
@@ -104,23 +87,49 @@ class AudioRouteButton extends Component<Props> {
104 87
         }
105 88
     }
106 89
 
90
+    /**
91
+     * Indicates whether this button is disabled or not.
92
+     *
93
+     * @override
94
+     * @private
95
+     * @returns {boolean}
96
+     */
97
+    _isDisabled() {
98
+        return false;
99
+    }
100
+
101
+    _setVolumeComponent: (?Object) => void;
102
+
103
+    /**
104
+     * Sets the internal reference to the React Component wrapping the
105
+     * {@code MPVolumeView} component.
106
+     *
107
+     * @param {ReactElement} component - React Component.
108
+     * @private
109
+     * @returns {void}
110
+     */
111
+    _setVolumeComponent(component) {
112
+        this._volumeComponent = component;
113
+    }
114
+
107 115
     /**
108 116
      * Implements React's {@link Component#render()}.
109 117
      *
110 118
      * @inheritdoc
111
-     * @returns {ReactElement}
119
+     * @returns {?ReactElement}
112 120
      */
113 121
     render() {
114
-        const { iconName, iconStyle, style, underlayColor } = this.props;
122
+        if (!MPVolumeView && !AudioRoutePickerDialog) {
123
+
124
+            // $FlowFixMe
125
+            return null;
126
+        }
127
+
128
+        const element = super.render();
115 129
 
116 130
         return (
117 131
             <View>
118
-                <ToolbarButton
119
-                    iconName = { iconName }
120
-                    iconStyle = { iconStyle }
121
-                    onClick = { this._onClick }
122
-                    style = { style }
123
-                    underlayColor = { underlayColor } />
132
+                { element }
124 133
                 {
125 134
                     MPVolumeView
126 135
                         && <MPVolumeView
@@ -130,21 +139,6 @@ class AudioRouteButton extends Component<Props> {
130 139
             </View>
131 140
         );
132 141
     }
133
-
134
-    _setVolumeComponent: (?Object) => void;
135
-
136
-    /**
137
-     * Sets the internal reference to the React Component wrapping the
138
-     * {@code MPVolumeView} component.
139
-     *
140
-     * @param {ReactComponent} component - React Component.
141
-     * @private
142
-     * @returns {void}
143
-     */
144
-    _setVolumeComponent(component) {
145
-        this._volumeComponent = component;
146
-    }
147 142
 }
148 143
 
149
-export default (MPVolumeView || AudioRoutePickerDialog)
150
-  && connect()(AudioRouteButton);
144
+export default translate(connect()(AudioRouteButton));

react/features/invite/components/InviteButton.native.js → react/features/toolbox/components/buttons/native/InviteButton.js View File

@@ -1,29 +1,18 @@
1 1
 // @flow
2 2
 
3
-import React, { Component } from 'react';
4 3
 import { connect } from 'react-redux';
5 4
 
6
-import { beginShareRoom } from '../../share-room';
7
-import { ToolbarButton } from '../../toolbox';
5
+import {
6
+    beginAddPeople,
7
+    isAddPeopleEnabled,
8
+    isDialOutEnabled
9
+} from '../../../../invite';
10
+import { beginShareRoom } from '../../../../share-room';
8 11
 
9
-import { beginAddPeople } from '../actions';
10
-import { isAddPeopleEnabled, isDialOutEnabled } from '../functions';
12
+import AbstractButton from '../AbstractButton';
13
+import type { Props as AbstractButtonProps } from '../AbstractButton';
11 14
 
12
-/**
13
- * The indicator which determines (at bundle time) whether there should be a
14
- * {@code ToolbarButton} in {@code Toolbox} to expose the functionality of the
15
- * feature share-room in the user interface of the app.
16
- *
17
- * @private
18
- * @type {boolean}
19
- */
20
-const _SHARE_ROOM_TOOLBAR_BUTTON = true;
21
-
22
-/**
23
- * The type of {@link EnterPictureInPictureToobarButton}'s React
24
- * {@code Component} props.
25
- */
26
-type Props = {
15
+type Props = AbstractButtonProps & {
27 16
 
28 17
     /**
29 18
      * Whether or not the feature to directly invite people into the
@@ -50,45 +39,71 @@ type Props = {
50 39
     _onShareRoom: Function
51 40
 };
52 41
 
42
+/**
43
+ * The indicator which determines (at bundle time) whether there should be a
44
+ * button in {@code Toolbox} to expose the functionality of the feature
45
+ * share-room in the user interface of the app.
46
+ *
47
+ * @private
48
+ * @type {boolean}
49
+ */
50
+const _SHARE_ROOM_TOOLBAR_BUTTON = true;
51
+
53 52
 /**
54 53
  * Implements a {@link ToolbarButton} to enter Picture-in-Picture.
55 54
  */
56
-class InviteButton extends Component<Props> {
55
+class InviteButton extends AbstractButton<Props, *> {
56
+    accessibilityLabel = 'Share room';
57
+    iconName = 'icon-link';
58
+    label = 'toolbar.shareRoom';
57 59
 
58 60
     /**
59
-     * Implements React's {@link Component#render()}.
61
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
60 62
      *
61
-     * @inheritdoc
62
-     * @returns {ReactElement}
63
+     * @private
64
+     * @returns {void}
63 65
      */
64
-    render() {
66
+    _handleClick() {
65 67
         const {
66 68
             _addPeopleEnabled,
67 69
             _dialOutEnabled,
68 70
             _onAddPeople,
69
-            _onShareRoom,
70
-            ...props
71
+            _onShareRoom
71 72
         } = this.props;
72 73
 
73 74
         if (_addPeopleEnabled || _dialOutEnabled) {
74
-            return (
75
-                <ToolbarButton
76
-                    iconName = { 'link' }
77
-                    onClick = { _onAddPeople }
78
-                    { ...props } />
79
-            );
75
+            _onAddPeople();
76
+        } else if (_SHARE_ROOM_TOOLBAR_BUTTON) {
77
+            _onShareRoom();
80 78
         }
79
+    }
81 80
 
82
-        if (_SHARE_ROOM_TOOLBAR_BUTTON) {
83
-            return (
84
-                <ToolbarButton
85
-                    iconName = 'link'
86
-                    onClick = { _onShareRoom }
87
-                    { ...props } />
88
-            );
89
-        }
81
+    /**
82
+     * Indicates whether this button is disabled or not.
83
+     *
84
+     * @override
85
+     * @private
86
+     * @returns {boolean}
87
+     */
88
+    _isDisabled() {
89
+        return false;
90
+    }
90 91
 
91
-        return null;
92
+    /**
93
+     * Implements React's {@link Component#render()}.
94
+     *
95
+     * @inheritdoc
96
+     * @returns {React$Node}
97
+     */
98
+    render() {
99
+        const { _addPeopleEnabled, _dialOutEnabled } = this.props;
100
+
101
+        return (
102
+            _SHARE_ROOM_TOOLBAR_BUTTON
103
+                    || _addPeopleEnabled
104
+                    || _dialOutEnabled
105
+                ? super.render()
106
+                : null);
92 107
     }
93 108
 }
94 109
 

+ 87
- 0
react/features/toolbox/components/buttons/native/PictureInPictureButton.js View File

@@ -0,0 +1,87 @@
1
+// @flow
2
+
3
+import { connect } from 'react-redux';
4
+
5
+import { getAppProp } from '../../../../app';
6
+import { translate } from '../../../../base/i18n';
7
+import { enterPictureInPicture } from '../../../../mobile/picture-in-picture';
8
+
9
+import AbstractButton from '../AbstractButton';
10
+import type { Props as AbstractButtonProps } from '../AbstractButton';
11
+
12
+type Props = AbstractButtonProps & {
13
+
14
+    /**
15
+     * Whether Picture-in-Picture is enabled or not.
16
+     */
17
+    _enabled: boolean,
18
+
19
+    /**
20
+     * The redux {@code dispatch} function.
21
+     */
22
+    dispatch: Function
23
+};
24
+
25
+/**
26
+ * An implementation of a button for entering Picture-in-Picture mode.
27
+ */
28
+class PictureInPictureButton extends AbstractButton<Props, *> {
29
+    accessibilityLabel = 'Picture in picture';
30
+    iconName = 'icon-menu-down';
31
+    label = 'toolbar.pip';
32
+
33
+    /**
34
+     * Handles clicking / pressing the button.
35
+     *
36
+     * @private
37
+     * @returns {void}
38
+     */
39
+    _handleClick() {
40
+        this.props.dispatch(enterPictureInPicture());
41
+    }
42
+
43
+    /**
44
+     * Indicates whether this button is disabled or not.
45
+     *
46
+     * @override
47
+     * @private
48
+     * @returns {boolean}
49
+     */
50
+    _isDisabled() {
51
+        return false;
52
+    }
53
+
54
+    /**
55
+     * Implements React's {@link Component#render()}.
56
+     *
57
+     * @inheritdoc
58
+     * @returns {?ReactElement}
59
+     */
60
+    render() {
61
+        if (!this.props._enabled) {
62
+
63
+            // $FlowFixMe
64
+            return null;
65
+        }
66
+
67
+        return super.render();
68
+    }
69
+}
70
+
71
+/**
72
+ * Maps (parts of) the redux state to the associated props for the
73
+ * {@code PictureInPictureButton} component.
74
+ *
75
+ * @param {Object} state - The Redux state.
76
+ * @private
77
+ * @returns {{
78
+ *     _enabled: boolean
79
+ * }}
80
+ */
81
+function _mapStateToProps(state): Object {
82
+    return {
83
+        _enabled: Boolean(getAppProp(state, 'pictureInPictureEnabled'))
84
+    };
85
+}
86
+
87
+export default translate(connect(_mapStateToProps)(PictureInPictureButton));

+ 90
- 0
react/features/toolbox/components/buttons/native/RoomLockButton.js View File

@@ -0,0 +1,90 @@
1
+// @flow
2
+
3
+import { connect } from 'react-redux';
4
+
5
+import { translate } from '../../../../base/i18n';
6
+import { beginRoomLockRequest } from '../../../../room-lock';
7
+
8
+import AbstractButton from '../AbstractButton';
9
+import type { Props as AbstractButtonProps } from '../AbstractButton';
10
+
11
+type Props = AbstractButtonProps & {
12
+
13
+    /**
14
+     * The current conference.
15
+     */
16
+    _conference: Object,
17
+
18
+    /**
19
+     * Whether the current conference is locked or not.
20
+     */
21
+    _locked: boolean,
22
+
23
+    /**
24
+     * The redux {@code dispatch} function.
25
+     */
26
+    dispatch: Function
27
+}
28
+
29
+/**
30
+ * An implementation of a button for locking / unlocking a room.
31
+ */
32
+class RoomLockButton extends AbstractButton<Props, *> {
33
+    accessibilityLabel = 'Room lock';
34
+    iconName = 'security';
35
+    label = 'toolbar.lock';
36
+    toggledIconName = 'security-locked';
37
+
38
+    /**
39
+     * Handles clicking / pressing the button.
40
+     *
41
+     * @private
42
+     * @returns {void}
43
+     */
44
+    _handleClick() {
45
+        this.props.dispatch(beginRoomLockRequest());
46
+    }
47
+
48
+    /**
49
+     * Indicates whether this button is disabled or not.
50
+     *
51
+     * @override
52
+     * @private
53
+     * @returns {boolean}
54
+     */
55
+    _isDisabled() {
56
+        return !this.props._conference;
57
+    }
58
+
59
+    /**
60
+     * Indicates whether this button is in toggled state or not.
61
+     *
62
+     * @override
63
+     * @private
64
+     * @returns {boolean}
65
+     */
66
+    _isToggled() {
67
+        return this.props._locked;
68
+    }
69
+}
70
+
71
+/**
72
+ * Maps (parts of) the redux state to the associated props for the
73
+ * {@code RoomLockButton} component.
74
+ *
75
+ * @param {Object} state - The Redux state.
76
+ * @private
77
+ * @returns {{
78
+ *     _audioOnly: boolean
79
+ * }}
80
+ */
81
+function _mapStateToProps(state): Object {
82
+    const { conference, locked } = state['features/base/conference'];
83
+
84
+    return {
85
+        _conference: conference,
86
+        _locked: Boolean(conference && locked)
87
+    };
88
+}
89
+
90
+export default translate(connect(_mapStateToProps)(RoomLockButton));

+ 50
- 0
react/features/toolbox/components/buttons/native/ShareRoomButton.js View File

@@ -0,0 +1,50 @@
1
+// @flow
2
+
3
+import { connect } from 'react-redux';
4
+
5
+import { translate } from '../../../../base/i18n';
6
+import { beginShareRoom } from '../../../../share-room';
7
+
8
+import AbstractButton from '../AbstractButton';
9
+import type { Props as AbstractButtonProps } from '../AbstractButton';
10
+
11
+type Props = AbstractButtonProps & {
12
+
13
+    /**
14
+     * The redux {@code dispatch} function.
15
+     */
16
+    dispatch: Function
17
+}
18
+
19
+/**
20
+ * An implementation of a button for sharing a room using the native OS sharing
21
+ * capabilities.
22
+ */
23
+class ShareRoomButton extends AbstractButton<Props, *> {
24
+    accessibilityLabel = 'Share room';
25
+    iconName = 'icon-link';
26
+    label = 'toolbar.shareRoom';
27
+
28
+    /**
29
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
30
+     *
31
+     * @private
32
+     * @returns {void}
33
+     */
34
+    _handleClick() {
35
+        this.props.dispatch(beginShareRoom());
36
+    }
37
+
38
+    /**
39
+     * Indicates whether this button is disabled or not.
40
+     *
41
+     * @override
42
+     * @private
43
+     * @returns {boolean}
44
+     */
45
+    _isDisabled() {
46
+        return false;
47
+    }
48
+}
49
+
50
+export default translate(connect()(ShareRoomButton));

+ 81
- 0
react/features/toolbox/components/buttons/native/ToggleCameraButton.js View File

@@ -0,0 +1,81 @@
1
+// @flow
2
+
3
+import { connect } from 'react-redux';
4
+
5
+import { translate } from '../../../../base/i18n';
6
+import { MEDIA_TYPE, toggleCameraFacingMode } from '../../../../base/media';
7
+import { isLocalTrackMuted } from '../../../../base/tracks';
8
+
9
+import AbstractButton from '../AbstractButton';
10
+import type { Props as AbstractButtonProps } from '../AbstractButton';
11
+
12
+type Props = AbstractButtonProps & {
13
+
14
+    /**
15
+     * Whether the current conference is in audio only mode or not.
16
+     */
17
+    _audioOnly: boolean,
18
+
19
+    /**
20
+     * Whether video is currently muted or not.
21
+     */
22
+    _videoMuted: boolean,
23
+
24
+    /**
25
+     * The redux {@code dispatch} function.
26
+     */
27
+    dispatch: Function
28
+}
29
+
30
+/**
31
+ * An implementation of a button for toggling the camera facing mode.
32
+ */
33
+class ToggleCameraButton extends AbstractButton<Props, *> {
34
+    accessibilityLabel = 'Share room';
35
+    iconName = 'icon-switch-camera';
36
+    label = 'toolbar.switchCamera';
37
+
38
+    /**
39
+     * Handles clicking / pressing the button.
40
+     *
41
+     * @private
42
+     * @returns {void}
43
+     */
44
+    _handleClick() {
45
+        this.props.dispatch(toggleCameraFacingMode());
46
+    }
47
+
48
+    /**
49
+     * Indicates whether this button is disabled or not.
50
+     *
51
+     * @override
52
+     * @private
53
+     * @returns {boolean}
54
+     */
55
+    _isDisabled() {
56
+        return this.props._audioOnly || this.props._videoMuted;
57
+    }
58
+}
59
+
60
+/**
61
+ * Maps (parts of) the redux state to the associated props for the
62
+ * {@code ToggleCameraButton} component.
63
+ *
64
+ * @param {Object} state - The Redux state.
65
+ * @private
66
+ * @returns {{
67
+ *     _audioOnly: boolean,
68
+ *     _videoMuted: boolean
69
+ * }}
70
+ */
71
+function _mapStateToProps(state): Object {
72
+    const { audioOnly } = state['features/base/conference'];
73
+    const tracks = state['features/base/tracks'];
74
+
75
+    return {
76
+        _audioOnly: Boolean(audioOnly),
77
+        _videoMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.VIDEO)
78
+    };
79
+}
80
+
81
+export default translate(connect(_mapStateToProps)(ToggleCameraButton));

+ 6
- 0
react/features/toolbox/components/buttons/native/index.js View File

@@ -0,0 +1,6 @@
1
+export { default as AudioOnlyButton } from './AudioOnlyButton';
2
+export { default as AudioRouteButton } from './AudioRouteButton';
3
+export { default as InviteButton } from './InviteButton';
4
+export { default as PictureInPictureButton } from './PictureInPictureButton';
5
+export { default as RoomLockButton } from './RoomLockButton';
6
+export { default as ToggleCameraButton } from './ToggleCameraButton';

+ 9
- 0
react/features/toolbox/components/styles.js View File

@@ -80,6 +80,15 @@ export default createStyleSheet({
80 80
         backgroundColor: ColorPalette.red
81 81
     },
82 82
 
83
+    /**
84
+     * The icon style of toolbar buttons in {@link #primaryToolbar} which
85
+     * hangs the current conference up.
86
+     */
87
+    hangupButtonIcon: {
88
+        ...primaryToolbarButtonIcon,
89
+        color: ColorPalette.white
90
+    },
91
+
83 92
     /**
84 93
      * The style of the toolbar which contains the primary buttons such as
85 94
      * hangup, audio and video mute.

+ 0
- 102
react/features/toolbox/functions.native.js View File

@@ -1,102 +0,0 @@
1
-// @flow
2
-
3
-import { appNavigate } from '../app';
4
-import { MEDIA_TYPE } from '../base/media';
5
-import { isLocalTrackMuted } from '../base/tracks';
6
-
7
-import type { Dispatch } from 'redux';
8
-
9
-/**
10
- * Maps redux actions to {@link Toolbox} (React {@code Component}) props.
11
- *
12
- * @param {Function} dispatch - The redux {@code dispatch} function.
13
- * @private
14
- * @returns {{
15
- *     _onHangup: Function,
16
- *     _onToggleAudio: Function,
17
- *     _onToggleVideo: Function
18
- * }}
19
- */
20
-export function abstractMapDispatchToProps(dispatch: Dispatch<*>): Object {
21
-    return {
22
-        // Inject {@code dispatch} into the React Component's props in case it
23
-        // needs to dispatch an action in the redux store without
24
-        // {@code mapDispatchToProps}.
25
-        dispatch,
26
-
27
-        /**
28
-         * Dispatches action to leave the current conference.
29
-         *
30
-         * @private
31
-         * @returns {void}
32
-         * @type {Function}
33
-         */
34
-        _onHangup() {
35
-            // XXX We don't know here which value is effectively/internally
36
-            // used when there's no valid room name to join. It isn't our
37
-            // business to know that anyway. The undefined value is our
38
-            // expression of (1) the lack of knowledge & (2) the desire to no
39
-            // longer have a valid room name to join.
40
-            dispatch(appNavigate(undefined));
41
-        }
42
-    };
43
-}
44
-
45
-/**
46
- * Maps parts of the redux state to {@link Toolbox} (React {@code Component})
47
- * props.
48
- *
49
- * @param {Object} state - The redux state of which parts are to be mapped to
50
- * {@code Toolbox} props.
51
- * @protected
52
- * @returns {{
53
- *     _audioMuted: boolean,
54
- *     _videoMuted: boolean,
55
- *     _visible: boolean
56
- * }}
57
- */
58
-export function abstractMapStateToProps(state: Object): Object {
59
-    const tracks = state['features/base/tracks'];
60
-    const { visible } = state['features/toolbox'];
61
-
62
-    return {
63
-        /**
64
-         * Flag showing whether audio is muted.
65
-         *
66
-         * @protected
67
-         * @type {boolean}
68
-         */
69
-        _audioMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO),
70
-
71
-        /**
72
-         * Flag showing whether video is muted.
73
-         *
74
-         * @protected
75
-         * @type {boolean}
76
-         */
77
-        _videoMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.VIDEO),
78
-
79
-        /**
80
-         * Flag showing whether toolbox is visible.
81
-         *
82
-         * @protected
83
-         * @type {boolean}
84
-         */
85
-        _visible: visible
86
-    };
87
-}
88
-
89
-/**
90
- * Returns the button object corresponding to a specific {@code buttonName}.
91
- *
92
- * @param {string} buttonName - The name of the button.
93
- * @param {Object} state - The current state.
94
- * @returns {Object} - The button object.
95
- */
96
-export function getButton(buttonName: string, state: Object) {
97
-    const { primaryToolbarButtons, secondaryToolbarButtons }
98
-        = state['features/toolbox'];
99
-
100
-    return primaryToolbarButtons.get(buttonName)
101
-        || secondaryToolbarButtons.get(buttonName);
102
-}

+ 0
- 6
react/features/toolbox/functions.web.js View File

@@ -2,12 +2,6 @@
2 2
 
3 3
 declare var interfaceConfig: Object;
4 4
 
5
-export {
6
-    abstractMapDispatchToProps,
7
-    abstractMapStateToProps,
8
-    getButton
9
-} from './functions.native';
10
-
11 5
 /**
12 6
  * Helper for getting the height of the toolbox.
13 7
  *

Loading…
Cancel
Save