@@ -6,79 +6,45 @@ import {
66 ButtonSize ,
77 ButtonVariant ,
88} from '../buttons/Button' ;
9- import { EmojiPicker } from '../fields/EmojiPicker' ;
10- import {
11- CameraIcon ,
12- ExitIcon ,
13- MegaphoneIcon ,
14- PlusIcon ,
15- SettingsIcon ,
16- } from '../icons' ;
9+ import { CameraIcon , ExitIcon , MegaphoneIcon , SettingsIcon } from '../icons' ;
1710import { RaiseHandIcon } from '../icons/RaiseHand' ;
18- import { IconSize } from '../Icon' ;
1911import { useLiveRoom } from '../../contexts/LiveRoomContext' ;
20- import { useLogContext } from '../../contexts/LogContext' ;
2112import { useToastNotification } from '../../hooks/useToastNotification' ;
2213import { LiveRoomMicLevel } from './LiveRoomMicLevel' ;
2314import {
2415 Typography ,
2516 TypographyColor ,
2617 TypographyType ,
2718} from '../typography/Typography' ;
28- import { Tooltip } from '../tooltip/Tooltip' ;
2919import { useAuthContext } from '../../contexts/AuthContext' ;
3020import { useViewSize , ViewSize } from '../../hooks' ;
3121import { AuthTriggers } from '../../lib/auth' ;
32- import { buildStandupAnalyticsExtra } from '../../lib/liveRoom/analytics' ;
3322import { getLiveRoomPrivilegeState } from '../../lib/liveRoom/privileges' ;
34- import { LIVE_ROOM_QUICK_REACTION_EMOJIS } from '../../lib/liveRoom/reactions' ;
3523import { LogEvent } from '../../lib/log' ;
36- import { Modal } from '../modals/common/Modal ' ;
24+ import { useLiveRoomStandupAnalytics } from '../../hooks/liveRooms/useLiveRoomStandupAnalytics ' ;
3725import {
38- AUDIO_ONLY_LABEL ,
3926 ControlGroup ,
4027 DeviceSplitButton ,
4128 Divider ,
4229 HIDE_SELF_VIEW_LABEL ,
4330 MIC_SETTING_ITEMS ,
4431 MicSettingRow ,
45- SelectSettingRow ,
4632 SlashedIcon ,
47- VIDEO_QUALITY_ITEMS ,
48- VIDEO_QUALITY_LABEL ,
4933} from './LiveRoomControlPrimitives' ;
34+ import { LiveRoomSettingsModal } from './LiveRoomSettingsModal' ;
35+ import { LiveRoomReactionsToolbar } from './LiveRoomReactionsToolbar' ;
36+ import { LiveRoomTooltipButton } from './LiveRoomTooltipButton' ;
5037
5138interface LiveRoomControlsProps {
5239 roomId : string ;
5340 onLeave : ( ) => void ;
5441}
5542
56- const TooltipButton = ( {
57- tooltip,
58- children,
59- wrapDisabled = false ,
60- } : {
61- tooltip : string ;
62- children : ReactElement ;
63- wrapDisabled ?: boolean ;
64- } ) : ReactElement => {
65- if ( wrapDisabled ) {
66- return (
67- < Tooltip content = { tooltip } >
68- < span className = "inline-flex" > { children } </ span >
69- </ Tooltip >
70- ) ;
71- }
72-
73- return < Tooltip content = { tooltip } > { children } </ Tooltip > ;
74- } ;
75-
7643export const LiveRoomControls = ( {
7744 roomId,
7845 onLeave,
7946} : LiveRoomControlsProps ) : ReactElement => {
8047 const { user, showLogin } = useAuthContext ( ) ;
81- const { logEvent } = useLogContext ( ) ;
8248 const isTablet = useViewSize ( ViewSize . Tablet ) ;
8349 const {
8450 status,
@@ -128,36 +94,18 @@ export const LiveRoomControls = ({
12894 ( ) => ( localAudioTrack ? new MediaStream ( [ localAudioTrack ] ) : null ) ,
12995 [ localAudioTrack ] ,
13096 ) ;
131- const buildStandupExtra = ( extra : Record < string , unknown > = { } ) : string =>
132- buildStandupAnalyticsExtra (
133- {
134- roomId,
135- authKind : user ? 'authenticated' : 'anonymous' ,
136- role,
137- roomStatus : roomState ?. status ?? null ,
138- roomMode : roomState ?. mode ?? null ,
139- connectionStatus : status ,
140- participantId,
141- isCoHost : privilegeState . isCoHost ,
142- hasLocalAudioTrack : ! ! localStream ?. getAudioTracks ( ) [ 0 ] ,
143- hasLocalVideoTrack : ! ! localStream ?. getVideoTracks ( ) [ 0 ] ,
144- videoQuality : videoSettings . quality ,
145- audioOnly : videoSettings . audioOnly ,
146- hideSelfView : videoSettings . hideSelfView ,
147- } ,
148- extra ,
149- ) ;
150- const logStandupAction = (
151- eventName : LogEvent ,
152- targetId : string ,
153- extra : Record < string , unknown > = { } ,
154- ) : void => {
155- logEvent ( {
156- event_name : eventName ,
157- target_id : targetId ,
158- extra : buildStandupExtra ( extra ) ,
159- } ) ;
160- } ;
97+ const { logStandupAction } = useLiveRoomStandupAnalytics ( {
98+ roomId,
99+ user,
100+ role,
101+ roomStatus : roomState ?. status ?? null ,
102+ roomMode : roomState ?. mode ?? null ,
103+ connectionStatus : status ,
104+ participantId,
105+ isCoHost : privilegeState . isCoHost ,
106+ localStream,
107+ videoSettings,
108+ } ) ;
161109
162110 const isBusy = ( key : string ) : boolean => busyKeys . includes ( key ) ;
163111
@@ -269,72 +217,19 @@ export const LiveRoomControls = ({
269217 < div className = "pointer-events-none absolute inset-x-0 bottom-2 z-2 px-1.5 tablet:bottom-6" >
270218 < div className = "pointer-events-auto relative mx-auto flex w-full max-w-[42rem] flex-col items-center gap-1.5 tablet:gap-2" >
271219 { reactionsOpen && isLive ? (
272- < div className = "flex items-center gap-1 rounded-16 border border-border-subtlest-tertiary bg-surface-float p-1.5 shadow-2" >
273- { LIVE_ROOM_QUICK_REACTION_EMOJIS . map ( ( emoji ) => (
274- < TooltipButton key = { emoji } tooltip = { `React ${ emoji } ` } >
275- < Button
276- type = "button"
277- size = { ButtonSize . Small }
278- variant = { ButtonVariant . Float }
279- loading = { isBusy ( `reaction-${ emoji } ` ) }
280- aria-label = { `React ${ emoji } ` }
281- onClick = { ( ) =>
282- runAuthenticatedAction ( `reaction-${ emoji } ` , async ( ) => {
283- await sendReaction ( emoji ) ;
284- logStandupAction ( LogEvent . SendStandupReaction , emoji , {
285- surface : 'controls' ,
286- } ) ;
287- } )
288- }
289- >
290- < span className = "text-lg leading-none" > { emoji } </ span >
291- </ Button >
292- </ TooltipButton >
293- ) ) }
294- < EmojiPicker
295- value = ""
296- label = { null }
297- onChange = { ( emoji ) => {
298- if ( ! emoji ) {
299- return ;
300- }
301-
302- runAuthenticatedAction ( 'reaction-custom' , async ( ) => {
303- await sendReaction ( emoji ) ;
304- logStandupAction ( LogEvent . SendStandupReaction , emoji , {
305- surface : 'controls' ,
306- } ) ;
220+ < LiveRoomReactionsToolbar
221+ isBusy = { isBusy }
222+ isAuthenticated = { ! ! user }
223+ onRequestLogin = { promptSignup }
224+ onSendReaction = { ( key , emoji ) =>
225+ runAuthenticatedAction ( key , async ( ) => {
226+ await sendReaction ( emoji ) ;
227+ logStandupAction ( LogEvent . SendStandupReaction , emoji , {
228+ surface : 'controls' ,
307229 } ) ;
308- } }
309- renderTrigger = { ( { isOpen, toggleOpen } ) => (
310- < TooltipButton
311- tooltip = "Custom reaction"
312- wrapDisabled = { isBusy ( 'reaction-custom' ) }
313- >
314- < Button
315- type = "button"
316- size = { ButtonSize . Small }
317- variant = {
318- isOpen ? ButtonVariant . Primary : ButtonVariant . Float
319- }
320- className = "!w-9 shrink-0"
321- icon = { < PlusIcon size = { IconSize . Size16 } /> }
322- aria-label = "Custom reaction"
323- aria-expanded = { isOpen }
324- disabled = { isBusy ( 'reaction-custom' ) }
325- onClick = { ( ) => {
326- if ( ! user ) {
327- promptSignup ( ) ;
328- return ;
329- }
330-
331- toggleOpen ( ) ;
332- } }
333- />
334- </ TooltipButton >
335- ) }
336- />
337- </ div >
230+ } )
231+ }
232+ />
338233 ) : null }
339234
340235 < div className = "flex items-center gap-1 rounded-12 border border-border-subtlest-tertiary bg-background-default p-1 shadow-2 backdrop-blur tablet:gap-2 tablet:rounded-16 tablet:p-1.5" >
@@ -473,7 +368,7 @@ export const LiveRoomControls = ({
473368 { showLiveInteractionControls ? (
474369 < >
475370 < ControlGroup >
476- < TooltipButton tooltip = { reactionTooltip } >
371+ < LiveRoomTooltipButton tooltip = { reactionTooltip } >
477372 < Button
478373 type = "button"
479374 size = { ButtonSize . Small }
@@ -505,9 +400,9 @@ export const LiveRoomControls = ({
505400 >
506401 < span className = "text-base leading-none" > 😀</ span >
507402 </ Button >
508- </ TooltipButton >
403+ </ LiveRoomTooltipButton >
509404 { canRaiseHand ? (
510- < TooltipButton
405+ < LiveRoomTooltipButton
511406 tooltip = { isHandRaised ? 'Lower hand' : 'Raise hand' }
512407 wrapDisabled = { isBusy ( 'hand' ) }
513408 >
@@ -551,9 +446,9 @@ export const LiveRoomControls = ({
551446 } )
552447 }
553448 />
554- </ TooltipButton >
449+ </ LiveRoomTooltipButton >
555450 ) : null }
556- < TooltipButton tooltip = { settingsTooltip } >
451+ < LiveRoomTooltipButton tooltip = { settingsTooltip } >
557452 < Button
558453 type = "button"
559454 size = { ButtonSize . Small }
@@ -576,7 +471,7 @@ export const LiveRoomControls = ({
576471 setIsSettingsOpen ( true ) ;
577472 } }
578473 />
579- </ TooltipButton >
474+ </ LiveRoomTooltipButton >
580475 { isModerated && isAudience ? (
581476 < Button
582477 type = "button"
@@ -599,7 +494,7 @@ export const LiveRoomControls = ({
599494 </ Button >
600495 ) : null }
601496 { isFreeForAll && isAudience ? (
602- < TooltipButton
497+ < LiveRoomTooltipButton
603498 tooltip = { joinStageTooltip }
604499 wrapDisabled = { ! canJoinStage || isBusy ( 'join-stage' ) }
605500 >
@@ -624,10 +519,10 @@ export const LiveRoomControls = ({
624519 >
625520 { joinStageTooltip }
626521 </ Button >
627- </ TooltipButton >
522+ </ LiveRoomTooltipButton >
628523 ) : null }
629524 { canLeaveStage ? (
630- < TooltipButton
525+ < LiveRoomTooltipButton
631526 tooltip = "Stop speaking"
632527 wrapDisabled = { isBusy ( 'leave-stage' ) }
633528 >
@@ -647,7 +542,7 @@ export const LiveRoomControls = ({
647542 >
648543 Stop speaking
649544 </ Button >
650- </ TooltipButton >
545+ </ LiveRoomTooltipButton >
651546 ) : null }
652547 </ ControlGroup >
653548
@@ -657,7 +552,10 @@ export const LiveRoomControls = ({
657552
658553 < ControlGroup >
659554 { showGoLive ? (
660- < TooltipButton tooltip = "Go live" wrapDisabled = { isBusy ( 'go-live' ) } >
555+ < LiveRoomTooltipButton
556+ tooltip = "Go live"
557+ wrapDisabled = { isBusy ( 'go-live' ) }
558+ >
661559 < Button
662560 type = "button"
663561 size = { ButtonSize . Small }
@@ -675,7 +573,7 @@ export const LiveRoomControls = ({
675573 >
676574 Go live
677575 </ Button >
678- </ TooltipButton >
576+ </ LiveRoomTooltipButton >
679577 ) : null }
680578 { privilegeState . hasHostPrivileges ? (
681579 < Button
@@ -720,52 +618,17 @@ export const LiveRoomControls = ({
720618 </ div >
721619 </ div >
722620 { isSettingsOpen ? (
723- < Modal
724- isOpen
725- kind = { Modal . Kind . FixedCenter }
726- size = { Modal . Size . Small }
727- isDrawerOnMobile
728- drawerProps = { { appendOnRoot : true } }
729- onRequestClose = { ( ) => setIsSettingsOpen ( false ) }
730- >
731- < Modal . Header title = "Standup settings" />
732- < Modal . Body className = "gap-1" >
733- < SelectSettingRow
734- label = { VIDEO_QUALITY_LABEL }
735- description = "Choose how aggressively remote video should use your connection."
736- value = { videoSettings . quality }
737- options = { VIDEO_QUALITY_ITEMS . map ( ( item ) => ( {
738- value : item . value ,
739- label : item . label ,
740- } ) ) }
741- onSelect = { ( quality ) => {
742- setVideoSetting ( 'quality' , quality ) ;
743- logStandupAction (
744- LogEvent . ChangeStandupSettings ,
745- 'video_quality' ,
746- {
747- surface : 'settings_modal' ,
748- value : quality ,
749- } ,
750- ) ;
751- } }
752- />
753- < MicSettingRow
754- id = "live-room-video-setting-audio-only"
755- label = { AUDIO_ONLY_LABEL }
756- description = "Hide remote video and keep the standup playing through audio."
757- checked = { videoSettings . audioOnly }
758- onToggle = { ( ) => {
759- const nextValue = ! videoSettings . audioOnly ;
760- setVideoSetting ( 'audioOnly' , nextValue ) ;
761- logStandupAction ( LogEvent . ChangeStandupSettings , 'audio_only' , {
762- surface : 'settings_modal' ,
763- value : nextValue ,
764- } ) ;
765- } }
766- />
767- </ Modal . Body >
768- </ Modal >
621+ < LiveRoomSettingsModal
622+ videoSettings = { videoSettings }
623+ setVideoSetting = { setVideoSetting }
624+ onClose = { ( ) => setIsSettingsOpen ( false ) }
625+ onTrackSettingChange = { ( targetId , surface , value ) => {
626+ logStandupAction ( LogEvent . ChangeStandupSettings , targetId , {
627+ surface,
628+ value,
629+ } ) ;
630+ } }
631+ />
769632 ) : null }
770633 </ div >
771634 ) ;
0 commit comments