feat: [closes #77] Peer audio volume control (#90)

* feat: add AudioVolume component
* feat: show volume slider label value
* feat: update audio volume icon
* feat: mute/unmute when volume icon is clicked
* feat: show peer dividers
This commit is contained in:
Jeremy Kahn 2023-02-26 18:26:53 -06:00 committed by GitHub
parent 99a9ab7838
commit 870a13eac1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 81 additions and 12 deletions

View File

@ -0,0 +1,58 @@
import { useState, useEffect } from 'react'
import Slider from '@mui/material/Slider'
import Box from '@mui/material/Box'
import ListItemIcon from '@mui/material/ListItemIcon'
import VolumeUp from '@mui/icons-material/VolumeUp'
import VolumeDown from '@mui/icons-material/VolumeDown'
import VolumeMute from '@mui/icons-material/VolumeMute'
interface AudioVolumeProps {
audioEl: HTMLAudioElement
}
export const AudioVolume = ({ audioEl }: AudioVolumeProps) => {
const [audioVolume, setAudioVolume] = useState(audioEl.volume)
useEffect(() => {
audioEl.volume = audioVolume
}, [audioEl, audioVolume])
const handleIconClick = () => {
if (audioVolume === 0) {
setAudioVolume(1)
} else {
setAudioVolume(0)
}
}
const handleSliderChange = (_event: Event, value: number | number[]) => {
value = Array.isArray(value) ? value[0] : value
setAudioVolume(value / 100)
}
const formatLabelValue = () => `${Math.round(audioVolume * 100)}%`
let VolumeIcon = VolumeUp
if (audioVolume === 0) {
VolumeIcon = VolumeMute
} else if (audioVolume < 0.5) {
VolumeIcon = VolumeDown
}
return (
<Box sx={{ display: 'flex', pt: 1, pr: 3, alignItems: 'center' }}>
<ListItemIcon>
<VolumeIcon sx={{ cursor: 'pointer' }} onClick={handleIconClick} />
</ListItemIcon>
<Slider
aria-label="Volume"
getAriaValueText={formatLabelValue}
valueLabelFormat={formatLabelValue}
valueLabelDisplay="auto"
onChange={handleSliderChange}
value={audioVolume * 100}
></Slider>
</Box>
)
}

View File

@ -0,0 +1 @@
export * from './AudioVolume'

View File

@ -14,9 +14,6 @@ interface UseRoomAudioConfig {
export function useRoomAudio({ peerRoom }: UseRoomAudioConfig) {
const shellContext = useContext(ShellContext)
const [isSpeakingToRoom, setIsSpeakingToRoom] = useState(false)
const [peerAudios, setPeerAudios] = useState<
Record<string, HTMLAudioElement>
>({})
const [audioStream, setAudioStream] = useState<MediaStream | null>()
const [audioDevices, setAudioDevices] = useState<MediaDeviceInfo[]>([])
@ -24,7 +21,8 @@ export function useRoomAudio({ peerRoom }: UseRoomAudioConfig) {
string | null
>(null)
const { peerList, setPeerList, setAudioState } = shellContext
const { peerList, setPeerList, setAudioState, peerAudios, setPeerAudios } =
shellContext
useEffect(() => {
;(async () => {

View File

@ -10,6 +10,7 @@ import VolumeUp from '@mui/icons-material/VolumeUp'
import ListItem from '@mui/material/ListItem'
import { PeerListHeader } from 'components/Shell/PeerListHeader'
import { AudioVolume } from 'components/AudioVolume'
import { PeerNameDisplay } from 'components/PeerNameDisplay'
import { AudioState, Peer } from 'models/chat'
@ -23,6 +24,7 @@ export interface PeerListProps extends PropsWithChildren {
onPeerListClose: () => void
peerList: Peer[]
audioState: AudioState
peerAudios: Record<string, HTMLAudioElement>
}
export const PeerList = ({
@ -31,6 +33,7 @@ export const PeerList = ({
onPeerListClose,
peerList,
audioState,
peerAudios,
}: PeerListProps) => {
return (
<MuiDrawer
@ -57,7 +60,7 @@ export const PeerList = ({
</PeerListHeader>
<Divider />
<List>
<ListItem>
<ListItem divider={true}>
{audioState === AudioState.PLAYING && (
<ListItemIcon>
<VolumeUp />
@ -68,20 +71,17 @@ export const PeerList = ({
</ListItemText>
</ListItem>
{peerList.map((peer: Peer) => (
<ListItem key={peer.peerId}>
<ListItem key={peer.peerId} divider={true}>
<PeerDownloadFileButton peer={peer} />
<ListItemText>
<PeerNameDisplay>{peer.userId}</PeerNameDisplay>
{peer.peerId in peerAudios && (
<AudioVolume audioEl={peerAudios[peer.peerId]} />
)}
</ListItemText>
{peer.audioState === AudioState.PLAYING && (
<ListItemIcon>
<VolumeUp />
</ListItemIcon>
)}
</ListItem>
))}
</List>
<Divider />
</MuiDrawer>
)
}

View File

@ -56,6 +56,9 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
const [screenState, setScreenState] = useState<ScreenShareState>(
ScreenShareState.NOT_SHARING
)
const [peerAudios, setPeerAudios] = useState<
Record<string, HTMLAudioElement>
>({})
const showAlert = useCallback<
(message: string, options?: AlertOptions) => void
>((message, options) => {
@ -89,6 +92,8 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
setVideoState,
screenState,
setScreenState,
peerAudios,
setPeerAudios,
}),
[
isPeerListOpen,
@ -112,6 +117,8 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
setVideoState,
screenState,
setScreenState,
peerAudios,
setPeerAudios,
]
)
@ -322,6 +329,7 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
onPeerListClose={handlePeerListClick}
peerList={peerList}
audioState={audioState}
peerAudios={peerAudios}
/>
<QRCodeDialog
isOpen={isQRCodeDialogOpen}

View File

@ -26,6 +26,8 @@ interface ShellContextProps {
setVideoState: Dispatch<SetStateAction<VideoState>>
screenState: ScreenShareState
setScreenState: Dispatch<SetStateAction<ScreenShareState>>
peerAudios: Record<string, HTMLAudioElement>
setPeerAudios: Dispatch<SetStateAction<Record<string, HTMLAudioElement>>>
}
export const ShellContext = createContext<ShellContextProps>({
@ -51,4 +53,6 @@ export const ShellContext = createContext<ShellContextProps>({
setVideoState: () => {},
screenState: ScreenShareState.NOT_SHARING,
setScreenState: () => {},
peerAudios: {},
setPeerAudios: () => {},
})