Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: show selected audio device indicator in header for mweb #2053

Merged
merged 12 commits into from
Oct 31, 2023
5 changes: 5 additions & 0 deletions packages/react-icons/assets/BluetoothIcon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
17 changes: 17 additions & 0 deletions packages/react-icons/src/BluetoothIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from 'react';
import { SVGProps } from 'react';
const SvgBluetoothIcon = (props: SVGProps<SVGSVGElement>) => (
<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 24 24" fill="none" {...props}>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M9.596 3.062a.818.818 0 0 1 .892.178l4.09 4.09c.32.32.32.838 0 1.158L11.066 12l3.512 3.512c.32.32.32.838 0 1.157l-4.09 4.091a.818.818 0 0 1-1.397-.578v-6.207L6.397 16.67a.818.818 0 1 1-1.157-1.157L8.752 12 5.24 8.488A.818.818 0 1 1 6.397 7.33l2.694 2.694V3.818c0-.33.2-.63.505-.756Zm1.131 10.913 2.116 2.116-2.116 2.115v-4.23Zm0-3.95V5.793l2.116 2.116-2.116 2.116Z"
fill="currentColor"
/>
<path
d="M16.555 9.106a.818.818 0 0 1 1.157 0 4.09 4.09 0 0 1 0 5.788.818.818 0 1 1-1.157-1.158 2.457 2.457 0 0 0 0-3.473.818.818 0 0 1 0-1.157ZM14.818 11.182a.818.818 0 1 0 0 1.636h.008a.818.818 0 1 0 0-1.636h-.008Z"
fill="currentColor"
/>
</svg>
);
export default SvgBluetoothIcon;
1 change: 1 addition & 0 deletions packages/react-icons/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export { default as BarIcon } from './BarIcon';
export { default as BatteryFullIcon } from './BatteryFullIcon';
export { default as BatteryPowerIcon } from './BatteryPowerIcon';
export { default as BillIcon } from './BillIcon';
export { default as BluetoothIcon } from './BluetoothIcon';
export { default as BoltIcon } from './BoltIcon';
export { default as BookIcon } from './BookIcon';
export { default as BookmarkIcon } from './BookmarkIcon';
Expand Down
40 changes: 33 additions & 7 deletions packages/roomkit-react/src/Prebuilt/components/Header/common.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,14 @@ import {
useHMSActions,
useHMSStore,
} from '@100mslive/react-sdk';
import { CameraFlipIcon, CheckIcon, CrossIcon, SpeakerIcon } from '@100mslive/react-icons';
import {
BluetoothIcon,
CameraFlipIcon,
CheckIcon,
CrossIcon,
HeadphonesIcon,
SpeakerIcon,
} from '@100mslive/react-icons';
import { HorizontalDivider } from '../../../Divider';
import { Label } from '../../../Label';
import { Box, Flex } from '../../../Layout';
Expand Down Expand Up @@ -52,21 +59,37 @@ export const CamaraFlipActions = () => {
export const AudioOutputActions = () => {
const { allDevices, selectedDeviceIDs, updateDevice } = useDevices();
const { audioOutput } = allDevices;
const hmsActions = useHMSActions();
// don't show speaker selector where the API is not supported, and use
// a generic word("Audio") for Mic. In some cases(Chrome Android for e.g.) this changes both mic and speaker keeping them in sync.
const shouldShowAudioOutput = 'setSinkId' in HTMLMediaElement.prototype;

/**
* Chromium browsers return an audioOutput with empty label when no permissions are given
*/
const audioOutputFiltered = audioOutput?.filter(item => !!item.label) ?? [];
const audioOutputLabel = audioOutput?.filter(item => item.deviceId === selectedDeviceIDs.audioOutput);
amar-1995 marked this conversation as resolved.
Show resolved Hide resolved

if (!shouldShowAudioOutput || !audioOutputFiltered?.length > 0) {
return null;
}
let AudioOutputIcon = <SpeakerIcon />;
if (
audioOutputLabel &&
raviteja83 marked this conversation as resolved.
Show resolved Hide resolved
audioOutputLabel.length > 0 &&
audioOutputLabel[0].label.toLowerCase().includes('bluetooth')
raviteja83 marked this conversation as resolved.
Show resolved Hide resolved
) {
AudioOutputIcon = <BluetoothIcon />;
} else if (
audioOutputLabel &&
audioOutputLabel.length > 0 &&
amar-1995 marked this conversation as resolved.
Show resolved Hide resolved
audioOutputLabel[0].label.toLowerCase().includes('wired')
) {
AudioOutputIcon = <HeadphonesIcon />;
}
return (
<AudioOutputSelectionSheet
outputDevices={audioOutput}
outputSelected={selectedDeviceIDs.outputDevices}
outputSelected={selectedDeviceIDs.audioOutput}
onChange={async deviceId => {
try {
await updateDevice({
Expand All @@ -81,10 +104,13 @@ export const AudioOutputActions = () => {
}
}}
>
<Box>
<IconButton>
<SpeakerIcon />
</IconButton>
<Box
onClick={async () => {
// refresh device as `devicechange` listener won't work in mobile device
await hmsActions.refreshDevices();
}}
>
<IconButton>{AudioOutputIcon} </IconButton>
</Box>
</AudioOutputSelectionSheet>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import React, { Fragment, useEffect, useRef, useState } from 'react';
import { useMedia } from 'react-use';
import {
DeviceType,
selectIsLocalVideoEnabled,
selectLocalVideoTrackID,
selectVideoTrackByID,
useDevices,
useHMSActions,
useHMSStore,
} from '@100mslive/react-sdk';
import { MicOnIcon, SpeakerIcon, VideoOnIcon } from '@100mslive/react-icons';
import { Box, Button, Dropdown, Flex, StyledVideoTile, Text, Video } from '../../../';
import { config as cssConfig } from '../../../Theme';
import { DialogDropdownTrigger } from '../../primitives/DropdownTrigger';
import { useUISettings } from '../AppData/useUISettings';
import { useDropdownSelection } from '../hooks/useDropdownSelection';
Expand All @@ -30,7 +33,9 @@ const Settings = ({ setHide }) => {
const shouldShowAudioOutput = 'setSinkId' in HTMLMediaElement.prototype;
const mirrorLocalVideo = useUISettings(UI_SETTINGS.mirrorLocalVideo);
const trackSelector = selectVideoTrackByID(videoTrackId);
const hmsActions = useHMSActions();
const track = useHMSStore(trackSelector);
const isMobile = useMedia(cssConfig.media.md);

/**
* Chromium browsers return an audioOutput with empty label when no permissions are given
Expand Down Expand Up @@ -100,6 +105,11 @@ const Settings = ({ setHide }) => {
deviceType: DeviceType.audioOutput,
})
}
refreshDevices={async () => {
raviteja83 marked this conversation as resolved.
Show resolved Hide resolved
if (isMobile) {
await hmsActions.refreshDevices();
}
}}
>
<TestAudio id={selectedDeviceIDs.audioOutput} />
</DeviceSelector>
Expand All @@ -108,7 +118,7 @@ const Settings = ({ setHide }) => {
);
};

const DeviceSelector = ({ title, devices, selection, onChange, icon, children = null }) => {
const DeviceSelector = ({ title, devices, selection, onChange, icon, refreshDevices = null, children = null }) => {
const [open, setOpen] = useState(false);
const selectionBg = useDropdownSelection();
const ref = useRef(null);
Expand Down Expand Up @@ -138,7 +148,15 @@ const DeviceSelector = ({ title, devices, selection, onChange, icon, children =
},
}}
>
<Dropdown.Root open={open} onOpenChange={setOpen}>
<Dropdown.Root
open={open}
onOpenChange={async () => {
if (refreshDevices) {
raviteja83 marked this conversation as resolved.
Show resolved Hide resolved
await refreshDevices();
}
setOpen();
}}
>
<DialogDropdownTrigger
ref={ref}
icon={icon}
Expand Down
Loading