Browse Source

feat(sound-settings) Added ability to control sounds

master
robertpin 3 years ago
parent
commit
c10805f81b
No account linked to committer's email address

+ 3
- 0
config.js View File

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

+ 1
- 1
interface_config.js View File

174
     RECENT_LIST_ENABLED: true,
174
     RECENT_LIST_ENABLED: true,
175
     REMOTE_THUMBNAIL_RATIO: 1, // 1:1
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
      * Specify which sharing features should be displayed. If the value is not set
180
      * Specify which sharing features should be displayed. If the value is not set

+ 6
- 0
lang/main.json View File

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

+ 1
- 0
react/features/base/config/configWhitelist.js View File

87
     'disableH264',
87
     'disableH264',
88
     'disableHPF',
88
     'disableHPF',
89
     'disableInviteFunctions',
89
     'disableInviteFunctions',
90
+    'disableIncomingMessageSound',
90
     'disableJoinLeaveSounds',
91
     'disableJoinLeaveSounds',
91
     'disableLocalVideoFlip',
92
     'disableLocalVideoFlip',
92
     'disableNS',
93
     'disableNS',

+ 5
- 1
react/features/base/participants/middleware.js View File

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

+ 4
- 0
react/features/base/settings/reducer.js View File

27
     micDeviceId: undefined,
27
     micDeviceId: undefined,
28
     serverURL: undefined,
28
     serverURL: undefined,
29
     hideShareAudioHelper: false,
29
     hideShareAudioHelper: false,
30
+    soundsIncomingMessage: true,
31
+    soundsParticipantJoined: true,
32
+    soundsParticipantLeft: true,
33
+    soundsTalkWhileMuted: true,
30
     startAudioOnly: false,
34
     startAudioOnly: false,
31
     startWithAudioMuted: false,
35
     startWithAudioMuted: false,
32
     startWithVideoMuted: false,
36
     startWithVideoMuted: false,

+ 3
- 1
react/features/chat/middleware.js View File

305
     // Logic for all platforms:
305
     // Logic for all platforms:
306
     const state = getState();
306
     const state = getState();
307
     const { isOpen: isChatOpen } = state['features/chat'];
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
         dispatch(playSound(INCOMING_MSG_SOUND_ID));
312
         dispatch(playSound(INCOMING_MSG_SOUND_ID));
311
     }
313
     }
312
 
314
 

+ 26
- 1
react/features/settings/actions.js View File

12
     SET_VIDEO_SETTINGS_VISIBILITY
12
     SET_VIDEO_SETTINGS_VISIBILITY
13
 } from './actionTypes';
13
 } from './actionTypes';
14
 import { LogoutDialog, SettingsDialog } from './components';
14
 import { LogoutDialog, SettingsDialog } from './components';
15
-import { getMoreTabProps, getProfileTabProps } from './functions';
15
+import { getMoreTabProps, getProfileTabProps, getSoundsTabProps } from './functions';
16
 
16
 
17
 declare var APP: Object;
17
 declare var APP: Object;
18
 
18
 
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
  * Toggles the visibility of the audio settings.
158
  * Toggles the visibility of the audio settings.
134
  *
159
  *

+ 15
- 2
react/features/settings/components/web/SettingsDialog.js View File

11
     getDeviceSelectionDialogProps,
11
     getDeviceSelectionDialogProps,
12
     submitDeviceSelectionTab
12
     submitDeviceSelectionTab
13
 } from '../../../device-selection';
13
 } from '../../../device-selection';
14
-import { submitMoreTab, submitProfileTab } from '../../actions';
14
+import { submitMoreTab, submitProfileTab, submitSoundsTab } from '../../actions';
15
 import { SETTINGS_TABS } from '../../constants';
15
 import { SETTINGS_TABS } from '../../constants';
16
-import { getMoreTabProps, getProfileTabProps } from '../../functions';
16
+import { getMoreTabProps, getProfileTabProps, getSoundsTabProps } from '../../functions';
17
 
17
 
18
 import CalendarTab from './CalendarTab';
18
 import CalendarTab from './CalendarTab';
19
 import MoreTab from './MoreTab';
19
 import MoreTab from './MoreTab';
20
 import ProfileTab from './ProfileTab';
20
 import ProfileTab from './ProfileTab';
21
+import SoundsTab from './SoundsTab';
21
 
22
 
22
 declare var APP: Object;
23
 declare var APP: Object;
23
 declare var interfaceConfig: Object;
24
 declare var interfaceConfig: Object;
135
         = configuredTabs.includes('profile') && !state['features/base/config'].disableProfile;
136
         = configuredTabs.includes('profile') && !state['features/base/config'].disableProfile;
136
     const showCalendarSettings
137
     const showCalendarSettings
137
         = configuredTabs.includes('calendar') && isCalendarEnabled(state);
138
         = configuredTabs.includes('calendar') && isCalendarEnabled(state);
139
+    const showSoundsSettings = configuredTabs.includes('sounds');
138
     const tabs = [];
140
     const tabs = [];
139
 
141
 
140
     if (showDeviceSettings) {
142
     if (showDeviceSettings) {
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
     if (showModeratorSettings || showLanguageSettings || showPrejoinSettings) {
199
     if (showModeratorSettings || showLanguageSettings || showPrejoinSettings) {
187
         tabs.push({
200
         tabs.push({
188
             name: SETTINGS_TABS.MORE,
201
             name: SETTINGS_TABS.MORE,

+ 123
- 0
react/features/settings/components/web/SoundsTab.js View File

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 View File

2
     CALENDAR: 'calendar_tab',
2
     CALENDAR: 'calendar_tab',
3
     DEVICES: 'devices_tab',
3
     DEVICES: 'devices_tab',
4
     MORE: 'more_tab',
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 View File

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
  * Returns a promise which resolves with a list of objects containing
186
  * Returns a promise which resolves with a list of objects containing
161
  * all the video jitsiTracks and appropriate errors for the given device ids.
187
  * all the video jitsiTracks and appropriate errors for the given device ids.

+ 6
- 1
react/features/talk-while-muted/middleware.js View File

47
                     customActionHandler: () => dispatch(setAudioMuted(false))
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
                 if (notification) {
57
                 if (notification) {
53
                     // we store the last start muted notification id that we showed,
58
                     // we store the last start muted notification id that we showed,

Loading…
Cancel
Save