Browse Source

fix(toolbar): Move buttons to overflow menu when the space isn't enough

j8
Hristo Terezov 6 years ago
parent
commit
a9d82a79ea

+ 8
- 0
css/_atlaskit_overrides.scss View File

@@ -40,3 +40,11 @@
40 40
 .videocontainer .tOoji {
41 41
     background: none;
42 42
 }
43
+
44
+/**
45
+ * Override @atlaskit/InlineDialog styling for the overflowmenu so it displays
46
+ * with the correct height.
47
+ */
48
+.toolbox-button-wth-dialog .eYJELv {
49
+    max-height: initial;
50
+}

+ 8
- 0
css/_toolbars.scss View File

@@ -190,6 +190,14 @@
190 190
                     cursor: initial;
191 191
                     color: #3b475c;
192 192
                 }
193
+
194
+                i.toggled {
195
+                    background: inherit;
196
+                }
197
+
198
+                i.toggled:hover {
199
+                    background: inherit;
200
+                }
193 201
             }
194 202
 
195 203
             .beta-tag {

+ 2
- 1
css/modals/invite/_info.scss View File

@@ -66,7 +66,7 @@
66 66
     }
67 67
 
68 68
     .info-dialog-dial-in {
69
-        white-space: nowrap;
69
+        word-break: break-all;
70 70
 
71 71
         .conference-id,
72 72
         .phone-number {
@@ -77,6 +77,7 @@
77 77
     .info-dialog-icon {
78 78
         color: #6453C0;
79 79
         font-size: 16px;
80
+        min-width: 30px;
80 81
     }
81 82
 
82 83
     .info-dialog-url-text,

+ 13
- 2
lang/main.json View File

@@ -362,7 +362,8 @@
362 362
         "numbers": "Dial-in Numbers",
363 363
         "password": "Password:",
364 364
         "title": "Share",
365
-        "tooltip": "Share link and dial-in info for this meeting"
365
+        "tooltip": "Share link and dial-in info for this meeting",
366
+        "label": "Meeting info"
366 367
     },
367 368
     "inviteDialog": {
368 369
         "alertOk": "Ok",
@@ -631,11 +632,14 @@
631 632
         "callQuality": "Manage call quality",
632 633
         "cameraDisabled": "Camera is not available",
633 634
         "chat": "Open / Close chat",
635
+        "closeChat": "Close chat",
634 636
         "documentClose": "Close shared document",
635 637
         "documentOpen": "Open shared document",
636 638
         "enterFullScreen": "View full screen",
639
+        "enterTileView": "Enter tile view",
637 640
         "etherpad": "Open / Close shared document",
638 641
         "exitFullScreen": "Exit full screen",
642
+        "exitTileView": "Exit tile view",
639 643
         "feedback": "Leave feedback",
640 644
         "filmstrip": "Show / Hide videos",
641 645
         "fullscreen": "View / Exit full screen",
@@ -644,13 +648,16 @@
644 648
         "lock": "Lock / Unlock room",
645 649
         "login": "Login",
646 650
         "logout": "Logout",
651
+        "lowerYourHand": "Lower your hand",
647 652
         "micDisabled": "Microphone is not available",
648 653
         "micMutedPopup": "Your microphone has been muted so that you would fully enjoy your shared video.",
649 654
         "moreActions": "More actions",
650 655
         "mute": "Mute / Unmute",
656
+        "openChat": "Open chat",
651 657
         "pip": "Enter Picture-in-Picture mode",
652 658
         "profile": "Edit your profile",
653 659
         "raiseHand": "Raise / Lower your hand",
660
+        "raiseYourHand": "Raise your hand",
654 661
         "Settings": "Settings",
655 662
         "sharedvideo": "Share a YouTube video",
656 663
         "sharedVideoMutedPopup": "Your shared video has been muted so that you can talk to the other members.",
@@ -658,6 +665,10 @@
658 665
         "shortcuts": "View shortcuts",
659 666
         "sip": "Call SIP number",
660 667
         "speakerStats": "Speaker stats",
668
+        "startScreenSharing": "Start screen sharing",
669
+        "startSubtitles": "Start subtitles",
670
+        "stopScreenSharing": "Stop screen sharing",
671
+        "stopSubtitles": "Stop subtitles",
661 672
         "stopSharedVideo": "Stop YouTube video",
662 673
         "talkWhileMutedPopup": "Trying to speak? You are muted.",
663 674
         "tileViewToggle": "Toggle tile view",
@@ -666,7 +677,7 @@
666 677
         "videomute": "Start / Stop camera"
667 678
     },
668 679
     "transcribing": {
669
-        "ccButtonTooltip": "Start / Stop showing subtitles",
680
+        "ccButtonTooltip": "Start / Stop subtitles",
670 681
         "error": "Transcribing failed. Please try again.",
671 682
         "expandedLabel": "Transcribing is currently on",
672 683
         "failedToStart": "Transcribing failed to start",

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

@@ -160,8 +160,9 @@ export default class AbstractButton<P: Props, S: *> extends Component<P, S> {
160 160
      * @returns {string}
161 161
      */
162 162
     _getIconName() {
163
-        return (this._isToggled() ? this.toggledIconName : this.iconName)
164
-            || this.iconName;
163
+        return (
164
+            this._isToggled() ? this.toggledIconName : this.iconName
165
+        ) || this.iconName;
165 166
     }
166 167
 
167 168
     /**

+ 0
- 0
react/features/base/toolbox/components/OverflowMenuItem.native.js View File


react/features/toolbox/components/web/OverflowMenuItem.js → react/features/base/toolbox/components/OverflowMenuItem.web.js View File


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

@@ -5,3 +5,4 @@ export { default as AbstractButton } from './AbstractButton';
5 5
 export type { Props as AbstractButtonProps } from './AbstractButton';
6 6
 export { default as AbstractHangupButton } from './AbstractHangupButton';
7 7
 export { default as AbstractVideoMuteButton } from './AbstractVideoMuteButton';
8
+export { default as OverflowMenuItem } from './OverflowMenuItem';

+ 39
- 2
react/features/invite/components/InfoDialogButton.web.js View File

@@ -5,12 +5,13 @@ import React, { Component } from 'react';
5 5
 import { connect } from 'react-redux';
6 6
 
7 7
 import { createToolbarEvent, sendAnalytics } from '../../analytics';
8
+import { openDialog } from '../../base/dialog';
8 9
 import { translate } from '../../base/i18n';
9 10
 import { JitsiRecordingConstants } from '../../base/lib-jitsi-meet';
10 11
 import { getParticipantCount } from '../../base/participants';
12
+import { OverflowMenuItem } from '../../base/toolbox';
11 13
 import { getActiveSession } from '../../recording';
12 14
 import { ToolbarButton } from '../../toolbox';
13
-
14 15
 import { updateDialInNumbers } from '../actions';
15 16
 
16 17
 import { InfoDialog } from './info-dialog';
@@ -59,6 +60,11 @@ type Props = {
59 60
      */
60 61
     dispatch: Dispatch<*>,
61 62
 
63
+    /**
64
+     * Whether to show the label or not.
65
+     */
66
+    showLabel: boolean,
67
+
62 68
     /**
63 69
      * Invoked to obtain translated strings.
64 70
      */
@@ -122,6 +128,8 @@ class InfoDialogButton extends Component<Props, State> {
122 128
         // Bind event handlers so they are only bound once for every instance.
123 129
         this._onDialogClose = this._onDialogClose.bind(this);
124 130
         this._onDialogToggle = this._onDialogToggle.bind(this);
131
+        this._onClickOverflowMenuButton
132
+            = this._onClickOverflowMenuButton.bind(this);
125 133
     }
126 134
 
127 135
     /**
@@ -142,16 +150,28 @@ class InfoDialogButton extends Component<Props, State> {
142 150
      * @returns {ReactElement}
143 151
      */
144 152
     render() {
145
-        const { _dialIn, _liveStreamViewURL, t } = this.props;
153
+        const { _dialIn, _liveStreamViewURL, showLabel, t } = this.props;
146 154
         const { showDialog } = this.state;
147 155
         const iconClass = `icon-info ${showDialog ? 'toggled' : ''}`;
148 156
 
157
+        if (showLabel) {
158
+            return (
159
+                <OverflowMenuItem
160
+                    accessibilityLabel = { t('info.accessibilityLabel') }
161
+                    icon = 'icon-info'
162
+                    key = 'info-button'
163
+                    onClick = { this._onClickOverflowMenuButton }
164
+                    text = { t('info.label') } />
165
+            );
166
+        }
167
+
149 168
         return (
150 169
             <div className = 'toolbox-button-wth-dialog'>
151 170
                 <InlineDialog
152 171
                     content = {
153 172
                         <InfoDialog
154 173
                             dialIn = { _dialIn }
174
+                            isInlineDialog = { true }
155 175
                             liveStreamViewURL = { _liveStreamViewURL }
156 176
                             onClose = { this._onDialogClose } /> }
157 177
                     isOpen = { showDialog }
@@ -179,6 +199,23 @@ class InfoDialogButton extends Component<Props, State> {
179 199
         this.setState({ showDialog: false });
180 200
     }
181 201
 
202
+    _onClickOverflowMenuButton: () => void;
203
+
204
+    /**
205
+     * Opens the Info dialog.
206
+     *
207
+     * @returns {void}
208
+     */
209
+    _onClickOverflowMenuButton() {
210
+        const { _dialIn, _liveStreamViewURL } = this.props;
211
+
212
+        this.props.dispatch(openDialog(InfoDialog, {
213
+            dialIn: _dialIn,
214
+            liveStreamViewURL: _liveStreamViewURL,
215
+            isInlineDialog: false
216
+        }));
217
+    }
218
+
182 219
     _onDialogToggle: () => void;
183 220
 
184 221
     /**

+ 28
- 3
react/features/invite/components/info-dialog/InfoDialog.web.js View File

@@ -5,6 +5,7 @@ import { connect } from 'react-redux';
5 5
 
6 6
 import { setPassword } from '../../../base/conference';
7 7
 import { getInviteURL } from '../../../base/connection';
8
+import { Dialog } from '../../../base/dialog';
8 9
 import { translate } from '../../../base/i18n';
9 10
 import { isLocalParticipantModerator } from '../../../base/participants';
10 11
 
@@ -66,6 +67,11 @@ type Props = {
66 67
      */
67 68
     dispatch: Dispatch<*>,
68 69
 
70
+    /**
71
+     * Whether is Atlaskit InlineDialog or a normal dialog.
72
+     */
73
+    isInlineDialog: boolean,
74
+
69 75
     /**
70 76
      * The current known URL for a live stream in progress.
71 77
      */
@@ -187,9 +193,14 @@ class InfoDialog extends Component<Props, State> {
187 193
      * @returns {ReactElement}
188 194
      */
189 195
     render() {
190
-        const { liveStreamViewURL, onMouseOver, t } = this.props;
191
-
192
-        return (
196
+        const {
197
+            isInlineDialog,
198
+            liveStreamViewURL,
199
+            onMouseOver,
200
+            t
201
+        } = this.props;
202
+
203
+        const inlineDialog = (
193 204
             <div
194 205
                 className = 'info-dialog'
195 206
                 onMouseOver = { onMouseOver } >
@@ -246,6 +257,20 @@ class InfoDialog extends Component<Props, State> {
246 257
                     value = { this._getTextToCopy() } />
247 258
             </div>
248 259
         );
260
+
261
+        if (isInlineDialog) {
262
+            return inlineDialog;
263
+        }
264
+
265
+        return (
266
+            <Dialog
267
+                cancelTitleKey = 'dialog.close'
268
+                submitDisabled = { true }
269
+                titleKey = 'info.label'
270
+                width = 'small'>
271
+                { inlineDialog }
272
+            </Dialog>
273
+        );
249 274
     }
250 275
 
251 276
     /**

+ 19
- 0
react/features/invite/components/info-dialog/PasswordForm.web.js View File

@@ -83,6 +83,7 @@ class PasswordForm extends Component<Props, State> {
83 83
         this._onEnteredPasswordChange
84 84
             = this._onEnteredPasswordChange.bind(this);
85 85
         this._onPasswordSubmit = this._onPasswordSubmit.bind(this);
86
+        this._onKeyDown = this._onKeyDown.bind(this);
86 87
     }
87 88
 
88 89
     /**
@@ -119,6 +120,7 @@ class PasswordForm extends Component<Props, State> {
119 120
             return (
120 121
                 <form
121 122
                     className = 'info-password-form'
123
+                    onKeyDown = { this._onKeyDown }
122 124
                     onSubmit = { this._onPasswordSubmit }>
123 125
                     <input
124 126
                         autoFocus = { true }
@@ -175,9 +177,26 @@ class PasswordForm extends Component<Props, State> {
175 177
      */
176 178
     _onPasswordSubmit(event) {
177 179
         event.preventDefault();
180
+        event.stopPropagation();
178 181
 
179 182
         this.props.onSubmit(this.state.enteredPassword);
180 183
     }
184
+
185
+    _onKeyDown: (Object) => void;
186
+
187
+    /**
188
+     * Stops the the EnterKey for propagation in order to prevent the dialog
189
+     * to close.
190
+     *
191
+     * @param {Object} event - The key event.
192
+     * @private
193
+     * @returns {void}
194
+     */
195
+    _onKeyDown(event) {
196
+        if (event.key === 'Enter') {
197
+            event.stopPropagation();
198
+        }
199
+    }
181 200
 }
182 201
 
183 202
 export default translate(PasswordForm);

+ 2
- 0
react/features/subtitles/components/ClosedCaptionButton.web.js View File

@@ -19,6 +19,8 @@ class ClosedCaptionButton
19 19
     iconName = 'icon-closed_caption';
20 20
     toggledIconName = 'icon-closed_caption toggled';
21 21
     tooltip = 'transcribing.ccButtonTooltip';
22
+    label = 'toolbar.startSubtitles';
23
+    toggledLabel = 'toolbar.stopSubtitles';
22 24
 }
23 25
 
24 26
 export default translate(connect(_abstractMapStateToProps)(

+ 254
- 20
react/features/toolbox/components/web/Toolbox.js View File

@@ -16,6 +16,7 @@ import {
16 16
     getParticipants,
17 17
     participantUpdated
18 18
 } from '../../../base/participants';
19
+import { OverflowMenuItem } from '../../../base/toolbox';
19 20
 import { getLocalVideoTrack, toggleScreensharing } from '../../../base/tracks';
20 21
 import { ChatCounter, toggleChat } from '../../../chat';
21 22
 import { toggleDocument } from '../../../etherpad';
@@ -56,7 +57,6 @@ import {
56 57
 import AudioMuteButton from '../AudioMuteButton';
57 58
 import HangupButton from '../HangupButton';
58 59
 import OverflowMenuButton from './OverflowMenuButton';
59
-import OverflowMenuItem from './OverflowMenuItem';
60 60
 import OverflowMenuProfileItem from './OverflowMenuProfileItem';
61 61
 import ToolbarButton from './ToolbarButton';
62 62
 import VideoMuteButton from '../VideoMuteButton';
@@ -177,6 +177,17 @@ type Props = {
177 177
     t: Function
178 178
 };
179 179
 
180
+/**
181
+ * The type of the React {@code Component} state of {@link Toolbox}.
182
+ */
183
+type State = {
184
+
185
+    /**
186
+     * The width of the browser's window.
187
+     */
188
+    windowWidth: number
189
+};
190
+
180 191
 declare var APP: Object;
181 192
 declare var interfaceConfig: Object;
182 193
 
@@ -185,7 +196,7 @@ declare var interfaceConfig: Object;
185 196
  *
186 197
  * @extends Component
187 198
  */
188
-class Toolbox extends Component<Props> {
199
+class Toolbox extends Component<Props, State> {
189 200
     /**
190 201
      * Initializes a new {@code Toolbox} instance.
191 202
      *
@@ -198,6 +209,7 @@ class Toolbox extends Component<Props> {
198 209
         // Bind event handlers so they are only bound once per instance.
199 210
         this._onMouseOut = this._onMouseOut.bind(this);
200 211
         this._onMouseOver = this._onMouseOver.bind(this);
212
+        this._onResize = this._onResize.bind(this);
201 213
         this._onSetOverflowVisible = this._onSetOverflowVisible.bind(this);
202 214
 
203 215
         this._onShortcutToggleChat = this._onShortcutToggleChat.bind(this);
@@ -232,6 +244,10 @@ class Toolbox extends Component<Props> {
232 244
             = this._onToolbarToggleSharedVideo.bind(this);
233 245
         this._onToolbarOpenLocalRecordingInfoDialog
234 246
             = this._onToolbarOpenLocalRecordingInfoDialog.bind(this);
247
+
248
+        this.state = {
249
+            windowWidth: window.innerWidth
250
+        };
235 251
     }
236 252
 
237 253
     /**
@@ -273,6 +289,8 @@ class Toolbox extends Component<Props> {
273 289
                     shortcut.helpDescription);
274 290
             }
275 291
         });
292
+
293
+        window.addEventListener('resize', this._onResize);
276 294
     }
277 295
 
278 296
     /**
@@ -303,6 +321,8 @@ class Toolbox extends Component<Props> {
303 321
     componentWillUnmount() {
304 322
         [ 'C', 'D', 'R', 'S' ].forEach(letter =>
305 323
             APP.keyboardshortcut.unregisterShortcut(letter));
324
+
325
+        window.removeEventListener('resize', this._onResize);
306 326
     }
307 327
 
308 328
     /**
@@ -482,6 +502,24 @@ class Toolbox extends Component<Props> {
482 502
         this.props.dispatch(setToolbarHovered(true));
483 503
     }
484 504
 
505
+    _onResize: () => void;
506
+
507
+    /**
508
+     * A window resize handler used to calculate the number of buttons we can
509
+     * fit in the toolbar.
510
+     *
511
+     * @private
512
+     * @returns {void}
513
+     */
514
+    _onResize() {
515
+        const width = window.innerWidth;
516
+
517
+        if (this.state.windowWidth !== width) {
518
+            this.setState({ windowWidth: width });
519
+        }
520
+    }
521
+
522
+
485 523
     _onSetOverflowVisible: (boolean) => void;
486 524
 
487 525
     /**
@@ -788,13 +826,30 @@ class Toolbox extends Component<Props> {
788 826
         this.props.dispatch(openDialog(LocalRecordingInfoDialog));
789 827
     }
790 828
 
829
+    /**
830
+     * Returns true if the the desktop sharing button should be visible and
831
+     * false otherwise.
832
+     *
833
+     * @returns {boolean}
834
+     */
835
+    _isDesktopSharingButtonVisible() {
836
+        const {
837
+            _desktopSharingEnabled,
838
+            _desktopSharingDisabledTooltipKey
839
+        } = this.props;
840
+
841
+        return _desktopSharingEnabled || _desktopSharingDisabledTooltipKey;
842
+    }
843
+
791 844
     /**
792 845
      * Renders a button for toggleing screen sharing.
793 846
      *
794 847
      * @private
848
+     * @param {boolean} isInOverflowMenu - True if the button is moved to the
849
+     * overflow menu.
795 850
      * @returns {ReactElement|null}
796 851
      */
797
-    _renderDesktopSharingButton() {
852
+    _renderDesktopSharingButton(isInOverflowMenu = false) {
798 853
         const {
799 854
             _desktopSharingEnabled,
800 855
             _desktopSharingDisabledTooltipKey,
@@ -802,13 +857,28 @@ class Toolbox extends Component<Props> {
802 857
             t
803 858
         } = this.props;
804 859
 
805
-        const visible
806
-            = _desktopSharingEnabled || _desktopSharingDisabledTooltipKey;
807
-
808
-        if (!visible) {
860
+        if (!this._isDesktopSharingButtonVisible()) {
809 861
             return null;
810 862
         }
811 863
 
864
+        if (isInOverflowMenu) {
865
+            return (
866
+                <OverflowMenuItem
867
+                    accessibilityLabel
868
+                        = { t('toolbar.accessibilityLabel.shareYourScreen') }
869
+                    disabled = { _desktopSharingEnabled }
870
+                    icon = { 'icon-share-desktop' }
871
+                    key = 'desktop'
872
+                    onClick = { this._onToolbarToggleScreenshare }
873
+                    text = {
874
+                        t(`toolbar.${
875
+                            _screensharing
876
+                                ? 'stopScreenSharing' : 'startScreenSharing'}`
877
+                        )
878
+                    } />
879
+            );
880
+        }
881
+
812 882
         const classNames = `icon-share-desktop ${
813 883
             _screensharing ? 'toggled' : ''} ${
814 884
             _desktopSharingEnabled ? '' : 'disabled'}`;
@@ -826,6 +896,15 @@ class Toolbox extends Component<Props> {
826 896
         );
827 897
     }
828 898
 
899
+    /**
900
+     * Returns true if the profile button is visible and false otherwise.
901
+     *
902
+     * @returns {boolean}
903
+     */
904
+    _isProfileVisible() {
905
+        return this.props._isGuest && this._shouldShowButton('profile');
906
+    }
907
+
829 908
     /**
830 909
      * Renders the list elements of the overflow menu.
831 910
      *
@@ -838,14 +917,12 @@ class Toolbox extends Component<Props> {
838 917
             _etherpadInitialized,
839 918
             _feedbackConfigured,
840 919
             _fullScreen,
841
-            _isGuest,
842 920
             _sharingVideo,
843 921
             t
844 922
         } = this.props;
845 923
 
846 924
         return [
847
-            _isGuest
848
-                && this._shouldShowButton('profile')
925
+            this._isProfileVisible()
849 926
                 && <OverflowMenuProfileItem
850 927
                     key = 'profile'
851 928
                     onClick = { this._onToolbarToggleProfile } />,
@@ -924,6 +1001,88 @@ class Toolbox extends Component<Props> {
924 1001
         ];
925 1002
     }
926 1003
 
1004
+    /**
1005
+     * Renders a list of buttons that are moved to the overflow menu.
1006
+     *
1007
+     * @private
1008
+     * @param {Array<string>} movedButtons - The names of the buttons to be
1009
+     * moved.
1010
+     * @returns {Array<ReactElement>}
1011
+     */
1012
+    _renderMovedButtons(movedButtons) {
1013
+        const {
1014
+            _chatOpen,
1015
+            _raisedHand,
1016
+            t
1017
+        } = this.props;
1018
+
1019
+        return movedButtons.map(buttonName => {
1020
+            switch (buttonName) {
1021
+            case 'desktop':
1022
+                return this._renderDesktopSharingButton(true);
1023
+            case 'raisehand':
1024
+                return (
1025
+                    <OverflowMenuItem
1026
+                        accessibilityLabel =
1027
+                            { t('toolbar.accessibilityLabel.raiseHand') }
1028
+                        icon = { 'icon-raised-hand' }
1029
+                        key = 'raisedHand'
1030
+                        onClick = { this._onToolbarToggleRaiseHand }
1031
+                        text = {
1032
+                            t(`toolbar.${
1033
+                                _raisedHand
1034
+                                    ? 'lowerYourHand' : 'raiseYourHand'}`
1035
+                            )
1036
+                        } />
1037
+                );
1038
+            case 'chat':
1039
+                return (
1040
+                    <OverflowMenuItem
1041
+                        accessibilityLabel =
1042
+                            { t('toolbar.accessibilityLabel.chat') }
1043
+                        icon = { 'icon-chat' }
1044
+                        key = 'chat'
1045
+                        onClick = { this._onToolbarToggleChat }
1046
+                        text = {
1047
+                            t(`toolbar.${
1048
+                                _chatOpen ? 'closeChat' : 'openChat'}`
1049
+                            )
1050
+                        } />
1051
+                );
1052
+            case 'closedcaptions':
1053
+                return <ClosedCaptionButton showLabel = { true } />;
1054
+            case 'info':
1055
+                return <InfoDialogButton showLabel = { true } />;
1056
+            case 'invite':
1057
+                return (
1058
+                    <OverflowMenuItem
1059
+                        accessibilityLabel =
1060
+                            { t('toolbar.accessibilityLabel.invite') }
1061
+                        icon = 'icon-invite'
1062
+                        key = 'invite'
1063
+                        onClick = { this._onToolbarOpenInvite }
1064
+                        text = { t('toolbar.invite') } />
1065
+                );
1066
+            case 'tileview':
1067
+                return <TileViewButton showLabel = { true } />;
1068
+            case 'localrecording':
1069
+                return (
1070
+                    <OverflowMenuItem
1071
+                        accessibilityLabel
1072
+                            = { t('toolbar.accessibilityLabel.localRecording') }
1073
+                        icon = { 'icon-thumb-menu icon-rec' }
1074
+                        key = 'localrecording'
1075
+                        onClick = {
1076
+                            this._onToolbarOpenLocalRecordingInfoDialog
1077
+                        }
1078
+                        text = { t('localRecording.dialogTitle') } />
1079
+                );
1080
+            default:
1081
+                return null;
1082
+            }
1083
+        });
1084
+    }
1085
+
927 1086
     /**
928 1087
      * Renders the toolbox content.
929 1088
      *
@@ -941,13 +1100,86 @@ class Toolbox extends Component<Props> {
941 1100
         const overflowHasItems = Boolean(overflowMenuContent.filter(
942 1101
             child => child).length);
943 1102
         const toolbarAccLabel = 'toolbar.accessibilityLabel.moreActionsMenu';
1103
+        const buttonsLeft = [];
1104
+        const buttonsRight = [];
1105
+
1106
+        const maxNumberOfButtonsPerGroup = Math.floor(
1107
+            (
1108
+                this.state.windowWidth
1109
+                    - 168 // the width of the central group by design
1110
+                    - 48 // the minimum space between the button groups
1111
+            )
1112
+            / 56 // the width + padding of a button
1113
+            / 2 // divide by the number of groups(left and right group)
1114
+        );
1115
+
1116
+        if (this._shouldShowButton('desktop')
1117
+                && this._isDesktopSharingButtonVisible()) {
1118
+            buttonsLeft.push('desktop');
1119
+        }
1120
+        if (this._shouldShowButton('raisehand')) {
1121
+            buttonsLeft.push('raisehand');
1122
+        }
1123
+        if (this._shouldShowButton('chat')) {
1124
+            buttonsLeft.push('chat');
1125
+        }
1126
+        if (this._shouldShowButton('closedcaptions')) {
1127
+            buttonsLeft.push('closedcaptions');
1128
+        }
1129
+        if (overflowHasItems) {
1130
+            buttonsRight.push('overflowmenu');
1131
+        }
1132
+        if (this._shouldShowButton('info')) {
1133
+            buttonsRight.push('info');
1134
+        }
1135
+        if (this._shouldShowButton('invite') && !_hideInviteButton) {
1136
+            buttonsRight.push('invite');
1137
+        }
1138
+        if (this._shouldShowButton('tileview')) {
1139
+            buttonsRight.push('tileview');
1140
+        }
1141
+        if (this._shouldShowButton('localrecording')) {
1142
+            buttonsRight.push('localrecording');
1143
+        }
1144
+
1145
+        const movedButtons = [];
1146
+
1147
+        if (buttonsLeft.length > maxNumberOfButtonsPerGroup) {
1148
+            movedButtons.push(...buttonsLeft.splice(
1149
+                maxNumberOfButtonsPerGroup,
1150
+                buttonsLeft.length - maxNumberOfButtonsPerGroup));
1151
+            if (buttonsRight.indexOf('overflowmenu') === -1) {
1152
+                buttonsRight.unshift('overflowmenu');
1153
+            }
1154
+        }
1155
+
1156
+        if (buttonsRight.length > maxNumberOfButtonsPerGroup) {
1157
+            if (buttonsRight.indexOf('overflowmenu') === -1) {
1158
+                buttonsRight.unshift('overflowmenu');
1159
+            }
1160
+
1161
+            let numberOfButtons = maxNumberOfButtonsPerGroup;
1162
+
1163
+            // make sure the more button will be displayed when we move buttons.
1164
+            if (numberOfButtons === 0) {
1165
+                numberOfButtons++;
1166
+            }
1167
+
1168
+            movedButtons.push(...buttonsRight.splice(
1169
+                numberOfButtons,
1170
+                buttonsRight.length - numberOfButtons));
1171
+
1172
+        }
1173
+
1174
+        overflowMenuContent.splice(
1175
+            1, 0, ...this._renderMovedButtons(movedButtons));
944 1176
 
945 1177
         return (
946 1178
             <div className = 'toolbox-content'>
947 1179
                 <div className = 'button-group-left'>
948
-                    { this._shouldShowButton('desktop')
1180
+                    { buttonsLeft.indexOf('desktop') !== -1
949 1181
                         && this._renderDesktopSharingButton() }
950
-                    { this._shouldShowButton('raisehand')
1182
+                    { buttonsLeft.indexOf('raisehand') !== -1
951 1183
                         && <ToolbarButton
952 1184
                             accessibilityLabel =
953 1185
                                 {
@@ -958,7 +1190,7 @@ class Toolbox extends Component<Props> {
958 1190
                                 : 'icon-raised-hand' }
959 1191
                             onClick = { this._onToolbarToggleRaiseHand }
960 1192
                             tooltip = { t('toolbar.raiseHand') } /> }
961
-                    { this._shouldShowButton('chat')
1193
+                    { buttonsLeft.indexOf('chat') !== -1
962 1194
                         && <div className = 'toolbar-button-with-badge'>
963 1195
                             <ToolbarButton
964 1196
                                 accessibilityLabel =
@@ -971,7 +1203,7 @@ class Toolbox extends Component<Props> {
971 1203
                             <ChatCounter />
972 1204
                         </div> }
973 1205
                     {
974
-                        this._shouldShowButton('closedcaptions')
1206
+                        buttonsLeft.indexOf('closedcaptions') !== -1
975 1207
                             && <ClosedCaptionButton />
976 1208
                     }
977 1209
                 </div>
@@ -984,24 +1216,26 @@ class Toolbox extends Component<Props> {
984 1216
                         visible = { this._shouldShowButton('camera') } />
985 1217
                 </div>
986 1218
                 <div className = 'button-group-right'>
987
-                    { this._shouldShowButton('localrecording')
1219
+                    { buttonsRight.indexOf('localrecording') !== -1
988 1220
                         && <LocalRecordingButton
989 1221
                             onClick = {
990 1222
                                 this._onToolbarOpenLocalRecordingInfoDialog
991 1223
                             } />
992 1224
                     }
993
-                    { this._shouldShowButton('tileview')
1225
+                    { buttonsRight.indexOf('tileview') !== -1
994 1226
                         && <TileViewButton /> }
995
-                    { this._shouldShowButton('invite')
996
-                        && !_hideInviteButton
1227
+                    { buttonsRight.indexOf('invite') !== -1
997 1228
                         && <ToolbarButton
998 1229
                             accessibilityLabel =
999 1230
                                 { t('toolbar.accessibilityLabel.invite') }
1000 1231
                             iconName = 'icon-invite'
1001 1232
                             onClick = { this._onToolbarOpenInvite }
1002 1233
                             tooltip = { t('toolbar.invite') } /> }
1003
-                    { this._shouldShowButton('info') && <InfoDialogButton /> }
1004
-                    { overflowHasItems
1234
+                    {
1235
+                        buttonsRight.indexOf('info') !== -1
1236
+                            && <InfoDialogButton />
1237
+                    }
1238
+                    { buttonsRight.indexOf('overflowmenu') !== -1
1005 1239
                         && <OverflowMenuButton
1006 1240
                             isOpen = { _overflowMenuVisible }
1007 1241
                             onVisibilityChange = { this._onSetOverflowVisible }>

+ 2
- 1
react/features/video-layout/components/TileViewButton.js View File

@@ -38,7 +38,8 @@ type Props = AbstractButtonProps & {
38 38
 class TileViewButton<P: Props> extends AbstractButton<P, *> {
39 39
     accessibilityLabel = 'toolbar.accessibilityLabel.tileView';
40 40
     iconName = 'icon-tiles-many';
41
-    label = 'toolbar.tileViewToggle';
41
+    label = 'toolbar.enterTileView';
42
+    toggledLabel = 'toolbar.exitTileView';
42 43
     toggledIconName = 'icon-tiles-many toggled';
43 44
     tooltip = 'toolbar.tileViewToggle';
44 45
 

Loading…
Cancel
Save