Procházet zdrojové kódy

feat(raise-hand) notify next speaker (#14904)

factor2
Mengyuan Liu před 10 měsíci
rodič
revize
c04000ea20
Žádný účet není propojen s e-mailovou adresou tvůrce revize

+ 1
- 0
lang/main.json Zobrazit soubor

786
         "newDeviceAction": "Use",
786
         "newDeviceAction": "Use",
787
         "newDeviceAudioTitle": "New audio device detected",
787
         "newDeviceAudioTitle": "New audio device detected",
788
         "newDeviceCameraTitle": "New camera detected",
788
         "newDeviceCameraTitle": "New camera detected",
789
+        "nextToSpeak": "You are the next in line to speak",
789
         "noiseSuppressionDesktopAudioDescription": "Noise suppression can't be enabled while sharing desktop audio, please disable it and try again.",
790
         "noiseSuppressionDesktopAudioDescription": "Noise suppression can't be enabled while sharing desktop audio, please disable it and try again.",
790
         "noiseSuppressionFailedTitle": "Failed to start noise suppression",
791
         "noiseSuppressionFailedTitle": "Failed to start noise suppression",
791
         "noiseSuppressionStereoDescription": "Stereo audio noise suppression is not currently supported.",
792
         "noiseSuppressionStereoDescription": "Stereo audio noise suppression is not currently supported.",

+ 26
- 0
react/features/base/config/functions.any.ts Zobrazit soubor

6
 import _ from 'lodash';
6
 import _ from 'lodash';
7
 
7
 
8
 import { IReduxState } from '../../app/types';
8
 import { IReduxState } from '../../app/types';
9
+import { getLocalParticipant } from '../participants/functions';
9
 import { parseURLParams } from '../util/parseURLParams';
10
 import { parseURLParams } from '../util/parseURLParams';
10
 
11
 
11
 import { IConfig } from './configType';
12
 import { IConfig } from './configType';
184
         || state['features/base/config'].readOnlyName);
185
         || state['features/base/config'].readOnlyName);
185
 }
186
 }
186
 
187
 
188
+/**
189
+ * Selector for determining if the participant is the next one in the queue to speak.
190
+ *
191
+ * @param {Object} state - The state of the app.
192
+ * @returns {boolean}
193
+ */
194
+export function isNextToSpeak(state: IReduxState): boolean {
195
+    const raisedHandsQueue = state['features/base/participants'].raisedHandsQueue || [];
196
+    const participantId = getLocalParticipant(state)?.id;
197
+
198
+    return participantId === raisedHandsQueue[0]?.id;
199
+}
200
+
201
+/**
202
+ * Selector for determining if the next to speak participant in the queue has been notified.
203
+ *
204
+ * @param {Object} state - The state of the app.
205
+ * @returns {boolean}
206
+ */
207
+export function hasBeenNotified(state: IReduxState): boolean {
208
+    const raisedHandsQueue = state['features/base/participants'].raisedHandsQueue;
209
+
210
+    return Boolean(raisedHandsQueue[0]?.hasBeenNotified);
211
+}
212
+
187
 /**
213
 /**
188
  * Selector for determining if the display name is visible.
214
  * Selector for determining if the display name is visible.
189
  *
215
  *

+ 9
- 0
react/features/base/participants/actionTypes.ts Zobrazit soubor

1
+/**
2
+ * Create an action to mark the participant as notified to speak next.
3
+ *
4
+ * {
5
+ *     type: NOTIFIED_TO_SPEAK
6
+ * }
7
+ */
8
+export const NOTIFIED_TO_SPEAK = 'NOTIFIED_TO_SPEAK';
9
+
1
 /**
10
 /**
2
  * Create an action for when dominant speaker changes.
11
  * Create an action for when dominant speaker changes.
3
  *
12
  *

+ 19
- 1
react/features/base/participants/reducer.ts Zobrazit soubor

6
 
6
 
7
 import {
7
 import {
8
     DOMINANT_SPEAKER_CHANGED,
8
     DOMINANT_SPEAKER_CHANGED,
9
+    NOTIFIED_TO_SPEAK,
9
     OVERWRITE_PARTICIPANT_NAME,
10
     OVERWRITE_PARTICIPANT_NAME,
10
     PARTICIPANT_ID_CHANGED,
11
     PARTICIPANT_ID_CHANGED,
11
     PARTICIPANT_JOINED,
12
     PARTICIPANT_JOINED,
92
     numberOfParticipantsNotSupportingE2EE: number;
93
     numberOfParticipantsNotSupportingE2EE: number;
93
     overwrittenNameList: { [id: string]: string; };
94
     overwrittenNameList: { [id: string]: string; };
94
     pinnedParticipant?: string;
95
     pinnedParticipant?: string;
95
-    raisedHandsQueue: Array<{ id: string; raisedHandTimestamp: number; }>;
96
+    raisedHandsQueue: Array<{ hasBeenNotified?: boolean; id: string; raisedHandTimestamp: number; }>;
96
     remote: Map<string, IParticipant>;
97
     remote: Map<string, IParticipant>;
97
     remoteVideoSources: Set<string>;
98
     remoteVideoSources: Set<string>;
98
     sortedRemoteParticipants: Map<string, string>;
99
     sortedRemoteParticipants: Map<string, string>;
114
 ReducerRegistry.register<IParticipantsState>('features/base/participants',
115
 ReducerRegistry.register<IParticipantsState>('features/base/participants',
115
 (state = DEFAULT_STATE, action): IParticipantsState => {
116
 (state = DEFAULT_STATE, action): IParticipantsState => {
116
     switch (action.type) {
117
     switch (action.type) {
118
+    case NOTIFIED_TO_SPEAK: {
119
+        return {
120
+            ...state,
121
+            raisedHandsQueue: state.raisedHandsQueue.map((item, index) => {
122
+                if (index === 0) {
123
+
124
+                    return {
125
+                        ...item,
126
+                        hasBeenNotified: true
127
+                    };
128
+                }
129
+
130
+                return item;
131
+            })
132
+        };
133
+    }
134
+
117
     case PARTICIPANT_ID_CHANGED: {
135
     case PARTICIPANT_ID_CHANGED: {
118
         const { local } = state;
136
         const { local } = state;
119
 
137
 

+ 35
- 1
react/features/base/participants/subscriber.ts Zobrazit soubor

1
+
1
 import _ from 'lodash';
2
 import _ from 'lodash';
3
+import { batch } from 'react-redux';
2
 
4
 
3
 import { IStore } from '../../app/types';
5
 import { IStore } from '../../app/types';
6
+import { showNotification } from '../../notifications/actions';
7
+import { NOTIFICATION_TIMEOUT_TYPE } from '../../notifications/constants';
4
 import { getCurrentConference } from '../conference/functions';
8
 import { getCurrentConference } from '../conference/functions';
5
-import { getSsrcRewritingFeatureFlag } from '../config/functions.any';
9
+import { getSsrcRewritingFeatureFlag, hasBeenNotified, isNextToSpeak } from '../config/functions.any';
6
 import { VIDEO_TYPE } from '../media/constants';
10
 import { VIDEO_TYPE } from '../media/constants';
7
 import StateListenerRegistry from '../redux/StateListenerRegistry';
11
 import StateListenerRegistry from '../redux/StateListenerRegistry';
8
 
12
 
13
+import { NOTIFIED_TO_SPEAK } from './actionTypes';
9
 import { createVirtualScreenshareParticipant, participantLeft } from './actions';
14
 import { createVirtualScreenshareParticipant, participantLeft } from './actions';
10
 import {
15
 import {
11
     getParticipantById,
16
     getParticipantById,
25
         && _updateScreenshareParticipantsBasedOnPresence(store)
30
         && _updateScreenshareParticipantsBasedOnPresence(store)
26
 );
31
 );
27
 
32
 
33
+StateListenerRegistry.register(
34
+    /* selector */ state => state['features/base/participants'].raisedHandsQueue,
35
+    /* listener */ (raisedHandsQueue, store) => {
36
+        if (isNextToSpeak(store.getState()) && !hasBeenNotified(store.getState())) {
37
+            _notifyNextSpeakerInRaisedHandQueue(store);
38
+        }
39
+    }
40
+);
41
+
28
 /**
42
 /**
29
  * Compares the old and new screenshare lists provided and creates/removes the virtual screenshare participant
43
  * Compares the old and new screenshare lists provided and creates/removes the virtual screenshare participant
30
  * tiles accodingly.
44
  * tiles accodingly.
121
 
135
 
122
     _createOrRemoveVirtualParticipants(previousScreenshareSourceNames, currentScreenshareSourceNames, store);
136
     _createOrRemoveVirtualParticipants(previousScreenshareSourceNames, currentScreenshareSourceNames, store);
123
 }
137
 }
138
+
139
+/**
140
+ * Handles notifying the next speaker in the raised hand queue.
141
+ *
142
+ * @param {*} store - The redux store.
143
+ * @returns {void}
144
+ */
145
+function _notifyNextSpeakerInRaisedHandQueue(store: IStore): void {
146
+    const { dispatch } = store;
147
+
148
+    batch(() => {
149
+        dispatch(showNotification({
150
+            titleKey: 'notify.nextToSpeak',
151
+            maxLines: 2
152
+        }, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
153
+        dispatch({
154
+            type: NOTIFIED_TO_SPEAK
155
+        });
156
+    });
157
+}

+ 0
- 1
react/features/video-menu/actions.any.ts Zobrazit soubor

108
         });
108
         });
109
     };
109
     };
110
 }
110
 }
111
-

Načítá se…
Zrušit
Uložit