ソースを参照

feat(sound-settings) Added ability to control sounds

master
robertpin 3年前
コミット
c10805f81b
コミッターのメールアドレスに関連付けられたアカウントが存在しません

+ 3
- 0
config.js ファイルの表示

@@ -603,6 +603,9 @@ var config = {
603 603
     // conference (if set to true, these sounds will not be played).
604 604
     // disableJoinLeaveSounds: false,
605 605
 
606
+    // Disables the sounds that play when a chat message is received.
607
+    // disableIncomingMessageSound: false,
608
+
606 609
     // Information for the chrome extension banner
607 610
     // chromeExtensionBanner: {
608 611
     //     // The chrome extension to be installed address

+ 1
- 1
interface_config.js ファイルの表示

@@ -174,7 +174,7 @@ var interfaceConfig = {
174 174
     RECENT_LIST_ENABLED: true,
175 175
     REMOTE_THUMBNAIL_RATIO: 1, // 1:1
176 176
 
177
-    SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar' ],
177
+    SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar', 'sounds' ],
178 178
 
179 179
     /**
180 180
      * Specify which sharing features should be displayed. If the value is not set

+ 6
- 0
lang/main.json ファイルの表示

@@ -744,6 +744,7 @@
744 744
         "devices": "Devices",
745 745
         "followMe": "Everyone follows me",
746 746
         "framesPerSecond": "frames-per-second",
747
+        "incomingMessage": "Incoming message",
747 748
         "language": "Language",
748 749
         "loggedIn": "Logged in as {{name}}",
749 750
         "microphones": "Microphones",
@@ -751,13 +752,18 @@
751 752
         "more": "More",
752 753
         "name": "Name",
753 754
         "noDevice": "None",
755
+        "participantJoined": "Participant Joined",
756
+        "participantLeft": "Participant Left",
757
+        "playSounds": "Play sound on",
754 758
         "sameAsSystem": "Same as system ({{label}})",
755 759
         "selectAudioOutput": "Audio output",
756 760
         "selectCamera": "Camera",
757 761
         "selectMic": "Microphone",
762
+        "sounds": "Sounds",
758 763
         "speakers": "Speakers",
759 764
         "startAudioMuted": "Everyone starts muted",
760 765
         "startVideoMuted": "Everyone starts hidden",
766
+        "talkWhileMuted": "Talk while muted",
761 767
         "title": "Settings"
762 768
     },
763 769
     "settingsView": {

+ 1
- 0
react/features/base/config/configWhitelist.js ファイルの表示

@@ -87,6 +87,7 @@ export default [
87 87
     'disableH264',
88 88
     'disableHPF',
89 89
     'disableInviteFunctions',
90
+    'disableIncomingMessageSound',
90 91
     'disableJoinLeaveSounds',
91 92
     'disableLocalVideoFlip',
92 93
     'disableNS',

+ 5
- 1
react/features/base/participants/middleware.js ファイルの表示

@@ -371,6 +371,7 @@ function _localParticipantLeft({ dispatch }, next, action) {
371 371
 function _maybePlaySounds({ getState, dispatch }, action) {
372 372
     const state = getState();
373 373
     const { startAudioMuted, disableJoinLeaveSounds } = state['features/base/config'];
374
+    const { soundsParticipantJoined: joinSound, soundsParticipantLeft: leftSound } = state['features/base/settings'];
374 375
 
375 376
     // If we have join/leave sounds disabled, don't play anything.
376 377
     if (disableJoinLeaveSounds) {
@@ -387,13 +388,16 @@ function _maybePlaySounds({ getState, dispatch }, action) {
387 388
         const { isReplacing, isReplaced } = action.participant;
388 389
 
389 390
         if (action.type === PARTICIPANT_JOINED) {
391
+            if (!joinSound) {
392
+                return;
393
+            }
390 394
             const { presence } = action.participant;
391 395
 
392 396
             // The sounds for the poltergeist are handled by features/invite.
393 397
             if (presence !== INVITED && presence !== CALLING && !isReplacing) {
394 398
                 dispatch(playSound(PARTICIPANT_JOINED_SOUND_ID));
395 399
             }
396
-        } else if (action.type === PARTICIPANT_LEFT && !isReplaced) {
400
+        } else if (action.type === PARTICIPANT_LEFT && !isReplaced && leftSound) {
397 401
             dispatch(playSound(PARTICIPANT_LEFT_SOUND_ID));
398 402
         }
399 403
     }

+ 4
- 0
react/features/base/settings/reducer.js ファイルの表示

@@ -27,6 +27,10 @@ const DEFAULT_STATE = {
27 27
     micDeviceId: undefined,
28 28
     serverURL: undefined,
29 29
     hideShareAudioHelper: false,
30
+    soundsIncomingMessage: true,
31
+    soundsParticipantJoined: true,
32
+    soundsParticipantLeft: true,
33
+    soundsTalkWhileMuted: true,
30 34
     startAudioOnly: false,
31 35
     startWithAudioMuted: false,
32 36
     startWithVideoMuted: false,

+ 3
- 1
react/features/chat/middleware.js ファイルの表示

@@ -305,8 +305,10 @@ function _handleReceivedMessage({ dispatch, getState },
305 305
     // Logic for all platforms:
306 306
     const state = getState();
307 307
     const { isOpen: isChatOpen } = state['features/chat'];
308
+    const { disableIncomingMessageSound } = state['features/base/config'];
309
+    const { soundsIncomingMessage: soundEnabled } = state['features/base/settings'];
308 310
 
309
-    if (shouldPlaySound && !isChatOpen) {
311
+    if (!disableIncomingMessageSound && soundEnabled && shouldPlaySound && !isChatOpen) {
310 312
         dispatch(playSound(INCOMING_MSG_SOUND_ID));
311 313
     }
312 314
 

+ 26
- 1
react/features/settings/actions.js ファイルの表示

@@ -12,7 +12,7 @@ import {
12 12
     SET_VIDEO_SETTINGS_VISIBILITY
13 13
 } from './actionTypes';
14 14
 import { LogoutDialog, SettingsDialog } from './components';
15
-import { getMoreTabProps, getProfileTabProps } from './functions';
15
+import { getMoreTabProps, getProfileTabProps, getSoundsTabProps } from './functions';
16 16
 
17 17
 declare var APP: Object;
18 18
 
@@ -129,6 +129,31 @@ export function submitProfileTab(newState: Object): Function {
129 129
     };
130 130
 }
131 131
 
132
+/**
133
+ * Submits the settings from the "Sounds" tab of the settings dialog.
134
+ *
135
+ * @param {Object} newState - The new settings.
136
+ * @returns {Function}
137
+ */
138
+export function submitSoundsTab(newState: Object): Function {
139
+    return (dispatch, getState) => {
140
+        const currentState = getSoundsTabProps(getState());
141
+        const shouldUpdate = (newState.soundsIncomingMessage !== currentState.soundsIncomingMessage)
142
+            || (newState.soundsParticipantJoined !== currentState.soundsParticipantJoined)
143
+            || (newState.soundsParticipantLeft !== currentState.soundsParticipantLeft)
144
+            || (newState.soundsTalkWhileMuted !== currentState.soundsTalkWhileMuted);
145
+
146
+        if (shouldUpdate) {
147
+            dispatch(updateSettings({
148
+                soundsIncomingMessage: newState.soundsIncomingMessage,
149
+                soundsParticipantJoined: newState.soundsParticipantJoined,
150
+                soundsParticipantLeft: newState.soundsParticipantLeft,
151
+                soundsTalkWhileMuted: newState.soundsTalkWhileMuted
152
+            }));
153
+        }
154
+    };
155
+}
156
+
132 157
 /**
133 158
  * Toggles the visibility of the audio settings.
134 159
  *

+ 15
- 2
react/features/settings/components/web/SettingsDialog.js ファイルの表示

@@ -11,13 +11,14 @@ import {
11 11
     getDeviceSelectionDialogProps,
12 12
     submitDeviceSelectionTab
13 13
 } from '../../../device-selection';
14
-import { submitMoreTab, submitProfileTab } from '../../actions';
14
+import { submitMoreTab, submitProfileTab, submitSoundsTab } from '../../actions';
15 15
 import { SETTINGS_TABS } from '../../constants';
16
-import { getMoreTabProps, getProfileTabProps } from '../../functions';
16
+import { getMoreTabProps, getProfileTabProps, getSoundsTabProps } from '../../functions';
17 17
 
18 18
 import CalendarTab from './CalendarTab';
19 19
 import MoreTab from './MoreTab';
20 20
 import ProfileTab from './ProfileTab';
21
+import SoundsTab from './SoundsTab';
21 22
 
22 23
 declare var APP: Object;
23 24
 declare var interfaceConfig: Object;
@@ -135,6 +136,7 @@ function _mapStateToProps(state) {
135 136
         = configuredTabs.includes('profile') && !state['features/base/config'].disableProfile;
136 137
     const showCalendarSettings
137 138
         = configuredTabs.includes('calendar') && isCalendarEnabled(state);
139
+    const showSoundsSettings = configuredTabs.includes('sounds');
138 140
     const tabs = [];
139 141
 
140 142
     if (showDeviceSettings) {
@@ -183,6 +185,17 @@ function _mapStateToProps(state) {
183 185
         });
184 186
     }
185 187
 
188
+    if (showSoundsSettings) {
189
+        tabs.push({
190
+            name: SETTINGS_TABS.SOUNDS,
191
+            component: SoundsTab,
192
+            label: 'settings.sounds',
193
+            props: getSoundsTabProps(state),
194
+            styles: 'settings-pane profile-pane',
195
+            submit: submitSoundsTab
196
+        });
197
+    }
198
+
186 199
     if (showModeratorSettings || showLanguageSettings || showPrejoinSettings) {
187 200
         tabs.push({
188 201
             name: SETTINGS_TABS.MORE,

+ 123
- 0
react/features/settings/components/web/SoundsTab.js ファイルの表示

@@ -0,0 +1,123 @@
1
+// @flow
2
+
3
+import Checkbox from '@atlaskit/checkbox';
4
+import React from 'react';
5
+
6
+import { AbstractDialogTab } from '../../../base/dialog';
7
+import type { Props as AbstractDialogTabProps } from '../../../base/dialog';
8
+import { translate } from '../../../base/i18n';
9
+
10
+declare var APP: Object;
11
+
12
+/**
13
+ * The type of the React {@code Component} props of {@link SoundsTab}.
14
+ */
15
+export type Props = {
16
+    ...$Exact<AbstractDialogTabProps>,
17
+
18
+    /**
19
+     * Whether or not the sound for the incoming message should play.
20
+     */
21
+    soundsIncomingMessage: Boolean,
22
+
23
+    /**
24
+     * Whether or not the sound for the participant joined should play.
25
+     */
26
+    soundsParticipantJoined: Boolean,
27
+
28
+    /**
29
+     * Whether or not the sound for the participant left should play.
30
+     */
31
+    soundsParticipantLeft: Boolean,
32
+
33
+    /**
34
+     * Whether or not the sound for the talk while muted notification should play.
35
+     */
36
+    soundsTalkWhileMuted: Boolean,
37
+
38
+    /**
39
+     * Invoked to obtain translated strings.
40
+     */
41
+    t: Function
42
+}
43
+
44
+/**
45
+ * React {@code Component} for modifying the local user's sound settings.
46
+ *
47
+ * @extends Component
48
+ */
49
+class SoundsTab extends AbstractDialogTab<Props> {
50
+    /**
51
+     * Initializes a new {@code SoundsTab} instance.
52
+     *
53
+     * @param {Props} props - The React {@code Component} props to initialize
54
+     * the new {@code SoundsTab} instance with.
55
+     */
56
+    constructor(props: Props) {
57
+        super(props);
58
+
59
+        // Bind event handlers so they are only bound once for every instance.
60
+        this._onChange = this._onChange.bind(this);
61
+    }
62
+
63
+    _onChange: (Object) => void;
64
+
65
+    /**
66
+     * Changes a sound setting state.
67
+     *
68
+     * @param {Object} e - The key event to handle.
69
+     *
70
+     * @returns {void}
71
+     */
72
+    _onChange({ target }) {
73
+        super._onChange({ [target.name]: target.checked });
74
+    }
75
+
76
+    /**
77
+     * Implements React's {@link Component#render()}.
78
+     *
79
+     * @inheritdoc
80
+     * @returns {ReactElement}
81
+     */
82
+    render() {
83
+        const {
84
+            soundsIncomingMessage,
85
+            soundsParticipantJoined,
86
+            soundsParticipantLeft,
87
+            soundsTalkWhileMuted,
88
+            t
89
+        } = this.props;
90
+
91
+        return (
92
+            <div
93
+                className = 'settings-sub-pane-element'
94
+                key = 'sounds'>
95
+                <h2 className = 'mock-atlaskit-label'>
96
+                    {t('settings.playSounds')}
97
+                </h2>
98
+                <Checkbox
99
+                    isChecked = { soundsIncomingMessage }
100
+                    label = { t('settings.incomingMessage') }
101
+                    name = 'soundsIncomingMessage'
102
+                    onChange = { this._onChange } />
103
+                <Checkbox
104
+                    isChecked = { soundsParticipantJoined }
105
+                    label = { t('settings.participantJoined') }
106
+                    name = 'soundsParticipantJoined'
107
+                    onChange = { this._onChange } />
108
+                <Checkbox
109
+                    isChecked = { soundsParticipantLeft }
110
+                    label = { t('settings.participantLeft') }
111
+                    name = 'soundsParticipantLeft'
112
+                    onChange = { this._onChange } />
113
+                <Checkbox
114
+                    isChecked = { soundsTalkWhileMuted }
115
+                    label = { t('settings.talkWhileMuted') }
116
+                    name = 'soundsTalkWhileMuted'
117
+                    onChange = { this._onChange } />
118
+            </div>
119
+        );
120
+    }
121
+}
122
+
123
+export default translate(SoundsTab);

+ 2
- 1
react/features/settings/constants.js ファイルの表示

@@ -2,7 +2,8 @@ export const SETTINGS_TABS = {
2 2
     CALENDAR: 'calendar_tab',
3 3
     DEVICES: 'devices_tab',
4 4
     MORE: 'more_tab',
5
-    PROFILE: 'profile_tab'
5
+    PROFILE: 'profile_tab',
6
+    SOUNDS: 'sounds_tab'
6 7
 };
7 8
 
8 9
 /**

+ 26
- 0
react/features/settings/functions.js ファイルの表示

@@ -156,6 +156,32 @@ export function getProfileTabProps(stateful: Object | Function) {
156 156
     };
157 157
 }
158 158
 
159
+/**
160
+ * Returns the properties for the "Sounds" tab from settings dialog from Redux
161
+ * state.
162
+ *
163
+ * @param {(Function|Object)} stateful -The (whole) redux state, or redux's
164
+ * {@code getState} function to be used to retrieve the state.
165
+ * @returns {Object} - The properties for the "Sounds" tab from settings
166
+ * dialog.
167
+ */
168
+export function getSoundsTabProps(stateful: Object | Function) {
169
+    const state = toState(stateful);
170
+    const {
171
+        soundsIncomingMessage,
172
+        soundsParticipantJoined,
173
+        soundsParticipantLeft,
174
+        soundsTalkWhileMuted
175
+    } = state['features/base/settings'];
176
+
177
+    return {
178
+        soundsIncomingMessage,
179
+        soundsParticipantJoined,
180
+        soundsParticipantLeft,
181
+        soundsTalkWhileMuted
182
+    };
183
+}
184
+
159 185
 /**
160 186
  * Returns a promise which resolves with a list of objects containing
161 187
  * all the video jitsiTracks and appropriate errors for the given device ids.

+ 6
- 1
react/features/talk-while-muted/middleware.js ファイルの表示

@@ -47,7 +47,12 @@ MiddlewareRegistry.register(store => next => action => {
47 47
                     customActionHandler: () => dispatch(setAudioMuted(false))
48 48
                 }));
49 49
 
50
-                dispatch(playSound(TALK_WHILE_MUTED_SOUND_ID));
50
+                const { soundsTalkWhileMuted } = getState()['features/base/settings'];
51
+
52
+                if (soundsTalkWhileMuted) {
53
+                    dispatch(playSound(TALK_WHILE_MUTED_SOUND_ID));
54
+                }
55
+
51 56
 
52 57
                 if (notification) {
53 58
                     // we store the last start muted notification id that we showed,

読み込み中…
キャンセル
保存