diff --git a/src/Bootstrap.tsx b/src/Bootstrap.tsx index c95ed54..fe029d7 100644 --- a/src/Bootstrap.tsx +++ b/src/Bootstrap.tsx @@ -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(function Alert( + props, + ref +) { + return +}) + 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('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, }} > + + + {alertText} + + > - setNumberOfPeers: Dispatch> numberOfPeers: number + setNumberOfPeers: Dispatch> + setTitle: Dispatch> + showAlert: (message: string, options?: AlertOptions) => void } export const ShellContext = createContext({ - setTitle: () => {}, - setNumberOfPeers: () => {}, numberOfPeers: 1, + setNumberOfPeers: () => {}, + setTitle: () => {}, + showAlert: () => {}, }) diff --git a/src/components/Room/Room.tsx b/src/components/Room/Room.tsx index 730efad..99c798b 100644 --- a/src/components/Room/Room.tsx +++ b/src/components/Room/Room.tsx @@ -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( peerRoom, diff --git a/src/models/shell.ts b/src/models/shell.ts new file mode 100644 index 0000000..8468d39 --- /dev/null +++ b/src/models/shell.ts @@ -0,0 +1,3 @@ +import { AlertProps } from '@mui/material/Alert' + +export type AlertOptions = Pick diff --git a/src/services/PeerRoom/PeerRoom.ts b/src/services/PeerRoom/PeerRoom.ts index 4633ac0..ba800fb 100644 --- a/src/services/PeerRoom/PeerRoom.ts +++ b/src/services/PeerRoom/PeerRoom.ts @@ -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 = (namespace: string) => { return this.room.makeAction(namespace) }