Skip to content

Commit

Permalink
feat(closes #141): implement direct messages (#386)
Browse files Browse the repository at this point in the history
* feat(#141): put public key in accordion

* refactor(#141): reuse room ref

* refactor(variables): rename PeerActions to PeerAction

* feat(#141): render direct message room

* refactor(#141): enable multiple action receivers

* fix(#141): keep app in small viewports when peer list is expanded

* fix(#141): prevent scrolling on small viewports when navigation menu is open

* feat(#141): show peer dialog in full screen for small viewports
  • Loading branch information
jeremyckahn authored Dec 5, 2024
1 parent ded097f commit 0bf0bf3
Show file tree
Hide file tree
Showing 21 changed files with 771 additions and 411 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Open https://chitchatter.im/ and join a room to start chatting with anyone else
- Public and private rooms.
- Video and audio chatting.
- Screen sharing.
- Direct messaging.
- File sharing:
- Unlimited file size transfers.
- Files are encrypted prior to sending and decrypted by the receiver (the key is the room name).
Expand Down
37 changes: 22 additions & 15 deletions src/components/ChatTranscript/ChatTranscript.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { HTMLAttributes, useRef, useEffect, useState, useContext } from 'react'
import Box from '@mui/material/Box'
import { useRef, useEffect, useState, useContext } from 'react'
import Box, { BoxProps } from '@mui/material/Box'
import useTheme from '@mui/material/styles/useTheme'

import { Message as IMessage, InlineMedia } from 'models/chat'
import { Message } from 'components/Message'
import { ShellContext } from 'contexts/ShellContext'

export interface ChatTranscriptProps extends HTMLAttributes<HTMLDivElement> {
export interface ChatTranscriptProps extends BoxProps {
messageLog: Array<IMessage | InlineMedia>
userId: string
}

export const ChatTranscript = ({ messageLog, userId }: ChatTranscriptProps) => {
export const ChatTranscript = ({
messageLog,
userId,
sx,
}: ChatTranscriptProps) => {
const { showRoomControls } = useContext(ShellContext)
const theme = useTheme()
const boxRef = useRef<HTMLDivElement>(null)
Expand Down Expand Up @@ -62,17 +66,20 @@ export const ChatTranscript = ({ messageLog, userId }: ChatTranscriptProps) => {
<Box
ref={boxRef}
className="ChatTranscript"
sx={{
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'auto',
pb: transcriptMinPadding,
pt: showRoomControls ? theme.spacing(10) : theme.spacing(2),
px: `max(${transcriptPaddingX}, ${transcriptMinPadding})`,
transition: `padding-top ${theme.transitions.duration.short}ms ${theme.transitions.easing.easeInOut}`,
width: '100%',
}}
sx={[
{
display: 'flex',
flexDirection: 'column',
flexGrow: 1,
overflow: 'auto',
pb: transcriptMinPadding,
pt: showRoomControls ? theme.spacing(10) : theme.spacing(2),
px: `max(${transcriptPaddingX}, ${transcriptMinPadding})`,
transition: `padding-top ${theme.transitions.duration.short}ms ${theme.transitions.easing.easeInOut}`,
width: '100%',
},
...(Array.isArray(sx) ? sx : [sx]),
]}
>
{messageLog.map((message, idx) => {
const previousMessage = messageLog[idx - 1]
Expand Down
17 changes: 10 additions & 7 deletions src/components/Room/Room.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ describe('Room', () => {
expect(textInput).toHaveValue('')
})

test('message is sent to peer', async () => {
test('message is sent to peers', async () => {
render(
<RouteStub>
<RoomStub
Expand All @@ -136,11 +136,14 @@ describe('Room', () => {
await userEvent.type(textInput, 'hello')
await userEvent.click(sendButton)

expect(mockMessagedSender).toHaveBeenCalledWith({
authorId: mockUserId,
text: 'hello',
timeSent: mockNowTime,
id: 'abc123',
})
expect(mockMessagedSender).toHaveBeenCalledWith(
{
authorId: mockUserId,
text: 'hello',
timeSent: mockNowTime,
id: 'abc123',
},
null
)
})
})
70 changes: 42 additions & 28 deletions src/components/Room/Room.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export interface RoomProps {
userId: string
encryptionService?: typeof encryption
timeService?: typeof time
targetPeerId?: string
}

export function Room({
Expand All @@ -43,12 +44,14 @@ export function Room({
roomId,
password,
userId,
targetPeerId,
}: RoomProps) {
const theme = useTheme()
const settingsContext = useContext(SettingsContext)
const { showActiveTypingStatus, publicKey } =
settingsContext.getUserSettings()
const {
isDirectMessageRoom,
isMessageSending,
handleInlineMediaUpload,
handleMessageChange,
Expand All @@ -72,6 +75,7 @@ export function Room({
publicKey,
encryptionService,
timeService,
targetPeerId,
}
)

Expand Down Expand Up @@ -105,32 +109,34 @@ export function Room({
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>
{!isDirectMessageRoom && (
<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',
Expand All @@ -157,15 +163,23 @@ export function Room({
height: landscape ? '100%' : '40%',
}}
>
<ChatTranscript messageLog={messageLog} userId={userId} />
<ChatTranscript
messageLog={messageLog}
userId={userId}
sx={{ ...(isDirectMessageRoom && { pt: 1 }) }}
/>
<Divider />
<Box>
<MessageForm
onMessageSubmit={handleMessageSubmit}
isMessageSending={isMessageSending}
onMessageChange={handleMessageChange}
/>
{showActiveTypingStatus ? <TypingStatusBar /> : null}
{showActiveTypingStatus ? (
<TypingStatusBar
isDirectMessageRoom={isDirectMessageRoom}
/>
) : null}
</Box>
</Box>
)}
Expand Down
11 changes: 9 additions & 2 deletions src/components/Room/TypingStatusBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,16 @@ import {
PeerNameDisplayProps,
} from 'components/PeerNameDisplay/PeerNameDisplay'

export const TypingStatusBar = () => {
export const TypingStatusBar = ({
isDirectMessageRoom,
}: {
isDirectMessageRoom: boolean
}) => {
const { peerList } = useContext(ShellContext)
const typingPeers = peerList.filter(({ isTyping }) => isTyping)
const typingPeers = peerList.filter(
({ isTypingGroupMessage, isTypingDirectMessage }) =>
isDirectMessageRoom ? isTypingDirectMessage : isTypingGroupMessage
)

const peerNameDisplayProps: Partial<PeerNameDisplayProps> = {
variant: 'caption',
Expand Down
Loading

0 comments on commit 0bf0bf3

Please sign in to comment.