Skip to content

Commit 7380e1d

Browse files
authored
refactor: split live room standup components (#5987)
1 parent 8c1e855 commit 7380e1d

12 files changed

Lines changed: 1153 additions & 831 deletions

packages/shared/src/components/liveRooms/LiveRoom.tsx

Lines changed: 96 additions & 639 deletions
Large diffs are not rendered by default.

packages/shared/src/components/liveRooms/LiveRoomControls.tsx

Lines changed: 55 additions & 192 deletions
Original file line numberDiff line numberDiff line change
@@ -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';
1710
import { RaiseHandIcon } from '../icons/RaiseHand';
18-
import { IconSize } from '../Icon';
1911
import { useLiveRoom } from '../../contexts/LiveRoomContext';
20-
import { useLogContext } from '../../contexts/LogContext';
2112
import { useToastNotification } from '../../hooks/useToastNotification';
2213
import { LiveRoomMicLevel } from './LiveRoomMicLevel';
2314
import {
2415
Typography,
2516
TypographyColor,
2617
TypographyType,
2718
} from '../typography/Typography';
28-
import { Tooltip } from '../tooltip/Tooltip';
2919
import { useAuthContext } from '../../contexts/AuthContext';
3020
import { useViewSize, ViewSize } from '../../hooks';
3121
import { AuthTriggers } from '../../lib/auth';
32-
import { buildStandupAnalyticsExtra } from '../../lib/liveRoom/analytics';
3322
import { getLiveRoomPrivilegeState } from '../../lib/liveRoom/privileges';
34-
import { LIVE_ROOM_QUICK_REACTION_EMOJIS } from '../../lib/liveRoom/reactions';
3523
import { LogEvent } from '../../lib/log';
36-
import { Modal } from '../modals/common/Modal';
24+
import { useLiveRoomStandupAnalytics } from '../../hooks/liveRooms/useLiveRoomStandupAnalytics';
3725
import {
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

5138
interface 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-
7643
export 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

Comments
 (0)