浏览代码

feat(toolbox): implement buttons using ToolboxItem

Currently the following are implemented:

- AudioMuteButton
- HangupButton
- VideoMuteButton

In order to implement these new buttons a new abstract class was introduced,
which abstracts the ToolboxItem into a button with enough hooks so a stateful
and a stateless version of it can be created.

This patch only adds the stateful implementation of the aforementioned buttons.
master
Saúl Ibarra Corretgé 7 年前
父节点
当前提交
b634f6b200

+ 7
- 1
react/features/toolbox/components/Toolbox.native.js 查看文件

@@ -14,6 +14,7 @@ import {
14 14
     isNarrowAspectRatio,
15 15
     makeAspectRatioAware
16 16
 } from '../../base/responsive-ui';
17
+import { ColorPalette } from '../../base/styles';
17 18
 import { InviteButton } from '../../invite';
18 19
 import {
19 20
     EnterPictureInPictureToolbarButton
@@ -159,6 +160,11 @@ class Toolbox extends Component<Props> {
159 160
     _renderPrimaryToolbar() {
160 161
         const audioButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.AUDIO);
161 162
         const videoButtonStyles = this._getMuteButtonStyles(MEDIA_TYPE.VIDEO);
163
+        const hangupButtonStyles = {
164
+            iconStyle: styles.whitePrimaryToolbarButtonIcon,
165
+            style: styles.hangup,
166
+            underlayColor: ColorPalette.buttonUnderlay
167
+        };
162 168
 
163 169
         /* eslint-disable react/jsx-handler-names */
164 170
 
@@ -168,7 +174,7 @@ class Toolbox extends Component<Props> {
168 174
                 pointerEvents = 'box-none'
169 175
                 style = { styles.primaryToolbar }>
170 176
                 <AudioMuteButton styles = { audioButtonStyles } />
171
-                <HangupButton />
177
+                <HangupButton styles = { hangupButtonStyles } />
172 178
                 <VideoMuteButton styles = { videoButtonStyles } />
173 179
             </View>
174 180
         );

+ 6
- 6
react/features/toolbox/components/Toolbox.web.js 查看文件

@@ -354,12 +354,12 @@ class Toolbox extends Component<Props, State> {
354 354
                         </div> }
355 355
                 </div>
356 356
                 <div className = 'button-group-center'>
357
-                    { this._shouldShowButton('microphone')
358
-                        && <AudioMuteButton /> }
359
-                    { this._shouldShowButton('hangup')
360
-                        && <HangupButton /> }
361
-                    { this._shouldShowButton('camera')
362
-                        && <VideoMuteButton /> }
357
+                    <AudioMuteButton
358
+                        visible = { this._shouldShowButton('microphone') } />
359
+                    <HangupButton
360
+                        visible = { this._shouldShowButton('hangup') } />
361
+                    <VideoMuteButton
362
+                        visible = { this._shouldShowButton('camera') } />
363 363
                 </div>
364 364
                 <div className = 'button-group-right'>
365 365
                     { this._shouldShowButton('invite')

+ 34
- 59
react/features/toolbox/components/buttons/AbstractAudioMuteButton.js 查看文件

@@ -1,87 +1,62 @@
1 1
 // @flow
2 2
 
3
-import PropTypes from 'prop-types';
4
-import { Component } from 'react';
5
-
6
-import {
7
-    AUDIO_MUTE,
8
-    createToolbarEvent,
9
-    sendAnalytics
10
-} from '../../../analytics';
11
-import {
12
-    VIDEO_MUTISM_AUTHORITY,
13
-    setAudioMuted
14
-} from '../../../base/media';
3
+import AbstractButton from './AbstractButton';
4
+import type { Props } from './AbstractButton';
15 5
 
16 6
 /**
17 7
  * An abstract implementation of a button for toggling audio mute.
18 8
  */
19
-export default class AbstractAudioMuteButton extends Component<*> {
9
+class AbstractAudioMuteButton<P: Props, S: *> extends AbstractButton<P, S> {
10
+    accessibilityLabel = 'Audio mute';
11
+    iconName = 'icon-microphone';
12
+    toggledIconName = 'icon-mic-disabled toggled';
13
+
20 14
     /**
21
-     * {@code AbstractAudioMuteButton} component's property types.
15
+     * Handles clicking / pressing the button, and toggles the audio mute state
16
+     * accordingly.
22 17
      *
23
-     * @static
18
+     * @override
19
+     * @private
20
+     * @returns {void}
24 21
      */
25
-    static propTypes = {
26
-        /**
27
-         * Whether or not the local microphone is muted.
28
-         */
29
-        _audioMuted: PropTypes.bool,
30
-
31
-        /**
32
-         * Invoked to toggle audio mute.
33
-         */
34
-        dispatch: PropTypes.func
35
-    };
22
+    _handleClick() {
23
+        this._setAudioMuted(!this._isAudioMuted());
24
+    }
36 25
 
37 26
     /**
38
-     * Initializes a new {@code AbstractAudioMuteButton} instance.
27
+     * Helper function to be implemented by subclasses, which must return a
28
+     * boolean value indicating if audio is muted or not.
39 29
      *
40
-     * @param {Props} props - The read-only React {@code Component} props with
41
-     * which the new instance is to be initialized.
30
+     * @abstract
31
+     * @private
32
+     * @returns {boolean}
42 33
      */
43
-    constructor(props: Object) {
44
-        super(props);
45
-
46
-        // Bind event handler so it is only bound once per instance.
47
-        this._onToolbarToggleAudio = this._onToolbarToggleAudio.bind(this);
34
+    _isAudioMuted() {
35
+        // To be implemented by subclass.
48 36
     }
49 37
 
50 38
     /**
51
-     * Dispatches an action to toggle audio mute.
39
+     * Indicates whether this button is in toggled state or not.
52 40
      *
41
+     * @override
53 42
      * @private
54
-     * @returns {void}
43
+     * @returns {boolean}
55 44
      */
56
-    _doToggleAudio() {
57
-        // The user sees the reality i.e. the state of base/tracks and intends
58
-        // to change reality by tapping on the respective button i.e. the user
59
-        // sets the state of base/media. Whether the user's intention will turn
60
-        // into reality is a whole different story which is of no concern to the
61
-        // tapping.
62
-        this.props.dispatch(
63
-            setAudioMuted(
64
-                !this.props._audioMuted,
65
-                VIDEO_MUTISM_AUTHORITY.USER,
66
-                /* ensureTrack */ true));
45
+    _isToggled() {
46
+        return this._isAudioMuted();
67 47
     }
68 48
 
69
-    _onToolbarToggleAudio: () => void;
70
-
71 49
     /**
72
-     * Creates an analytics toolbar event and dispatches an action for toggling
73
-     * audio mute.
50
+     * Helper function to perform the actual setting of the audio mute / unmute
51
+     * action.
74 52
      *
53
+     * @param {boolean} audioMuted - Whether video should be muted or not.
75 54
      * @private
76 55
      * @returns {void}
77 56
      */
78
-    _onToolbarToggleAudio() {
79
-        sendAnalytics(createToolbarEvent(
80
-            AUDIO_MUTE,
81
-            {
82
-                enable: !this.props._audioMuted
83
-            }));
84
-
85
-        this._doToggleAudio();
57
+    _setAudioMuted(audioMuted: boolean) { // eslint-disable-line no-unused-vars
58
+        // To be implemented by subclass.
86 59
     }
87 60
 }
61
+
62
+export default AbstractAudioMuteButton;

+ 195
- 0
react/features/toolbox/components/buttons/AbstractButton.js 查看文件

@@ -0,0 +1,195 @@
1
+// @flow
2
+
3
+import React, { Component } from 'react';
4
+
5
+import ToolboxItem from '../ToolboxItem';
6
+import type { Styles } from '../AbstractToolboxItem';
7
+
8
+export type Props = {
9
+
10
+    /**
11
+     * Whether to show the label or not.
12
+     */
13
+    showLabel: boolean,
14
+
15
+    /**
16
+     * Collection of styles for the button.
17
+     */
18
+    styles: ?Styles,
19
+
20
+    /**
21
+     * Collection of styles for the button, when in toggled state.
22
+     */
23
+    toggledStyles: ?Styles,
24
+
25
+    /**
26
+     * From which direction the tooltip should appear, relative to the
27
+     * button.
28
+     */
29
+    tooltipPosition: string,
30
+
31
+    /**
32
+     * Whether this button is visible or not.
33
+     */
34
+    visible: boolean
35
+};
36
+
37
+/**
38
+ * An abstract implementation of a button.
39
+ */
40
+export default class AbstractButton<P: Props, S : *> extends Component<P, S> {
41
+    static defaultProps = {
42
+        showLabel: false,
43
+        styles: undefined,
44
+        toggledStyles: undefined,
45
+        tooltipPosition: 'top',
46
+        visible: true
47
+    };
48
+
49
+    /**
50
+     * A succinct description of what the button does. Used by accessibility
51
+     * tools and torture tests.
52
+     *
53
+     * @abstract
54
+     */
55
+    accessibilityLabel: string;
56
+
57
+    /**
58
+     * The name of the icon of this button.
59
+     *
60
+     * @abstract
61
+     */
62
+    iconName: string;
63
+
64
+    /**
65
+     * The text associated with this button. When `showLabel` is set to
66
+     * {@code true}, it will be displayed alongside the icon.
67
+     *
68
+     * @abstract
69
+     */
70
+    label: string;
71
+
72
+    /**
73
+     * The name of the icon of this button, when toggled.
74
+     *
75
+     * @abstract
76
+     */
77
+    toggledIconName: string;
78
+
79
+    /**
80
+     * The text to display in the tooltip. Used only on web.
81
+     *
82
+     * @abstract
83
+     */
84
+    tooltip: string;
85
+
86
+    /**
87
+     * Initializes a new {@code AbstractButton} instance.
88
+     *
89
+     * @param {Props} props - The React {@code Component} props to initialize
90
+     * the new {@code AbstractAudioMuteButton} instance with.
91
+     */
92
+    constructor(props: P) {
93
+        super(props);
94
+
95
+        this._onClick = this._onClick.bind(this);
96
+    }
97
+
98
+    /**
99
+     * Helper function to be implemented by subclasses, which should be used
100
+     * to handle the button being clicked / pressed.
101
+     *
102
+     * @abstract
103
+     * @private
104
+     * @returns {void}
105
+     */
106
+    _handleClick() {
107
+        // To be implemented by subclass.
108
+    }
109
+
110
+    /**
111
+     * Gets the current icon name, taking the toggled state into account. If no
112
+     * toggled icon is provided, the regular icon will also be used in the
113
+     * toggled state.
114
+     *
115
+     * @private
116
+     * @returns {string}
117
+     */
118
+    _getIconName() {
119
+        return (this._isToggled() ? this.toggledIconName : this.iconName)
120
+            || this.iconName;
121
+    }
122
+
123
+    /**
124
+     * Gets the current styles, taking the toggled state into account. If no
125
+     * toggled styles are provided, the regular styles will also be used in the
126
+     * toggled state.
127
+     *
128
+     * @private
129
+     * @returns {?Styles}
130
+     */
131
+    _getStyles() {
132
+        const { styles, toggledStyles } = this.props;
133
+
134
+        return (this._isToggled() ? toggledStyles : styles) || styles;
135
+    }
136
+
137
+    /**
138
+     * Helper function to be implemented by subclasses, which must return a
139
+     * boolean value indicating if this button is disabled or not.
140
+     *
141
+     * @private
142
+     * @returns {boolean}
143
+     */
144
+    _isDisabled() {
145
+        return false;
146
+    }
147
+
148
+    /**
149
+     * Helper function to be implemented by subclasses, which must return a
150
+     * boolean value indicating if this button is toggled or not.
151
+     *
152
+     * @private
153
+     * @returns {boolean}
154
+     */
155
+    _isToggled() {
156
+        return false;
157
+    }
158
+
159
+    _onClick: (*) => void;
160
+
161
+    /**
162
+     * Handles clicking / pressing the button, and toggles the audio mute state
163
+     * accordingly.
164
+     *
165
+     * @private
166
+     * @returns {void}
167
+     */
168
+    _onClick() {
169
+        this._handleClick();
170
+    }
171
+
172
+    /**
173
+     * Implements React's {@link Component#render()}.
174
+     *
175
+     * @inheritdoc
176
+     * @returns {ReactElement}
177
+     */
178
+    render() {
179
+        const props = {
180
+            ...this.props,
181
+            accessibilityLabel: this.accessibilityLabel,
182
+            iconName: this._getIconName(),
183
+            label: this.label,
184
+            styles: this._getStyles(),
185
+            tooltip: this.tooltip
186
+        };
187
+
188
+        return (
189
+            <ToolboxItem
190
+                disabled = { this._isDisabled() }
191
+                onClick = { this._onClick }
192
+                { ...props } />
193
+        );
194
+    }
195
+}

+ 15
- 31
react/features/toolbox/components/buttons/AbstractHangupButton.js 查看文件

@@ -1,51 +1,35 @@
1 1
 // @flow
2 2
 
3
-import { Component } from 'react';
4
-
5
-import {
6
-    createToolbarEvent,
7
-    sendAnalytics
8
-} from '../../../analytics';
3
+import AbstractButton from './AbstractButton';
4
+import type { Props } from './AbstractButton';
9 5
 
10 6
 /**
11
- * An abstract implementation of a button for leaving the conference.
7
+ * An abstract implementation of a button for disconnecting a conference.
12 8
  */
13
-export default class AbstractHangupButton extends Component<*> {
14
-    /**
15
-     * Initializes a new {@code AbstractHangupButton} instance.
16
-     *
17
-     * @param {Props} props - The read-only React {@code Component} props with
18
-     * which the new instance is to be initialized.
19
-     */
20
-    constructor(props: Object) {
21
-        super(props);
22
-
23
-        // Bind event handler so it is only bound once per instance.
24
-        this._onToolbarHangup = this._onToolbarHangup.bind(this);
25
-    }
9
+class AbstractHangupButton<P : Props, S: *> extends AbstractButton<P, S> {
10
+    accessibilityLabel = 'Hangup';
11
+    iconName = 'icon-hangup';
26 12
 
27 13
     /**
28
-     * Dispatches an action for leaving the current conference.
14
+     * Handles clicking / pressing the button, and disconnects the conference.
29 15
      *
30 16
      * @private
31 17
      * @returns {void}
32 18
      */
33
-    _doHangup() {
34
-        /* to be implemented by descendants */
19
+    _handleClick() {
20
+        this._doHangup();
35 21
     }
36 22
 
37
-    _onToolbarHangup: () => void;
38
-
39 23
     /**
40
-     * Creates an analytics toolbar event and dispatches an action for leaving
41
-     * the current conference.
24
+     * Helper function to perform the actual hangup action.
42 25
      *
26
+     * @abstract
43 27
      * @private
44 28
      * @returns {void}
45 29
      */
46
-    _onToolbarHangup() {
47
-        sendAnalytics(createToolbarEvent('hangup'));
48
-
49
-        this._doHangup();
30
+    _doHangup() {
31
+        // To be implemented by subclass.
50 32
     }
51 33
 }
34
+
35
+export default AbstractHangupButton;

+ 33
- 60
react/features/toolbox/components/buttons/AbstractVideoMuteButton.js 查看文件

@@ -1,88 +1,61 @@
1 1
 // @flow
2 2
 
3
-import PropTypes from 'prop-types';
4
-import { Component } from 'react';
5
-
6
-import {
7
-    VIDEO_MUTE,
8
-    createToolbarEvent,
9
-    sendAnalytics
10
-} from '../../../analytics';
11
-import {
12
-    VIDEO_MUTISM_AUTHORITY,
13
-    setVideoMuted
14
-} from '../../../base/media';
3
+import AbstractButton from './AbstractButton';
4
+import type { Props } from './AbstractButton';
15 5
 
16 6
 /**
17 7
  * An abstract implementation of a button for toggling video mute.
18 8
  */
19
-export default class AbstractVideoMuteButton extends Component<*> {
9
+class AbstractVideoMuteButton<P : Props, S : *> extends AbstractButton<P, S> {
10
+    accessibilityLabel = 'Video mute';
11
+    iconName = 'icon-camera';
12
+    toggledIconName = 'icon-camera-disabled toggled';
13
+
20 14
     /**
21
-     * {@code AbstractVideoMuteButton} component's property types.
15
+     * Handles clicking / pressing the button, and toggles the video mute state
16
+     * accordingly.
22 17
      *
23
-     * @static
18
+     * @private
19
+     * @returns {void}
24 20
      */
25
-    static propTypes = {
26
-        /**
27
-         * Whether or not the local camera is muted.
28
-         */
29
-        _videoMuted: PropTypes.bool,
30
-
31
-        /**
32
-         * Invoked to toggle video mute.
33
-         */
34
-        dispatch: PropTypes.func
35
-    };
21
+    _handleClick() {
22
+        this._setVideoMuted(!this._isVideoMuted());
23
+    }
36 24
 
37 25
     /**
38
-     * Initializes a new {@code AbstractVideoMuteButton} instance.
26
+     * Indicates whether this button is in toggled state or not.
39 27
      *
40
-     * @param {Props} props - The read-only React {@code Component} props with
41
-     * which the new instance is to be initialized.
28
+     * @override
29
+     * @private
30
+     * @returns {boolean}
42 31
      */
43
-    constructor(props: Object) {
44
-        super(props);
45
-
46
-        // Bind event handler so it is only bound once per instance.
47
-        this._onToolbarToggleVideo = this._onToolbarToggleVideo.bind(this);
32
+    _isToggled() {
33
+        return this._isVideoMuted();
48 34
     }
49 35
 
50 36
     /**
51
-     * Dispatches an action to toggle the mute state of the video/camera.
37
+     * Helper function to be implemented by subclasses, which must return a
38
+     * boolean value indicating if video is muted or not.
52 39
      *
40
+     * @abstract
53 41
      * @private
54
-     * @returns {void}
42
+     * @returns {boolean}
55 43
      */
56
-    _doToggleVideo() {
57
-        // The user sees the reality i.e. the state of base/tracks and intends
58
-        // to change reality by tapping on the respective button i.e. the user
59
-        // sets the state of base/media. Whether the user's intention will turn
60
-        // into reality is a whole different story which is of no concern to the
61
-        // tapping.
62
-        this.props.dispatch(
63
-            setVideoMuted(
64
-                !this.props._videoMuted,
65
-                VIDEO_MUTISM_AUTHORITY.USER,
66
-                /* ensureTrack */ true));
44
+    _isVideoMuted() {
45
+        // To be implemented by subclass.
67 46
     }
68 47
 
69
-
70
-    _onToolbarToggleVideo: () => void;
71
-
72 48
     /**
73
-     * Creates an analytics toolbar event and dispatches an action for toggling
74
-     * video mute.
49
+     * Helper function to perform the actual setting of the video mute / unmute
50
+     * action.
75 51
      *
52
+     * @param {boolean} videoMuted - Whether video should be muted or not.
76 53
      * @private
77 54
      * @returns {void}
78 55
      */
79
-    _onToolbarToggleVideo() {
80
-        sendAnalytics(createToolbarEvent(
81
-            VIDEO_MUTE,
82
-            {
83
-                enable: !this.props._videoMuted
84
-            }));
85
-
86
-        this._doToggleVideo();
56
+    _setVideoMuted(videoMuted: boolean) { // eslint-disable-line no-unused-vars
57
+        // To be implemented by subclass.
87 58
     }
88 59
 }
60
+
61
+export default AbstractVideoMuteButton;

+ 96
- 0
react/features/toolbox/components/buttons/AudioMuteButton.js 查看文件

@@ -0,0 +1,96 @@
1
+// @flow
2
+
3
+import { connect } from 'react-redux';
4
+
5
+import {
6
+    AUDIO_MUTE,
7
+    createToolbarEvent,
8
+    sendAnalytics
9
+} from '../../../analytics';
10
+import { translate } from '../../../base/i18n';
11
+import {
12
+    MEDIA_TYPE,
13
+    setAudioMuted
14
+} from '../../../base/media';
15
+import { isLocalTrackMuted } from '../../../base/tracks';
16
+
17
+import AbstractAudioMuteButton from './AbstractAudioMuteButton';
18
+import type { Props as AbstractButtonProps } from './AbstractButton';
19
+
20
+type Props = AbstractButtonProps & {
21
+
22
+    /**
23
+     * Whether audio is currently muted or not.
24
+     */
25
+    _audioMuted: boolean,
26
+
27
+    /**
28
+     * The redux {@code dispatch} function.
29
+     */
30
+    dispatch: Function
31
+}
32
+
33
+/**
34
+ * Component that renders a toolbar button for toggling audio mute.
35
+ *
36
+ * @extends AbstractAudioMuteButton
37
+ */
38
+class AudioMuteButton extends AbstractAudioMuteButton<Props, *> {
39
+    label = 'toolbar.mute';
40
+    tooltip = 'toolbar.mute';
41
+
42
+    /**
43
+     * Indicates if this button should be disabled or not.
44
+     *
45
+     * @override
46
+     * @private
47
+     * @returns {boolean}
48
+     */
49
+    _isDisabled() {
50
+        return false;
51
+    }
52
+
53
+    /**
54
+     * Indicates if audio is currently muted ot nor.
55
+     *
56
+     * @override
57
+     * @private
58
+     * @returns {boolean}
59
+     */
60
+    _isAudioMuted() {
61
+        return this.props._audioMuted;
62
+    }
63
+
64
+    /**
65
+     * Changes the muted state.
66
+     *
67
+     * @param {boolean} audioMuted - Whether audio should be muted or not.
68
+     * @private
69
+     * @returns {void}
70
+     */
71
+    _setAudioMuted(audioMuted: boolean) {
72
+        sendAnalytics(createToolbarEvent(AUDIO_MUTE, { enable: audioMuted }));
73
+        this.props.dispatch(setAudioMuted(audioMuted));
74
+    }
75
+
76
+}
77
+
78
+/**
79
+ * Maps (parts of) the redux state to the associated props for the
80
+ * {@code AudioMuteButton} component.
81
+ *
82
+ * @param {Object} state - The Redux state.
83
+ * @private
84
+ * @returns {{
85
+ *     _audioMuted: boolean
86
+ * }}
87
+ */
88
+function _mapStateToProps(state): Object {
89
+    const tracks = state['features/base/tracks'];
90
+
91
+    return {
92
+        _audioMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO)
93
+    };
94
+}
95
+
96
+export default translate(connect(_mapStateToProps)(AudioMuteButton));

+ 0
- 73
react/features/toolbox/components/buttons/AudioMuteButton.native.js 查看文件

@@ -1,73 +0,0 @@
1
-// @flow
2
-
3
-import PropTypes from 'prop-types';
4
-import React from 'react';
5
-import { connect } from 'react-redux';
6
-
7
-import { MEDIA_TYPE } from '../../../base/media';
8
-import { isLocalTrackMuted } from '../../../base/tracks';
9
-
10
-import AbstractAudioMuteButton from './AbstractAudioMuteButton';
11
-import ToolbarButton from '../ToolbarButton';
12
-
13
-/**
14
- * Component that renders a toolbar button for toggling audio mute.
15
- *
16
- * @extends AbstractAudioMuteButton
17
- */
18
-export class AudioMuteButton extends AbstractAudioMuteButton {
19
-    /**
20
-     * {@code AbstractAudioMuteButton} component's property types.
21
-     *
22
-     * @static
23
-     */
24
-    static propTypes = {
25
-        ...AbstractAudioMuteButton.propTypes,
26
-
27
-        /**
28
-         * Styles to be applied to the button and the icon to show.
29
-         */
30
-        buttonStyles: PropTypes.object
31
-    };
32
-
33
-    /**
34
-     * Implements React's {@link Component#render()}.
35
-     *
36
-     * @inheritdoc
37
-     * @returns {ReactElement}
38
-     */
39
-    render() {
40
-        const { buttonStyles } = this.props;
41
-
42
-        return (
43
-            <ToolbarButton
44
-                iconName = { buttonStyles.iconName }
45
-                iconStyle = { buttonStyles.iconStyle }
46
-                onClick = { this._onToolbarToggleAudio }
47
-                style = { buttonStyles.style } />
48
-        );
49
-    }
50
-
51
-    _onToolbarToggleAudio: () => void;
52
-}
53
-
54
-/**
55
- * Maps (parts of) the Redux state to the associated props for the
56
- * {@code AudioMuteButton} component.
57
- *
58
- * @param {Object} state - The Redux state.
59
- * @private
60
- * @returns {{
61
- *     _audioMuted: boolean,
62
- * }}
63
- */
64
-function _mapStateToProps(state) {
65
-    const tracks = state['features/base/tracks'];
66
-
67
-    return {
68
-        _audioMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO)
69
-    };
70
-}
71
-
72
-
73
-export default connect(_mapStateToProps)(AudioMuteButton);

+ 0
- 181
react/features/toolbox/components/buttons/AudioMuteButton.web.js 查看文件

@@ -1,181 +0,0 @@
1
-// @flow
2
-
3
-import PropTypes from 'prop-types';
4
-import React from 'react';
5
-import { connect } from 'react-redux';
6
-
7
-import UIEvents from '../../../../../service/UI/UIEvents';
8
-import {
9
-    ACTION_SHORTCUT_TRIGGERED,
10
-    AUDIO_MUTE,
11
-    createShortcutEvent,
12
-    sendAnalytics
13
-} from '../../../analytics';
14
-import { translate } from '../../../base/i18n';
15
-import { MEDIA_TYPE } from '../../../base/media';
16
-import { isLocalTrackMuted } from '../../../base/tracks';
17
-
18
-import AbstractAudioMuteButton from './AbstractAudioMuteButton';
19
-import ToolbarButton from '../ToolbarButton';
20
-
21
-declare var APP: Object;
22
-
23
-/**
24
- * Component that renders a toolbar button for toggling audio mute.
25
- *
26
- * @extends Component
27
- */
28
-export class AudioMuteButton extends AbstractAudioMuteButton {
29
-    /**
30
-     * Default values for {@code AudioMuteButton} component's properties.
31
-     *
32
-     * @static
33
-     */
34
-    static defaultProps = {
35
-        tooltipPosition: 'top'
36
-    };
37
-
38
-    /**
39
-     * {@code AudioMuteButton} component's property types.
40
-     *
41
-     * @static
42
-     */
43
-    static propTypes = {
44
-        ...AbstractAudioMuteButton.propTypes,
45
-
46
-        /**
47
-         * The {@code JitsiConference} for the current conference.
48
-         */
49
-        _conference: PropTypes.object,
50
-
51
-        /**
52
-         * Invoked to update the audio mute status.
53
-         */
54
-        dispatch: PropTypes.func,
55
-
56
-        /**
57
-         * Invoked to obtain translated strings.
58
-         */
59
-        t: PropTypes.func,
60
-
61
-        /**
62
-         * Where the tooltip should display, relative to the button.
63
-         */
64
-        tooltipPosition: PropTypes.string
65
-    };
66
-
67
-    /**
68
-     * Initializes a new {@code AudioMuteButton} instance.
69
-     *
70
-     * @param {Props} props - The read-only React {@code Component} props with
71
-     * which the new instance is to be initialized.
72
-     */
73
-    constructor(props: Object) {
74
-        super(props);
75
-
76
-        // Bind event handlers so it is only bound once per instance.
77
-        this._onShortcutToggleAudio = this._onShortcutToggleAudio.bind(this);
78
-    }
79
-
80
-    /**
81
-     * Sets a keyboard shortcuts for toggling audio mute.
82
-     *
83
-     * @inheritdoc
84
-     * @returns {void}
85
-     */
86
-    componentDidMount() {
87
-        APP.keyboardshortcut.registerShortcut(
88
-            'M',
89
-            null,
90
-            this._onShortcutToggleAudio,
91
-            'keyboardShortcuts.mute');
92
-    }
93
-
94
-    /**
95
-     * Removes the registered keyboard shortcut handler.
96
-     *
97
-     * @inheritdoc
98
-     * @returns {void}
99
-     */
100
-    componentWillUnmount() {
101
-        APP.keyboardshortcut.unregisterShortcut('M');
102
-    }
103
-
104
-    /**
105
-     * Implements React's {@link Component#render()}.
106
-     *
107
-     * @inheritdoc
108
-     * @returns {ReactElement}
109
-     */
110
-    render() {
111
-        const { _audioMuted, _conference, t, tooltipPosition } = this.props;
112
-
113
-        return (
114
-            <ToolbarButton
115
-                accessibilityLabel = 'Audio mute'
116
-                iconName = { _audioMuted && _conference
117
-                    ? 'icon-mic-disabled toggled'
118
-                    : 'icon-microphone' }
119
-                onClick = { this._onToolbarToggleAudio }
120
-                tooltip = { t('toolbar.mute') }
121
-                tooltipPosition = { tooltipPosition } />
122
-        );
123
-    }
124
-
125
-    _doToggleAudio: () => void;
126
-
127
-    /**
128
-     * Emits an event to signal audio mute should be toggled.
129
-     *
130
-     * @private
131
-     * @returns {void}
132
-     */
133
-    _doToggleAudio() {
134
-        // The old conference logic must be used for now as the redux flows do
135
-        // not handle all cases, such as unmuting when the config
136
-        // startWithAudioMuted is true.
137
-        APP.UI.emitEvent(UIEvents.AUDIO_MUTED, !this.props._audioMuted, true);
138
-    }
139
-
140
-    _onShortcutToggleAudio: () => void;
141
-
142
-    /**
143
-     * Creates an analytics keyboard shortcut event and dispatches an action for
144
-     * toggling audio mute.
145
-     *
146
-     * @private
147
-     * @returns {void}
148
-     */
149
-    _onShortcutToggleAudio() {
150
-        sendAnalytics(createShortcutEvent(
151
-            AUDIO_MUTE,
152
-            ACTION_SHORTCUT_TRIGGERED,
153
-            { enable: !this.props._audioMuted }));
154
-
155
-        this._doToggleAudio();
156
-    }
157
-
158
-    _onToolbarToggleAudio: () => void;
159
-}
160
-
161
-/**
162
- * Maps (parts of) the Redux state to the associated props for the
163
- * {@code AudioMuteButton} component.
164
- *
165
- * @param {Object} state - The Redux state.
166
- * @private
167
- * @returns {{
168
- *     _audioMuted: boolean,
169
- *     _conference: Object,
170
- * }}
171
- */
172
-function _mapStateToProps(state) {
173
-    const tracks = state['features/base/tracks'];
174
-
175
-    return {
176
-        _audioMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.AUDIO),
177
-        _conference: state['features/base/conference'].conference
178
-    };
179
-}
180
-
181
-export default translate(connect(_mapStateToProps)(AudioMuteButton));

+ 61
- 0
react/features/toolbox/components/buttons/HangupButton.js 查看文件

@@ -0,0 +1,61 @@
1
+// @flow
2
+
3
+import { connect } from 'react-redux';
4
+
5
+import { createToolbarEvent, sendAnalytics } from '../../../analytics';
6
+import { appNavigate } from '../../../app';
7
+
8
+import { disconnect } from '../../../base/connection';
9
+import { translate } from '../../../base/i18n';
10
+
11
+import AbstractHangupButton from './AbstractHangupButton';
12
+import type { Props as AbstractButtonProps } from './AbstractButton';
13
+
14
+type Props = AbstractButtonProps & {
15
+
16
+    /**
17
+     * The redux {@code dispatch} function.
18
+     */
19
+    dispatch: Function
20
+}
21
+
22
+/**
23
+ * Component that renders a toolbar button for leaving the current conference.
24
+ *
25
+ * @extends AbstractHangupButton
26
+ */
27
+class HangupButton extends AbstractHangupButton<Props, *> {
28
+    label = 'toolbar.hangup';
29
+    tooltip = 'toolbar.hangup';
30
+
31
+    /**
32
+     * Helper function to perform the actual hangup action.
33
+     *
34
+     * @override
35
+     * @private
36
+     * @returns {void}
37
+     */
38
+    _doHangup() {
39
+        sendAnalytics(createToolbarEvent('hangup'));
40
+
41
+        // FIXME: these should be unified.
42
+        if (navigator.product === 'ReactNative') {
43
+            this.props.dispatch(appNavigate(undefined));
44
+        } else {
45
+            this.props.dispatch(disconnect(true));
46
+        }
47
+    }
48
+
49
+    /**
50
+     * Indicates if this button should be disabled or not.
51
+     *
52
+     * @override
53
+     * @private
54
+     * @returns {boolean}
55
+     */
56
+    _isDisabled() {
57
+        return false;
58
+    }
59
+}
60
+
61
+export default translate(connect()(HangupButton));

+ 0
- 63
react/features/toolbox/components/buttons/HangupButton.native.js 查看文件

@@ -1,63 +0,0 @@
1
-// @flow
2
-
3
-import PropTypes from 'prop-types';
4
-import React from 'react';
5
-import { connect } from 'react-redux';
6
-
7
-import { appNavigate } from '../../../app';
8
-import { ColorPalette } from '../../../base/styles';
9
-
10
-import AbstractHangupButton from './AbstractHangupButton';
11
-import ToolbarButton from '../ToolbarButton';
12
-import styles from '../styles';
13
-
14
-/**
15
- * Component that renders a toolbar button for leaving the current conference.
16
- *
17
- * @extends Component
18
- */
19
-class HangupButton extends AbstractHangupButton {
20
-    /**
21
-     * {@code HangupButton} component's property types.
22
-     *
23
-     * @static
24
-     */
25
-    static propTypes = {
26
-        /**
27
-         * Invoked to leave the conference.
28
-         */
29
-        dispatch: PropTypes.func
30
-    };
31
-
32
-    /**
33
-     * Implements React's {@link Component#render()}.
34
-     *
35
-     * @inheritdoc
36
-     * @returns {ReactElement}
37
-     */
38
-    render() {
39
-        return (
40
-            <ToolbarButton
41
-                accessibilityLabel = 'Hangup'
42
-                iconName = 'hangup'
43
-                iconStyle = { styles.whitePrimaryToolbarButtonIcon }
44
-                onClick = { this._onToolbarHangup }
45
-                style = { styles.hangup }
46
-                underlayColor = { ColorPalette.buttonUnderlay } />
47
-        );
48
-    }
49
-
50
-    /**
51
-     * Dispatches an action for leaving the current conference.
52
-     *
53
-     * @private
54
-     * @returns {void}
55
-     */
56
-    _doHangup() {
57
-        this.props.dispatch(appNavigate(undefined));
58
-    }
59
-
60
-    _onToolbarHangup: () => void;
61
-}
62
-
63
-export default connect()(HangupButton);

+ 0
- 83
react/features/toolbox/components/buttons/HangupButton.web.js 查看文件

@@ -1,83 +0,0 @@
1
-// @flow
2
-
3
-import React from 'react';
4
-import PropTypes from 'prop-types';
5
-
6
-import { connect } from 'react-redux';
7
-
8
-import { disconnect } from '../../../base/connection';
9
-import { translate } from '../../../base/i18n';
10
-
11
-import AbstractHangupButton from './AbstractHangupButton';
12
-import ToolbarButton from '../ToolbarButton';
13
-
14
-/**
15
- * Component that renders a toolbar button for leaving the current conference.
16
- *
17
- * @extends Component
18
- */
19
-export class HangupButton extends AbstractHangupButton {
20
-    /**
21
-     * Default values for {@code HangupButton} component's properties.
22
-     *
23
-     * @static
24
-     */
25
-    static defaultProps = {
26
-        tooltipPosition: 'top'
27
-    };
28
-
29
-    /**
30
-     * {@code HangupButton} component's property types.
31
-     *
32
-     * @static
33
-     */
34
-    static propTypes = {
35
-        /**
36
-         * Invoked to trigger conference leave.
37
-         */
38
-        dispatch: PropTypes.func,
39
-
40
-        /**
41
-         * Invoked to obtain translated strings.
42
-         */
43
-        t: PropTypes.func,
44
-
45
-        /**
46
-         * Where the tooltip should display, relative to the button.
47
-         */
48
-        tooltipPosition: PropTypes.string
49
-    }
50
-
51
-    /**
52
-     * Implements React's {@link Component#render()}.
53
-     *
54
-     * @inheritdoc
55
-     * @returns {ReactElement}
56
-     */
57
-    render() {
58
-        const { t, tooltipPosition } = this.props;
59
-
60
-        return (
61
-            <ToolbarButton
62
-                accessibilityLabel = 'Hangup'
63
-                iconName = 'icon-hangup'
64
-                onClick = { this._onToolbarHangup }
65
-                tooltip = { t('toolbar.hangup') }
66
-                tooltipPosition = { tooltipPosition } />
67
-        );
68
-    }
69
-
70
-    _onToolbarHangup: () => void;
71
-
72
-    /**
73
-     * Dispatches an action for leaving the current conference.
74
-     *
75
-     * @private
76
-     * @returns {void}
77
-     */
78
-    _doHangup() {
79
-        this.props.dispatch(disconnect(true));
80
-    }
81
-}
82
-
83
-export default translate(connect()(HangupButton));

+ 109
- 0
react/features/toolbox/components/buttons/VideoMuteButton.js 查看文件

@@ -0,0 +1,109 @@
1
+// @flow
2
+
3
+import { connect } from 'react-redux';
4
+
5
+import {
6
+    VIDEO_MUTE,
7
+    createToolbarEvent,
8
+    sendAnalytics
9
+} from '../../../analytics';
10
+import { translate } from '../../../base/i18n';
11
+import {
12
+    MEDIA_TYPE,
13
+    VIDEO_MUTISM_AUTHORITY,
14
+    setVideoMuted
15
+} from '../../../base/media';
16
+import { isLocalTrackMuted } from '../../../base/tracks';
17
+
18
+import AbstractVideoMuteButton from './AbstractVideoMuteButton';
19
+import type { Props as AbstractButtonProps } from './AbstractButton';
20
+
21
+type Props = AbstractButtonProps & {
22
+
23
+    /**
24
+     * Whether the current conference is in audio only mode or not.
25
+     */
26
+    _audioOnly: boolean,
27
+
28
+    /**
29
+     * Whether video is currently muted or not.
30
+     */
31
+    _videoMuted: boolean,
32
+
33
+    /**
34
+     * The redux {@code dispatch} function.
35
+     */
36
+    dispatch: Function
37
+}
38
+
39
+/**
40
+ * Component that renders a toolbar button for toggling video mute.
41
+ *
42
+ * @extends AbstractVideoMuteButton
43
+ */
44
+class VideoMuteButton extends AbstractVideoMuteButton<Props, *> {
45
+    label = 'toolbar.videomute';
46
+    tooltip = 'toolbar.videomute';
47
+
48
+    /**
49
+     * Indicates if this button should be disabled or not.
50
+     *
51
+     * @override
52
+     * @private
53
+     * @returns {boolean}
54
+     */
55
+    _isDisabled() {
56
+        return this.props._audioOnly;
57
+    }
58
+
59
+    /**
60
+     * Indicates if video is currently muted ot nor.
61
+     *
62
+     * @override
63
+     * @private
64
+     * @returns {boolean}
65
+     */
66
+    _isVideoMuted() {
67
+        return this.props._videoMuted;
68
+    }
69
+
70
+    /**
71
+     * Changes the muted state.
72
+     *
73
+     * @param {boolean} videoMuted - Whether video should be muted or not.
74
+     * @private
75
+     * @returns {void}
76
+     */
77
+    _setVideoMuted(videoMuted: boolean) {
78
+        sendAnalytics(createToolbarEvent(VIDEO_MUTE, { enable: videoMuted }));
79
+        this.props.dispatch(
80
+            setVideoMuted(
81
+                videoMuted,
82
+                VIDEO_MUTISM_AUTHORITY.USER,
83
+                /* ensureTrack */ true));
84
+    }
85
+
86
+}
87
+
88
+/**
89
+ * Maps (parts of) the redux state to the associated props for the
90
+ * {@code VideoMuteButton} component.
91
+ *
92
+ * @param {Object} state - The Redux state.
93
+ * @private
94
+ * @returns {{
95
+ *     _audioOnly: boolean,
96
+ *     _videoMuted: boolean
97
+ * }}
98
+ */
99
+function _mapStateToProps(state): Object {
100
+    const { audioOnly } = state['features/base/conference'];
101
+    const tracks = state['features/base/tracks'];
102
+
103
+    return {
104
+        _audioOnly: Boolean(audioOnly),
105
+        _videoMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.VIDEO)
106
+    };
107
+}
108
+
109
+export default translate(connect(_mapStateToProps)(VideoMuteButton));

+ 0
- 82
react/features/toolbox/components/buttons/VideoMuteButton.native.js 查看文件

@@ -1,82 +0,0 @@
1
-// @flow
2
-
3
-import PropTypes from 'prop-types';
4
-import React from 'react';
5
-import { connect } from 'react-redux';
6
-
7
-import { MEDIA_TYPE } from '../../../base/media';
8
-import { isLocalTrackMuted } from '../../../base/tracks';
9
-
10
-import AbstractVideoMuteButton from './AbstractVideoMuteButton';
11
-import ToolbarButton from '../ToolbarButton';
12
-
13
-/**
14
- * Component that renders a toolbar button for toggling video mute.
15
- *
16
- * @extends AbstractVideoMuteButton
17
- */
18
-class VideoMuteButton extends AbstractVideoMuteButton {
19
-    /**
20
-     * {@code VideoMuteButton} component's property types.
21
-     *
22
-     * @static
23
-     */
24
-    static propTypes = {
25
-        ...AbstractVideoMuteButton.propTypes,
26
-
27
-        /**
28
-         * Whether or not the local participant is current in audio only mode.
29
-         * Video mute toggling is disabled in audio only mode.
30
-         */
31
-        _audioOnly: PropTypes.bool,
32
-
33
-        /**
34
-         * Styles to be applied to the button and the icon to show.
35
-         */
36
-        buttonStyles: PropTypes.object
37
-    };
38
-
39
-    /**
40
-     * Implements React's {@link Component#render()}.
41
-     *
42
-     * @inheritdoc
43
-     * @returns {ReactElement}
44
-     */
45
-    render() {
46
-        const { _audioOnly, buttonStyles } = this.props;
47
-
48
-        return (
49
-            <ToolbarButton
50
-                disabled = { _audioOnly }
51
-                iconName = { buttonStyles.iconName }
52
-                iconStyle = { buttonStyles.iconStyle }
53
-                onClick = { this._onToolbarToggleVideo }
54
-                style = { buttonStyles.style } />
55
-        );
56
-    }
57
-
58
-    _onToolbarToggleVideo: () => void;
59
-}
60
-
61
-/**
62
- * Maps (parts of) the Redux state to the associated props for the
63
- * {@code VideoMuteButton} component.
64
- *
65
- * @param {Object} state - The Redux state.
66
- * @private
67
- * @returns {{
68
- *     _audioOnly: boolean,
69
- *     _videoMuted: boolean
70
- * }}
71
- */
72
-function _mapStateToProps(state) {
73
-    const conference = state['features/base/conference'];
74
-    const tracks = state['features/base/tracks'];
75
-
76
-    return {
77
-        _audioOnly: Boolean(conference.audioOnly),
78
-        _videoMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.VIDEO)
79
-    };
80
-}
81
-
82
-export default connect(_mapStateToProps)(VideoMuteButton);

+ 0
- 173
react/features/toolbox/components/buttons/VideoMuteButton.web.js 查看文件

@@ -1,173 +0,0 @@
1
-// @flow
2
-
3
-import PropTypes from 'prop-types';
4
-import React from 'react';
5
-import { connect } from 'react-redux';
6
-
7
-import UIEvents from '../../../../../service/UI/UIEvents';
8
-import {
9
-    ACTION_SHORTCUT_TRIGGERED,
10
-    VIDEO_MUTE,
11
-    createShortcutEvent,
12
-    sendAnalytics
13
-} from '../../../analytics';
14
-import { translate } from '../../../base/i18n';
15
-import { MEDIA_TYPE } from '../../../base/media';
16
-import { isLocalTrackMuted } from '../../../base/tracks';
17
-
18
-import AbstractVideoMuteButton from './AbstractVideoMuteButton';
19
-import ToolbarButton from '../ToolbarButton';
20
-
21
-declare var APP: Object;
22
-
23
-/**
24
- * Component that renders a toolbar button for toggling video mute.
25
- *
26
- * @extends AbstractVideoMuteButton
27
- */
28
-export class VideoMuteButton extends AbstractVideoMuteButton {
29
-    /**
30
-     * Default values for {@code VideoMuteButton} component's properties.
31
-     *
32
-     * @static
33
-     */
34
-    static defaultProps = {
35
-        tooltipPosition: 'top'
36
-    };
37
-
38
-    /**
39
-     * {@code VideoMuteButton} component's property types.
40
-     *
41
-     * @static
42
-     */
43
-    static propTypes = {
44
-        ...AbstractVideoMuteButton.propTypes,
45
-
46
-        /**
47
-         * The {@code JitsiConference} for the current conference.
48
-         */
49
-        _conference: PropTypes.object,
50
-
51
-        /**
52
-         * Invoked to obtain translated strings.
53
-         */
54
-        t: PropTypes.func,
55
-
56
-        /**
57
-         * Where the tooltip should display, relative to the button.
58
-         */
59
-        tooltipPosition: PropTypes.string
60
-    };
61
-
62
-    /**
63
-     * Initializes a new {@code VideoMuteButton} instance.
64
-     *
65
-     * @param {Props} props - The read-only React {@code Component} props with
66
-     * which the new instance is to be initialized.
67
-     */
68
-    constructor(props: Object) {
69
-        super(props);
70
-
71
-        // Bind event handlers so they are only bound once per instance.
72
-        this._onShortcutToggleVideo = this._onShortcutToggleVideo.bind(this);
73
-    }
74
-
75
-    /**
76
-     * Sets a keyboard shortcuts for toggling video mute.
77
-     *
78
-     * @inheritdoc
79
-     * @returns {void}
80
-     */
81
-    componentDidMount() {
82
-        APP.keyboardshortcut.registerShortcut(
83
-            'V',
84
-            null,
85
-            this._onShortcutToggleVideo,
86
-            'keyboardShortcuts.videoMute');
87
-    }
88
-
89
-    /**
90
-     * Removes the registered keyboard shortcut handler.
91
-     *
92
-     * @inheritdoc
93
-     * @returns {void}
94
-     */
95
-    componentWillUnmount() {
96
-        APP.keyboardshortcut.unregisterShortcut('V');
97
-    }
98
-
99
-    /**
100
-     * Implements React's {@link Component#render()}.
101
-     *
102
-     * @inheritdoc
103
-     * @returns {ReactElement}
104
-     */
105
-    render() {
106
-        const { _conference, _videoMuted, t, tooltipPosition } = this.props;
107
-
108
-        return (
109
-            <ToolbarButton
110
-                accessibilityLabel = 'Video mute'
111
-                iconName = { _videoMuted && _conference
112
-                    ? 'icon-camera-disabled toggled'
113
-                    : 'icon-camera' }
114
-                onClick = { this._onToolbarToggleVideo }
115
-                tooltip = { t('toolbar.videomute') }
116
-                tooltipPosition = { tooltipPosition } />
117
-        );
118
-    }
119
-
120
-    _doToggleVideo: () => void;
121
-
122
-    /**
123
-     * Emits an event to signal video mute should be toggled.
124
-     *
125
-     * @private
126
-     * @returns {void}
127
-     */
128
-    _doToggleVideo() {
129
-        APP.UI.emitEvent(UIEvents.VIDEO_MUTED, !this.props._videoMuted);
130
-    }
131
-
132
-    _onShortcutToggleVideo: () => void;
133
-
134
-    /**
135
-     * Creates an analytics keyboard shortcut event for and dispatches an action
136
-     * for toggling video mute.
137
-     *
138
-     * @private
139
-     * @returns {void}
140
-     */
141
-    _onShortcutToggleVideo() {
142
-        sendAnalytics(createShortcutEvent(
143
-            VIDEO_MUTE,
144
-            ACTION_SHORTCUT_TRIGGERED,
145
-            { enable: !this.props._videoMuted }));
146
-
147
-        this._doToggleVideo();
148
-    }
149
-
150
-    _onToolbarToggleVideo: () => void;
151
-}
152
-
153
-/**
154
- * Maps (parts of) the Redux state to the associated props for the
155
- * {@code AudioMuteButton} component.
156
- *
157
- * @param {Object} state - The Redux state.
158
- * @private
159
- * @returns {{
160
- *     _conference: Object,
161
- *     _videoMuted: boolean,
162
- * }}
163
- */
164
-function _mapStateToProps(state) {
165
-    const tracks = state['features/base/tracks'];
166
-
167
-    return {
168
-        _conference: state['features/base/conference'].conference,
169
-        _videoMuted: isLocalTrackMuted(tracks, MEDIA_TYPE.VIDEO)
170
-    };
171
-}
172
-
173
-export default translate(connect(_mapStateToProps)(VideoMuteButton));

正在加载...
取消
保存