forked from Shiloh/remnantchat
* feat(typing-indicator): wire up handleMessageChange * feat(typing-indicator): send typing: true status * feat(typing-indicator): expire typing state * feat(typing-indicator): update typing state received from peers * refactor(shell): add updatePeer utility * feat(typing-indicator): display peer typing status * feat(typing-indicator): reset typing status when a message is sent * feat(typing-indicator): move indicator below message form * feat(typing-indicator): keep status text to one line
This commit is contained in:
parent
af4cba8449
commit
e597a667a1
34
package-lock.json
generated
34
package-lock.json
generated
@ -13,6 +13,7 @@
|
|||||||
"@emotion/styled": "^11.10.0",
|
"@emotion/styled": "^11.10.0",
|
||||||
"@mui/icons-material": "^5.8.4",
|
"@mui/icons-material": "^5.8.4",
|
||||||
"@mui/material": "^5.9.3",
|
"@mui/material": "^5.9.3",
|
||||||
|
"@react-hook/debounce": "^4.0.0",
|
||||||
"@react-hook/window-size": "^3.1.1",
|
"@react-hook/window-size": "^3.1.1",
|
||||||
"@testing-library/jest-dom": "^5.16.5",
|
"@testing-library/jest-dom": "^5.16.5",
|
||||||
"@testing-library/react": "^13.3.0",
|
"@testing-library/react": "^13.3.0",
|
||||||
@ -4587,9 +4588,9 @@
|
|||||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
|
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
|
||||||
},
|
},
|
||||||
"node_modules/@react-hook/debounce": {
|
"node_modules/@react-hook/debounce": {
|
||||||
"version": "3.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@react-hook/debounce/-/debounce-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@react-hook/debounce/-/debounce-4.0.0.tgz",
|
||||||
"integrity": "sha512-ir/kPrSfAzY12Gre0sOHkZ2rkEmM4fS5M5zFxCi4BnCeXh2nvx9Ujd+U4IGpKCuPA+EQD0pg1eK2NGLvfWejag==",
|
"integrity": "sha512-706Xcg+KKWHk9BuZQUQ0ZQKp9zhv3/MbqFenWVfHcynYpSGRVwQTzJRGvPxvsdtXxJv+HfgKTY/O/hEejakwmA==",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-hook/latest": "^1.0.2"
|
"@react-hook/latest": "^1.0.2"
|
||||||
},
|
},
|
||||||
@ -4637,6 +4638,17 @@
|
|||||||
"react": ">=16.8"
|
"react": ">=16.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@react-hook/window-size/node_modules/@react-hook/debounce": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-hook/debounce/-/debounce-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-ir/kPrSfAzY12Gre0sOHkZ2rkEmM4fS5M5zFxCi4BnCeXh2nvx9Ujd+U4IGpKCuPA+EQD0pg1eK2NGLvfWejag==",
|
||||||
|
"dependencies": {
|
||||||
|
"@react-hook/latest": "^1.0.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": ">=16.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@rollup/plugin-babel": {
|
"node_modules/@rollup/plugin-babel": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
||||||
@ -29935,9 +29947,9 @@
|
|||||||
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
|
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
|
||||||
},
|
},
|
||||||
"@react-hook/debounce": {
|
"@react-hook/debounce": {
|
||||||
"version": "3.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@react-hook/debounce/-/debounce-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@react-hook/debounce/-/debounce-4.0.0.tgz",
|
||||||
"integrity": "sha512-ir/kPrSfAzY12Gre0sOHkZ2rkEmM4fS5M5zFxCi4BnCeXh2nvx9Ujd+U4IGpKCuPA+EQD0pg1eK2NGLvfWejag==",
|
"integrity": "sha512-706Xcg+KKWHk9BuZQUQ0ZQKp9zhv3/MbqFenWVfHcynYpSGRVwQTzJRGvPxvsdtXxJv+HfgKTY/O/hEejakwmA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@react-hook/latest": "^1.0.2"
|
"@react-hook/latest": "^1.0.2"
|
||||||
}
|
}
|
||||||
@ -29970,6 +29982,16 @@
|
|||||||
"@react-hook/debounce": "^3.0.0",
|
"@react-hook/debounce": "^3.0.0",
|
||||||
"@react-hook/event": "^1.2.1",
|
"@react-hook/event": "^1.2.1",
|
||||||
"@react-hook/throttle": "^2.2.0"
|
"@react-hook/throttle": "^2.2.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@react-hook/debounce": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@react-hook/debounce/-/debounce-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-ir/kPrSfAzY12Gre0sOHkZ2rkEmM4fS5M5zFxCi4BnCeXh2nvx9Ujd+U4IGpKCuPA+EQD0pg1eK2NGLvfWejag==",
|
||||||
|
"requires": {
|
||||||
|
"@react-hook/latest": "^1.0.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@rollup/plugin-babel": {
|
"@rollup/plugin-babel": {
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
"@emotion/styled": "^11.10.0",
|
"@emotion/styled": "^11.10.0",
|
||||||
"@mui/icons-material": "^5.8.4",
|
"@mui/icons-material": "^5.8.4",
|
||||||
"@mui/material": "^5.9.3",
|
"@mui/material": "^5.9.3",
|
||||||
|
"@react-hook/debounce": "^4.0.0",
|
||||||
"@react-hook/window-size": "^3.1.1",
|
"@react-hook/window-size": "^3.1.1",
|
||||||
"@testing-library/jest-dom": "^5.16.5",
|
"@testing-library/jest-dom": "^5.16.5",
|
||||||
"@testing-library/react": "^13.3.0",
|
"@testing-library/react": "^13.3.0",
|
||||||
|
@ -15,11 +15,13 @@ import { messageCharacterSizeLimit } from 'config/messaging'
|
|||||||
|
|
||||||
interface MessageFormProps {
|
interface MessageFormProps {
|
||||||
onMessageSubmit: (message: string) => void
|
onMessageSubmit: (message: string) => void
|
||||||
|
onMessageChange: (message: string) => void
|
||||||
isMessageSending: boolean
|
isMessageSending: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MessageForm = ({
|
export const MessageForm = ({
|
||||||
onMessageSubmit,
|
onMessageSubmit,
|
||||||
|
onMessageChange,
|
||||||
isMessageSending,
|
isMessageSending,
|
||||||
}: MessageFormProps) => {
|
}: MessageFormProps) => {
|
||||||
const textFieldRef = useRef<HTMLInputElement>(null)
|
const textFieldRef = useRef<HTMLInputElement>(null)
|
||||||
@ -43,6 +45,7 @@ export const MessageForm = ({
|
|||||||
const handleMessageChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
const handleMessageChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const { value } = event.target
|
const { value } = event.target
|
||||||
setTextMessage(value)
|
setTextMessage(value)
|
||||||
|
onMessageChange(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const submitMessage = () => {
|
const submitMessage = () => {
|
||||||
@ -68,7 +71,7 @@ export const MessageForm = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleMessageSubmit} className="p-4">
|
<form onSubmit={handleMessageSubmit} className="pt-4 px-4">
|
||||||
<Stack direction="row" spacing={2}>
|
<Stack direction="row" spacing={2}>
|
||||||
<FormControl fullWidth>
|
<FormControl fullWidth>
|
||||||
<TextField
|
<TextField
|
||||||
|
@ -3,7 +3,7 @@ import Typography, { TypographyProps } from '@mui/material/Typography'
|
|||||||
import { usePeerNameDisplay } from './usePeerNameDisplay'
|
import { usePeerNameDisplay } from './usePeerNameDisplay'
|
||||||
import { getPeerName } from './getPeerName'
|
import { getPeerName } from './getPeerName'
|
||||||
|
|
||||||
interface PeerNameDisplayProps extends TypographyProps {
|
export interface PeerNameDisplayProps extends TypographyProps {
|
||||||
children: string
|
children: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import { RoomFileUploadControls } from './RoomFileUploadControls'
|
|||||||
import { RoomVideoDisplay } from './RoomVideoDisplay'
|
import { RoomVideoDisplay } from './RoomVideoDisplay'
|
||||||
import { RoomShowMessagesControls } from './RoomShowMessagesControls'
|
import { RoomShowMessagesControls } from './RoomShowMessagesControls'
|
||||||
import { RoomHideRoomControls } from './RoomHideRoomControls'
|
import { RoomHideRoomControls } from './RoomHideRoomControls'
|
||||||
|
import { TypingStatusBar } from './TypingStatusBar'
|
||||||
|
|
||||||
export interface RoomProps {
|
export interface RoomProps {
|
||||||
appId?: string
|
appId?: string
|
||||||
@ -43,6 +44,7 @@ export function Room({
|
|||||||
const {
|
const {
|
||||||
isMessageSending,
|
isMessageSending,
|
||||||
handleInlineMediaUpload,
|
handleInlineMediaUpload,
|
||||||
|
handleMessageChange,
|
||||||
messageLog,
|
messageLog,
|
||||||
peerRoom,
|
peerRoom,
|
||||||
roomContextValue,
|
roomContextValue,
|
||||||
@ -150,10 +152,14 @@ export function Room({
|
|||||||
className="grow overflow-auto px-4"
|
className="grow overflow-auto px-4"
|
||||||
/>
|
/>
|
||||||
<Divider />
|
<Divider />
|
||||||
<MessageForm
|
<Box>
|
||||||
onMessageSubmit={handleMessageSubmit}
|
<MessageForm
|
||||||
isMessageSending={isMessageSending}
|
onMessageSubmit={handleMessageSubmit}
|
||||||
/>
|
isMessageSending={isMessageSending}
|
||||||
|
onMessageChange={handleMessageChange}
|
||||||
|
/>
|
||||||
|
<TypingStatusBar />
|
||||||
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Box>
|
</Box>
|
||||||
|
71
src/components/Room/TypingStatusBar.tsx
Normal file
71
src/components/Room/TypingStatusBar.tsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import Box from '@mui/material/Box'
|
||||||
|
import { Typography } from '@mui/material'
|
||||||
|
import { useContext } from 'react'
|
||||||
|
import { ShellContext } from 'contexts/ShellContext'
|
||||||
|
import {
|
||||||
|
PeerNameDisplay,
|
||||||
|
PeerNameDisplayProps,
|
||||||
|
} from 'components/PeerNameDisplay/PeerNameDisplay'
|
||||||
|
|
||||||
|
export const TypingStatusBar = () => {
|
||||||
|
const { peerList } = useContext(ShellContext)
|
||||||
|
const typingPeers = peerList.filter(({ isTyping }) => isTyping)
|
||||||
|
|
||||||
|
const peerNameDisplayProps: Partial<PeerNameDisplayProps> = {
|
||||||
|
variant: 'caption',
|
||||||
|
sx: theme => ({
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
fontWeight: theme.typography.fontWeightBold,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
|
||||||
|
let statusMessage = <></>
|
||||||
|
|
||||||
|
if (typingPeers.length === 1) {
|
||||||
|
statusMessage = (
|
||||||
|
<>
|
||||||
|
<PeerNameDisplay {...peerNameDisplayProps}>
|
||||||
|
{typingPeers[0].userId}
|
||||||
|
</PeerNameDisplay>{' '}
|
||||||
|
is typing...
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
} else if (typingPeers.length === 2) {
|
||||||
|
statusMessage = (
|
||||||
|
<>
|
||||||
|
<PeerNameDisplay {...peerNameDisplayProps}>
|
||||||
|
{typingPeers[0].userId}
|
||||||
|
</PeerNameDisplay>{' '}
|
||||||
|
and{' '}
|
||||||
|
<PeerNameDisplay {...peerNameDisplayProps}>
|
||||||
|
{typingPeers[1].userId}
|
||||||
|
</PeerNameDisplay>{' '}
|
||||||
|
are typing...
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
} else if (typingPeers.length > 2) {
|
||||||
|
statusMessage = <>Several people are typing...</>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Box>
|
||||||
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
sx={theme => ({
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
display: 'block',
|
||||||
|
fontWeight: theme.typography.fontWeightBold,
|
||||||
|
height: '1.75rem',
|
||||||
|
maxHeight: '1.75rem',
|
||||||
|
overflow: 'hidden',
|
||||||
|
px: 2,
|
||||||
|
py: 0.5,
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{statusMessage}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
|
}
|
@ -2,6 +2,7 @@ import { useContext, useEffect, useMemo, useState } from 'react'
|
|||||||
import { BaseRoomConfig } from 'trystero'
|
import { BaseRoomConfig } from 'trystero'
|
||||||
import { TorrentRoomConfig } from 'trystero/torrent'
|
import { TorrentRoomConfig } from 'trystero/torrent'
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
|
import { useDebounce } from '@react-hook/debounce'
|
||||||
|
|
||||||
import { ShellContext } from 'contexts/ShellContext'
|
import { ShellContext } from 'contexts/ShellContext'
|
||||||
import { SettingsContext } from 'contexts/SettingsContext'
|
import { SettingsContext } from 'contexts/SettingsContext'
|
||||||
@ -19,6 +20,7 @@ import {
|
|||||||
isMessageReceived,
|
isMessageReceived,
|
||||||
isInlineMedia,
|
isInlineMedia,
|
||||||
FileOfferMetadata,
|
FileOfferMetadata,
|
||||||
|
TypingStatus,
|
||||||
} from 'models/chat'
|
} from 'models/chat'
|
||||||
import { getPeerName, usePeerNameDisplay } from 'components/PeerNameDisplay'
|
import { getPeerName, usePeerNameDisplay } from 'components/PeerNameDisplay'
|
||||||
import { NotificationService } from 'services/Notification'
|
import { NotificationService } from 'services/Notification'
|
||||||
@ -60,6 +62,7 @@ export function useRoom(
|
|||||||
setRoomId,
|
setRoomId,
|
||||||
setPassword,
|
setPassword,
|
||||||
customUsername,
|
customUsername,
|
||||||
|
updatePeer,
|
||||||
} = useContext(ShellContext)
|
} = useContext(ShellContext)
|
||||||
|
|
||||||
const settingsContext = useContext(SettingsContext)
|
const settingsContext = useContext(SettingsContext)
|
||||||
@ -151,12 +154,26 @@ export function useRoom(
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const [sendTypingStatusChange, receiveTypingStatusChange] =
|
||||||
|
usePeerRoomAction<TypingStatus>(peerRoom, PeerActions.TYPING_STATUS_CHANGE)
|
||||||
|
|
||||||
|
const [isTyping, setIsTypingDebounced, setIsTyping] = useDebounce(
|
||||||
|
false,
|
||||||
|
2000,
|
||||||
|
true
|
||||||
|
)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
sendTypingStatusChange({ isTyping })
|
||||||
|
}, [isTyping, sendTypingStatusChange])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
|
sendTypingStatusChange({ isTyping: false })
|
||||||
peerRoom.leaveRoom()
|
peerRoom.leaveRoom()
|
||||||
setPeerList([])
|
setPeerList([])
|
||||||
}
|
}
|
||||||
}, [peerRoom, setPeerList])
|
}, [peerRoom, setPeerList, sendTypingStatusChange])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPassword(password)
|
setPassword(password)
|
||||||
@ -201,6 +218,7 @@ export function useRoom(
|
|||||||
id: getUuid(),
|
id: getUuid(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setIsTyping(false)
|
||||||
setIsMessageSending(true)
|
setIsMessageSending(true)
|
||||||
setMessageLog([...messageLog, unsentMessage])
|
setMessageLog([...messageLog, unsentMessage])
|
||||||
await sendPeerMessage(unsentMessage)
|
await sendPeerMessage(unsentMessage)
|
||||||
@ -226,6 +244,7 @@ export function useRoom(
|
|||||||
videoState: VideoState.STOPPED,
|
videoState: VideoState.STOPPED,
|
||||||
screenShareState: ScreenShareState.NOT_SHARING,
|
screenShareState: ScreenShareState.NOT_SHARING,
|
||||||
offeredFileId: null,
|
offeredFileId: null,
|
||||||
|
isTyping: false,
|
||||||
},
|
},
|
||||||
])
|
])
|
||||||
} else {
|
} else {
|
||||||
@ -250,7 +269,7 @@ export function useRoom(
|
|||||||
setMessageLog(transcript)
|
setMessageLog(transcript)
|
||||||
})
|
})
|
||||||
|
|
||||||
receivePeerMessage(message => {
|
receivePeerMessage((message, peerId) => {
|
||||||
const userSettings = settingsContext.getUserSettings()
|
const userSettings = settingsContext.getUserSettings()
|
||||||
|
|
||||||
if (!isShowingMessages) {
|
if (!isShowingMessages) {
|
||||||
@ -272,6 +291,7 @@ export function useRoom(
|
|||||||
}
|
}
|
||||||
|
|
||||||
setMessageLog([...messageLog, { ...message, timeReceived: Date.now() }])
|
setMessageLog([...messageLog, { ...message, timeReceived: Date.now() }])
|
||||||
|
updatePeer(peerId, { isTyping: false })
|
||||||
})
|
})
|
||||||
|
|
||||||
peerRoom.onPeerJoin(PeerHookType.NEW_PEER, (peerId: string) => {
|
peerRoom.onPeerJoin(PeerHookType.NEW_PEER, (peerId: string) => {
|
||||||
@ -299,18 +319,20 @@ export function useRoom(
|
|||||||
|
|
||||||
peerRoom.onPeerLeave(PeerHookType.NEW_PEER, (peerId: string) => {
|
peerRoom.onPeerLeave(PeerHookType.NEW_PEER, (peerId: string) => {
|
||||||
const peerIndex = peerList.findIndex(peer => peer.peerId === peerId)
|
const peerIndex = peerList.findIndex(peer => peer.peerId === peerId)
|
||||||
const peerExist = peerIndex !== -1
|
const doesPeerExist = peerIndex !== -1
|
||||||
|
|
||||||
showAlert(
|
showAlert(
|
||||||
`${
|
`${
|
||||||
peerExist ? getDisplayUsername(peerList[peerIndex].userId) : 'Someone'
|
doesPeerExist
|
||||||
|
? getDisplayUsername(peerList[peerIndex].userId)
|
||||||
|
: 'Someone'
|
||||||
} has left the room`,
|
} has left the room`,
|
||||||
{
|
{
|
||||||
severity: 'warning',
|
severity: 'warning',
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (peerExist) {
|
if (doesPeerExist) {
|
||||||
const peerListClone = [...peerList]
|
const peerListClone = [...peerList]
|
||||||
peerListClone.splice(peerIndex, 1)
|
peerListClone.splice(peerIndex, 1)
|
||||||
setPeerList(peerListClone)
|
setPeerList(peerListClone)
|
||||||
@ -347,6 +369,18 @@ export function useRoom(
|
|||||||
setIsMessageSending(false)
|
setIsMessageSending(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleMessageChange = () => {
|
||||||
|
if (isTyping) {
|
||||||
|
setIsTypingDebounced(true)
|
||||||
|
} else {
|
||||||
|
setIsTyping(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This queues up the expiration of the typing state. It is effectively
|
||||||
|
// cancelled once this message change handler is called again.
|
||||||
|
setIsTypingDebounced(false)
|
||||||
|
}
|
||||||
|
|
||||||
receivePeerInlineMedia(inlineMedia => {
|
receivePeerInlineMedia(inlineMedia => {
|
||||||
const userSettings = settingsContext.getUserSettings()
|
const userSettings = settingsContext.getUserSettings()
|
||||||
|
|
||||||
@ -365,6 +399,11 @@ export function useRoom(
|
|||||||
setMessageLog([...messageLog, { ...inlineMedia, timeReceived: Date.now() }])
|
setMessageLog([...messageLog, { ...inlineMedia, timeReceived: Date.now() }])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
receiveTypingStatusChange((typingStatus, peerId) => {
|
||||||
|
const { isTyping } = typingStatus
|
||||||
|
updatePeer(peerId, { isTyping })
|
||||||
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
sendPeerMetadata({ customUsername, userId })
|
sendPeerMetadata({ customUsername, userId })
|
||||||
}, [customUsername, userId, sendPeerMetadata])
|
}, [customUsername, userId, sendPeerMetadata])
|
||||||
@ -378,6 +417,7 @@ export function useRoom(
|
|||||||
return {
|
return {
|
||||||
isPrivate,
|
isPrivate,
|
||||||
handleInlineMediaUpload,
|
handleInlineMediaUpload,
|
||||||
|
handleMessageChange,
|
||||||
isMessageSending,
|
isMessageSending,
|
||||||
messageLog,
|
messageLog,
|
||||||
peerRoom,
|
peerRoom,
|
||||||
|
@ -88,9 +88,8 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
|
|||||||
const [peerAudios, setPeerAudios] = useState<
|
const [peerAudios, setPeerAudios] = useState<
|
||||||
Record<string, HTMLAudioElement>
|
Record<string, HTMLAudioElement>
|
||||||
>({})
|
>({})
|
||||||
const showAlert = useCallback<
|
|
||||||
(message: string, options?: AlertOptions) => void
|
const showAlert = useCallback((message: string, options?: AlertOptions) => {
|
||||||
>((message, options) => {
|
|
||||||
setAlertText(message)
|
setAlertText(message)
|
||||||
setAlertSeverity(options?.severity ?? 'info')
|
setAlertSeverity(options?.severity ?? 'info')
|
||||||
setIsAlertShowing(true)
|
setIsAlertShowing(true)
|
||||||
@ -98,6 +97,21 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
|
|||||||
|
|
||||||
const { connectionTestResults } = useConnectionTest()
|
const { connectionTestResults } = useConnectionTest()
|
||||||
|
|
||||||
|
const updatePeer = useCallback(
|
||||||
|
(peerId: string, updatedProperties: Partial<Peer>) => {
|
||||||
|
const peerIndex = peerList.findIndex(peer => peer.peerId === peerId)
|
||||||
|
const doesPeerExist = peerIndex !== -1
|
||||||
|
|
||||||
|
if (!doesPeerExist) return
|
||||||
|
|
||||||
|
const peerListClone = [...peerList]
|
||||||
|
const peer = peerList[peerIndex]
|
||||||
|
peerListClone[peerIndex] = { ...peer, ...updatedProperties }
|
||||||
|
setPeerList(peerListClone)
|
||||||
|
},
|
||||||
|
[peerList]
|
||||||
|
)
|
||||||
|
|
||||||
const shellContextValue = useMemo(
|
const shellContextValue = useMemo(
|
||||||
() => ({
|
() => ({
|
||||||
tabHasFocus,
|
tabHasFocus,
|
||||||
@ -129,6 +143,7 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
|
|||||||
customUsername,
|
customUsername,
|
||||||
setCustomUsername,
|
setCustomUsername,
|
||||||
connectionTestResults,
|
connectionTestResults,
|
||||||
|
updatePeer,
|
||||||
}),
|
}),
|
||||||
[
|
[
|
||||||
isPeerListOpen,
|
isPeerListOpen,
|
||||||
@ -157,6 +172,7 @@ export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
|
|||||||
customUsername,
|
customUsername,
|
||||||
setCustomUsername,
|
setCustomUsername,
|
||||||
connectionTestResults,
|
connectionTestResults,
|
||||||
|
updatePeer,
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@ interface ShellContextProps {
|
|||||||
customUsername: string
|
customUsername: string
|
||||||
setCustomUsername: Dispatch<SetStateAction<string>>
|
setCustomUsername: Dispatch<SetStateAction<string>>
|
||||||
connectionTestResults: ConnectionTestResults
|
connectionTestResults: ConnectionTestResults
|
||||||
|
updatePeer: (peerId: string, updatedProperties: Partial<Peer>) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ShellContext = createContext<ShellContextProps>({
|
export const ShellContext = createContext<ShellContextProps>({
|
||||||
@ -72,4 +73,5 @@ export const ShellContext = createContext<ShellContextProps>({
|
|||||||
hasRelay: false,
|
hasRelay: false,
|
||||||
trackerConnection: TrackerConnection.SEARCHING,
|
trackerConnection: TrackerConnection.SEARCHING,
|
||||||
},
|
},
|
||||||
|
updatePeer: () => {},
|
||||||
})
|
})
|
||||||
|
@ -49,6 +49,7 @@ export interface Peer {
|
|||||||
videoState: VideoState
|
videoState: VideoState
|
||||||
screenShareState: ScreenShareState
|
screenShareState: ScreenShareState
|
||||||
offeredFileId: string | null
|
offeredFileId: string | null
|
||||||
|
isTyping: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
export const isMessageReceived = (
|
export const isMessageReceived = (
|
||||||
@ -65,3 +66,7 @@ export interface FileOfferMetadata {
|
|||||||
magnetURI: string
|
magnetURI: string
|
||||||
isAllInlineMedia: boolean
|
isAllInlineMedia: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TypingStatus {
|
||||||
|
isTyping: boolean
|
||||||
|
}
|
||||||
|
@ -8,4 +8,5 @@ export enum PeerActions {
|
|||||||
VIDEO_CHANGE = 'VIDEO_CHANGE',
|
VIDEO_CHANGE = 'VIDEO_CHANGE',
|
||||||
SCREEN_SHARE = 'SCREEN_SHARE',
|
SCREEN_SHARE = 'SCREEN_SHARE',
|
||||||
FILE_OFFER = 'FILE_OFFER',
|
FILE_OFFER = 'FILE_OFFER',
|
||||||
|
TYPING_STATUS_CHANGE = 'TYPNG_CHANGE',
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user