forked from Shiloh/remnantchat
feat: [#19] display which peers are speaking to the room
This commit is contained in:
parent
931ddf267f
commit
ba21936c96
@ -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) => {
|
||||||
|
@ -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>
|
||||||
|
@ -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>
|
||||||
|
@ -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: () => {},
|
||||||
})
|
})
|
||||||
|
@ -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 {
|
||||||
|
@ -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',
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user