2
2
// Licensed under the MIT License.
3
3
4
4
/* @conditional -compile-remove(together-mode) */
5
- import React , { useMemo , useState , memo } from 'react' ;
5
+ import React , { useMemo , useState , memo , useEffect } from 'react' ;
6
6
/* @conditional -compile-remove(together-mode) */
7
7
import {
8
8
Reaction ,
@@ -84,13 +84,22 @@ export const TogetherModeOverlay = memo(
84
84
[ key : string ] : TogetherModeParticipantStatus ;
85
85
} > ( { } ) ;
86
86
const [ hoveredParticipantID , setHoveredParticipantID ] = useState ( '' ) ;
87
+ const [ tabbedParticipantID , setTabbedParticipantID ] = useState ( '' ) ;
88
+ // const [tabFocused, setTabFocused] = useState(false);
89
+
90
+ // Reset the Tab key tracking on any other key press
91
+ const handleKeyUp = ( e , participantId : string ) => {
92
+ if ( e . key === 'Tab' ) {
93
+ setTabbedParticipantID ( participantId ) ;
94
+ }
95
+ } ;
87
96
88
97
/*
89
98
* The useMemo hook is used to calculate the participant status for the Together Mode overlay.
90
99
* It updates the togetherModeParticipantStatus state when there's a change in the remoteParticipants, localParticipant,
91
100
* raisedHand, spotlight, isMuted, displayName, or hoveredParticipantID.
92
101
*/
93
- useMemo ( ( ) => {
102
+ const updatedParticipantStatus = useMemo ( ( ) => {
94
103
const allParticipants = [ ...remoteParticipants , localParticipant ] ;
95
104
96
105
const participantsWithVideoAvailable = allParticipants . filter (
@@ -109,7 +118,12 @@ export const TogetherModeOverlay = memo(
109
118
isSpotlighted : ! ! spotlight ,
110
119
isMuted,
111
120
displayName : displayName || locale . strings . videoGallery . displayNamePlaceholder ,
112
- showDisplayName : ! ! ( spotlight || raisedHand || hoveredParticipantID === userId ) ,
121
+ showDisplayName : ! ! (
122
+ spotlight ||
123
+ raisedHand ||
124
+ hoveredParticipantID === userId ||
125
+ tabbedParticipantID === userId
126
+ ) ,
113
127
scaledSize : calculateScaledSize ( seatingPosition . width , seatingPosition . height ) ,
114
128
seatPositionStyle : setTogetherModeSeatPositionStyle ( seatingPosition )
115
129
} ;
@@ -121,68 +135,58 @@ export const TogetherModeOverlay = memo(
121
135
( id ) => ! updatedSignals [ id ]
122
136
) ;
123
137
124
- setTogetherModeParticipantStatus ( ( prevSignals ) => {
125
- const newSignals = { ...prevSignals , ...updatedSignals } ;
138
+ const newSignals = { ...togetherModeParticipantStatus , ...updatedSignals } ;
126
139
127
- participantsNotInTogetherModeStream . forEach ( ( id ) => {
128
- delete newSignals [ id ] ;
129
- } ) ;
140
+ participantsNotInTogetherModeStream . forEach ( ( id ) => {
141
+ delete newSignals [ id ] ;
142
+ } ) ;
130
143
131
- const hasSignalingChange = Object . keys ( newSignals ) . some (
132
- ( key ) => JSON . stringify ( newSignals [ key ] ) !== JSON . stringify ( prevSignals [ key ] )
133
- ) ;
144
+ const hasSignalingChange = Object . keys ( newSignals ) . some (
145
+ ( key ) => JSON . stringify ( newSignals [ key ] ) !== JSON . stringify ( togetherModeParticipantStatus [ key ] )
146
+ ) ;
134
147
135
- const updateTogetherModeParticipantStatusState =
136
- hasSignalingChange || Object . keys ( newSignals ) . length !== Object . keys ( prevSignals ) . length ;
137
- return updateTogetherModeParticipantStatusState ? newSignals : prevSignals ;
138
- } ) ;
148
+ const updateTogetherModeParticipantStatusState =
149
+ hasSignalingChange || Object . keys ( newSignals ) . length !== Object . keys ( togetherModeParticipantStatus ) . length ;
150
+ return updateTogetherModeParticipantStatusState ? newSignals : togetherModeParticipantStatus ;
139
151
} , [
140
152
remoteParticipants ,
141
153
localParticipant ,
142
154
togetherModeParticipantStatus ,
143
155
togetherModeSeatPositions ,
144
156
reactionResources ,
145
157
locale . strings . videoGallery . displayNamePlaceholder ,
146
- hoveredParticipantID
158
+ hoveredParticipantID ,
159
+ tabbedParticipantID
147
160
] ) ;
148
161
149
- /*
150
- * When a larger participant scene switches to a smaller group in Together Mode,
151
- * participant video streams remain available because their video is still active,
152
- * even though they are not visible in the Together Mode stream.
153
- * Therefore, we rely on the updated seating position values to identify who is included in the Together Mode stream.
154
- * The Together mode seat position will only contain seat coordinates of participants who are visible in the Together Mode stream.
155
- */
156
- useMemo ( ( ) => {
157
- const removedVisibleParticipants = Object . keys ( togetherModeParticipantStatus ) . filter (
158
- ( participantId ) => ! togetherModeSeatPositions [ participantId ]
159
- ) ;
162
+ useEffect ( ( ) => {
163
+ if ( hoveredParticipantID && ! updatedParticipantStatus [ hoveredParticipantID ] ) {
164
+ setHoveredParticipantID ( '' ) ;
165
+ }
160
166
161
- setTogetherModeParticipantStatus ( ( prevSignals ) => {
162
- const newSignals = { ...prevSignals } ;
163
- removedVisibleParticipants . forEach ( ( participantId ) => {
164
- delete newSignals [ participantId ] ;
165
- } ) ;
167
+ if ( tabbedParticipantID && ! updatedParticipantStatus [ tabbedParticipantID ] ) {
168
+ setTabbedParticipantID ( '' ) ;
169
+ }
166
170
167
- // Trigger a re-render only if changes occurred
168
- const updateTogetherModeParticipantStatusState =
169
- Object . keys ( newSignals ) . length !== Object . keys ( prevSignals ) . length ;
170
- return updateTogetherModeParticipantStatusState ? newSignals : prevSignals ;
171
- } ) ;
172
- } , [ togetherModeParticipantStatus , togetherModeSeatPositions ] ) ;
171
+ setTogetherModeParticipantStatus ( updatedParticipantStatus ) ;
172
+ } , [ hoveredParticipantID , tabbedParticipantID , updatedParticipantStatus ] ) ;
173
173
174
174
return (
175
175
< div style = { { position : 'absolute' , width : '100%' , height : '100%' } } >
176
176
{ Object . values ( togetherModeParticipantStatus ) . map (
177
- ( participantStatus ) =>
177
+ ( participantStatus , index ) =>
178
178
participantStatus . id && (
179
179
< div
180
180
key = { participantStatus . id }
181
181
style = { {
182
182
...getTogetherModeParticipantOverlayStyle ( participantStatus . seatPositionStyle )
183
+ // border: '1px solid yellow'
183
184
} }
184
185
onMouseEnter = { ( ) => setHoveredParticipantID ( participantStatus . id ) }
185
186
onMouseLeave = { ( ) => setHoveredParticipantID ( '' ) }
187
+ onKeyUp = { ( e ) => handleKeyUp ( e , participantStatus . id ) }
188
+ onBlur = { ( ) => setTabbedParticipantID ( '' ) }
189
+ tabIndex = { index }
186
190
>
187
191
< div >
188
192
{ participantStatus . reaction ?. reactionType && (
@@ -219,7 +223,7 @@ export const TogetherModeOverlay = memo(
219
223
) }
220
224
221
225
{ participantStatus . showDisplayName && (
222
- < div style = { { ...participantStatusTransitionStyle } } >
226
+ < div style = { { ...participantStatusTransitionStyle } } tabIndex = { index } >
223
227
< div
224
228
style = { {
225
229
...togetherModeParticipantStatusContainer (
0 commit comments