feat: show alerts when someone joins or leaves the room
This commit is contained in:
parent
70ac4f34da
commit
6bc8bf8b88
@ -1,4 +1,11 @@
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import {
|
||||
forwardRef,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
SyntheticEvent,
|
||||
} from 'react'
|
||||
import { Routes, Route } from 'react-router-dom'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import localforage from 'localforage'
|
||||
@ -8,18 +15,28 @@ import Box from '@mui/material/Box'
|
||||
import Typography from '@mui/material/Typography'
|
||||
import StepIcon from '@mui/material/StepIcon'
|
||||
import Tooltip from '@mui/material/Tooltip'
|
||||
import Snackbar from '@mui/material/Snackbar'
|
||||
import MuiAlert, { AlertProps, AlertColor } from '@mui/material/Alert'
|
||||
|
||||
import { ShellContext } from 'ShellContext'
|
||||
import { Home } from 'pages/Home/'
|
||||
import { PublicRoom } from 'pages/PublicRoom/'
|
||||
import { UserSettings } from 'models/settings'
|
||||
import { PersistedStorageKeys } from 'models/storage'
|
||||
import { AlertOptions } from 'models/shell'
|
||||
|
||||
export interface BootstrapProps {
|
||||
persistedStorage?: typeof localforage
|
||||
getUuid?: typeof uuid
|
||||
}
|
||||
|
||||
const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(
|
||||
props,
|
||||
ref
|
||||
) {
|
||||
return <MuiAlert elevation={6} ref={ref} variant="filled" {...props} />
|
||||
})
|
||||
|
||||
function Bootstrap({
|
||||
persistedStorage = localforage.createInstance({
|
||||
name: 'chitchatter',
|
||||
@ -28,16 +45,38 @@ function Bootstrap({
|
||||
getUuid = uuid,
|
||||
}: BootstrapProps) {
|
||||
const [hasLoadedSettings, setHasLoadedSettings] = useState(false)
|
||||
const [isAlertShowing, setIsAlertShowing] = useState(false)
|
||||
const [alertSeverity, setAlertSeverity] = useState<AlertColor>('info')
|
||||
const [settings, setSettings] = useState({ userId: getUuid() })
|
||||
const { userId } = settings
|
||||
const [title, setTitle] = useState('')
|
||||
const [alertText, setAlertText] = useState('')
|
||||
const [numberOfPeers, setNumberOfPeers] = useState(1)
|
||||
|
||||
const showAlert = useCallback<
|
||||
(message: string, options?: AlertOptions) => void
|
||||
>((message, options) => {
|
||||
setAlertText(message)
|
||||
setAlertSeverity(options?.severity ?? 'info')
|
||||
setIsAlertShowing(true)
|
||||
}, [])
|
||||
|
||||
const shellContextValue = useMemo(
|
||||
() => ({ numberOfPeers, setNumberOfPeers, setTitle }),
|
||||
[numberOfPeers, setNumberOfPeers, setTitle]
|
||||
() => ({ numberOfPeers, setNumberOfPeers, setTitle, showAlert }),
|
||||
[numberOfPeers, setNumberOfPeers, setTitle, showAlert]
|
||||
)
|
||||
|
||||
const handleAlertClose = (
|
||||
_event?: SyntheticEvent | Event,
|
||||
reason?: string
|
||||
) => {
|
||||
if (reason === 'clickaway') {
|
||||
return
|
||||
}
|
||||
|
||||
setIsAlertShowing(false)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
;(async () => {
|
||||
if (hasLoadedSettings) return
|
||||
@ -71,6 +110,20 @@ function Bootstrap({
|
||||
paddingTop: 7,
|
||||
}}
|
||||
>
|
||||
<Snackbar
|
||||
open={isAlertShowing}
|
||||
autoHideDuration={6000}
|
||||
onClose={handleAlertClose}
|
||||
anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
|
||||
>
|
||||
<Alert
|
||||
onClose={handleAlertClose}
|
||||
severity={alertSeverity}
|
||||
variant="standard"
|
||||
>
|
||||
{alertText}
|
||||
</Alert>
|
||||
</Snackbar>
|
||||
<AppBar position="fixed">
|
||||
<Toolbar
|
||||
variant="regular"
|
||||
|
@ -1,13 +1,17 @@
|
||||
import { createContext, Dispatch, SetStateAction } from 'react'
|
||||
|
||||
import { AlertOptions } from 'models/shell'
|
||||
|
||||
interface ShellContextProps {
|
||||
setTitle: Dispatch<SetStateAction<string>>
|
||||
setNumberOfPeers: Dispatch<SetStateAction<number>>
|
||||
numberOfPeers: number
|
||||
setNumberOfPeers: Dispatch<SetStateAction<number>>
|
||||
setTitle: Dispatch<SetStateAction<string>>
|
||||
showAlert: (message: string, options?: AlertOptions) => void
|
||||
}
|
||||
|
||||
export const ShellContext = createContext<ShellContextProps>({
|
||||
setTitle: () => {},
|
||||
setNumberOfPeers: () => {},
|
||||
numberOfPeers: 1,
|
||||
setNumberOfPeers: () => {},
|
||||
setTitle: () => {},
|
||||
showAlert: () => {},
|
||||
})
|
||||
|
@ -26,6 +26,7 @@ export function Room({
|
||||
roomId,
|
||||
userId,
|
||||
}: RoomProps) {
|
||||
const [numberOfPeers, setNumberOfPeers] = useState(1) // Includes this peer
|
||||
const shellContext = useContext(ShellContext)
|
||||
const [isMessageSending, setIsMessageSending] = useState(false)
|
||||
const [textMessage, setTextMessage] = useState('')
|
||||
@ -66,10 +67,26 @@ export function Room({
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
peerRoom.onPeersChange((numberOfPeers: number) => {
|
||||
shellContext.setNumberOfPeers(numberOfPeers)
|
||||
peerRoom.onPeerJoin(() => {
|
||||
shellContext.showAlert(`Someone has has joined the room`, {
|
||||
severity: 'success',
|
||||
})
|
||||
}, [peerRoom, shellContext])
|
||||
|
||||
const newNumberOfPeers = numberOfPeers + 1
|
||||
setNumberOfPeers(newNumberOfPeers)
|
||||
shellContext.setNumberOfPeers(newNumberOfPeers)
|
||||
})
|
||||
|
||||
peerRoom.onPeerLeave(() => {
|
||||
shellContext.showAlert(`Someone has has left the room`, {
|
||||
severity: 'warning',
|
||||
})
|
||||
|
||||
const newNumberOfPeers = numberOfPeers - 1
|
||||
setNumberOfPeers(newNumberOfPeers)
|
||||
shellContext.setNumberOfPeers(newNumberOfPeers)
|
||||
})
|
||||
}, [numberOfPeers, peerRoom, shellContext])
|
||||
|
||||
const [sendMessage, receiveMessage] = usePeerRoomAction<UnsentMessage>(
|
||||
peerRoom,
|
||||
|
3
src/models/shell.ts
Normal file
3
src/models/shell.ts
Normal file
@ -0,0 +1,3 @@
|
||||
import { AlertProps } from '@mui/material/Alert'
|
||||
|
||||
export type AlertOptions = Pick<AlertProps, 'severity'>
|
@ -5,26 +5,9 @@ export class PeerRoom {
|
||||
|
||||
private roomConfig: RoomConfig
|
||||
|
||||
private numberOfPeers: number
|
||||
|
||||
constructor(config: RoomConfig, roomId: string) {
|
||||
this.roomConfig = config
|
||||
this.room = joinRoom(this.roomConfig, roomId)
|
||||
this.numberOfPeers = 1 // Includes this peer
|
||||
}
|
||||
|
||||
onPeersChange = (handlePeersChange: (numberOfPeers: number) => void) => {
|
||||
if (!this.room) return
|
||||
|
||||
this.room.onPeerJoin(() => {
|
||||
this.numberOfPeers++
|
||||
handlePeersChange(this.numberOfPeers)
|
||||
})
|
||||
|
||||
this.room.onPeerLeave(() => {
|
||||
this.numberOfPeers--
|
||||
handlePeersChange(this.numberOfPeers)
|
||||
})
|
||||
}
|
||||
|
||||
leaveRoom = () => {
|
||||
@ -32,6 +15,16 @@ export class PeerRoom {
|
||||
this.room.leave()
|
||||
}
|
||||
|
||||
onPeerJoin: Room['onPeerJoin'] = fn => {
|
||||
if (!this.room) return
|
||||
this.room.onPeerJoin((...args) => fn(...args))
|
||||
}
|
||||
|
||||
onPeerLeave: Room['onPeerLeave'] = fn => {
|
||||
if (!this.room) return
|
||||
this.room.onPeerLeave((...args) => fn(...args))
|
||||
}
|
||||
|
||||
makeAction = <T>(namespace: string) => {
|
||||
return this.room.makeAction<T>(namespace)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user