feat: show alerts when someone joins or leaves the room

This commit is contained in:
Jeremy Kahn 2022-08-31 09:55:57 -05:00
parent 70ac4f34da
commit 6bc8bf8b88
5 changed files with 97 additions and 27 deletions

View File

@ -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"

View File

@ -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: () => {},
})

View File

@ -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',
})
const newNumberOfPeers = numberOfPeers + 1
setNumberOfPeers(newNumberOfPeers)
shellContext.setNumberOfPeers(newNumberOfPeers)
})
}, [peerRoom, shellContext])
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
View File

@ -0,0 +1,3 @@
import { AlertProps } from '@mui/material/Alert'
export type AlertOptions = Pick<AlertProps, 'severity'>

View File

@ -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)
}