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 { Routes, Route } from 'react-router-dom'
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
import localforage from 'localforage'
|
import localforage from 'localforage'
|
||||||
@ -8,18 +15,28 @@ import Box from '@mui/material/Box'
|
|||||||
import Typography from '@mui/material/Typography'
|
import Typography from '@mui/material/Typography'
|
||||||
import StepIcon from '@mui/material/StepIcon'
|
import StepIcon from '@mui/material/StepIcon'
|
||||||
import Tooltip from '@mui/material/Tooltip'
|
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 { ShellContext } from 'ShellContext'
|
||||||
import { Home } from 'pages/Home/'
|
import { Home } from 'pages/Home/'
|
||||||
import { PublicRoom } from 'pages/PublicRoom/'
|
import { PublicRoom } from 'pages/PublicRoom/'
|
||||||
import { UserSettings } from 'models/settings'
|
import { UserSettings } from 'models/settings'
|
||||||
import { PersistedStorageKeys } from 'models/storage'
|
import { PersistedStorageKeys } from 'models/storage'
|
||||||
|
import { AlertOptions } from 'models/shell'
|
||||||
|
|
||||||
export interface BootstrapProps {
|
export interface BootstrapProps {
|
||||||
persistedStorage?: typeof localforage
|
persistedStorage?: typeof localforage
|
||||||
getUuid?: typeof uuid
|
getUuid?: typeof uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Alert = forwardRef<HTMLDivElement, AlertProps>(function Alert(
|
||||||
|
props,
|
||||||
|
ref
|
||||||
|
) {
|
||||||
|
return <MuiAlert elevation={6} ref={ref} variant="filled" {...props} />
|
||||||
|
})
|
||||||
|
|
||||||
function Bootstrap({
|
function Bootstrap({
|
||||||
persistedStorage = localforage.createInstance({
|
persistedStorage = localforage.createInstance({
|
||||||
name: 'chitchatter',
|
name: 'chitchatter',
|
||||||
@ -28,16 +45,38 @@ function Bootstrap({
|
|||||||
getUuid = uuid,
|
getUuid = uuid,
|
||||||
}: BootstrapProps) {
|
}: BootstrapProps) {
|
||||||
const [hasLoadedSettings, setHasLoadedSettings] = useState(false)
|
const [hasLoadedSettings, setHasLoadedSettings] = useState(false)
|
||||||
|
const [isAlertShowing, setIsAlertShowing] = useState(false)
|
||||||
|
const [alertSeverity, setAlertSeverity] = useState<AlertColor>('info')
|
||||||
const [settings, setSettings] = useState({ userId: getUuid() })
|
const [settings, setSettings] = useState({ userId: getUuid() })
|
||||||
const { userId } = settings
|
const { userId } = settings
|
||||||
const [title, setTitle] = useState('')
|
const [title, setTitle] = useState('')
|
||||||
|
const [alertText, setAlertText] = useState('')
|
||||||
const [numberOfPeers, setNumberOfPeers] = useState(1)
|
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(
|
const shellContextValue = useMemo(
|
||||||
() => ({ numberOfPeers, setNumberOfPeers, setTitle }),
|
() => ({ numberOfPeers, setNumberOfPeers, setTitle, showAlert }),
|
||||||
[numberOfPeers, setNumberOfPeers, setTitle]
|
[numberOfPeers, setNumberOfPeers, setTitle, showAlert]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const handleAlertClose = (
|
||||||
|
_event?: SyntheticEvent | Event,
|
||||||
|
reason?: string
|
||||||
|
) => {
|
||||||
|
if (reason === 'clickaway') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsAlertShowing(false)
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
;(async () => {
|
;(async () => {
|
||||||
if (hasLoadedSettings) return
|
if (hasLoadedSettings) return
|
||||||
@ -71,6 +110,20 @@ function Bootstrap({
|
|||||||
paddingTop: 7,
|
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">
|
<AppBar position="fixed">
|
||||||
<Toolbar
|
<Toolbar
|
||||||
variant="regular"
|
variant="regular"
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
import { createContext, Dispatch, SetStateAction } from 'react'
|
import { createContext, Dispatch, SetStateAction } from 'react'
|
||||||
|
|
||||||
|
import { AlertOptions } from 'models/shell'
|
||||||
|
|
||||||
interface ShellContextProps {
|
interface ShellContextProps {
|
||||||
setTitle: Dispatch<SetStateAction<string>>
|
|
||||||
setNumberOfPeers: Dispatch<SetStateAction<number>>
|
|
||||||
numberOfPeers: number
|
numberOfPeers: number
|
||||||
|
setNumberOfPeers: Dispatch<SetStateAction<number>>
|
||||||
|
setTitle: Dispatch<SetStateAction<string>>
|
||||||
|
showAlert: (message: string, options?: AlertOptions) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ShellContext = createContext<ShellContextProps>({
|
export const ShellContext = createContext<ShellContextProps>({
|
||||||
setTitle: () => {},
|
|
||||||
setNumberOfPeers: () => {},
|
|
||||||
numberOfPeers: 1,
|
numberOfPeers: 1,
|
||||||
|
setNumberOfPeers: () => {},
|
||||||
|
setTitle: () => {},
|
||||||
|
showAlert: () => {},
|
||||||
})
|
})
|
||||||
|
@ -26,6 +26,7 @@ export function Room({
|
|||||||
roomId,
|
roomId,
|
||||||
userId,
|
userId,
|
||||||
}: RoomProps) {
|
}: RoomProps) {
|
||||||
|
const [numberOfPeers, setNumberOfPeers] = useState(1) // Includes this peer
|
||||||
const shellContext = useContext(ShellContext)
|
const shellContext = useContext(ShellContext)
|
||||||
const [isMessageSending, setIsMessageSending] = useState(false)
|
const [isMessageSending, setIsMessageSending] = useState(false)
|
||||||
const [textMessage, setTextMessage] = useState('')
|
const [textMessage, setTextMessage] = useState('')
|
||||||
@ -66,10 +67,26 @@ export function Room({
|
|||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
peerRoom.onPeersChange((numberOfPeers: number) => {
|
peerRoom.onPeerJoin(() => {
|
||||||
shellContext.setNumberOfPeers(numberOfPeers)
|
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>(
|
const [sendMessage, receiveMessage] = usePeerRoomAction<UnsentMessage>(
|
||||||
peerRoom,
|
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 roomConfig: RoomConfig
|
||||||
|
|
||||||
private numberOfPeers: number
|
|
||||||
|
|
||||||
constructor(config: RoomConfig, roomId: string) {
|
constructor(config: RoomConfig, roomId: string) {
|
||||||
this.roomConfig = config
|
this.roomConfig = config
|
||||||
this.room = joinRoom(this.roomConfig, roomId)
|
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 = () => {
|
leaveRoom = () => {
|
||||||
@ -32,6 +15,16 @@ export class PeerRoom {
|
|||||||
this.room.leave()
|
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) => {
|
makeAction = <T>(namespace: string) => {
|
||||||
return this.room.makeAction<T>(namespace)
|
return this.room.makeAction<T>(namespace)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user