236 lines
6.3 KiB
TypeScript

import {
PropsWithChildren,
SyntheticEvent,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react'
import CssBaseline from '@mui/material/CssBaseline'
import { ThemeProvider, createTheme } from '@mui/material/styles'
import Box from '@mui/material/Box'
import { AlertColor } from '@mui/material/Alert'
import { ShellContext } from 'contexts/ShellContext'
import { SettingsContext } from 'contexts/SettingsContext'
import { AlertOptions } from 'models/shell'
import { AudioState, VideoState, Peer } from 'models/chat'
import { ErrorBoundary } from 'components/ErrorBoundary'
import { Drawer } from './Drawer'
import { UpgradeDialog } from './UpgradeDialog'
import { ShellAppBar } from './ShellAppBar'
import { NotificationArea } from './NotificationArea'
import { RouteContent } from './RouteContent'
import { PeerList } from './PeerList'
import { QRCodeDialog } from './QRCodeDialog'
export interface ShellProps extends PropsWithChildren {
userPeerId: string
appNeedsUpdate: boolean
}
export const Shell = ({ appNeedsUpdate, children, userPeerId }: ShellProps) => {
const settingsContext = useContext(SettingsContext)
const [isAlertShowing, setIsAlertShowing] = useState(false)
const [isDrawerOpen, setIsDrawerOpen] = useState(false)
const [isQRCodeDialogOpen, setIsQRCodeDialogOpen] = useState(false)
const [doShowPeers, setDoShowPeers] = useState(false)
const [alertSeverity, setAlertSeverity] = useState<AlertColor>('info')
const [title, setTitle] = useState('')
const [alertText, setAlertText] = useState('')
const [numberOfPeers, setNumberOfPeers] = useState(1)
const [isPeerListOpen, setIsPeerListOpen] = useState(false)
const [peerList, setPeerList] = useState<Peer[]>([]) // except you
const [tabHasFocus, setTabHasFocus] = useState(true)
const [audioState, setAudioState] = useState<AudioState>(AudioState.STOPPED)
const [videoState, setVideoState] = useState<VideoState>(VideoState.STOPPED)
const showAlert = useCallback<
(message: string, options?: AlertOptions) => void
>((message, options) => {
setAlertText(message)
setAlertSeverity(options?.severity ?? 'info')
setIsAlertShowing(true)
}, [])
const shellContextValue = useMemo(
() => ({
numberOfPeers,
tabHasFocus,
setDoShowPeers,
setNumberOfPeers,
setTitle,
showAlert,
isPeerListOpen,
setIsQRCodeDialogOpen,
setIsPeerListOpen,
peerList,
setPeerList,
audioState,
setAudioState,
videoState,
setVideoState,
}),
[
isPeerListOpen,
setIsQRCodeDialogOpen,
numberOfPeers,
peerList,
tabHasFocus,
setDoShowPeers,
setNumberOfPeers,
setTitle,
showAlert,
audioState,
setAudioState,
videoState,
setVideoState,
]
)
const colorMode = settingsContext.getUserSettings().colorMode
const theme = useMemo(
() =>
createTheme({
palette: {
mode: colorMode,
},
}),
[colorMode]
)
const handleAlertClose = (
_event?: SyntheticEvent | Event,
reason?: string
) => {
if (reason === 'clickaway') {
return
}
setIsAlertShowing(false)
}
useEffect(() => {
document.title = title
}, [title])
useEffect(() => {
const handleFocus = () => {
setTabHasFocus(true)
}
const handleBlur = () => {
setTabHasFocus(false)
}
window.addEventListener('focus', handleFocus)
window.addEventListener('blur', handleBlur)
return () => {
window.removeEventListener('focus', handleFocus)
window.removeEventListener('blur', handleBlur)
}
}, [])
const handleDrawerOpen = () => {
setIsDrawerOpen(true)
}
const handlePeerListClick = () => {
setIsPeerListOpen(!isPeerListOpen)
}
const handleLinkButtonClick = async () => {
await navigator.clipboard.writeText(window.location.href)
shellContextValue.showAlert('Current URL copied to clipboard', {
severity: 'success',
})
}
const handleDrawerClose = () => {
setIsDrawerOpen(false)
}
const handleHomeLinkClick = () => {
setIsDrawerOpen(false)
}
const handleAboutLinkClick = () => {
setIsDrawerOpen(false)
}
const handleDisclaimerLinkClick = () => {
setIsDrawerOpen(false)
}
const handleSettingsLinkClick = () => {
setIsDrawerOpen(false)
}
const handleQRCodeDialogClose = () => {
setIsQRCodeDialogOpen(false)
}
return (
<ShellContext.Provider value={shellContextValue}>
<ThemeProvider theme={theme}>
<CssBaseline />
<UpgradeDialog appNeedsUpdate={appNeedsUpdate} />
<Box
className="Chitchatter"
sx={{
height: '100vh',
display: 'flex',
}}
>
<NotificationArea
alertSeverity={alertSeverity}
alertText={alertText}
isAlertShowing={isAlertShowing}
onAlertClose={handleAlertClose}
/>
<ShellAppBar
doShowPeers={doShowPeers}
onDrawerOpen={handleDrawerOpen}
onLinkButtonClick={handleLinkButtonClick}
isDrawerOpen={isDrawerOpen}
isPeerListOpen={isPeerListOpen}
numberOfPeers={numberOfPeers}
title={title}
onPeerListClick={handlePeerListClick}
setIsQRCodeDialogOpen={setIsQRCodeDialogOpen}
/>
<Drawer
isDrawerOpen={isDrawerOpen}
onAboutLinkClick={handleAboutLinkClick}
onDisclaimerClick={handleDisclaimerLinkClick}
onDrawerClose={handleDrawerClose}
onHomeLinkClick={handleHomeLinkClick}
onSettingsLinkClick={handleSettingsLinkClick}
theme={theme}
userPeerId={userPeerId}
/>
<RouteContent
isDrawerOpen={isDrawerOpen}
isPeerListOpen={isPeerListOpen}
>
<ErrorBoundary>{children}</ErrorBoundary>
</RouteContent>
<PeerList
userId={userPeerId}
isPeerListOpen={isPeerListOpen}
onPeerListClose={handlePeerListClick}
peerList={peerList}
audioState={audioState}
/>
<QRCodeDialog
isOpen={isQRCodeDialogOpen}
handleClose={handleQRCodeDialogClose}
/>
</Box>
</ThemeProvider>
</ShellContext.Provider>
)
}