Jeremy Kahn 6cbfaacf1a
feat(verification): [closes #209] Verified peers (#216)
* refactor(bootstrap): add BootstrapShim
* feat(security): [#209] generate public/private keys
* refactor(encryption): move encryption utils to a service
* feat(encryption): [wip] implement convertCryptoKeyToString
* fix(user-settings): serialize crypto keys to strings
* feat(user-settings): deserialize user settings from IndexedDB
* feat(user-settings): upgrade persisted settings on boot
* feat(user-settings): automatically migrate persisted user settings
* refactor(encryption): simplify CryptoKey stringification
* refactor(encryption): DRY up EncryptionService
* feat(verification): send public key to new peers
* refactor(encryption): use class instance
* refactor(serialization): use class instance
* refactor(verification): [wip] create usePeerVerification hook
* feat(verification): encrypt verification token
* feat(verification): send encrypted token to peer
* feat(verification): verify peer
* refactor(verification): use enum for verification state
* feat(verification): expire verification requests
* fix(updatePeer): update with fresh state data
* feat(verification): display verification state
* refactor(usePeerVerification): store verification timer in Peer
* feat(verification): present tooltips explaining verification state
* feat(ui): show full page loading indicator
* feat(init): present bootup failure reasons
* refactor(init): move init to its own file
* feat(verification): show errors upon verification failure
* refactor(verification): move workaround to usePeerVerification
* feat(verification): present peer public keys
* refactor(verification): move peer public key rendering to its own component
* refactor(verification): only pass publicKey into renderer
* feat(verification): show user's own public key
* refactor(naming): rename Username to UserInfo
* refactor(loading): encapsulate height styling
* feat(verification): improve user messaging
* refactor(style): improve formatting and variable names
* feat(verification): add user info tooltip
* docs(verification): explain verification
2023-12-09 17:47:05 -06:00

178 lines
5.2 KiB
TypeScript

import { useContext } from 'react'
import { useWindowSize } from '@react-hook/window-size'
import Zoom from '@mui/material/Zoom'
import Box from '@mui/material/Box'
import Divider from '@mui/material/Divider'
import useTheme from '@mui/material/styles/useTheme'
import { v4 as uuid } from 'uuid'
import { rtcConfig } from 'config/rtcConfig'
import { trackerUrls } from 'config/trackerUrls'
import { RoomContext } from 'contexts/RoomContext'
import { ShellContext } from 'contexts/ShellContext'
import { MessageForm } from 'components/MessageForm'
import { ChatTranscript } from 'components/ChatTranscript'
import { encryptionService as encryptionServiceInstance } from 'services/Encryption'
import { SettingsContext } from 'contexts/SettingsContext'
import { useRoom } from './useRoom'
import { RoomAudioControls } from './RoomAudioControls'
import { RoomVideoControls } from './RoomVideoControls'
import { RoomScreenShareControls } from './RoomScreenShareControls'
import { RoomFileUploadControls } from './RoomFileUploadControls'
import { RoomVideoDisplay } from './RoomVideoDisplay'
import { RoomShowMessagesControls } from './RoomShowMessagesControls'
import { TypingStatusBar } from './TypingStatusBar'
export interface RoomProps {
appId?: string
getUuid?: typeof uuid
password?: string
roomId: string
userId: string
encryptionService?: typeof encryptionServiceInstance
}
export function Room({
appId = `${encodeURI(window.location.origin)}_${process.env.REACT_APP_NAME}`,
getUuid = uuid,
encryptionService = encryptionServiceInstance,
roomId,
password,
userId,
}: RoomProps) {
const theme = useTheme()
const settingsContext = useContext(SettingsContext)
const { showActiveTypingStatus, publicKey } =
settingsContext.getUserSettings()
const {
isMessageSending,
handleInlineMediaUpload,
handleMessageChange,
messageLog,
peerRoom,
roomContextValue,
sendMessage,
showVideoDisplay,
} = useRoom(
{
appId,
trackerUrls,
rtcConfig,
password,
trackerRedundancy: 4,
},
{
roomId,
userId,
getUuid,
publicKey,
encryptionService,
}
)
const handleMessageSubmit = async (message: string) => {
await sendMessage(message)
}
const showMessages = roomContextValue.isShowingMessages
const { showRoomControls } = useContext(ShellContext)
const [windowWidth, windowHeight] = useWindowSize()
const landscape = windowWidth > windowHeight
return (
<RoomContext.Provider value={roomContextValue}>
<Box
className="Room"
sx={{
height: '100%',
display: 'flex',
flexGrow: '1',
overflow: 'auto',
}}
>
<Box
sx={{
display: 'flex',
flexDirection: 'column',
flexGrow: '1',
overflow: 'auto',
}}
>
<Zoom in={showRoomControls}>
<Box
sx={{
alignItems: 'flex-start',
display: 'flex',
justifyContent: 'center',
overflow: 'visible',
height: 0,
position: 'relative',
top: theme.spacing(1),
}}
>
<RoomAudioControls peerRoom={peerRoom} />
<RoomVideoControls peerRoom={peerRoom} />
<RoomScreenShareControls peerRoom={peerRoom} />
<RoomFileUploadControls
peerRoom={peerRoom}
onInlineMediaUpload={handleInlineMediaUpload}
/>
<Zoom in={showVideoDisplay} mountOnEnter unmountOnExit>
<span>
<RoomShowMessagesControls />
</span>
</Zoom>
</Box>
</Zoom>
<Box
sx={{
display: 'flex',
flexDirection: landscape ? 'row' : 'column',
height: '100%',
width: '100%',
overflow: 'auto',
}}
>
{showVideoDisplay && (
<RoomVideoDisplay
userId={userId}
width="100%"
height={landscape || !showMessages ? '100%' : '60%'}
/>
)}
{showMessages && (
<Box
sx={{
display: 'flex',
flexDirection: 'column',
flexGrow: '1',
width: showVideoDisplay && landscape ? '400px' : '100%',
height: landscape ? '100%' : '40%',
}}
>
<ChatTranscript
messageLog={messageLog}
userId={userId}
className="grow overflow-auto"
/>
<Divider />
<Box>
<MessageForm
onMessageSubmit={handleMessageSubmit}
isMessageSending={isMessageSending}
onMessageChange={handleMessageChange}
/>
{showActiveTypingStatus ? <TypingStatusBar /> : null}
</Box>
</Box>
)}
</Box>
</Box>
</Box>
</RoomContext.Provider>
)
}