Просмотр исходного кода

fix(MainToolbar): replace hidden buttons.

Currently if a button in the main toolbar is not visible, the button is
not replaced by another button from the overflow menu.
factor2
Hristo Terezov 1 год назад
Родитель
Сommit
6d9bbe0376

+ 25
- 0
react/features/embed-meeting/hooks.ts Просмотреть файл

@@ -0,0 +1,25 @@
1
+import { useSelector } from 'react-redux';
2
+
3
+import { isMobileBrowser } from '../base/environment/utils';
4
+import { isVpaasMeeting } from '../jaas/functions';
5
+
6
+import EmbedMeetingButton from './components/EmbedMeetingButton';
7
+
8
+const embed = {
9
+    key: 'embedmeeting',
10
+    Content: EmbedMeetingButton,
11
+    group: 4
12
+};
13
+
14
+/**
15
+ * A hook that returns the embed button if it is enabled and undefined otherwise.
16
+ *
17
+ *  @returns {Object | undefined}
18
+ */
19
+export function useEmbedButton() {
20
+    const _isVpaasMeeting = useSelector(isVpaasMeeting);
21
+
22
+    if (!isMobileBrowser() && !_isVpaasMeeting) {
23
+        return embed;
24
+    }
25
+}

+ 24
- 0
react/features/etherpad/hooks.ts Просмотреть файл

@@ -0,0 +1,24 @@
1
+import { useSelector } from 'react-redux';
2
+
3
+import { IReduxState } from '../app/types';
4
+
5
+import SharedDocumentButtonWeb from './components/SharedDocumentButton';
6
+
7
+const etherpad = {
8
+    key: 'etherpad',
9
+    Content: SharedDocumentButtonWeb,
10
+    group: 3
11
+};
12
+
13
+/**
14
+ * A hook that returns the etherpad button if it is enabled and undefined otherwise.
15
+ *
16
+ *  @returns {Object | undefined}
17
+ */
18
+export function useEtherpadButton() {
19
+    const visible = useSelector((state: IReduxState) => Boolean(state['features/etherpad'].documentUrl));
20
+
21
+    if (visible) {
22
+        return etherpad;
23
+    }
24
+}

+ 23
- 0
react/features/feedback/hooks.web.ts Просмотреть файл

@@ -0,0 +1,23 @@
1
+import { useSelector } from 'react-redux';
2
+
3
+import FeedbackButtonWeb from './components/FeedbackButton.web';
4
+import { shouldSendJaaSFeedbackMetadata } from './functions.web';
5
+
6
+const feedback = {
7
+    key: 'feedback',
8
+    Content: FeedbackButtonWeb,
9
+    group: 4
10
+};
11
+
12
+/**
13
+ * A hook that returns the feedback button if it is enabled and undefined otherwise.
14
+ *
15
+ *  @returns {Object | undefined}
16
+ */
17
+export function useFeedbackButton() {
18
+    const visible = useSelector(shouldSendJaaSFeedbackMetadata);
19
+
20
+    if (visible) {
21
+        return feedback;
22
+    }
23
+}

+ 25
- 0
react/features/keyboard-shortcuts/hooks.web.ts Просмотреть файл

@@ -0,0 +1,25 @@
1
+import { useSelector } from 'react-redux';
2
+
3
+import { isMobileBrowser } from '../base/environment/utils';
4
+
5
+import KeyboardShortcutsButton from './components/web/KeyboardShortcutsButton';
6
+import { areKeyboardShortcutsEnabled } from './functions';
7
+
8
+const shortcuts = {
9
+    key: 'shortcuts',
10
+    Content: KeyboardShortcutsButton,
11
+    group: 4
12
+};
13
+
14
+/**
15
+ * A hook that returns the keyboard shortcuts button if it is enabled and undefined otherwise.
16
+ *
17
+ *  @returns {Object | undefined}
18
+ */
19
+export function useKeyboardShortcutsButton() {
20
+    const _areKeyboardShortcutsEnabled = useSelector(areKeyboardShortcutsEnabled);
21
+
22
+    if (!isMobileBrowser() && _areKeyboardShortcutsEnabled) {
23
+        return shortcuts;
24
+    }
25
+}

+ 23
- 1
react/features/participants-pane/hooks.web.ts Просмотреть файл

@@ -1,14 +1,23 @@
1 1
 import { useCallback, useState } from 'react';
2
-import { useDispatch } from 'react-redux';
2
+import { useDispatch, useSelector } from 'react-redux';
3 3
 
4 4
 import { handleLobbyChatInitialized } from '../chat/actions.web';
5 5
 import { approveKnockingParticipant, rejectKnockingParticipant } from '../lobby/actions.web';
6 6
 
7
+import ParticipantsPaneButton from './components/web/ParticipantsPaneButton';
8
+import { isParticipantsPaneEnabled } from './functions';
9
+
7 10
 interface IDrawerParticipant {
8 11
     displayName?: string;
9 12
     participantID: string;
10 13
 }
11 14
 
15
+const participants = {
16
+    key: 'participants-pane',
17
+    Content: ParticipantsPaneButton,
18
+    group: 2
19
+};
20
+
12 21
 /**
13 22
  * Hook used to create admit/reject lobby actions.
14 23
  *
@@ -57,3 +66,16 @@ export function useParticipantDrawer(): [
57 66
         openDrawerForParticipant
58 67
     ];
59 68
 }
69
+
70
+/**
71
+ * A hook that returns the participants pane button if it is enabled and undefined otherwise.
72
+ *
73
+ *  @returns {Object | undefined}
74
+ */
75
+export function useParticipantPaneButton() {
76
+    const participantsPaneEnabled = useSelector(isParticipantsPaneEnabled);
77
+
78
+    if (participantsPaneEnabled) {
79
+        return participants;
80
+    }
81
+}

+ 23
- 0
react/features/reactions/hooks.web.ts Просмотреть файл

@@ -0,0 +1,23 @@
1
+import { useSelector } from 'react-redux';
2
+
3
+import ReactionsMenuButton from './components/web/ReactionsMenuButton';
4
+import { isReactionsButtonEnabled } from './functions';
5
+
6
+const reactions = {
7
+    key: 'reactions',
8
+    Content: ReactionsMenuButton,
9
+    group: 2
10
+};
11
+
12
+/**
13
+ * A hook that returns the reactions button if it is enabled and undefined otherwise.
14
+ *
15
+ *  @returns {Object | undefined}
16
+ */
17
+export function useReactionsButton() {
18
+    const reactionsButtonEnabled = useSelector(isReactionsButtonEnabled);
19
+
20
+    if (reactionsButtonEnabled) {
21
+        return reactions;
22
+    }
23
+}

+ 7
- 7
react/features/recording/components/LiveStream/AbstractLiveStreamButton.ts Просмотреть файл

@@ -8,7 +8,7 @@ import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/too
8 8
 import { isInBreakoutRoom } from '../../../breakout-rooms/functions';
9 9
 import { maybeShowPremiumFeatureDialog } from '../../../jaas/actions';
10 10
 import { isRecorderTranscriptionsRunning } from '../../../transcribing/functions';
11
-import { getActiveSession, isCloudRecordingRunning } from '../../functions';
11
+import { getActiveSession, isCloudRecordingRunning, isLiveStreamingButtonVisible } from '../../functions';
12 12
 
13 13
 import { getLiveStreaming } from './functions';
14 14
 
@@ -133,11 +133,12 @@ export function _mapStateToProps(state: IReduxState, ownProps: IProps) {
133 133
         const isModerator = isLocalParticipantModerator(state);
134 134
         const liveStreaming = getLiveStreaming(state);
135 135
 
136
-        if (isModerator) {
137
-            visible = liveStreaming.enabled ? isJwtFeatureEnabled(state, 'livestreaming', true) : false;
138
-        } else {
139
-            visible = false;
140
-        }
136
+        visible = isLiveStreamingButtonVisible({
137
+            localParticipantIsModerator: isModerator,
138
+            liveStreamingEnabled: liveStreaming?.enabled,
139
+            liveStreamingEnabledInJwt: isJwtFeatureEnabled(state, 'livestreaming', true),
140
+            isInBreakoutRoom: isInBreakoutRoom(state)
141
+        });
141 142
     }
142 143
 
143 144
     // disable the button if the recording is running.
@@ -149,7 +150,6 @@ export function _mapStateToProps(state: IReduxState, ownProps: IProps) {
149 150
     // disable the button if we are in a breakout room.
150 151
     if (isInBreakoutRoom(state)) {
151 152
         _disabled = true;
152
-        visible = false;
153 153
     }
154 154
 
155 155
     return {

+ 30
- 2
react/features/recording/functions.ts Просмотреть файл

@@ -11,7 +11,7 @@ import {
11 11
     isLocalParticipantModerator
12 12
 } from '../base/participants/functions';
13 13
 import { registerSound, unregisterSound } from '../base/sounds/actions';
14
-import { isInBreakoutRoom } from '../breakout-rooms/functions';
14
+import { isInBreakoutRoom as isInBreakoutRoomF } from '../breakout-rooms/functions';
15 15
 import { isEnabled as isDropboxEnabled } from '../dropbox/functions';
16 16
 import { extractFqnFromPath } from '../dynamic-branding/functions.any';
17 17
 import { canAddTranscriber, isRecorderTranscriptionsRunning } from '../transcribing/functions';
@@ -268,7 +268,7 @@ export function getRecordButtonProps(state: IReduxState) {
268 268
     }
269 269
 
270 270
     // disable the button if we are in a breakout room.
271
-    if (isInBreakoutRoom(state)) {
271
+    if (isInBreakoutRoomF(state)) {
272 272
         disabled = true;
273 273
         visible = false;
274 274
     }
@@ -404,3 +404,31 @@ export function registerRecordingAudioFiles(dispatch: IStore['dispatch'], should
404 404
         RECORDING_ON_SOUND_ID,
405 405
         getSoundFileSrc(RECORDING_ON_SOUND_FILE, language)));
406 406
 }
407
+
408
+/**
409
+ * Returns true if the live streaming button should be visible.
410
+ *
411
+ * @param {boolean} localParticipantIsModerator - True if the local participant is moderator.
412
+ * @param {boolean} liveStreamingEnabled - True if the live streaming is enabled.
413
+ * @param {boolean} liveStreamingEnabledInJwt - True if the lives treaming feature is enabled in JWT.
414
+ * @returns {boolean}
415
+ */
416
+export function isLiveStreamingButtonVisible({
417
+    localParticipantIsModerator,
418
+    liveStreamingEnabled,
419
+    liveStreamingEnabledInJwt,
420
+    isInBreakoutRoom
421
+}: {
422
+    isInBreakoutRoom: boolean;
423
+    liveStreamingEnabled: boolean;
424
+    liveStreamingEnabledInJwt: boolean;
425
+    localParticipantIsModerator: boolean;
426
+}) {
427
+    let visible = false;
428
+
429
+    if (localParticipantIsModerator && !isInBreakoutRoom) {
430
+        visible = liveStreamingEnabled ? liveStreamingEnabledInJwt : false;
431
+    }
432
+
433
+    return visible;
434
+}

+ 63
- 0
react/features/recording/hooks.web.ts Просмотреть файл

@@ -0,0 +1,63 @@
1
+import { useSelector } from 'react-redux';
2
+
3
+import { IReduxState } from '../app/types';
4
+import { isJwtFeatureEnabled } from '../base/jwt/functions';
5
+import { isLocalParticipantModerator } from '../base/participants/functions';
6
+import { isInBreakoutRoom } from '../breakout-rooms/functions';
7
+
8
+import { getLiveStreaming } from './components/LiveStream/functions';
9
+import LiveStreamButton from './components/LiveStream/web/LiveStreamButton';
10
+import RecordButton from './components/Recording/web/RecordButton';
11
+import { getRecordButtonProps, isLiveStreamingButtonVisible } from './functions';
12
+
13
+
14
+const recording = {
15
+    key: 'recording',
16
+    Content: RecordButton,
17
+    group: 2
18
+};
19
+
20
+const livestreaming = {
21
+    key: 'livestreaming',
22
+    Content: LiveStreamButton,
23
+    group: 2
24
+};
25
+
26
+/**
27
+ * A hook that returns the recording button if it is enabled and undefined otherwise.
28
+ *
29
+ *  @returns {Object | undefined}
30
+ */
31
+export function useRecordingButton() {
32
+    const recordingProps = useSelector(getRecordButtonProps);
33
+    const toolbarButtons = useSelector((state: IReduxState) => state['features/toolbox'].toolbarButtons);
34
+
35
+    if (toolbarButtons?.includes('recording') && recordingProps.visible) {
36
+        return recording;
37
+    }
38
+
39
+}
40
+
41
+/**
42
+ * A hook that returns the livestreaming button if it is enabled and undefined otherwise.
43
+ *
44
+ *  @returns {Object | undefined}
45
+ */
46
+export function useLiveStreamingButton() {
47
+    const toolbarButtons = useSelector((state: IReduxState) => state['features/toolbox'].toolbarButtons);
48
+    const localParticipantIsModerator = useSelector(isLocalParticipantModerator);
49
+    const liveStreaming = useSelector(getLiveStreaming);
50
+    const liveStreamingEnabledInJwt
51
+        = useSelector((state: IReduxState) => isJwtFeatureEnabled(state, 'livestreaming', true));
52
+    const _isInBreakoutRoom = useSelector(isInBreakoutRoom);
53
+
54
+    if (toolbarButtons?.includes('recording')
55
+            && isLiveStreamingButtonVisible({
56
+                localParticipantIsModerator,
57
+                liveStreamingEnabled: liveStreaming?.enabled,
58
+                liveStreamingEnabledInJwt,
59
+                isInBreakoutRoom: _isInBreakoutRoom
60
+            })) {
61
+        return livestreaming;
62
+    }
63
+}

+ 11
- 6
react/features/security/components/security-dialog/AbstractSecurityDialogButton.ts Просмотреть файл

@@ -7,6 +7,7 @@ import { getFeatureFlag } from '../../../base/flags/functions';
7 7
 import { IconSecurityOff, IconSecurityOn } from '../../../base/icons/svg';
8 8
 import { isLocalParticipantModerator } from '../../../base/participants/functions';
9 9
 import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
10
+import { isSecurityDialogButtonVisible } from '../../functions';
10 11
 
11 12
 export interface IProps extends AbstractButtonProps {
12 13
 
@@ -71,17 +72,21 @@ export default class AbstractSecurityDialogButton<P extends IProps, S>
71 72
  */
72 73
 export function _mapStateToProps(state: IReduxState) {
73 74
     const { conference } = state['features/base/conference'];
74
-    const { hideLobbyButton } = getSecurityUiConfig(state);
75 75
     const { locked } = state['features/base/conference'];
76 76
     const { lobbyEnabled } = state['features/lobby'];
77
-    const lobbySupported = conference?.isLobbySupported();
78
-    const lobby = lobbySupported && isLocalParticipantModerator(state) && !hideLobbyButton;
79
-    const enabledFlag = getFeatureFlag(state, SECURITY_OPTIONS_ENABLED, true);
80
-    const enabledLobbyModeFlag = getFeatureFlag(state, LOBBY_MODE_ENABLED, true) && lobby;
77
+    const enabledSecurityOptionsFlag = getFeatureFlag(state, SECURITY_OPTIONS_ENABLED, true);
78
+    const enabledLobbyModeFlag = getFeatureFlag(state, LOBBY_MODE_ENABLED, true);
81 79
     const enabledMeetingPassFlag = getFeatureFlag(state, MEETING_PASSWORD_ENABLED, true);
82 80
 
83 81
     return {
84 82
         _locked: Boolean(locked || lobbyEnabled),
85
-        visible: enabledFlag && (enabledLobbyModeFlag || enabledMeetingPassFlag)
83
+        visible: isSecurityDialogButtonVisible({
84
+            conference,
85
+            securityUIConfig: getSecurityUiConfig(state),
86
+            isModerator: isLocalParticipantModerator(state),
87
+            enabledLobbyModeFlag,
88
+            enabledMeetingPassFlag,
89
+            enabledSecurityOptionsFlag
90
+        })
86 91
     };
87 92
 }

+ 28
- 0
react/features/security/functions.ts Просмотреть файл

@@ -0,0 +1,28 @@
1
+/**
2
+ * Returns true if the security dialog button should be visible and false otherwise.
3
+ *
4
+ * @param {Object} options - The parameters needed to determine the security dialog button visibility.
5
+ * @returns {boolean}
6
+ */
7
+export function isSecurityDialogButtonVisible({
8
+    conference,
9
+    securityUIConfig,
10
+    isModerator,
11
+    enabledLobbyModeFlag,
12
+    enabledSecurityOptionsFlag,
13
+    enabledMeetingPassFlag
14
+}: {
15
+    conference: any;
16
+    enabledLobbyModeFlag: boolean;
17
+    enabledMeetingPassFlag: boolean;
18
+    enabledSecurityOptionsFlag: boolean;
19
+    isModerator: boolean;
20
+    securityUIConfig: { hideLobbyButton?: boolean; };
21
+}) {
22
+    const { hideLobbyButton } = securityUIConfig;
23
+    const lobbySupported = conference?.isLobbySupported();
24
+    const lobby = lobbySupported && isModerator && !hideLobbyButton;
25
+
26
+
27
+    return enabledSecurityOptionsFlag && ((enabledLobbyModeFlag && lobby) || enabledMeetingPassFlag);
28
+}

+ 45
- 0
react/features/security/hooks.web.ts Просмотреть файл

@@ -0,0 +1,45 @@
1
+import { useSelector } from 'react-redux';
2
+
3
+import { IReduxState } from '../app/types';
4
+import { getSecurityUiConfig } from '../base/config/functions.any';
5
+import { LOBBY_MODE_ENABLED, MEETING_PASSWORD_ENABLED, SECURITY_OPTIONS_ENABLED } from '../base/flags/constants';
6
+import { getFeatureFlag } from '../base/flags/functions';
7
+import { isLocalParticipantModerator } from '../base/participants/functions';
8
+
9
+import SecurityDialogButton from './components/security-dialog/web/SecurityDialogButton';
10
+import { isSecurityDialogButtonVisible } from './functions';
11
+
12
+const security = {
13
+    key: 'security',
14
+    alias: 'info',
15
+    Content: SecurityDialogButton,
16
+    group: 2
17
+};
18
+
19
+/**
20
+ * A hook that returns the security dialog button if it is enabled and undefined otherwise.
21
+ *
22
+ *  @returns {Object | undefined}
23
+ */
24
+export function useSecurityDialogButton() {
25
+    const conference = useSelector((state: IReduxState) => state['features/base/conference'].conference);
26
+    const securityUIConfig = useSelector(getSecurityUiConfig);
27
+    const isModerator = useSelector(isLocalParticipantModerator);
28
+    const enabledLobbyModeFlag
29
+        = useSelector((state: IReduxState) => getFeatureFlag(state, LOBBY_MODE_ENABLED, true));
30
+    const enabledSecurityOptionsFlag
31
+        = useSelector((state: IReduxState) => getFeatureFlag(state, SECURITY_OPTIONS_ENABLED, true));
32
+    const enabledMeetingPassFlag
33
+        = useSelector((state: IReduxState) => getFeatureFlag(state, MEETING_PASSWORD_ENABLED, true));
34
+
35
+    if (isSecurityDialogButtonVisible({
36
+        conference,
37
+        securityUIConfig,
38
+        isModerator,
39
+        enabledLobbyModeFlag,
40
+        enabledSecurityOptionsFlag,
41
+        enabledMeetingPassFlag
42
+    })) {
43
+        return security;
44
+    }
45
+}

+ 23
- 0
react/features/speaker-stats/hooks.web.ts Просмотреть файл

@@ -0,0 +1,23 @@
1
+import { useSelector } from 'react-redux';
2
+
3
+import SpeakerStatsButton from './components/web/SpeakerStatsButton';
4
+import { isSpeakerStatsDisabled } from './functions';
5
+
6
+const speakerStats = {
7
+    key: 'stats',
8
+    Content: SpeakerStatsButton,
9
+    group: 3
10
+};
11
+
12
+/**
13
+ * A hook that returns the speaker stats button if it is enabled and undefined otherwise.
14
+ *
15
+ *  @returns {Object | undefined}
16
+ */
17
+export function useSpeakerStatsButton() {
18
+    const disabled = useSelector(isSpeakerStatsDisabled);
19
+
20
+    if (!disabled) {
21
+        return speakerStats;
22
+    }
23
+}

+ 23
- 0
react/features/subtitles/hooks.web.ts Просмотреть файл

@@ -0,0 +1,23 @@
1
+import { useSelector } from 'react-redux';
2
+
3
+import ClosedCaptionButton from './components/web/ClosedCaptionButton';
4
+import { canStartSubtitles } from './functions.any';
5
+
6
+const cc = {
7
+    key: 'closedcaptions',
8
+    Content: ClosedCaptionButton,
9
+    group: 2
10
+};
11
+
12
+/**
13
+ * A hook that returns the CC button if it is enabled and undefined otherwise.
14
+ *
15
+ *  @returns {Object | undefined}
16
+ */
17
+export function useClosedCaptionButton() {
18
+    const isStartSubtitlesButtonVisible = useSelector(canStartSubtitles);
19
+
20
+    if (isStartSubtitlesButtonVisible) {
21
+        return cc;
22
+    }
23
+}

+ 1
- 0
react/features/toolbox/components/index.native.ts Просмотреть файл

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

+ 1
- 0
react/features/toolbox/components/index.web.ts Просмотреть файл

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

+ 6
- 6
react/features/toolbox/components/web/Toolbox.tsx Просмотреть файл

@@ -20,7 +20,7 @@ import {
20 20
     isButtonEnabled,
21 21
     isToolboxVisible
22 22
 } from '../../functions.web';
23
-import { useKeyboardShortcuts } from '../../hooks.web';
23
+import { useKeyboardShortcuts, useToolboxButtons } from '../../hooks.web';
24 24
 import { IToolboxButton } from '../../types';
25 25
 import HangupButton from '../HangupButton';
26 26
 
@@ -105,6 +105,7 @@ export default function Toolbox({
105 105
     const toolbarVisible = useSelector(isToolboxVisible);
106 106
     const mainToolbarButtonsThresholds
107 107
         = useSelector((state: IReduxState) => state['features/toolbox'].mainToolbarButtonsThresholds);
108
+    const allButtons = useToolboxButtons(customToolbarButtons);
108 109
 
109 110
     useKeyboardShortcuts(toolbarButtonsToUse);
110 111
 
@@ -216,7 +217,7 @@ export default function Toolbox({
216 217
     const containerClassName = `toolbox-content${isMobile || isNarrowLayout ? ' toolbox-content-mobile' : ''}`;
217 218
 
218 219
     const { mainMenuButtons, overflowMenuButtons } = getVisibleButtons({
219
-        customToolbarButtons,
220
+        allButtons,
220 221
         buttonsWithNotifyClick,
221 222
         toolbarButtons: toolbarButtonsToUse,
222 223
         clientWidth,
@@ -225,10 +226,9 @@ export default function Toolbox({
225 226
     });
226 227
     const raiseHandInOverflowMenu = overflowMenuButtons.some(({ key }) => key === 'raisehand');
227 228
     const showReactionsInOverflowMenu = _shouldDisplayReactionsButtons
228
-    && (
229
-        (!reactionsButtonEnabled && (raiseHandInOverflowMenu || isNarrowLayout || isMobile))
230
-            || overflowMenuButtons.some(({ key }) => key === 'reactions')
231
-    );
229
+        && (
230
+            (!reactionsButtonEnabled && (raiseHandInOverflowMenu || isNarrowLayout || isMobile))
231
+            || overflowMenuButtons.some(({ key }) => key === 'reactions'));
232 232
     const showRaiseHandInReactionsMenu = showReactionsInOverflowMenu && raiseHandInOverflowMenu;
233 233
 
234 234
     return (

+ 7
- 294
react/features/toolbox/functions.web.ts Просмотреть файл

@@ -3,41 +3,9 @@ import { hasAvailableDevices } from '../base/devices/functions';
3 3
 import { MEET_FEATURES } from '../base/jwt/constants';
4 4
 import { isJwtFeatureEnabled } from '../base/jwt/functions';
5 5
 import { IGUMPendingState } from '../base/media/types';
6
-import ChatButton from '../chat/components/web/ChatButton';
7
-import EmbedMeetingButton from '../embed-meeting/components/EmbedMeetingButton';
8
-import SharedDocumentButton from '../etherpad/components/SharedDocumentButton.web';
9
-import FeedbackButton from '../feedback/components/FeedbackButton.web';
10
-import InviteButton from '../invite/components/add-people-dialog/web/InviteButton';
11
-import KeyboardShortcutsButton from '../keyboard-shortcuts/components/web/KeyboardShortcutsButton';
12
-import NoiseSuppressionButton from '../noise-suppression/components/NoiseSuppressionButton';
13
-import ParticipantsPaneButton from '../participants-pane/components/web/ParticipantsPaneButton';
14
-import RaiseHandContainerButton from '../reactions/components/web/RaiseHandContainerButtons';
15
-import ReactionsMenuButton from '../reactions/components/web/ReactionsMenuButton';
16
-import LiveStreamButton from '../recording/components/LiveStream/web/LiveStreamButton';
17
-import RecordButton from '../recording/components/Recording/web/RecordButton';
18
-import ShareAudioButton from '../screen-share/components/web/ShareAudioButton';
19 6
 import { isScreenMediaShared } from '../screen-share/functions';
20
-import SecurityDialogButton from '../security/components/security-dialog/web/SecurityDialogButton';
21
-import SettingsButton from '../settings/components/web/SettingsButton';
22
-import SharedVideoButton from '../shared-video/components/web/SharedVideoButton';
23
-import SpeakerStatsButton from '../speaker-stats/components/web/SpeakerStatsButton';
24
-import ClosedCaptionButton from '../subtitles/components/web/ClosedCaptionButton';
25
-import TileViewButton from '../video-layout/components/TileViewButton';
26
-import VideoQualityButton from '../video-quality/components/VideoQualityButton.web';
27
-import VideoBackgroundButton from '../virtual-background/components/VideoBackgroundButton';
28
-import WhiteboardButton from '../whiteboard/components/web/WhiteboardButton';
29 7
 import { isWhiteboardVisible } from '../whiteboard/functions';
30 8
 
31
-import DownloadButton from './components/DownloadButton';
32
-import HelpButton from './components/HelpButton';
33
-import AudioSettingsButton from './components/web/AudioSettingsButton';
34
-import CustomOptionButton from './components/web/CustomOptionButton';
35
-import FullscreenButton from './components/web/FullscreenButton';
36
-import LinkToSalesforceButton from './components/web/LinkToSalesforceButton';
37
-import ProfileButton from './components/web/ProfileButton';
38
-import ShareDesktopButton from './components/web/ShareDesktopButton';
39
-import ToggleCameraButton from './components/web/ToggleCameraButton';
40
-import VideoSettingsButton from './components/web/VideoSettingsButton';
41 9
 import { MAIN_TOOLBAR_BUTTONS_PRIORITY, TOOLBAR_TIMEOUT } from './constants';
42 10
 import { IMainToolbarButtonThresholds, IToolboxButton, NOTIFY_CLICK_MODE } from './types';
43 11
 
@@ -189,261 +157,6 @@ export function getToolbarTimeout(state: IReduxState) {
189 157
     return toolbarConfig?.timeout || TOOLBAR_TIMEOUT;
190 158
 }
191 159
 
192
-interface ICustomToolbarButton {
193
-    backgroundColor?: string;
194
-    icon: string;
195
-    id: string;
196
-    text: string;
197
-}
198
-
199
-/**
200
-    * Returns all buttons that could be rendered.
201
-    *
202
-    * @param {Object} _customToolbarButtons - An array containing custom buttons objects.
203
-    * @returns {Object} The button maps mainMenuButtons and overflowMenuButtons.
204
-    */
205
-export function getAllToolboxButtons(
206
-        _customToolbarButtons?: ICustomToolbarButton[]): { [key: string]: IToolboxButton; } {
207
-    const microphone = {
208
-        key: 'microphone',
209
-        Content: AudioSettingsButton,
210
-        group: 0
211
-    };
212
-
213
-    const camera = {
214
-        key: 'camera',
215
-        Content: VideoSettingsButton,
216
-        group: 0
217
-    };
218
-
219
-    const profile = {
220
-        key: 'profile',
221
-        Content: ProfileButton,
222
-        group: 1
223
-    };
224
-
225
-    const chat = {
226
-        key: 'chat',
227
-        Content: ChatButton,
228
-        group: 2
229
-    };
230
-
231
-    const desktop = {
232
-        key: 'desktop',
233
-        Content: ShareDesktopButton,
234
-        group: 2
235
-    };
236
-
237
-    // In Narrow layout and mobile web we are using drawer for popups and that is why it is better to include
238
-    // all forms of reactions in the overflow menu. Otherwise the toolbox will be hidden and the reactions popup
239
-    // misaligned.
240
-    const raisehand = {
241
-        key: 'raisehand',
242
-        Content: RaiseHandContainerButton,
243
-        group: 2
244
-    };
245
-
246
-    const reactions = {
247
-        key: 'reactions',
248
-        Content: ReactionsMenuButton,
249
-        group: 2
250
-    };
251
-
252
-    const participants = {
253
-        key: 'participants-pane',
254
-        Content: ParticipantsPaneButton,
255
-        group: 2
256
-    };
257
-
258
-    const invite = {
259
-        key: 'invite',
260
-        Content: InviteButton,
261
-        group: 2
262
-    };
263
-
264
-    const tileview = {
265
-        key: 'tileview',
266
-        Content: TileViewButton,
267
-        group: 2
268
-    };
269
-
270
-    const toggleCamera = {
271
-        key: 'toggle-camera',
272
-        Content: ToggleCameraButton,
273
-        group: 2
274
-    };
275
-
276
-    const videoquality = {
277
-        key: 'videoquality',
278
-        Content: VideoQualityButton,
279
-        group: 2
280
-    };
281
-
282
-    const fullscreen = {
283
-        key: 'fullscreen',
284
-        Content: FullscreenButton,
285
-        group: 2
286
-    };
287
-
288
-    const security = {
289
-        key: 'security',
290
-        Content: SecurityDialogButton,
291
-        group: 2
292
-    };
293
-
294
-    const closedcaptions = {
295
-        key: 'closedcaptions',
296
-        Content: ClosedCaptionButton,
297
-        group: 2
298
-    };
299
-
300
-    const recording = {
301
-        key: 'recording',
302
-        Content: RecordButton,
303
-        group: 2
304
-    };
305
-
306
-    const livestreaming = {
307
-        key: 'livestreaming',
308
-        Content: LiveStreamButton,
309
-        group: 2
310
-    };
311
-
312
-    const linktosalesforce = {
313
-        key: 'linktosalesforce',
314
-        Content: LinkToSalesforceButton,
315
-        group: 2
316
-    };
317
-
318
-    const sharedvideo = {
319
-        key: 'sharedvideo',
320
-        Content: SharedVideoButton,
321
-        group: 3
322
-    };
323
-
324
-    const shareaudio = {
325
-        key: 'shareaudio',
326
-        Content: ShareAudioButton,
327
-        group: 3
328
-    };
329
-
330
-    const noisesuppression = {
331
-        key: 'noisesuppression',
332
-        Content: NoiseSuppressionButton,
333
-        group: 3
334
-    };
335
-
336
-
337
-    const whiteboard = {
338
-        key: 'whiteboard',
339
-        Content: WhiteboardButton,
340
-        group: 3
341
-    };
342
-
343
-    const etherpad = {
344
-        key: 'etherpad',
345
-        Content: SharedDocumentButton,
346
-        group: 3
347
-    };
348
-
349
-    const virtualBackground = {
350
-        key: 'select-background',
351
-        Content: VideoBackgroundButton,
352
-        group: 3
353
-    };
354
-
355
-    const stats = {
356
-        key: 'stats',
357
-        Content: SpeakerStatsButton,
358
-        group: 3
359
-    };
360
-
361
-    const settings = {
362
-        key: 'settings',
363
-        Content: SettingsButton,
364
-        group: 4
365
-    };
366
-
367
-    const shortcuts = {
368
-        key: 'shortcuts',
369
-        Content: KeyboardShortcutsButton,
370
-        group: 4
371
-    };
372
-
373
-    const embedmeeting = {
374
-        key: 'embedmeeting',
375
-        Content: EmbedMeetingButton,
376
-        group: 4
377
-    };
378
-
379
-    const feedback = {
380
-        key: 'feedback',
381
-        Content: FeedbackButton,
382
-        group: 4
383
-    };
384
-
385
-    const download = {
386
-        key: 'download',
387
-        Content: DownloadButton,
388
-        group: 4
389
-    };
390
-
391
-    const help = {
392
-        key: 'help',
393
-        Content: HelpButton,
394
-        group: 4
395
-    };
396
-
397
-    const customButtons = _customToolbarButtons?.reduce((prev, { backgroundColor, icon, id, text }) => {
398
-        return {
399
-            ...prev,
400
-            [id]: {
401
-                backgroundColor,
402
-                key: id,
403
-                Content: CustomOptionButton,
404
-                group: 4,
405
-                icon,
406
-                text
407
-            }
408
-        };
409
-    }, {});
410
-
411
-    return {
412
-        microphone,
413
-        camera,
414
-        profile,
415
-        desktop,
416
-        chat,
417
-        raisehand,
418
-        reactions,
419
-        'participants-pane': participants,
420
-        invite,
421
-        tileview,
422
-        'toggle-camera': toggleCamera,
423
-        videoquality,
424
-        fullscreen,
425
-        security,
426
-        closedcaptions,
427
-        recording,
428
-        livestreaming,
429
-        linktosalesforce,
430
-        sharedvideo,
431
-        shareaudio,
432
-        noisesuppression,
433
-        whiteboard,
434
-        etherpad,
435
-        'select-background': virtualBackground,
436
-        stats,
437
-        settings,
438
-        shortcuts,
439
-        embedmeeting,
440
-        feedback,
441
-        download,
442
-        help,
443
-        ...customButtons
444
-    };
445
-}
446
-
447 160
 /**
448 161
  * Sets the notify click mode for the buttons.
449 162
  *
@@ -464,9 +177,9 @@ function setButtonsNotifyClickMode(buttons: Object, buttonsWithNotifyClick: Map<
464 177
 }
465 178
 
466 179
 interface IGetVisibleButtonsParams {
180
+    allButtons: { [key: string]: IToolboxButton; };
467 181
     buttonsWithNotifyClick: Map<string, NOTIFY_CLICK_MODE>;
468 182
     clientWidth: number;
469
-    customToolbarButtons?: ICustomToolbarButton[];
470 183
     jwtDisabledButtons: string[];
471 184
     mainToolbarButtonsThresholds: IMainToolbarButtonThresholds;
472 185
     toolbarButtons: string[];
@@ -479,22 +192,22 @@ interface IGetVisibleButtonsParams {
479 192
  * @returns {Object} - The visible buttons arrays .
480 193
  */
481 194
 export function getVisibleButtons({
482
-    customToolbarButtons,
195
+    allButtons,
483 196
     buttonsWithNotifyClick,
484 197
     toolbarButtons,
485 198
     clientWidth,
486 199
     jwtDisabledButtons,
487 200
     mainToolbarButtonsThresholds
488 201
 }: IGetVisibleButtonsParams) {
489
-    const buttons = getAllToolboxButtons(customToolbarButtons);
202
+    setButtonsNotifyClickMode(allButtons, buttonsWithNotifyClick);
490 203
 
491
-    const filteredButtons = Object.keys(buttons).filter(key =>
204
+    const filteredButtons = Object.keys(allButtons).filter(key =>
492 205
         typeof key !== 'undefined' // filter invalid buttons that may be comming from config.mainToolbarButtons
493 206
         // override
494 207
         && !jwtDisabledButtons.includes(key)
495 208
         && isButtonEnabled(key, toolbarButtons));
496 209
 
497
-    setButtonsNotifyClickMode(buttons, buttonsWithNotifyClick);
210
+
498 211
     const { order } = mainToolbarButtonsThresholds.find(({ width }) => clientWidth > width)
499 212
         || mainToolbarButtonsThresholds[mainToolbarButtonsThresholds.length - 1];
500 213
 
@@ -507,7 +220,7 @@ export function getVisibleButtons({
507 220
     const mainButtonsKeys = mainToolbarButtonKeysOrder.slice(0, order.length);
508 221
     const overflowMenuButtons = filteredButtons.reduce((acc, key) => {
509 222
         if (!mainButtonsKeys.includes(key)) {
510
-            acc.push(buttons[key]);
223
+            acc.push(allButtons[key]);
511 224
         }
512 225
 
513 226
         return acc;
@@ -522,7 +235,7 @@ export function getVisibleButtons({
522 235
     }
523 236
 
524 237
     return {
525
-        mainMenuButtons: mainButtonsKeys.map(key => buttons[key]),
238
+        mainMenuButtons: mainButtonsKeys.map(key => allButtons[key]),
526 239
         overflowMenuButtons
527 240
     };
528 241
 }

+ 322
- 1
react/features/toolbox/hooks.web.ts Просмотреть файл

@@ -5,13 +5,24 @@ import { ACTION_SHORTCUT_TRIGGERED, createShortcutEvent } from '../analytics/Ana
5 5
 import { sendAnalytics } from '../analytics/functions';
6 6
 import { IReduxState } from '../app/types';
7 7
 import { toggleDialog } from '../base/dialog/actions';
8
+import { isIosMobileBrowser } from '../base/environment/utils';
9
+import { HELP_BUTTON_ENABLED } from '../base/flags/constants';
10
+import { getFeatureFlag } from '../base/flags/functions';
8 11
 import JitsiMeetJS from '../base/lib-jitsi-meet';
9 12
 import { raiseHand } from '../base/participants/actions';
10 13
 import { getLocalParticipant, hasRaisedHand } from '../base/participants/functions';
14
+import { isToggleCameraEnabled } from '../base/tracks/functions.web';
11 15
 import { toggleChat } from '../chat/actions.web';
16
+import ChatButton from '../chat/components/web/ChatButton';
17
+import { useEmbedButton } from '../embed-meeting/hooks';
18
+import { useEtherpadButton } from '../etherpad/hooks';
19
+import { useFeedbackButton } from '../feedback/hooks.web';
12 20
 import { setGifMenuVisibility } from '../gifs/actions';
13 21
 import { isGifEnabled } from '../gifs/function.any';
22
+import InviteButton from '../invite/components/add-people-dialog/web/InviteButton';
14 23
 import { registerShortcut, unregisterShortcut } from '../keyboard-shortcuts/actions.any';
24
+import { useKeyboardShortcutsButton } from '../keyboard-shortcuts/hooks.web';
25
+import NoiseSuppressionButton from '../noise-suppression/components/NoiseSuppressionButton';
15 26
 import {
16 27
     close as closeParticipantsPane,
17 28
     open as openParticipantsPane
@@ -20,20 +31,330 @@ import {
20 31
     getParticipantsPaneOpen,
21 32
     isParticipantsPaneEnabled
22 33
 } from '../participants-pane/functions';
34
+import { useParticipantPaneButton } from '../participants-pane/hooks.web';
23 35
 import { addReactionToBuffer } from '../reactions/actions.any';
24 36
 import { toggleReactionsMenuVisibility } from '../reactions/actions.web';
37
+import RaiseHandContainerButton from '../reactions/components/web/RaiseHandContainerButtons';
25 38
 import { REACTIONS } from '../reactions/constants';
26 39
 import { shouldDisplayReactionsButtons } from '../reactions/functions.any';
40
+import { useReactionsButton } from '../reactions/hooks';
41
+import { useLiveStreamingButton, useRecordingButton } from '../recording/hooks.web';
42
+import { isSalesforceEnabled } from '../salesforce/functions';
27 43
 import { startScreenShareFlow } from '../screen-share/actions.web';
28
-import { isScreenVideoShared } from '../screen-share/functions';
44
+import ShareAudioButton from '../screen-share/components/web/ShareAudioButton';
45
+import { isScreenAudioSupported, isScreenVideoShared } from '../screen-share/functions';
46
+import { useSecurityDialogButton } from '../security/hooks';
47
+import SettingsButton from '../settings/components/web/SettingsButton';
48
+import SharedVideoButton from '../shared-video/components/web/SharedVideoButton';
29 49
 import SpeakerStats from '../speaker-stats/components/web/SpeakerStats';
30 50
 import { isSpeakerStatsDisabled } from '../speaker-stats/functions';
51
+import { useSpeakerStatsButton } from '../speaker-stats/hooks.web';
52
+import { useClosedCaptionButton } from '../subtitles/hooks.web';
31 53
 import { toggleTileView } from '../video-layout/actions.any';
32 54
 import { shouldDisplayTileView } from '../video-layout/functions.any';
55
+import { useTileViewButton } from '../video-layout/hooks';
56
+import VideoQualityButton from '../video-quality/components/VideoQualityButton.web';
33 57
 import VideoQualityDialog from '../video-quality/components/VideoQualityDialog.web';
58
+import { useVirtualBackgroundButton } from '../virtual-background/hooks';
59
+import { useWhiteboardButton } from '../whiteboard/hooks';
34 60
 
35 61
 import { setFullScreen } from './actions.web';
62
+import DownloadButton from './components/DownloadButton';
63
+import HelpButton from './components/HelpButton';
64
+import AudioSettingsButton from './components/web/AudioSettingsButton';
65
+import CustomOptionButton from './components/web/CustomOptionButton';
66
+import FullscreenButton from './components/web/FullscreenButton';
67
+import LinkToSalesforceButton from './components/web/LinkToSalesforceButton';
68
+import ProfileButton from './components/web/ProfileButton';
69
+import ShareDesktopButton from './components/web/ShareDesktopButton';
70
+import ToggleCameraButton from './components/web/ToggleCameraButton';
71
+import VideoSettingsButton from './components/web/VideoSettingsButton';
36 72
 import { isButtonEnabled, isDesktopShareButtonDisabled } from './functions.web';
73
+import { ICustomToolbarButton, IToolboxButton, ToolbarButton } from './types';
74
+
75
+const microphone = {
76
+    key: 'microphone',
77
+    Content: AudioSettingsButton,
78
+    group: 0
79
+};
80
+
81
+const camera = {
82
+    key: 'camera',
83
+    Content: VideoSettingsButton,
84
+    group: 0
85
+};
86
+
87
+const profile = {
88
+    key: 'profile',
89
+    Content: ProfileButton,
90
+    group: 1
91
+};
92
+
93
+const chat = {
94
+    key: 'chat',
95
+    Content: ChatButton,
96
+    group: 2
97
+};
98
+
99
+const desktop = {
100
+    key: 'desktop',
101
+    Content: ShareDesktopButton,
102
+    group: 2
103
+};
104
+
105
+// In Narrow layout and mobile web we are using drawer for popups and that is why it is better to include
106
+// all forms of reactions in the overflow menu. Otherwise the toolbox will be hidden and the reactions popup
107
+// misaligned.
108
+const raisehand = {
109
+    key: 'raisehand',
110
+    Content: RaiseHandContainerButton,
111
+    group: 2
112
+};
113
+
114
+const invite = {
115
+    key: 'invite',
116
+    Content: InviteButton,
117
+    group: 2
118
+};
119
+
120
+const toggleCamera = {
121
+    key: 'toggle-camera',
122
+    Content: ToggleCameraButton,
123
+    group: 2
124
+};
125
+
126
+const videoQuality = {
127
+    key: 'videoquality',
128
+    Content: VideoQualityButton,
129
+    group: 2
130
+};
131
+
132
+const fullscreen = {
133
+    key: 'fullscreen',
134
+    Content: FullscreenButton,
135
+    group: 2
136
+};
137
+
138
+const linkToSalesforce = {
139
+    key: 'linktosalesforce',
140
+    Content: LinkToSalesforceButton,
141
+    group: 2
142
+};
143
+
144
+const shareVideo = {
145
+    key: 'sharedvideo',
146
+    Content: SharedVideoButton,
147
+    group: 3
148
+};
149
+
150
+const shareAudio = {
151
+    key: 'shareaudio',
152
+    Content: ShareAudioButton,
153
+    group: 3
154
+};
155
+
156
+const noiseSuppression = {
157
+    key: 'noisesuppression',
158
+    Content: NoiseSuppressionButton,
159
+    group: 3
160
+};
161
+
162
+const settings = {
163
+    key: 'settings',
164
+    Content: SettingsButton,
165
+    group: 4
166
+};
167
+
168
+const download = {
169
+    key: 'download',
170
+    Content: DownloadButton,
171
+    group: 4
172
+};
173
+
174
+const help = {
175
+    key: 'help',
176
+    Content: HelpButton,
177
+    group: 4
178
+};
179
+
180
+/**
181
+ * A hook that returns the toggle camera button if it is enabled and undefined otherwise.
182
+ *
183
+ *  @returns {Object | undefined}
184
+ */
185
+function useToggleCameraButton() {
186
+    const toggleCameraEnabled = useSelector(isToggleCameraEnabled);
187
+
188
+    if (toggleCameraEnabled) {
189
+        return toggleCamera;
190
+    }
191
+}
192
+
193
+/**
194
+ * A hook that returns the desktop sharing button if it is enabled and undefined otherwise.
195
+ *
196
+ *  @returns {Object | undefined}
197
+ */
198
+function getDesktopSharingButton() {
199
+    if (JitsiMeetJS.isDesktopSharingEnabled()) {
200
+        return desktop;
201
+    }
202
+}
203
+
204
+/**
205
+ * A hook that returns the fullscreen button if it is enabled and undefined otherwise.
206
+ *
207
+ *  @returns {Object | undefined}
208
+ */
209
+function getFullscreenButton() {
210
+    if (!isIosMobileBrowser()) {
211
+        return fullscreen;
212
+    }
213
+}
214
+
215
+/**
216
+ * A hook that returns the "link to salesforce" button if it is enabled and undefined otherwise.
217
+ *
218
+ *  @returns {Object | undefined}
219
+ */
220
+function useLinkToSalesforceButton() {
221
+    const _isSalesforceEnabled = useSelector(isSalesforceEnabled);
222
+
223
+    if (_isSalesforceEnabled) {
224
+        return linkToSalesforce;
225
+    }
226
+}
227
+
228
+
229
+/**
230
+ * A hook that returns the share audio button if it is enabled and undefined otherwise.
231
+ *
232
+ *  @returns {Object | undefined}
233
+ */
234
+function getShareAudioButton() {
235
+    if (JitsiMeetJS.isDesktopSharingEnabled() && isScreenAudioSupported()) {
236
+        return shareAudio;
237
+    }
238
+}
239
+
240
+/**
241
+ * A hook that returns the download button if it is enabled and undefined otherwise.
242
+ *
243
+ *  @returns {Object | undefined}
244
+ */
245
+function useDownloadButton() {
246
+    const visible = useSelector(
247
+        (state: IReduxState) => typeof state['features/base/config'].deploymentUrls?.downloadAppsUrl === 'string');
248
+
249
+    if (visible) {
250
+        return download;
251
+    }
252
+}
253
+
254
+/**
255
+ * A hook that returns the help button if it is enabled and undefined otherwise.
256
+ *
257
+ *  @returns {Object | undefined}
258
+ */
259
+function useHelpButton() {
260
+    const visible = useSelector(
261
+        (state: IReduxState) =>
262
+            typeof state['features/base/config'].deploymentUrls?.userDocumentationURL === 'string'
263
+                && getFeatureFlag(state, HELP_BUTTON_ENABLED, true));
264
+
265
+    if (visible) {
266
+        return help;
267
+    }
268
+}
269
+
270
+/**
271
+* Returns all buttons that could be rendered.
272
+*
273
+* @param {Object} _customToolbarButtons - An array containing custom buttons objects.
274
+* @returns {Object} The button maps mainMenuButtons and overflowMenuButtons.
275
+*/
276
+export function useToolboxButtons(
277
+        _customToolbarButtons?: ICustomToolbarButton[]): { [key: string]: IToolboxButton; } {
278
+    const dekstopSharing = getDesktopSharingButton();
279
+    const toggleCameraButton = useToggleCameraButton();
280
+    const _fullscreen = getFullscreenButton();
281
+    const security = useSecurityDialogButton();
282
+    const reactions = useReactionsButton();
283
+    const participants = useParticipantPaneButton();
284
+    const tileview = useTileViewButton();
285
+    const cc = useClosedCaptionButton();
286
+    const recording = useRecordingButton();
287
+    const liveStreaming = useLiveStreamingButton();
288
+    const linktosalesforce = useLinkToSalesforceButton();
289
+    const shareaudio = getShareAudioButton();
290
+    const whiteboard = useWhiteboardButton();
291
+    const etherpad = useEtherpadButton();
292
+    const virtualBackground = useVirtualBackgroundButton();
293
+    const speakerStats = useSpeakerStatsButton();
294
+    const shortcuts = useKeyboardShortcutsButton();
295
+    const embed = useEmbedButton();
296
+    const feedback = useFeedbackButton();
297
+    const _download = useDownloadButton();
298
+    const _help = useHelpButton();
299
+
300
+    const buttons: { [key in ToolbarButton]?: IToolboxButton; } = {
301
+        microphone,
302
+        camera,
303
+        profile,
304
+        desktop: dekstopSharing,
305
+        chat,
306
+        raisehand,
307
+        reactions,
308
+        'participants-pane': participants,
309
+        invite,
310
+        tileview,
311
+        'toggle-camera': toggleCameraButton,
312
+        videoquality: videoQuality,
313
+        fullscreen: _fullscreen,
314
+        security,
315
+        closedcaptions: cc,
316
+        recording,
317
+        livestreaming: liveStreaming,
318
+        linktosalesforce,
319
+        sharedvideo: shareVideo,
320
+        shareaudio,
321
+        noisesuppression: noiseSuppression,
322
+        whiteboard,
323
+        etherpad,
324
+        'select-background': virtualBackground,
325
+        stats: speakerStats,
326
+        settings,
327
+        shortcuts,
328
+        embedmeeting: embed,
329
+        feedback,
330
+        download: _download,
331
+        help: _help
332
+    };
333
+    const buttonKeys = Object.keys(buttons) as ToolbarButton[];
334
+
335
+    buttonKeys.forEach(
336
+            key => typeof buttons[key] === 'undefined' && delete buttons[key]);
337
+
338
+    const customButtons = _customToolbarButtons?.reduce((prev, { backgroundColor, icon, id, text }) => {
339
+        prev[id] = {
340
+            backgroundColor,
341
+            key: id,
342
+            id,
343
+            Content: CustomOptionButton,
344
+            group: 4,
345
+            icon,
346
+            text
347
+        };
348
+
349
+        return prev;
350
+    }, {} as { [key: string]: ICustomToolbarButton; });
351
+
352
+    return {
353
+        ...buttons,
354
+        ...customButtons
355
+    };
356
+}
357
+
37 358
 
38 359
 export const useKeyboardShortcuts = (toolbarButtons: Array<string>) => {
39 360
     const dispatch = useDispatch();

+ 12
- 0
react/features/toolbox/types.ts Просмотреть файл

@@ -1,5 +1,7 @@
1 1
 import { ComponentType } from 'react';
2 2
 
3
+import { CustomOptionButton } from './components';
4
+
3 5
 export interface IToolboxButton {
4 6
     Content: ComponentType<any>;
5 7
     group: number;
@@ -53,3 +55,13 @@ export type IMainToolbarButtonThresholds = Array<{
53 55
     order: Array<ToolbarButton | string>;
54 56
     width: number;
55 57
 }>;
58
+
59
+export interface ICustomToolbarButton {
60
+    Content?: typeof CustomOptionButton;
61
+    backgroundColor?: string;
62
+    group?: number;
63
+    icon: string;
64
+    id: string;
65
+    key?: string;
66
+    text: string;
67
+}

+ 26
- 0
react/features/video-layout/hooks.ts Просмотреть файл

@@ -0,0 +1,26 @@
1
+import { useSelector } from 'react-redux';
2
+
3
+import { IReduxState } from '../app/types';
4
+import { TILE_VIEW_ENABLED } from '../base/flags/constants';
5
+import { getFeatureFlag } from '../base/flags/functions';
6
+
7
+import TileViewButton from './components/TileViewButton';
8
+
9
+const tileview = {
10
+    key: 'tileview',
11
+    Content: TileViewButton,
12
+    group: 2
13
+};
14
+
15
+/**
16
+ * A hook that returns the tile view button if it is enabled and undefined otherwise.
17
+ *
18
+ *  @returns {Object | undefined}
19
+ */
20
+export function useTileViewButton() {
21
+    const tileViewEnabled = useSelector((state: IReduxState) => getFeatureFlag(state, TILE_VIEW_ENABLED, true));
22
+
23
+    if (tileViewEnabled) {
24
+        return tileview;
25
+    }
26
+}

+ 27
- 0
react/features/virtual-background/hooks.ts Просмотреть файл

@@ -0,0 +1,27 @@
1
+import { useSelector } from 'react-redux';
2
+
3
+import { isScreenVideoShared } from '../screen-share/functions';
4
+
5
+import VideoBackgroundButton from './components/VideoBackgroundButton';
6
+import { checkBlurSupport, checkVirtualBackgroundEnabled } from './functions';
7
+
8
+const virtualBackground = {
9
+    key: 'select-background',
10
+    Content: VideoBackgroundButton,
11
+    group: 3
12
+};
13
+
14
+/**
15
+ * A hook that returns the virtual background button if it is enabled and undefined otherwise.
16
+ *
17
+ *  @returns {Object | undefined}
18
+ */
19
+export function useVirtualBackgroundButton() {
20
+    const _checkBlurSupport = checkBlurSupport();
21
+    const _isScreenVideoShared = useSelector(isScreenVideoShared);
22
+    const _checkVirtualBackgroundEnabled = useSelector(checkVirtualBackgroundEnabled);
23
+
24
+    if (_checkBlurSupport && !_isScreenVideoShared && _checkVirtualBackgroundEnabled) {
25
+        return virtualBackground;
26
+    }
27
+}

+ 23
- 0
react/features/whiteboard/hooks.ts Просмотреть файл

@@ -0,0 +1,23 @@
1
+import { useSelector } from 'react-redux';
2
+
3
+import WhiteboardButton from './components/web/WhiteboardButton';
4
+import { isWhiteboardButtonVisible } from './functions';
5
+
6
+const whiteboard = {
7
+    key: 'whiteboard',
8
+    Content: WhiteboardButton,
9
+    group: 3
10
+};
11
+
12
+/**
13
+ * A hook that returns the whiteboard button if it is enabled and undefined otherwise.
14
+ *
15
+ *  @returns {Object | undefined}
16
+ */
17
+export function useWhiteboardButton() {
18
+    const _isWhiteboardButtonVisible = useSelector(isWhiteboardButtonVisible);
19
+
20
+    if (_isWhiteboardButtonVisible) {
21
+        return whiteboard;
22
+    }
23
+}

Загрузка…
Отмена
Сохранить