瀏覽代碼

feat(toolbox) allow any toolbox button to be displayed in main toolbar (#9488)

* feat(toolbox) allow any toolbox button to be displayed as main

fixes the previous behaviour where only a certain set of buttons were whitelisted for being displayed in the main toolbar

* code review

* code review - fix avatar icon position
j8
Avram Tudor 3 年之前
父節點
當前提交
62c78950cd
沒有連結到貢獻者的電子郵件帳戶。
共有 41 個檔案被更改,包括 1385 行新增971 行删除
  1. 4
    0
      css/_toolbars.scss
  2. 1
    1
      react/features/base/config/constants.js
  3. 109
    0
      react/features/chat/components/web/ChatButton.js
  4. 1
    0
      react/features/chat/components/web/index.js
  5. 46
    0
      react/features/embed-meeting/components/EmbedMeetingButton.js
  6. 1
    0
      react/features/embed-meeting/components/index.js
  7. 20
    0
      react/features/etherpad/components/SharedDocumentButton.js
  8. 0
    0
      react/features/feedback/components/FeedbackButton.native.js
  9. 56
    0
      react/features/feedback/components/FeedbackButton.web.js
  10. 1
    0
      react/features/feedback/components/index.js
  11. 0
    0
      react/features/keyboard-shortcuts/components/KeyboardShortcutsButton.native.js
  12. 44
    0
      react/features/keyboard-shortcuts/components/KeyboardShortcutsButton.web.js
  13. 1
    0
      react/features/keyboard-shortcuts/components/index.js
  14. 26
    65
      react/features/local-recording/components/LocalRecordingButton.web.js
  15. 39
    0
      react/features/participants-pane/components/ParticipantsPaneButton.js
  16. 1
    0
      react/features/participants-pane/components/index.js
  17. 1
    0
      react/features/security/components/security-dialog/SecurityDialogButton.js
  18. 1
    0
      react/features/settings/components/web/SettingsButton.js
  19. 20
    1
      react/features/shared-video/components/web/SharedVideoButton.js
  20. 67
    0
      react/features/speaker-stats/components/SpeakerStatsButton.js
  21. 1
    0
      react/features/speaker-stats/components/index.js
  22. 1
    0
      react/features/toolbox/components/DownloadButton.js
  23. 1
    0
      react/features/toolbox/components/HelpButton.js
  24. 1
    0
      react/features/toolbox/components/MuteEveryoneButton.js
  25. 1
    0
      react/features/toolbox/components/MuteEveryonesVideoButton.js
  26. 103
    0
      react/features/toolbox/components/web/FullscreenButton.js
  27. 0
    148
      react/features/toolbox/components/web/OverflowMenuProfileItem.js
  28. 123
    0
      react/features/toolbox/components/web/ProfileButton.js
  29. 63
    0
      react/features/toolbox/components/web/ProfileButtonAvatar.js
  30. 83
    0
      react/features/toolbox/components/web/RaiseHandButton.js
  31. 3
    0
      react/features/toolbox/components/web/Separator.js
  32. 139
    0
      react/features/toolbox/components/web/ShareDesktopButton.js
  33. 0
    75
      react/features/toolbox/components/web/ToggleCameraButton.js
  34. 1
    1
      react/features/toolbox/components/web/ToolbarButton.js
  35. 363
    556
      react/features/toolbox/components/web/Toolbox.js
  36. 29
    0
      react/features/toolbox/constants.js
  37. 0
    64
      react/features/toolbox/functions.web.js
  38. 2
    2
      react/features/video-layout/components/TileViewButton.js
  39. 0
    0
      react/features/video-quality/components/VideoQualityButton.native.js
  40. 31
    55
      react/features/video-quality/components/VideoQualityButton.web.js
  41. 1
    3
      react/features/video-quality/components/index.js

+ 4
- 0
css/_toolbars.scss 查看文件

@@ -278,6 +278,10 @@
278 278
     }
279 279
 }
280 280
 
281
+.profile-button-avatar {
282
+    align-items: center;
283
+}
284
+
281 285
 /**
282 286
  * START of fade in animation for main toolbar
283 287
  */

+ 1
- 1
react/features/base/config/constants.js 查看文件

@@ -19,5 +19,5 @@ export const TOOLBAR_BUTTONS = [
19 19
     'livestreaming', 'etherpad', 'sharedvideo', 'shareaudio', 'settings', 'raisehand',
20 20
     'videoquality', 'filmstrip', 'participants-pane', 'feedback', 'stats', 'shortcuts',
21 21
     'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone',
22
-    'security', 'toggle-camera'
22
+    'security'
23 23
 ];

+ 109
- 0
react/features/chat/components/web/ChatButton.js 查看文件

@@ -0,0 +1,109 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+
5
+import { translate } from '../../../base/i18n';
6
+import { IconChat } from '../../../base/icons';
7
+import { connect } from '../../../base/redux';
8
+import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
9
+
10
+import ChatCounter from './ChatCounter';
11
+
12
+/**
13
+ * The type of the React {@code Component} props of {@link ChatButton}.
14
+ */
15
+type Props = AbstractButtonProps & {
16
+
17
+    /**
18
+     * Whether or not the chat feature is currently displayed.
19
+     */
20
+     _chatOpen: boolean,
21
+
22
+    /**
23
+     * External handler for click action.
24
+     */
25
+    handleClick: Function
26
+};
27
+
28
+/**
29
+ * Implementation of a button for accessing chat pane.
30
+ */
31
+class ChatButton extends AbstractButton<Props, *> {
32
+    accessibilityLabel = 'toolbar.accessibilityLabel.chat';
33
+    icon = IconChat;
34
+    label = 'toolbar.openChat';
35
+    toggledLabel = 'toolbar.closeChat';
36
+
37
+    /**
38
+     * Retrieves tooltip dynamically.
39
+     */
40
+    get tooltip() {
41
+        if (this._isToggled()) {
42
+            return 'toolbar.closeChat';
43
+        }
44
+
45
+        return 'toolbar.openChat';
46
+    }
47
+
48
+    /**
49
+     * Required by linter due to AbstractButton overwritten prop being writable.
50
+     *
51
+     * @param {string} value - The value.
52
+     */
53
+    set tooltip(value) {
54
+        return value;
55
+    }
56
+
57
+    /**
58
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
59
+     *
60
+     * @protected
61
+     * @returns {void}
62
+     */
63
+    _handleClick() {
64
+        this.props.handleClick();
65
+    }
66
+
67
+    /**
68
+     * Indicates whether this button is in toggled state or not.
69
+     *
70
+     * @override
71
+     * @protected
72
+     * @returns {boolean}
73
+     */
74
+    _isToggled() {
75
+        return this.props._chatOpen;
76
+    }
77
+
78
+    /**
79
+     * Overrides AbstractButton's {@link Component#render()}.
80
+     *
81
+     * @override
82
+     * @protected
83
+     * @returns {boReact$Nodeolean}
84
+     */
85
+    render(): React$Node {
86
+        return (
87
+            <div
88
+                className = 'toolbar-button-with-badge'
89
+                key = 'chatcontainer'>
90
+                {super.render()}
91
+                <ChatCounter />
92
+            </div>
93
+        );
94
+    }
95
+}
96
+
97
+/**
98
+ * Function that maps parts of Redux state tree into component props.
99
+ *
100
+ * @param {Object} state - Redux state.
101
+ * @returns {Object}
102
+ */
103
+const mapStateToProps = state => {
104
+    return {
105
+        _chatOpen: state['features/chat'].isOpen
106
+    };
107
+};
108
+
109
+export default translate(connect(mapStateToProps)(ChatButton));

+ 1
- 0
react/features/chat/components/web/index.js 查看文件

@@ -1,5 +1,6 @@
1 1
 // @flow
2 2
 
3 3
 export { default as Chat } from './Chat';
4
+export { default as ChatButton } from './ChatButton';
4 5
 export { default as ChatCounter } from './ChatCounter';
5 6
 export { default as ChatPrivacyDialog } from './ChatPrivacyDialog';

+ 46
- 0
react/features/embed-meeting/components/EmbedMeetingButton.js 查看文件

@@ -0,0 +1,46 @@
1
+// @flow
2
+
3
+import { createToolbarEvent, sendAnalytics } from '../../analytics';
4
+import { openDialog } from '../../base/dialog';
5
+import { translate } from '../../base/i18n';
6
+import { IconCodeBlock } from '../../base/icons';
7
+import { connect } from '../../base/redux';
8
+import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
9
+
10
+import EmbedMeetingDialog from './EmbedMeetingDialog';
11
+
12
+/**
13
+ * The type of the React {@code Component} props of {@link EmbedMeetingButton}.
14
+ */
15
+type Props = AbstractButtonProps & {
16
+
17
+    /**
18
+     * The redux {@code dispatch} function.
19
+     */
20
+    dispatch: Function
21
+};
22
+
23
+/**
24
+ * Implementation of a button for opening embed meeting dialog.
25
+ */
26
+class EmbedMeetingButton extends AbstractButton<Props, *> {
27
+    accessibilityLabel = 'toolbar.accessibilityLabel.embedMeeting';
28
+    icon = IconCodeBlock;
29
+    label = 'toolbar.embedMeeting';
30
+    tooltip = 'toolbar.embedMeeting';
31
+
32
+    /**
33
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
34
+     *
35
+     * @protected
36
+     * @returns {void}
37
+     */
38
+    _handleClick() {
39
+        const { dispatch } = this.props;
40
+
41
+        sendAnalytics(createToolbarEvent('embed.meeting'));
42
+        dispatch(openDialog(EmbedMeetingDialog));
43
+    }
44
+}
45
+
46
+export default translate(connect()(EmbedMeetingButton));

+ 1
- 0
react/features/embed-meeting/components/index.js 查看文件

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

+ 20
- 0
react/features/etherpad/components/SharedDocumentButton.js 查看文件

@@ -32,6 +32,26 @@ class SharedDocumentButton extends AbstractButton<Props, *> {
32 32
     label = 'toolbar.documentOpen';
33 33
     toggledLabel = 'toolbar.documentClose';
34 34
 
35
+    /**
36
+     * Dynamically retrieves tooltip based on sharing state.
37
+     */
38
+    get tooltip() {
39
+        if (this._isToggled()) {
40
+            return 'toolbar.documentClose';
41
+        }
42
+
43
+        return 'toolbar.documentOpen';
44
+    }
45
+
46
+    /**
47
+     * Required by linter due to AbstractButton overwritten prop being writable.
48
+     *
49
+     * @param {string} value - The value.
50
+     */
51
+    set tooltip(value) {
52
+        return value;
53
+    }
54
+
35 55
     /**
36 56
      * Handles clicking / pressing the button, and opens / closes the appropriate dialog.
37 57
      *

react/features/video-quality/components/OverflowMenuVideoQualityItem.native.js → react/features/feedback/components/FeedbackButton.native.js 查看文件


+ 56
- 0
react/features/feedback/components/FeedbackButton.web.js 查看文件

@@ -0,0 +1,56 @@
1
+
2
+// @flow
3
+
4
+import { createToolbarEvent, sendAnalytics } from '../../analytics';
5
+import { translate } from '../../base/i18n';
6
+import { IconFeedback } from '../../base/icons';
7
+import { connect } from '../../base/redux';
8
+import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
9
+import { openFeedbackDialog } from '../actions';
10
+
11
+/**
12
+ * The type of the React {@code Component} props of {@link FeedbackButton}.
13
+ */
14
+type Props = AbstractButtonProps & {
15
+
16
+    /**
17
+     * The {@code JitsiConference} for the current conference.
18
+     */
19
+     _conference: Object,
20
+
21
+    /**
22
+     * The redux {@code dispatch} function.
23
+     */
24
+    dispatch: Function
25
+};
26
+
27
+/**
28
+ * Implementation of a button for opening feedback dialog.
29
+ */
30
+class FeedbackButton extends AbstractButton<Props, *> {
31
+    accessibilityLabel = 'toolbar.accessibilityLabel.feedback';
32
+    icon = IconFeedback;
33
+    label = 'toolbar.feedback';
34
+    tooltip = 'toolbar.feedback';
35
+
36
+    /**
37
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
38
+     *
39
+     * @protected
40
+     * @returns {void}
41
+     */
42
+    _handleClick() {
43
+        const { _conference, dispatch } = this.props;
44
+
45
+        sendAnalytics(createToolbarEvent('feedback'));
46
+        dispatch(openFeedbackDialog(_conference));
47
+    }
48
+}
49
+
50
+const mapStateToProps = state => {
51
+    return {
52
+        _conference: state['features/base/conference'].conference
53
+    };
54
+};
55
+
56
+export default translate(connect(mapStateToProps)(FeedbackButton));

+ 1
- 0
react/features/feedback/components/index.js 查看文件

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

+ 0
- 0
react/features/keyboard-shortcuts/components/KeyboardShortcutsButton.native.js 查看文件


+ 44
- 0
react/features/keyboard-shortcuts/components/KeyboardShortcutsButton.web.js 查看文件

@@ -0,0 +1,44 @@
1
+// @flow
2
+
3
+import { createToolbarEvent, sendAnalytics } from '../../analytics';
4
+import { translate } from '../../base/i18n';
5
+import { IconDeviceDocument } from '../../base/icons';
6
+import { connect } from '../../base/redux';
7
+import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
8
+import { openKeyboardShortcutsDialog } from '../actions';
9
+
10
+/**
11
+ * The type of the React {@code Component} props of {@link KeyboardShortcutsButton}.
12
+ */
13
+type Props = AbstractButtonProps & {
14
+
15
+    /**
16
+     * The redux {@code dispatch} function.
17
+     */
18
+    dispatch: Function
19
+};
20
+
21
+/**
22
+ * Implementation of a button for opening keyboard shortcuts dialog.
23
+ */
24
+class KeyboardShortcutsButton extends AbstractButton<Props, *> {
25
+    accessibilityLabel = 'toolbar.accessibilityLabel.shortcuts';
26
+    icon = IconDeviceDocument;
27
+    label = 'toolbar.shortcuts';
28
+    tooltip = 'toolbar.shortcuts';
29
+
30
+    /**
31
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
32
+     *
33
+     * @protected
34
+     * @returns {void}
35
+     */
36
+    _handleClick() {
37
+        const { dispatch } = this.props;
38
+
39
+        sendAnalytics(createToolbarEvent('shortcuts'));
40
+        dispatch(openKeyboardShortcutsDialog());
41
+    }
42
+}
43
+
44
+export default translate(connect()(KeyboardShortcutsButton));

+ 1
- 0
react/features/keyboard-shortcuts/components/index.js 查看文件

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

+ 26
- 65
react/features/local-recording/components/LocalRecordingButton.web.js 查看文件

@@ -1,85 +1,46 @@
1
-/* @flow */
2
-
3
-import React, { Component } from 'react';
1
+// @flow
4 2
 
3
+import { createToolbarEvent, sendAnalytics } from '../../analytics';
4
+import { openDialog } from '../../base/dialog';
5 5
 import { translate } from '../../base/i18n';
6 6
 import { IconRec } from '../../base/icons';
7
-import { ToolbarButton } from '../../toolbox/components/web';
7
+import { connect } from '../../base/redux';
8
+import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
9
+
10
+import LocalRecordingInfoDialog from './LocalRecordingInfoDialog';
8 11
 
9 12
 /**
10
- * The type of the React {@code Component} state of
11
- * {@link LocalRecordingButton}.
13
+ * The type of the React {@code Component} props of {@link LocalRecording}.
12 14
  */
13
-type Props = {
14
-
15
-    /**
16
-     * Whether or not {@link LocalRecordingInfoDialog} should be displayed.
17
-     */
18
-    isDialogShown: boolean,
19
-
20
-    /**
21
-     * Callback function called when {@link LocalRecordingButton} is clicked.
22
-     */
23
-    onClick: Function,
15
+type Props = AbstractButtonProps & {
24 16
 
25 17
     /**
26
-     * Invoked to obtain translated strings.
18
+     * The redux {@code dispatch} function.
27 19
      */
28
-    t: Function
29
-}
20
+    dispatch: Function
21
+};
30 22
 
31 23
 /**
32
- * A React {@code Component} for opening or closing the
33
- * {@code LocalRecordingInfoDialog}.
34
- *
35
- * @extends Component
24
+ * Implementation of a button for opening local recording dialog.
36 25
  */
37
-class LocalRecordingButton extends Component<Props> {
26
+class LocalRecording extends AbstractButton<Props, *> {
27
+    accessibilityLabel = 'toolbar.accessibilityLabel.localRecording';
28
+    icon = IconRec;
29
+    label = 'localRecording.dialogTitle';
30
+    tooltip = 'localRecording.dialogTitle';
38 31
 
39 32
     /**
40
-     * Initializes a new {@code LocalRecordingButton} instance.
33
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
41 34
      *
42
-     * @param {Object} props - The read-only properties with which the new
43
-     * instance is to be initialized.
44
-     */
45
-    constructor(props: Props) {
46
-        super(props);
47
-
48
-        // Bind event handlers so they are only bound once per instance.
49
-        this._onClick = this._onClick.bind(this);
50
-    }
51
-
52
-    /**
53
-     * Implements React's {@link Component#render()}.
54
-     *
55
-     * @inheritdoc
56
-     * @returns {ReactElement}
57
-     */
58
-    render() {
59
-        const { isDialogShown, t } = this.props;
60
-
61
-        return (
62
-            <ToolbarButton
63
-                accessibilityLabel
64
-                    = { t('toolbar.accessibilityLabel.localRecording') }
65
-                icon = { IconRec }
66
-                onClick = { this._onClick }
67
-                toggled = { isDialogShown }
68
-                tooltip = { t('localRecording.dialogTitle') } />
69
-        );
70
-    }
71
-
72
-    _onClick: () => void;
73
-
74
-    /**
75
-     * Callback invoked when the Toolbar button is clicked.
76
-     *
77
-     * @private
35
+     * @protected
78 36
      * @returns {void}
79 37
      */
80
-    _onClick() {
81
-        this.props.onClick();
38
+    _handleClick() {
39
+        const { dispatch } = this.props;
40
+
41
+        sendAnalytics(createToolbarEvent('local.recording'));
42
+        dispatch(openDialog(LocalRecordingInfoDialog));
82 43
     }
83 44
 }
84 45
 
85
-export default translate(LocalRecordingButton);
46
+export default translate(connect()(LocalRecording));

+ 39
- 0
react/features/participants-pane/components/ParticipantsPaneButton.js 查看文件

@@ -0,0 +1,39 @@
1
+// @flow
2
+
3
+import { translate } from '../../base/i18n';
4
+import { IconParticipants } from '../../base/icons';
5
+import { connect } from '../../base/redux';
6
+import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
7
+
8
+/**
9
+ * The type of the React {@code Component} props of {@link ParticipantsPaneButton}.
10
+ */
11
+type Props = AbstractButtonProps & {
12
+
13
+    /**
14
+     * External handler for click action.
15
+     */
16
+    handleClick: Function
17
+};
18
+
19
+/**
20
+ * Implementation of a button for accessing participants pane.
21
+ */
22
+class ParticipantsPaneButton extends AbstractButton<Props, *> {
23
+    accessibilityLabel = 'toolbar.accessibilityLabel.participants';
24
+    icon = IconParticipants;
25
+    label = 'toolbar.participants';
26
+    tooltip = 'toolbar.participants';
27
+
28
+    /**
29
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
30
+     *
31
+     * @protected
32
+     * @returns {void}
33
+     */
34
+    _handleClick() {
35
+        this.props.handleClick();
36
+    }
37
+}
38
+
39
+export default translate(connect()(ParticipantsPaneButton));

+ 1
- 0
react/features/participants-pane/components/index.js 查看文件

@@ -6,4 +6,5 @@ export * from './MeetingParticipantItem';
6 6
 export * from './MeetingParticipantList';
7 7
 export * from './ParticipantItem';
8 8
 export * from './ParticipantsPane';
9
+export * from './ParticipantsPaneButton';
9 10
 export * from './RaisedHandIndicator';

+ 1
- 0
react/features/security/components/security-dialog/SecurityDialogButton.js 查看文件

@@ -39,6 +39,7 @@ class SecurityDialogButton extends AbstractButton<Props, *> {
39 39
     icon = IconSecurityOff;
40 40
     label = 'toolbar.security';
41 41
     toggledIcon = IconSecurityOn;
42
+    tooltip = 'toolbar.security';
42 43
 
43 44
     /**
44 45
      * Handles clicking / pressing the button, and opens / closes the appropriate dialog.

+ 1
- 0
react/features/settings/components/web/SettingsButton.js 查看文件

@@ -31,6 +31,7 @@ class SettingsButton extends AbstractButton<Props, *> {
31 31
     accessibilityLabel = 'toolbar.accessibilityLabel.Settings';
32 32
     icon = IconSettings;
33 33
     label = 'toolbar.Settings';
34
+    tooltip = 'toolbar.Settings';
34 35
 
35 36
     /**
36 37
      * Handles clicking / pressing the button, and opens the appropriate dialog.

+ 20
- 1
react/features/shared-video/components/web/SharedVideoButton.js 查看文件

@@ -37,9 +37,28 @@ class SharedVideoButton extends AbstractButton<Props, *> {
37 37
     accessibilityLabel = 'toolbar.accessibilityLabel.sharedvideo';
38 38
     icon = IconShareVideo;
39 39
     label = 'toolbar.sharedvideo';
40
-    tooltip = 'toolbar.sharedvideo';
41 40
     toggledLabel = 'toolbar.stopSharedVideo';
42 41
 
42
+    /**
43
+     * Dynamically retrieves tooltip based on sharing state.
44
+     */
45
+    get tooltip() {
46
+        if (this._isToggled()) {
47
+            return 'toolbar.stopSharedVideo';
48
+        }
49
+
50
+        return 'toolbar.sharedvideo';
51
+    }
52
+
53
+    /**
54
+     * Required by linter due to AbstractButton overwritten prop being writable.
55
+     *
56
+     * @param {string} value - The value.
57
+     */
58
+    set tooltip(value) {
59
+        return value;
60
+    }
61
+
43 62
     /**
44 63
      * Handles clicking / pressing the button, and opens a new dialog.
45 64
      *

+ 67
- 0
react/features/speaker-stats/components/SpeakerStatsButton.js 查看文件

@@ -0,0 +1,67 @@
1
+// @flow
2
+
3
+import { createToolbarEvent, sendAnalytics } from '../../analytics';
4
+import { openDialog } from '../../base/dialog';
5
+import { translate } from '../../base/i18n';
6
+import { IconPresentation } from '../../base/icons';
7
+import { connect } from '../../base/redux';
8
+import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
9
+
10
+import SpeakerStats from './SpeakerStats';
11
+
12
+/**
13
+ * The type of the React {@code Component} props of {@link SpeakerStatsButton}.
14
+ */
15
+type Props = AbstractButtonProps & {
16
+
17
+    /**
18
+     * The {@code JitsiConference} for the current conference.
19
+     */
20
+     _conference: Object,
21
+
22
+    /**
23
+     * The redux {@code dispatch} function.
24
+     */
25
+    dispatch: Function
26
+};
27
+
28
+/**
29
+ * Implementation of a button for opening speaker stats dialog.
30
+ */
31
+class SpeakerStatsButton extends AbstractButton<Props, *> {
32
+    accessibilityLabel = 'toolbar.accessibilityLabel.speakerStats';
33
+    icon = IconPresentation;
34
+    label = 'toolbar.speakerStats';
35
+    tooltip = 'toolbar.speakerStats';
36
+
37
+    /**
38
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
39
+     *
40
+     * @protected
41
+     * @returns {void}
42
+     */
43
+    _handleClick() {
44
+        const { _conference, dispatch } = this.props;
45
+
46
+        sendAnalytics(createToolbarEvent('speaker.stats'));
47
+        dispatch(openDialog(SpeakerStats, {
48
+            conference: _conference
49
+        }));
50
+    }
51
+}
52
+
53
+/**
54
+ * Maps (parts of) the Redux state to the associated
55
+ * {@code SpeakerStatsButton} component's props.
56
+ *
57
+ * @param {Object} state - The Redux state.
58
+ * @private
59
+ * @returns {Object}
60
+ */
61
+function mapStateToProps(state) {
62
+    return {
63
+        _conference: state['features/base/conference'].conference
64
+    };
65
+}
66
+
67
+export default translate(connect(mapStateToProps)(SpeakerStatsButton));

+ 1
- 0
react/features/speaker-stats/components/index.js 查看文件

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

+ 1
- 0
react/features/toolbox/components/DownloadButton.js 查看文件

@@ -23,6 +23,7 @@ class DownloadButton extends AbstractButton<Props, *> {
23 23
     accessibilityLabel = 'toolbar.accessibilityLabel.download';
24 24
     icon = IconDownload;
25 25
     label = 'toolbar.download';
26
+    tooltip = 'toolbar.download';
26 27
 
27 28
     /**
28 29
      * Handles clicking / pressing the button, and opens a new window with the user documentation.

+ 1
- 0
react/features/toolbox/components/HelpButton.js 查看文件

@@ -24,6 +24,7 @@ class HelpButton extends AbstractButton<Props, *> {
24 24
     accessibilityLabel = 'toolbar.accessibilityLabel.help';
25 25
     icon = IconHelp;
26 26
     label = 'toolbar.help';
27
+    tooltip = 'toolbar.help';
27 28
 
28 29
     /**
29 30
      * Handles clicking / pressing the button, and opens a new window with the user documentation.

+ 1
- 0
react/features/toolbox/components/MuteEveryoneButton.js 查看文件

@@ -30,6 +30,7 @@ class MuteEveryoneButton extends AbstractButton<Props, *> {
30 30
     accessibilityLabel = 'toolbar.accessibilityLabel.muteEveryone';
31 31
     icon = IconMuteEveryone;
32 32
     label = 'toolbar.muteEveryone';
33
+    tooltip = 'toolbar.muteEveryone';
33 34
 
34 35
     /**
35 36
      * Handles clicking / pressing the button, and opens a confirmation dialog.

+ 1
- 0
react/features/toolbox/components/MuteEveryonesVideoButton.js 查看文件

@@ -30,6 +30,7 @@ class MuteEveryonesVideoButton extends AbstractButton<Props, *> {
30 30
     accessibilityLabel = 'toolbar.accessibilityLabel.muteEveryonesVideo';
31 31
     icon = IconMuteVideoEveryone;
32 32
     label = 'toolbar.muteEveryonesVideo';
33
+    tooltip = 'toolbar.muteEveryonesVideo';
33 34
 
34 35
     /**
35 36
      * Handles clicking / pressing the button, and opens a confirmation dialog.

+ 103
- 0
react/features/toolbox/components/web/FullscreenButton.js 查看文件

@@ -0,0 +1,103 @@
1
+// @flow
2
+
3
+import { translate } from '../../../base/i18n';
4
+import { IconExitFullScreen, IconFullScreen } from '../../../base/icons';
5
+import { connect } from '../../../base/redux';
6
+import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
7
+
8
+type Props = AbstractButtonProps & {
9
+
10
+  /**
11
+   * Whether or not the app is currently in full screen.
12
+   */
13
+   _fullScreen: boolean,
14
+
15
+    /**
16
+     * External handler for click action.
17
+     */
18
+    handleClick: Function
19
+};
20
+
21
+/**
22
+ * Implementation of a button for toggling fullscreen state.
23
+ */
24
+class FullscreenButton extends AbstractButton<Props, *> {
25
+    accessibilityLabel = 'toolbar.accessibilityLabel.fullScreen';
26
+    label = 'toolbar.enterFullScreen';
27
+    toggledLabel = 'toolbar.exitFullScreen'
28
+
29
+    /**
30
+     * Retrieves icon dynamically.
31
+     */
32
+    get icon() {
33
+        if (this._isToggled()) {
34
+            return IconExitFullScreen;
35
+        }
36
+
37
+        return IconFullScreen;
38
+    }
39
+
40
+    /**
41
+     * Required by linter due to AbstractButton overwritten prop being writable.
42
+     *
43
+     * @param {string} value - The value.
44
+     */
45
+    set icon(value) {
46
+        return value;
47
+    }
48
+
49
+    /**
50
+     * Retrieves icon dynamically.
51
+     */
52
+    get tooltip() {
53
+        if (this._isToggled()) {
54
+            return 'toolbar.exitFullScreen';
55
+        }
56
+
57
+        return 'toolbar.enterFullScreen';
58
+    }
59
+
60
+    /**
61
+     * Required by linter due to AbstractButton overwritten prop being writable.
62
+     *
63
+     * @param {string} value - The value.
64
+     */
65
+    set tooltip(value) {
66
+        return value;
67
+    }
68
+
69
+    /**
70
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
71
+     *
72
+     * @protected
73
+     * @returns {void}
74
+     */
75
+    _handleClick() {
76
+        this.props.handleClick();
77
+    }
78
+
79
+    /**
80
+     * Indicates whether this button is in toggled state or not.
81
+     *
82
+     * @override
83
+     * @protected
84
+     * @returns {boolean}
85
+     */
86
+    _isToggled() {
87
+        return this.props._fullScreen;
88
+    }
89
+}
90
+
91
+/**
92
+ * Function that maps parts of Redux state tree into component props.
93
+ *
94
+ * @param {Object} state - Redux state.
95
+ * @returns {Object}
96
+ */
97
+const mapStateToProps = state => {
98
+    return {
99
+        _fullScreen: state['features/toolbox'].fullScreen
100
+    };
101
+};
102
+
103
+export default translate(connect(mapStateToProps)(FullscreenButton));

+ 0
- 148
react/features/toolbox/components/web/OverflowMenuProfileItem.js 查看文件

@@ -1,148 +0,0 @@
1
-// @flow
2
-
3
-import React, { Component } from 'react';
4
-
5
-import { Avatar } from '../../../base/avatar';
6
-import { translate } from '../../../base/i18n';
7
-import { getLocalParticipant } from '../../../base/participants';
8
-import { connect } from '../../../base/redux';
9
-
10
-declare var interfaceConfig: Object;
11
-
12
-/**
13
- * The type of the React {@code Component} props of
14
- * {@link OverflowMenuProfileItem}.
15
- */
16
-type Props = {
17
-
18
-    /**
19
-     * The redux representation of the local participant.
20
-     */
21
-    _localParticipant: Object,
22
-
23
-    /**
24
-     * Whether the button support clicking or not.
25
-     */
26
-    _unclickable: boolean,
27
-
28
-    /**
29
-     * The callback to invoke when {@code OverflowMenuProfileItem} is
30
-     * clicked.
31
-     */
32
-    onClick: Function,
33
-
34
-    /**
35
-     * Invoked to obtain translated strings.
36
-     */
37
-    t: Function
38
-};
39
-
40
-/**
41
- * A React {@code Component} for displaying a link with a profile avatar as an
42
- * icon.
43
- *
44
- * @extends Component
45
- */
46
-class OverflowMenuProfileItem extends Component<Props> {
47
-    /**
48
-     * Initializes a new {@code OverflowMenuProfileItem} instance.
49
-     *
50
-     * @param {Object} props - The read-only properties with which the new
51
-     * instance is to be initialized.
52
-     */
53
-    constructor(props: Props) {
54
-        super(props);
55
-
56
-        // Bind event handler so it is only bound once for every instance.
57
-        this._onClick = this._onClick.bind(this);
58
-        this._onKeyPress = this._onKeyPress.bind(this);
59
-    }
60
-
61
-    /**
62
-     * Implements React's {@link Component#render()}.
63
-     *
64
-     * @inheritdoc
65
-     * @returns {ReactElement}
66
-     */
67
-    render() {
68
-        const { _localParticipant, _unclickable, t } = this.props;
69
-        const classNames = `overflow-menu-item ${
70
-            _unclickable ? 'unclickable' : ''}`;
71
-        let displayName;
72
-
73
-        if (_localParticipant && _localParticipant.name) {
74
-            displayName = _localParticipant.name;
75
-        } else {
76
-            displayName = interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME;
77
-        }
78
-
79
-        return (
80
-            <li
81
-                aria-label = { t('toolbar.accessibilityLabel.profile') }
82
-                className = { classNames }
83
-                onClick = { this._onClick }
84
-                onKeyPress = { this._onKeyPress }
85
-                role = 'menuitem'
86
-                tabIndex = { 0 }>
87
-                <span className = 'overflow-menu-item-icon'>
88
-                    <Avatar
89
-                        participantId = { _localParticipant.id }
90
-                        size = { 20 } />
91
-                </span>
92
-                <span className = 'profile-text'>
93
-                    { displayName }
94
-                </span>
95
-            </li>
96
-        );
97
-    }
98
-
99
-    _onClick: () => void;
100
-
101
-    /**
102
-     * Invokes an on click callback if clicking is allowed.
103
-     *
104
-     * @returns {void}
105
-     */
106
-    _onClick() {
107
-        if (!this.props._unclickable) {
108
-            this.props.onClick();
109
-        }
110
-    }
111
-
112
-    _onKeyPress: (Object) => void;
113
-
114
-    /**
115
-     * KeyPress handler for accessibility.
116
-     *
117
-     * @param {Object} e - The key event to handle.
118
-     *
119
-     * @returns {void}
120
-     */
121
-    _onKeyPress(e) {
122
-        if (!this.props._unclickable && (e.key === ' ' || e.key === 'Enter')) {
123
-            e.preventDefault();
124
-            this.props.onClick();
125
-        }
126
-    }
127
-}
128
-
129
-/**
130
- * Maps (parts of) the Redux state to the associated
131
- * {@code OverflowMenuProfileItem} component's props.
132
- *
133
- * @param {Object} state - The Redux state.
134
- * @private
135
- * @returns {{
136
- *     _localParticipant: Object,
137
- *     _unclickable: boolean
138
- * }}
139
- */
140
-function _mapStateToProps(state) {
141
-    return {
142
-        _localParticipant: getLocalParticipant(state),
143
-        _unclickable: state['features/base/config'].disableProfile
144
-            || !interfaceConfig.SETTINGS_SECTIONS.includes('profile')
145
-    };
146
-}
147
-
148
-export default translate(connect(_mapStateToProps)(OverflowMenuProfileItem));

+ 123
- 0
react/features/toolbox/components/web/ProfileButton.js 查看文件

@@ -0,0 +1,123 @@
1
+// @flow
2
+
3
+import { createToolbarEvent, sendAnalytics } from '../../../analytics';
4
+import { translate } from '../../../base/i18n';
5
+import { getLocalParticipant } from '../../../base/participants';
6
+import { connect } from '../../../base/redux';
7
+import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
8
+import { openSettingsDialog, SETTINGS_TABS } from '../../../settings';
9
+
10
+import ProfileButtonAvatar from './ProfileButtonAvatar';
11
+
12
+/**
13
+ * The type of the React {@code Component} props of {@link ProfileButton}.
14
+ */
15
+type Props = AbstractButtonProps & {
16
+
17
+    /**
18
+     * The redux representation of the local participant.
19
+     */
20
+     _localParticipant: Object,
21
+
22
+     /**
23
+      * Whether the button support clicking or not.
24
+      */
25
+     _unclickable: boolean,
26
+
27
+    /**
28
+     * The redux {@code dispatch} function.
29
+     */
30
+    dispatch: Function
31
+};
32
+
33
+declare var interfaceConfig: Object;
34
+
35
+/**
36
+ * Implementation of a button for opening profile dialog.
37
+ */
38
+class ProfileButton extends AbstractButton<Props, *> {
39
+    accessibilityLabel = 'toolbar.accessibilityLabel.profile';
40
+    icon = ProfileButtonAvatar;
41
+
42
+    /**
43
+     * Retrieves the label.
44
+     */
45
+    get label() {
46
+        const { _localParticipant } = this.props;
47
+        let displayName;
48
+
49
+        if (_localParticipant && _localParticipant.name) {
50
+            displayName = _localParticipant.name;
51
+        } else {
52
+            displayName = interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME;
53
+        }
54
+
55
+        return displayName;
56
+    }
57
+
58
+    /**
59
+     * Required by linter due to AbstractButton overwritten prop being writable.
60
+     *
61
+     * @param {string} value - The value.
62
+     */
63
+    set label(value) {
64
+        return value;
65
+    }
66
+
67
+    /**
68
+     * Retrieves the tooltip.
69
+     */
70
+    get tooltip() {
71
+        return this.label;
72
+    }
73
+
74
+    /**
75
+     * Required by linter due to AbstractButton overwritten prop being writable.
76
+     *
77
+     * @param {string} value - The value.
78
+     */
79
+    set tooltip(value) {
80
+        return value;
81
+    }
82
+
83
+    /**
84
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
85
+     *
86
+     * @protected
87
+     * @returns {void}
88
+     */
89
+    _handleClick() {
90
+        const { dispatch, _unclickable } = this.props;
91
+
92
+        if (!_unclickable) {
93
+            sendAnalytics(createToolbarEvent('profile'));
94
+            dispatch(openSettingsDialog(SETTINGS_TABS.PROFILE));
95
+        }
96
+    }
97
+
98
+    /**
99
+     * Indicates whether the button should be disabled or not.
100
+     *
101
+     * @protected
102
+     * @returns {void}
103
+     */
104
+    _isDisabled() {
105
+        return this.props._unclickable;
106
+    }
107
+}
108
+
109
+/**
110
+ * Function that maps parts of Redux state tree into component props.
111
+ *
112
+ * @param {Object} state - Redux state.
113
+ * @returns {Object}
114
+ */
115
+const mapStateToProps = state => {
116
+    return {
117
+        _localParticipant: getLocalParticipant(state),
118
+        _unclickable: !interfaceConfig.SETTINGS_SECTIONS.includes('profile'),
119
+        customClass: 'profile-button-avatar'
120
+    };
121
+};
122
+
123
+export default translate(connect(mapStateToProps)(ProfileButton));

+ 63
- 0
react/features/toolbox/components/web/ProfileButtonAvatar.js 查看文件

@@ -0,0 +1,63 @@
1
+// @flow
2
+
3
+import React, { Component } from 'react';
4
+
5
+import { Avatar } from '../../../base/avatar';
6
+import { translate } from '../../../base/i18n';
7
+import { getLocalParticipant } from '../../../base/participants';
8
+import { connect } from '../../../base/redux';
9
+
10
+/**
11
+ * The type of the React {@code Component} props of
12
+ * {@link ProfileButtonAvatar}.
13
+ */
14
+type Props = {
15
+
16
+    /**
17
+     * The redux representation of the local participant.
18
+     */
19
+    _localParticipant: Object,
20
+
21
+};
22
+
23
+/**
24
+ * A React {@code Component} for displaying a profile avatar as an
25
+ * icon.
26
+ *
27
+ * @extends Component
28
+ */
29
+class ProfileButtonAvatar extends Component<Props> {
30
+    /**
31
+     * Implements React's {@link Component#render()}.
32
+     *
33
+     * @inheritdoc
34
+     * @returns {ReactElement}
35
+     */
36
+    render() {
37
+        const { _localParticipant } = this.props;
38
+
39
+        return (
40
+            <Avatar
41
+                participantId = { _localParticipant.id }
42
+                size = { 20 } />
43
+        );
44
+    }
45
+}
46
+
47
+/**
48
+ * Maps (parts of) the Redux state to the associated
49
+ * {@code ProfileButtonAvatar} component's props.
50
+ *
51
+ * @param {Object} state - The Redux state.
52
+ * @private
53
+ * @returns {{
54
+ *     _localParticipant: Object,
55
+ * }}
56
+ */
57
+function _mapStateToProps(state) {
58
+    return {
59
+        _localParticipant: getLocalParticipant(state)
60
+    };
61
+}
62
+
63
+export default translate(connect(_mapStateToProps)(ProfileButtonAvatar));

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

@@ -0,0 +1,83 @@
1
+// @flow
2
+
3
+import { translate } from '../../../base/i18n';
4
+import { IconRaisedHand } from '../../../base/icons';
5
+import { getLocalParticipant } from '../../../base/participants';
6
+import { connect } from '../../../base/redux';
7
+import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
8
+
9
+type Props = AbstractButtonProps & {
10
+
11
+    /**
12
+     * Whether or not the local participant's hand is raised.
13
+     */
14
+     _raisedHand: boolean,
15
+
16
+     /**
17
+      * External handler for click action.
18
+      */
19
+     handleClick: Function
20
+};
21
+
22
+/**
23
+ * Implementation of a button for toggling raise hand functionality.
24
+ */
25
+class RaiseHandButton extends AbstractButton<Props, *> {
26
+    accessibilityLabel = 'toolbar.accessibilityLabel.raiseHand';
27
+    icon = IconRaisedHand
28
+    label = 'toolbar.raiseYourHand';
29
+    toggledLabel = 'toolbar.lowerYourHand'
30
+
31
+    /**
32
+     * Retrieves tooltip dynamically.
33
+     */
34
+    get tooltip() {
35
+        return this.props._raisedHand ? 'toolbar.lowerYourHand' : 'toolbar.raiseYourHand';
36
+    }
37
+
38
+    /**
39
+     * Required by linter due to AbstractButton overwritten prop being writable.
40
+     *
41
+     * @param {string} value - The value.
42
+     */
43
+    set tooltip(value) {
44
+        return value;
45
+    }
46
+
47
+    /**
48
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
49
+     *
50
+     * @protected
51
+     * @returns {void}
52
+     */
53
+    _handleClick() {
54
+        this.props.handleClick();
55
+    }
56
+
57
+    /**
58
+     * Indicates whether this button is in toggled state or not.
59
+     *
60
+     * @override
61
+     * @protected
62
+     * @returns {boolean}
63
+     */
64
+    _isToggled() {
65
+        return this.props._raisedHand;
66
+    }
67
+}
68
+
69
+/**
70
+ * Function that maps parts of Redux state tree into component props.
71
+ *
72
+ * @param {Object} state - Redux state.
73
+ * @returns {Object}
74
+ */
75
+const mapStateToProps = state => {
76
+    const localParticipant = getLocalParticipant(state);
77
+
78
+    return {
79
+        _raisedHand: localParticipant.raisedHand
80
+    };
81
+};
82
+
83
+export default translate(connect(mapStateToProps)(RaiseHandButton));

+ 3
- 0
react/features/toolbox/components/web/Separator.js 查看文件

@@ -0,0 +1,3 @@
1
+import React from 'react';
2
+
3
+export default () => <hr className = 'overflow-menu-hr' />;

+ 139
- 0
react/features/toolbox/components/web/ShareDesktopButton.js 查看文件

@@ -0,0 +1,139 @@
1
+// @flow
2
+
3
+import { translate } from '../../../base/i18n';
4
+import { IconShareDesktop } from '../../../base/icons';
5
+import JitsiMeetJS from '../../../base/lib-jitsi-meet/_';
6
+import { getParticipants } from '../../../base/participants';
7
+import { connect } from '../../../base/redux';
8
+import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
9
+import { getLocalVideoTrack } from '../../../base/tracks';
10
+import { isScreenAudioShared } from '../../../screen-share';
11
+
12
+type Props = AbstractButtonProps & {
13
+
14
+     /**
15
+     * Whether or not screensharing is initialized.
16
+     */
17
+      _desktopSharingEnabled: boolean,
18
+
19
+    /**
20
+     * The tooltip key to use when screensharing is disabled. Or undefined
21
+     * if non to be shown and the button to be hidden.
22
+     */
23
+    _desktopSharingDisabledTooltipKey: string,
24
+
25
+    /**
26
+     * Whether or not the local participant is screensharing.
27
+     */
28
+     _screensharing: boolean,
29
+
30
+    /**
31
+     * The redux {@code dispatch} function.
32
+     */
33
+     dispatch: Function,
34
+
35
+     /**
36
+      * External handler for click action.
37
+      */
38
+      handleClick: Function
39
+};
40
+
41
+/**
42
+ * Implementation of a button for sharing desktop / windows.
43
+ */
44
+class ShareDesktopButton extends AbstractButton<Props, *> {
45
+    accessibilityLabel = 'toolbar.accessibilityLabel.shareYourScreen';
46
+    label = 'toolbar.startScreenSharing';
47
+    icon = IconShareDesktop;
48
+    toggledLabel = 'toolbar.stopScreenSharing'
49
+    tooltip = 'toolbar.accessibilityLabel.shareYourScreen';
50
+
51
+    /**
52
+     * Retrieves tooltip dynamically.
53
+     */
54
+    get tooltip() {
55
+        const { _desktopSharingDisabledTooltipKey, _desktopSharingEnabled, _screensharing } = this.props;
56
+
57
+        if (_desktopSharingEnabled) {
58
+            if (_screensharing) {
59
+                return 'toolbar.stopScreenSharing';
60
+            }
61
+
62
+            return 'toolbar.startScreenSharing';
63
+        }
64
+
65
+        return _desktopSharingDisabledTooltipKey;
66
+    }
67
+
68
+    /**
69
+     * Required by linter due to AbstractButton overwritten prop being writable.
70
+     *
71
+     * @param {string} value - The icon value.
72
+     */
73
+    set tooltip(value) {
74
+        return value;
75
+    }
76
+
77
+    /**
78
+     * Handles clicking / pressing the button, and opens the appropriate dialog.
79
+     *
80
+     * @protected
81
+     * @returns {void}
82
+     */
83
+    _handleClick() {
84
+        this.props.handleClick();
85
+    }
86
+
87
+    /**
88
+     * Indicates whether this button is in toggled state or not.
89
+     *
90
+     * @override
91
+     * @protected
92
+     * @returns {boolean}
93
+     */
94
+    _isToggled() {
95
+        return this.props._screensharing;
96
+    }
97
+
98
+    /**
99
+     * Indicates whether this button is in disabled state or not.
100
+     *
101
+     * @override
102
+     * @protected
103
+     * @returns {boolean}
104
+     */
105
+    _isDisabled() {
106
+        return !this.props._desktopSharingEnabled;
107
+    }
108
+}
109
+
110
+/**
111
+ * Function that maps parts of Redux state tree into component props.
112
+*
113
+ * @param {Object} state - Redux state.
114
+ * @returns {Object}
115
+ */
116
+const mapStateToProps = state => {
117
+    const localVideo = getLocalVideoTrack(state['features/base/tracks']);
118
+    let desktopSharingEnabled = JitsiMeetJS.isDesktopSharingEnabled();
119
+    const { enableFeaturesBasedOnToken } = state['features/base/config'];
120
+
121
+    let desktopSharingDisabledTooltipKey;
122
+
123
+    if (enableFeaturesBasedOnToken) {
124
+        // we enable desktop sharing if any participant already have this
125
+        // feature enabled
126
+        desktopSharingEnabled = getParticipants(state)
127
+            .find(({ features = {} }) =>
128
+                String(features['screen-sharing']) === 'true') !== undefined;
129
+        desktopSharingDisabledTooltipKey = 'dialog.shareYourScreenDisabled';
130
+    }
131
+
132
+    return {
133
+        _desktopSharingDisabledTooltipKey: desktopSharingDisabledTooltipKey,
134
+        _desktopSharingEnabled: desktopSharingEnabled,
135
+        _screensharing: (localVideo && localVideo.videoType === 'desktop') || isScreenAudioShared(state)
136
+    };
137
+};
138
+
139
+export default translate(connect(mapStateToProps)(ShareDesktopButton));

+ 0
- 75
react/features/toolbox/components/web/ToggleCameraButton.js 查看文件

@@ -1,75 +0,0 @@
1
-// @flow
2
-
3
-import { translate } from '../../../base/i18n';
4
-import { IconCameraRefresh } from '../../../base/icons';
5
-import { connect } from '../../../base/redux';
6
-import { AbstractButton, type AbstractButtonProps } from '../../../base/toolbox/components';
7
-import { isLocalCameraTrackMuted, isToggleCameraEnabled, toggleCamera } from '../../../base/tracks';
8
-
9
-/**
10
- * The type of the React {@code Component} props of {@link ToggleCameraButton}.
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 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, any> {
34
-    accessibilityLabel = 'toolbar.accessibilityLabel.toggleCamera';
35
-    icon = IconCameraRefresh;
36
-    label = 'toolbar.toggleCamera';
37
-
38
-    /**
39
-     * Handles clicking/pressing the button.
40
-     *
41
-     * @returns {void}
42
-     */
43
-    _handleClick() {
44
-        this.props.dispatch(toggleCamera());
45
-    }
46
-
47
-    /**
48
-     * Whether this button is disabled or not.
49
-     *
50
-     * @returns {boolean}
51
-     */
52
-    _isDisabled() {
53
-        return this.props._audioOnly || this.props._videoMuted;
54
-    }
55
-}
56
-
57
-/**
58
- * Maps (parts of) the redux state to the associated props for the
59
- * {@code ToggleCameraButton} component.
60
- *
61
- * @param {Object} state - The Redux state.
62
- * @returns {Props}
63
- */
64
-function mapStateToProps(state): Object {
65
-    const { enabled: audioOnly } = state['features/base/audio-only'];
66
-    const tracks = state['features/base/tracks'];
67
-
68
-    return {
69
-        _audioOnly: Boolean(audioOnly),
70
-        _videoMuted: isLocalCameraTrackMuted(tracks),
71
-        visible: isToggleCameraEnabled(state)
72
-    };
73
-}
74
-
75
-export default translate(connect(mapStateToProps)(ToggleCameraButton));

+ 1
- 1
react/features/toolbox/components/web/ToolbarButton.js 查看文件

@@ -11,7 +11,7 @@ import type { Props as AbstractToolbarButtonProps }
11 11
 /**
12 12
  * The type of the React {@code Component} props of {@link ToolbarButton}.
13 13
  */
14
-type Props = AbstractToolbarButtonProps & {
14
+export type Props = AbstractToolbarButtonProps & {
15 15
 
16 16
     /**
17 17
      * The text to display in the tooltip.

+ 363
- 556
react/features/toolbox/components/web/Toolbox.js
文件差異過大導致無法顯示
查看文件


+ 29
- 0
react/features/toolbox/constants.js 查看文件

@@ -0,0 +1,29 @@
1
+/**
2
+ * Thresholds for displaying toolbox buttons
3
+ */
4
+export const THRESHOLDS = [
5
+    {
6
+        width: 520,
7
+        order: [ 'microphone', 'camera', 'desktop', 'chat', 'raisehand', 'participants', 'tileview' ]
8
+    },
9
+    {
10
+        width: 470,
11
+        order: [ 'microphone', 'camera', 'desktop', 'chat', 'raisehand', 'participants' ]
12
+    },
13
+    {
14
+        width: 420,
15
+        order: [ 'microphone', 'camera', 'desktop', 'chat', 'participants' ]
16
+    },
17
+    {
18
+        width: 370,
19
+        order: [ 'microphone', 'camera', 'chat', 'participants' ]
20
+    },
21
+    {
22
+        width: 320,
23
+        order: [ 'microphone', 'camera', 'chat' ]
24
+    },
25
+    {
26
+        width: 270,
27
+        order: [ 'microphone', 'camera' ]
28
+    }
29
+];

+ 0
- 64
react/features/toolbox/functions.web.js 查看文件

@@ -3,70 +3,6 @@
3 3
 import { getToolbarButtons } from '../base/config';
4 4
 import { hasAvailableDevices } from '../base/devices';
5 5
 
6
-const WIDTH = {
7
-    FIT_9_ICONS: 520,
8
-    FIT_8_ICONS: 470,
9
-    FIT_7_ICONS: 420,
10
-    FIT_6_ICONS: 370,
11
-    FIT_5_ICONS: 320,
12
-    FIT_4_ICONS: 280
13
-};
14
-
15
-/**
16
- * Returns a set of button names to be displayed in the toolbox, based on the screen width and platform.
17
- *
18
- * @param {number} width - The width of the screen.
19
- * @param {number} isMobile - The device is a mobile one.
20
- * @returns {Set} The button set.
21
- */
22
-export function getToolbarAdditionalButtons(width: number, isMobile: boolean): Set<string> {
23
-    let buttons = [];
24
-
25
-    switch (true) {
26
-    case width >= WIDTH.FIT_9_ICONS: {
27
-        buttons = isMobile
28
-            ? [ 'chat', 'raisehand', 'tileview', 'participants-pane', 'overflow' ]
29
-            : [ 'desktop', 'chat', 'raisehand', 'tileview', 'participants-pane', 'overflow' ];
30
-        break;
31
-    }
32
-
33
-    case width >= WIDTH.FIT_8_ICONS: {
34
-        buttons = [ 'desktop', 'chat', 'raisehand', 'participants-pane', 'overflow' ];
35
-        break;
36
-    }
37
-
38
-    case width >= WIDTH.FIT_7_ICONS: {
39
-        buttons = [ 'desktop', 'chat', 'participants-pane', 'overflow' ];
40
-        break;
41
-    }
42
-
43
-    case width >= WIDTH.FIT_6_ICONS: {
44
-        buttons = [ 'chat', 'participants-pane', 'overflow' ];
45
-        break;
46
-    }
47
-
48
-    case width >= WIDTH.FIT_5_ICONS: {
49
-        buttons = [ 'chat', 'overflow' ];
50
-        break;
51
-    }
52
-
53
-    case width >= WIDTH.FIT_4_ICONS: {
54
-        buttons = isMobile
55
-            ? [ 'chat', 'overflow' ]
56
-            : [ 'overflow' ];
57
-        break;
58
-    }
59
-
60
-    default: {
61
-        buttons = isMobile
62
-            ? [ 'chat', 'overflow' ]
63
-            : [];
64
-    }
65
-    }
66
-
67
-    return new Set(buttons);
68
-}
69
-
70 6
 /**
71 7
  * Helper for getting the height of the toolbox.
72 8
  *

+ 2
- 2
react/features/video-layout/components/TileViewButton.js 查看文件

@@ -52,13 +52,13 @@ class TileViewButton<P: Props> extends AbstractButton<P, *> {
52 52
      */
53 53
     _handleClick() {
54 54
         const { _tileViewEnabled, dispatch } = this.props;
55
+        const value = !_tileViewEnabled;
55 56
 
56 57
         sendAnalytics(createToolbarEvent(
57 58
             'tileview.button',
58 59
             {
59
-                'is_enabled': _tileViewEnabled
60
+                'is_enabled': value
60 61
             }));
61
-        const value = !_tileViewEnabled;
62 62
 
63 63
         logger.debug(`Tile view ${value ? 'enable' : 'disable'}`);
64 64
         dispatch(setTileView(value));

+ 0
- 0
react/features/video-quality/components/VideoQualityButton.native.js 查看文件


react/features/video-quality/components/OverflowMenuVideoQualityItem.web.js → react/features/video-quality/components/VideoQualityButton.web.js 查看文件

@@ -1,16 +1,14 @@
1 1
 // @flow
2 2
 
3
-import React, { Component } from 'react';
4
-
5 3
 import { translate } from '../../base/i18n';
6 4
 import {
7
-    Icon,
8 5
     IconVideoQualityAudioOnly,
9 6
     IconVideoQualityHD,
10 7
     IconVideoQualityLD,
11 8
     IconVideoQualitySD
12 9
 } from '../../base/icons';
13 10
 import { connect } from '../../base/redux';
11
+import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
14 12
 import { VIDEO_QUALITY_LEVELS } from '../constants';
15 13
 import { findNearestQualityLevel } from '../functions';
16 14
 
@@ -29,9 +27,9 @@ const VIDEO_QUALITY_TO_ICON = {
29 27
 
30 28
 /**
31 29
  * The type of the React {@code Component} props of
32
- * {@link OverflowMenuVideoQualityItem}.
30
+ * {@link VideoQualityButton}.
33 31
  */
34
-type Props = {
32
+type Props = AbstractButtonProps & {
35 33
 
36 34
     /**
37 35
      * Whether or not audio only mode is currently enabled.
@@ -45,9 +43,9 @@ type Props = {
45 43
     _videoQuality: number,
46 44
 
47 45
     /**
48
-     * Callback to invoke when {@link OverflowMenuVideoQualityItem} is clicked.
46
+     * Callback to invoke when {@link VideoQualityButton} is clicked.
49 47
      */
50
-    onClick: Function,
48
+     handleClick: Function,
51 49
 
52 50
     /**
53 51
      * Invoked to obtain translated strings.
@@ -62,72 +60,50 @@ type Props = {
62 60
  *
63 61
  * @extends Component
64 62
  */
65
-class OverflowMenuVideoQualityItem extends Component<Props> {
63
+class VideoQualityButton extends AbstractButton<Props, *> {
64
+    accessibilityLabel = 'toolbar.accessibilityLabel.callQuality';
65
+    label = 'toolbar.callQuality';
66
+    tooltip = 'toolbar.callQuality';
66 67
 
67 68
     /**
68
-     * Initializes a new {@code OverflowMenuVideoQualityItem} instance.
69
-     *
70
-     * @param {*} props - The read-only properties with which the new instance
71
-     * is to be initialized.
69
+     * Dynamically retrieves the icon.
72 70
      */
73
-    constructor(props) {
74
-        super(props);
71
+    get icon() {
72
+        const { _audioOnly, _videoQuality } = this.props;
75 73
 
76
-        // Bind event handler so it is only bound once for every instance.
77
-        this._onKeyPress = this._onKeyPress.bind(this);
78
-    }
74
+        const videoQualityLevel = findNearestQualityLevel(_videoQuality);
79 75
 
80
-    _onKeyPress: (Object) => void;
76
+        const icon = _audioOnly || !videoQualityLevel
77
+            ? IconVideoQualityAudioOnly
78
+            : VIDEO_QUALITY_TO_ICON[videoQualityLevel];
79
+
80
+        return icon;
81
+    }
81 82
 
82 83
     /**
83
-     * KeyPress handler for accessibility.
84
+     * Required by linter due to AbstractButton overwritten prop being writable.
84 85
      *
85
-     * @param {Object} e - The key event to handle.
86
-     *
87
-     * @returns {void}
86
+     * @param {string} value - The icon value.
88 87
      */
89
-    _onKeyPress(e) {
90
-        if (this.props.onClick && (e.key === ' ' || e.key === 'Enter')) {
91
-            e.preventDefault();
92
-            this.props.onClick();
93
-        }
88
+    set icon(value) {
89
+        return value;
94 90
     }
95 91
 
96 92
     /**
97
-     * Implements React's {@link Component#render()}.
93
+     * Handles clicking / pressing the button.
98 94
      *
99
-     * @inheritdoc
100
-     * @returns {ReactElement}
95
+     * @override
96
+     * @protected
97
+     * @returns {void}
101 98
      */
102
-    render() {
103
-        const { _audioOnly, _videoQuality } = this.props;
104
-        const videoQualityLevel = findNearestQualityLevel(_videoQuality);
105
-        const icon = _audioOnly || !videoQualityLevel
106
-            ? IconVideoQualityAudioOnly
107
-            : VIDEO_QUALITY_TO_ICON[videoQualityLevel];
108
-
109
-        return (
110
-            <li
111
-                aria-label = { this.props.t('toolbar.accessibilityLabel.callQuality') }
112
-                className = 'overflow-menu-item'
113
-                onClick = { this.props.onClick }
114
-                onKeyPress = { this._onKeyPress }
115
-                role = 'menuitem'
116
-                tabIndex = { 0 }>
117
-                <span className = 'overflow-menu-item-icon'>
118
-                    <Icon src = { icon } />
119
-                </span>
120
-                <span className = 'profile-text'>
121
-                    { this.props.t('toolbar.callQuality') }
122
-                </span>
123
-            </li>
124
-        );
99
+    _handleClick() {
100
+        this.props.handleClick();
125 101
     }
126 102
 }
127 103
 
128 104
 /**
129 105
  * Maps (parts of) the Redux state to the associated props for the
130
- * {@code OverflowMenuVideoQualityItem} component.
106
+ * {@code VideoQualityButton} component.
131 107
  *
132 108
  * @param {Object} state - The Redux state.
133 109
  * @private
@@ -144,4 +120,4 @@ function _mapStateToProps(state) {
144 120
 }
145 121
 
146 122
 export default translate(
147
-    connect(_mapStateToProps)(OverflowMenuVideoQualityItem));
123
+    connect(_mapStateToProps)(VideoQualityButton));

+ 1
- 3
react/features/video-quality/components/index.js 查看文件

@@ -1,6 +1,4 @@
1
-export {
2
-    default as OverflowMenuVideoQualityItem
3
-} from './OverflowMenuVideoQualityItem';
1
+export { default as VideoQualityButton } from './VideoQualityButton.web';
4 2
 export { default as VideoQualityDialog } from './VideoQualityDialog';
5 3
 export { default as VideoQualityLabel } from './VideoQualityLabel';
6 4
 export {

Loading…
取消
儲存