refactor: move audio logic to new hook

This commit is contained in:
Jeremy Kahn 2022-11-01 09:10:53 -05:00
parent 166fc9ff84
commit 9c18f40d61
2 changed files with 160 additions and 118 deletions

View File

@ -12,7 +12,6 @@ import {
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'
@ -23,6 +22,7 @@ import { PeerRoom } from 'services/PeerRoom'
import { messageTranscriptSizeLimit } from 'config/messaging' import { messageTranscriptSizeLimit } from 'config/messaging'
import { usePeerRoomAction } from './usePeerRoomAction' import { usePeerRoomAction } from './usePeerRoomAction'
import { useRoomAudio } from './useRoomAudio'
interface UseRoomConfig { interface UseRoomConfig {
roomId: string roomId: string
@ -49,19 +49,19 @@ export function useRoom(
const [newMessageAudio] = useState( const [newMessageAudio] = useState(
() => new AudioService(process.env.PUBLIC_URL + '/sounds/new-message.aac') () => new AudioService(process.env.PUBLIC_URL + '/sounds/new-message.aac')
) )
const [isSpeakingToRoom, setIsSpeakingToRoom] = useState(false)
const [peerAudios, setPeerAudios] = useState<
Record<string, HTMLAudioElement>
>({})
const [audioStream, setAudioStream] = useState<MediaStream | null>()
const setMessageLog = (messages: Message[]) => { const setMessageLog = (messages: Message[]) => {
_setMessageLog(messages.slice(-messageTranscriptSizeLimit)) _setMessageLog(messages.slice(-messageTranscriptSizeLimit))
} }
const [audioDevices, setAudioDevices] = useState<MediaDeviceInfo[]>([])
const [selectedAudioDeviceId, setSelectedAudioDeviceId] = useState< const {
string | null audioDevices,
>(null) isSpeakingToRoom,
setIsSpeakingToRoom,
handleAudioDeviceSelect,
handleAudioForNewPeer,
handleAudioForLeavingPeer,
} = useRoomAudio({ peerRoom })
useEffect(() => { useEffect(() => {
return () => { return () => {
@ -77,16 +77,6 @@ export function useRoom(
} }
}, [shellContext]) }, [shellContext])
useEffect(() => {
;(async () => {
if (!audioStream) return
const devices = await window.navigator.mediaDevices.enumerateDevices()
const audioDevices = devices.filter(({ kind }) => kind === 'audioinput')
setAudioDevices(audioDevices)
})()
}, [audioStream])
const [sendPeerId, receivePeerId] = usePeerRoomAction<string>( const [sendPeerId, receivePeerId] = usePeerRoomAction<string>(
peerRoom, peerRoom,
PeerActions.PEER_NAME PeerActions.PEER_NAME
@ -99,11 +89,6 @@ 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
@ -165,20 +150,6 @@ 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',
@ -188,10 +159,7 @@ export function useRoom(
setNumberOfPeers(newNumberOfPeers) setNumberOfPeers(newNumberOfPeers)
shellContext.setNumberOfPeers(newNumberOfPeers) shellContext.setNumberOfPeers(newNumberOfPeers)
if (audioStream) { handleAudioForNewPeer(peerId)
peerRoom.addStream(audioStream, peerId)
}
;(async () => { ;(async () => {
try { try {
const promises: Promise<any>[] = [sendPeerId(userId, peerId)] const promises: Promise<any>[] = [sendPeerId(userId, peerId)]
@ -229,9 +197,7 @@ export function useRoom(
setNumberOfPeers(newNumberOfPeers) setNumberOfPeers(newNumberOfPeers)
shellContext.setNumberOfPeers(newNumberOfPeers) shellContext.setNumberOfPeers(newNumberOfPeers)
if (audioStream) { handleAudioForLeavingPeer(peerId)
peerRoom.removeStream(audioStream, peerId)
}
if (peerExist) { if (peerExist) {
const peerListClone = [...shellContext.peerList] const peerListClone = [...shellContext.peerList]
@ -240,78 +206,6 @@ export function useRoom(
} }
}) })
peerRoom.onPeerStream((stream, peerId) => {
const audio = new Audio()
audio.srcObject = stream
audio.autoplay = true
setPeerAudios({ ...peerAudios, [peerId]: audio })
})
useEffect(() => {
;(async () => {
if (isSpeakingToRoom) {
if (!audioStream) {
const newSelfStream = await navigator.mediaDevices.getUserMedia({
audio: selectedAudioDeviceId
? { deviceId: selectedAudioDeviceId }
: true,
video: false,
})
peerRoom.addStream(newSelfStream)
sendAudioChange(AudioState.PLAYING)
shellContext.setAudioState(AudioState.PLAYING)
setAudioStream(newSelfStream)
}
} else {
if (audioStream) {
for (const audioTrack of audioStream.getTracks()) {
audioTrack.stop()
audioStream.removeTrack(audioTrack)
}
peerRoom.removeStream(audioStream, peerRoom.getPeers())
sendAudioChange(AudioState.STOPPED)
shellContext.setAudioState(AudioState.STOPPED)
setAudioStream(null)
}
}
})()
}, [
isSpeakingToRoom,
peerAudios,
peerRoom,
audioStream,
selectedAudioDeviceId,
sendAudioChange,
shellContext,
])
const handleAudioDeviceSelect = async (audioDevice: MediaDeviceInfo) => {
const { deviceId } = audioDevice
setSelectedAudioDeviceId(deviceId)
if (!audioStream) return
for (const audioTrack of audioStream.getTracks()) {
audioTrack.stop()
audioStream.removeTrack(audioTrack)
}
peerRoom.removeStream(audioStream, peerRoom.getPeers())
const newSelfStream = await navigator.mediaDevices.getUserMedia({
audio: {
deviceId,
},
video: false,
})
peerRoom.addStream(newSelfStream)
setAudioStream(newSelfStream)
}
return { return {
audioDevices, audioDevices,
peerRoom, peerRoom,

View File

@ -0,0 +1,148 @@
import { useContext, useEffect, useState } from 'react'
import { ShellContext } from 'contexts/ShellContext'
import { PeerActions } from 'models/network'
import { AudioState, Peer } from 'models/chat'
import { PeerRoom } from 'services/PeerRoom'
import { usePeerRoomAction } from './usePeerRoomAction'
interface UseRoomAudioConfig {
peerRoom: PeerRoom
}
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[]>([])
const [selectedAudioDeviceId, setSelectedAudioDeviceId] = useState<
string | null
>(null)
useEffect(() => {
;(async () => {
if (!audioStream) return
const devices = await window.navigator.mediaDevices.enumerateDevices()
const audioDevices = devices.filter(({ kind }) => kind === 'audioinput')
setAudioDevices(audioDevices)
})()
}, [audioStream])
const [sendAudioChange, receiveAudioChange] = usePeerRoomAction<AudioState>(
peerRoom,
PeerActions.AUDIO_CHANGE
)
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.onPeerStream((stream, peerId) => {
const audio = new Audio()
audio.srcObject = stream
audio.autoplay = true
setPeerAudios({ ...peerAudios, [peerId]: audio })
})
useEffect(() => {
;(async () => {
if (isSpeakingToRoom) {
if (!audioStream) {
const newSelfStream = await navigator.mediaDevices.getUserMedia({
audio: selectedAudioDeviceId
? { deviceId: selectedAudioDeviceId }
: true,
video: false,
})
peerRoom.addStream(newSelfStream)
sendAudioChange(AudioState.PLAYING)
shellContext.setAudioState(AudioState.PLAYING)
setAudioStream(newSelfStream)
}
} else {
if (audioStream) {
for (const audioTrack of audioStream.getTracks()) {
audioTrack.stop()
audioStream.removeTrack(audioTrack)
}
peerRoom.removeStream(audioStream, peerRoom.getPeers())
sendAudioChange(AudioState.STOPPED)
shellContext.setAudioState(AudioState.STOPPED)
setAudioStream(null)
}
}
})()
}, [
isSpeakingToRoom,
peerAudios,
peerRoom,
audioStream,
selectedAudioDeviceId,
sendAudioChange,
shellContext,
])
const handleAudioDeviceSelect = async (audioDevice: MediaDeviceInfo) => {
const { deviceId } = audioDevice
setSelectedAudioDeviceId(deviceId)
if (!audioStream) return
for (const audioTrack of audioStream.getTracks()) {
audioTrack.stop()
audioStream.removeTrack(audioTrack)
}
peerRoom.removeStream(audioStream, peerRoom.getPeers())
const newSelfStream = await navigator.mediaDevices.getUserMedia({
audio: {
deviceId,
},
video: false,
})
peerRoom.addStream(newSelfStream)
setAudioStream(newSelfStream)
}
const handleAudioForNewPeer = (peerId: string) => {
if (audioStream) {
peerRoom.addStream(audioStream, peerId)
}
}
const handleAudioForLeavingPeer = (peerId: string) => {
if (audioStream) {
peerRoom.removeStream(audioStream, peerId)
}
}
return {
audioDevices,
isSpeakingToRoom,
setIsSpeakingToRoom,
handleAudioDeviceSelect,
handleAudioForNewPeer,
handleAudioForLeavingPeer,
}
}