feat: [#19] display which peers are speaking to the room

This commit is contained in:
Jeremy Kahn 2022-10-31 21:40:44 -05:00
parent 931ddf267f
commit ba21936c96
6 changed files with 72 additions and 12 deletions

View File

@ -7,10 +7,12 @@ import { ShellContext } from 'contexts/ShellContext'
import { SettingsContext } from 'contexts/SettingsContext' import { SettingsContext } from 'contexts/SettingsContext'
import { PeerActions } from 'models/network' import { PeerActions } from 'models/network'
import { import {
AudioState,
Message, Message,
ReceivedMessage, ReceivedMessage,
UnsentMessage, UnsentMessage,
isMessageReceived, isMessageReceived,
Peer,
} from 'models/chat' } from 'models/chat'
import { funAnimalName } from 'fun-animal-names' import { funAnimalName } from 'fun-animal-names'
import { getPeerName } from 'components/PeerNameDisplay' import { getPeerName } from 'components/PeerNameDisplay'
@ -97,6 +99,11 @@ export function useRoom(
const [sendPeerMessage, receivePeerMessage] = const [sendPeerMessage, receivePeerMessage] =
usePeerRoomAction<UnsentMessage>(peerRoom, PeerActions.MESSAGE) usePeerRoomAction<UnsentMessage>(peerRoom, PeerActions.MESSAGE)
const [sendAudioChange, receiveAudioChange] = usePeerRoomAction<AudioState>(
peerRoom,
PeerActions.AUDIO_CHANGE
)
const sendMessage = async (message: string) => { const sendMessage = async (message: string) => {
if (isMessageSending) return if (isMessageSending) return
@ -125,12 +132,12 @@ export function useRoom(
if (peerIndex === -1) { if (peerIndex === -1) {
shellContext.setPeerList([ shellContext.setPeerList([
...shellContext.peerList, ...shellContext.peerList,
{ peerId: peerId, userId: userId }, { peerId: peerId, userId: userId, audioState: AudioState.STOPPED },
]) ])
} else { } else {
const peerListClone = [...shellContext.peerList] const newPeerList = [...shellContext.peerList]
peerListClone[peerIndex].userId = userId newPeerList[peerIndex].userId = userId
shellContext.setPeerList(peerListClone) shellContext.setPeerList(newPeerList)
} }
}) })
@ -158,6 +165,20 @@ export function useRoom(
setMessageLog([...messageLog, { ...message, timeReceived: Date.now() }]) setMessageLog([...messageLog, { ...message, timeReceived: Date.now() }])
}) })
receiveAudioChange((audioState, peerId) => {
const newPeerList = shellContext.peerList.map(peer => {
const newPeer: Peer = { ...peer }
if (peer.peerId === peerId) {
newPeer.audioState = audioState
}
return newPeer
})
shellContext.setPeerList(newPeerList)
})
peerRoom.onPeerJoin((peerId: string) => { peerRoom.onPeerJoin((peerId: string) => {
shellContext.showAlert(`Someone has joined the room`, { shellContext.showAlert(`Someone has joined the room`, {
severity: 'success', severity: 'success',
@ -239,6 +260,8 @@ export function useRoom(
}) })
peerRoom.addStream(newSelfStream) peerRoom.addStream(newSelfStream)
sendAudioChange(AudioState.PLAYING)
shellContext.setAudioState(AudioState.PLAYING)
setAudioStream(newSelfStream) setAudioStream(newSelfStream)
} }
} else { } else {
@ -249,6 +272,8 @@ export function useRoom(
} }
peerRoom.removeStream(audioStream, peerRoom.getPeers()) peerRoom.removeStream(audioStream, peerRoom.getPeers())
sendAudioChange(AudioState.STOPPED)
shellContext.setAudioState(AudioState.STOPPED)
setAudioStream(null) setAudioStream(null)
} }
} }
@ -259,6 +284,8 @@ export function useRoom(
peerRoom, peerRoom,
audioStream, audioStream,
selectedAudioDeviceId, selectedAudioDeviceId,
sendAudioChange,
shellContext,
]) ])
const handleAudioDeviceSelect = async (audioDevice: MediaDeviceInfo) => { const handleAudioDeviceSelect = async (audioDevice: MediaDeviceInfo) => {

View File

@ -1,24 +1,27 @@
import { PropsWithChildren } from 'react' import { PropsWithChildren } from 'react'
import MuiDrawer from '@mui/material/Drawer' import MuiDrawer from '@mui/material/Drawer'
import List from '@mui/material/List' import List from '@mui/material/List'
import ListItemIcon from '@mui/material/ListItemIcon'
import ListItemText from '@mui/material/ListItemText'
import Divider from '@mui/material/Divider' import Divider from '@mui/material/Divider'
import IconButton from '@mui/material/IconButton' import IconButton from '@mui/material/IconButton'
import ChevronRightIcon from '@mui/icons-material/ChevronRight' import ChevronRightIcon from '@mui/icons-material/ChevronRight'
import VolumeUp from '@mui/icons-material/VolumeUp'
import ListItemButton from '@mui/material/ListItemButton' import ListItemButton from '@mui/material/ListItemButton'
import Typography from '@mui/material/Typography'
import { PeerListHeader } from 'components/Shell/PeerListHeader' import { PeerListHeader } from 'components/Shell/PeerListHeader'
import { PeerNameDisplay } from 'components/PeerNameDisplay' import { PeerNameDisplay } from 'components/PeerNameDisplay'
import { Peer } from 'models/chat' import { AudioState, Peer } from 'models/chat'
export const peerListWidth = 240 export const peerListWidth = 300
export interface PeerListProps extends PropsWithChildren { export interface PeerListProps extends PropsWithChildren {
userId: string userId: string
isPeerListOpen: boolean isPeerListOpen: boolean
onPeerListClose: () => void onPeerListClose: () => void
peerList: Peer[] peerList: Peer[]
audioState: AudioState
} }
export const PeerList = ({ export const PeerList = ({
@ -26,6 +29,7 @@ export const PeerList = ({
isPeerListOpen, isPeerListOpen,
onPeerListClose, onPeerListClose,
peerList, peerList,
audioState,
}: PeerListProps) => { }: PeerListProps) => {
return ( return (
<MuiDrawer <MuiDrawer
@ -48,13 +52,25 @@ export const PeerList = ({
<Divider /> <Divider />
<List> <List>
<ListItemButton disableRipple={true}> <ListItemButton disableRipple={true}>
<Typography> {audioState === AudioState.PLAYING && (
<ListItemIcon>
<VolumeUp />
</ListItemIcon>
)}
<ListItemText>
<PeerNameDisplay>{userId}</PeerNameDisplay> (you) <PeerNameDisplay>{userId}</PeerNameDisplay> (you)
</Typography> </ListItemText>
</ListItemButton> </ListItemButton>
{peerList.map((peer: Peer) => ( {peerList.map((peer: Peer) => (
<ListItemButton key={peer.peerId} disableRipple={true}> <ListItemButton key={peer.peerId} disableRipple={true}>
{peer.audioState === AudioState.PLAYING && (
<ListItemIcon>
<VolumeUp />
</ListItemIcon>
)}
<ListItemText>
<PeerNameDisplay>{peer.userId}</PeerNameDisplay> <PeerNameDisplay>{peer.userId}</PeerNameDisplay>
</ListItemText>
</ListItemButton> </ListItemButton>
))} ))}
</List> </List>

View File

@ -15,7 +15,7 @@ import { AlertColor } from '@mui/material/Alert'
import { ShellContext } from 'contexts/ShellContext' import { ShellContext } from 'contexts/ShellContext'
import { SettingsContext } from 'contexts/SettingsContext' import { SettingsContext } from 'contexts/SettingsContext'
import { AlertOptions } from 'models/shell' import { AlertOptions } from 'models/shell'
import { Peer } from 'models/chat' import { AudioState, Peer } from 'models/chat'
import { ErrorBoundary } from 'components/ErrorBoundary' import { ErrorBoundary } from 'components/ErrorBoundary'
import { Drawer } from './Drawer' import { Drawer } from './Drawer'
@ -42,6 +42,7 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
const [isPeerListOpen, setIsPeerListOpen] = useState(false) const [isPeerListOpen, setIsPeerListOpen] = useState(false)
const [peerList, setPeerList] = useState<Peer[]>([]) // except you const [peerList, setPeerList] = useState<Peer[]>([]) // except you
const [tabHasFocus, setTabHasFocus] = useState(true) const [tabHasFocus, setTabHasFocus] = useState(true)
const [audioState, setAudioState] = useState<AudioState>(AudioState.STOPPED)
const showAlert = useCallback< const showAlert = useCallback<
(message: string, options?: AlertOptions) => void (message: string, options?: AlertOptions) => void
@ -63,6 +64,8 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
setIsPeerListOpen, setIsPeerListOpen,
peerList, peerList,
setPeerList, setPeerList,
audioState,
setAudioState,
}), }),
[ [
isPeerListOpen, isPeerListOpen,
@ -73,6 +76,8 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
setNumberOfPeers, setNumberOfPeers,
setTitle, setTitle,
showAlert, showAlert,
audioState,
setAudioState,
] ]
) )
@ -203,6 +208,7 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
isPeerListOpen={isPeerListOpen} isPeerListOpen={isPeerListOpen}
onPeerListClose={handlePeerListClick} onPeerListClose={handlePeerListClick}
peerList={peerList} peerList={peerList}
audioState={audioState}
/> />
</Box> </Box>
</ThemeProvider> </ThemeProvider>

View File

@ -1,7 +1,7 @@
import { createContext, Dispatch, SetStateAction } from 'react' import { createContext, Dispatch, SetStateAction } from 'react'
import { AlertOptions } from 'models/shell' import { AlertOptions } from 'models/shell'
import { Peer } from 'models/chat' import { AudioState, Peer } from 'models/chat'
interface ShellContextProps { interface ShellContextProps {
numberOfPeers: number numberOfPeers: number
@ -14,6 +14,8 @@ interface ShellContextProps {
setIsPeerListOpen: Dispatch<SetStateAction<boolean>> setIsPeerListOpen: Dispatch<SetStateAction<boolean>>
peerList: Peer[] peerList: Peer[]
setPeerList: Dispatch<SetStateAction<Peer[]>> setPeerList: Dispatch<SetStateAction<Peer[]>>
audioState: AudioState
setAudioState: Dispatch<SetStateAction<AudioState>>
} }
export const ShellContext = createContext<ShellContextProps>({ export const ShellContext = createContext<ShellContextProps>({
@ -27,4 +29,6 @@ export const ShellContext = createContext<ShellContextProps>({
setIsPeerListOpen: () => {}, setIsPeerListOpen: () => {},
peerList: [], peerList: [],
setPeerList: () => {}, setPeerList: () => {},
audioState: AudioState.STOPPED,
setAudioState: () => {},
}) })

View File

@ -5,9 +5,15 @@ export interface UnsentMessage {
authorId: string authorId: string
} }
export enum AudioState {
PLAYING = 'PLAYING',
STOPPED = 'STOPPED',
}
export interface Peer { export interface Peer {
peerId: string peerId: string
userId: string userId: string
audioState: AudioState
} }
export interface ReceivedMessage extends UnsentMessage { export interface ReceivedMessage extends UnsentMessage {

View File

@ -3,4 +3,5 @@ export enum PeerActions {
MESSAGE = 'MESSAGE', MESSAGE = 'MESSAGE',
MESSAGE_TRANSCRIPT = 'MSG_XSCRIPT', MESSAGE_TRANSCRIPT = 'MSG_XSCRIPT',
PEER_NAME = 'PEER_NAME', PEER_NAME = 'PEER_NAME',
AUDIO_CHANGE = 'AUDIO_CHANGE',
} }