feat: show animation when message is successfully sent

This commit is contained in:
Jeremy Kahn 2022-08-22 21:57:45 -05:00
parent 3be1f2e88a
commit 0d28df82c2
6 changed files with 64 additions and 32 deletions

View File

@ -5,7 +5,7 @@ export const joinRoom: typeof trysteroJoinRoom = (
_roomId: string _roomId: string
) => { ) => {
const room: Room = { const room: Room = {
makeAction: () => [() => {}, () => {}, () => {}], makeAction: () => [() => Promise.resolve([]), () => {}, () => {}],
ping: () => Promise.resolve(0), ping: () => Promise.resolve(0),
leave: () => {}, leave: () => {},
getPeers: () => [], getPeers: () => [],

View File

@ -1,6 +1,6 @@
import Typography from '@mui/material/Typography' import Typography from '@mui/material/Typography'
import { UnsentMessage, ReceivedMessage } from 'models/chat' import { isMessageReceived, UnsentMessage, ReceivedMessage } from 'models/chat'
export interface ChatTranscriptProps { export interface ChatTranscriptProps {
messageLog: Array<UnsentMessage | ReceivedMessage> messageLog: Array<UnsentMessage | ReceivedMessage>
@ -10,24 +10,35 @@ export interface ChatTranscriptProps {
export const ChatTranscript = ({ messageLog, userId }: ChatTranscriptProps) => { export const ChatTranscript = ({ messageLog, userId }: ChatTranscriptProps) => {
return ( return (
<div className="ChatTranscript flex flex-col"> <div className="ChatTranscript flex flex-col">
{messageLog.map((message, idx) => ( {messageLog.map(message => {
<div className="block"> let backgroundColor: string
if (message.authorId === userId) {
backgroundColor = isMessageReceived(message)
? 'primary.dark'
: 'primary.main'
} else {
backgroundColor = 'grey.700'
}
return (
<div className="block" key={message.id}>
<Typography <Typography
key={`${idx}_${message}`}
variant="body1" variant="body1"
sx={{ sx={{
backgroundColor: backgroundColor,
message.authorId === userId ? 'primary.dark' : 'grey.700',
margin: 0.5, margin: 0.5,
padding: 1, padding: 1,
borderRadius: 4, borderRadius: 6,
float: message.authorId === userId ? 'right' : 'left', float: message.authorId === userId ? 'right' : 'left',
transition: 'background-color 1s',
}} }}
> >
{message.text} {message.text}
</Typography> </Typography>
</div> </div>
))} )
})}
</div> </div>
) )
} }

View File

@ -1,5 +1,5 @@
import { PropsWithChildren } from 'react' import { PropsWithChildren } from 'react'
import { render, screen } from '@testing-library/react' import { waitFor, render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event' import userEvent from '@testing-library/user-event'
import { MemoryRouter as Router, Route, Routes } from 'react-router-dom' import { MemoryRouter as Router, Route, Routes } from 'react-router-dom'
@ -8,7 +8,9 @@ import { Room } from './'
const stubUserId = 'user-id' const stubUserId = 'user-id'
const mockGetUuid = jest.fn() const mockGetUuid = jest.fn()
const mockMessagedSender = jest.fn() const mockMessagedSender = jest
.fn()
.mockImplementation(() => Promise.resolve([]))
jest.mock('trystero', () => ({ jest.mock('trystero', () => ({
joinRoom: () => ({ joinRoom: () => ({
@ -73,7 +75,7 @@ describe('Room', () => {
expect(sendButton).not.toBeDisabled() expect(sendButton).not.toBeDisabled()
}) })
test('sending a message clears the text input', () => { test('sending a message clears the text input', async () => {
render( render(
<RouteStub> <RouteStub>
<Room userId={stubUserId} /> <Room userId={stubUserId} />
@ -83,11 +85,15 @@ describe('Room', () => {
const sendButton = screen.getByText('Send') const sendButton = screen.getByText('Send')
const textInput = screen.getByPlaceholderText('Your message') const textInput = screen.getByPlaceholderText('Your message')
userEvent.type(textInput, 'hello') userEvent.type(textInput, 'hello')
await waitFor(() => {
userEvent.click(sendButton) userEvent.click(sendButton)
})
expect(textInput).toHaveValue('') expect(textInput).toHaveValue('')
}) })
test('message is sent to peer', () => { test('message is sent to peer', async () => {
render( render(
<RouteStub> <RouteStub>
<Room <Room
@ -100,7 +106,11 @@ describe('Room', () => {
const sendButton = screen.getByText('Send') const sendButton = screen.getByText('Send')
const textInput = screen.getByPlaceholderText('Your message') const textInput = screen.getByPlaceholderText('Your message')
userEvent.type(textInput, 'hello') userEvent.type(textInput, 'hello')
await waitFor(() => {
userEvent.click(sendButton) userEvent.click(sendButton)
})
expect(mockMessagedSender).toHaveBeenCalledWith({ expect(mockMessagedSender).toHaveBeenCalledWith({
authorId: stubUserId, authorId: stubUserId,
text: 'hello', text: 'hello',

View File

@ -24,6 +24,7 @@ export function Room({
}: RoomProps) { }: RoomProps) {
const { roomId = '' } = useParams() const { roomId = '' } = useParams()
const [isMessageSending, setIsMessageSending] = useState(false)
const [textMessage, setTextMessage] = useState('') const [textMessage, setTextMessage] = useState('')
const [messageLog, setMessageLog] = useState< const [messageLog, setMessageLog] = useState<
Array<ReceivedMessage | UnsentMessage> Array<ReceivedMessage | UnsentMessage>
@ -46,7 +47,7 @@ export function Room({
setTextMessage(value) setTextMessage(value)
} }
const handleMessageSubmit = ( const handleMessageSubmit = async (
event: React.SyntheticEvent<HTMLFormElement> event: React.SyntheticEvent<HTMLFormElement>
) => { ) => {
event.preventDefault() event.preventDefault()
@ -58,10 +59,16 @@ export function Room({
id: getUuid(), id: getUuid(),
} }
sendMessage(unsentMessage)
setTextMessage('') setTextMessage('')
setIsMessageSending(true)
setMessageLog([...messageLog, unsentMessage]) setMessageLog([...messageLog, unsentMessage])
await sendMessage(unsentMessage)
setMessageLog([
...messageLog,
{ ...unsentMessage, timeReceived: Date.now() },
])
setIsMessageSending(false)
} }
receiveMessage(message => { receiveMessage(message => {
@ -85,7 +92,7 @@ export function Room({
<Button <Button
variant="contained" variant="contained"
type="submit" type="submit"
disabled={textMessage.length === 0} disabled={textMessage.length === 0 || isMessageSending}
sx={{ sx={{
marginTop: 2, marginTop: 2,
}} }}

View File

@ -8,3 +8,7 @@ export interface UnsentMessage {
export interface ReceivedMessage extends UnsentMessage { export interface ReceivedMessage extends UnsentMessage {
timeReceived: number timeReceived: number
} }
export const isMessageReceived = (
message: UnsentMessage
): message is ReceivedMessage => 'timeReceived' in message

View File

@ -25,13 +25,13 @@ declare module 'trystero' {
export type RoomConfig = BaseRoomConfig & export type RoomConfig = BaseRoomConfig &
(BitTorrentRoomConfig | FirebaseRoomConfig | IpfsRoomConfig) (BitTorrentRoomConfig | FirebaseRoomConfig | IpfsRoomConfig)
export interface ActionSender<T> { export interface ActionSender<T> extends Promise {
( (
data: T, data: T,
targetPeers?: string[], targetPeers?: string[],
metadata?: Record, metadata?: Record,
progress?: (percent: number, peerId: string) => void progress?: (percent: number, peerId: string) => void
): void ): Promise<Array<undefined>>
} }
export interface ActionReceiver<T> { export interface ActionReceiver<T> {