-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add update message capability in the SDK, react hooks, and demo app #378
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import { useEffect, useRef, useState } from 'react'; | ||
import { useCallback, useEffect, useRef, useState } from 'react'; | ||
import { MessageComponent } from '../../components/MessageComponent'; | ||
import { MessageInput } from '../../components/MessageInput'; | ||
import { useChatClient, useChatConnection, useMessages, useRoomReactions, useTyping } from '@ably/chat/react'; | ||
|
@@ -30,25 +30,78 @@ export const Chat = (props: { roomId: string; setRoomId: (roomId: string) => voi | |
} | ||
}; | ||
|
||
const handleUpdatedMessage = (message: Message) => { | ||
setMessages((prevMessages) => { | ||
const index = prevMessages.findIndex((m) => m.serial === message.serial); | ||
if (index === -1) { | ||
return prevMessages; | ||
} | ||
|
||
// skip update if the received action is not newer | ||
if (!prevMessages[index].actionBefore(message)) { | ||
return prevMessages; | ||
} | ||
|
||
const updatedArray = [...prevMessages]; | ||
updatedArray[index] = message; | ||
return updatedArray; | ||
}); | ||
}; | ||
|
||
const { | ||
send: sendMessage, | ||
getPreviousMessages, | ||
deleteMessage, | ||
update, | ||
} = useMessages({ | ||
listener: (message: MessageEventPayload) => { | ||
switch (message.type) { | ||
case MessageEvents.Created: | ||
setMessages((prevMessage) => [...prevMessage, message.message]); | ||
case MessageEvents.Created: { | ||
setMessages((prevMessages) => { | ||
// if already exists do nothing | ||
const index = prevMessages.findIndex((m) => m.serial === message.message.serial); | ||
if (index !== -1) { | ||
return prevMessages; | ||
} | ||
|
||
// if the message is not in the list, add it | ||
const newArray = [...prevMessages, message.message]; | ||
|
||
// and put it at the right place | ||
for (let i = newArray.length - 1; i > 1; i--) { | ||
if (newArray[i].before(newArray[i - 1])) { | ||
const temp = newArray[i]; | ||
newArray[i] = newArray[i - 1]; | ||
newArray[i - 1] = temp; | ||
} | ||
} | ||
|
||
return newArray; | ||
}); | ||
break; | ||
case MessageEvents.Deleted: | ||
} | ||
case MessageEvents.Deleted: { | ||
setMessages((prevMessage) => { | ||
return prevMessage.filter((m) => { | ||
const updatedArray = prevMessage.filter((m) => { | ||
return m.serial !== message.message.serial; | ||
}); | ||
|
||
// don't change state if deleted message is not in the current list | ||
if (prevMessage.length === updatedArray.length) { | ||
return prevMessage; | ||
} | ||
|
||
return updatedArray; | ||
}); | ||
break; | ||
default: | ||
} | ||
case MessageEvents.Updated: { | ||
handleUpdatedMessage(message.message); | ||
break; | ||
} | ||
default: { | ||
console.error('Unknown message', message); | ||
} | ||
} | ||
}, | ||
onDiscontinuity: (discontinuity) => { | ||
|
@@ -153,6 +206,38 @@ export const Chat = (props: { roomId: string; setRoomId: (roomId: string) => voi | |
} | ||
}, [messages, loading]); | ||
|
||
const onUpdateMessage = useCallback( | ||
(message: Message) => { | ||
const newText = prompt('Enter new text'); | ||
if (!newText) { | ||
return; | ||
} | ||
update(message, { | ||
text: newText, | ||
metadata: message.metadata, | ||
headers: message.headers, | ||
}) | ||
.then((updatedMessage: Message) => { | ||
handleUpdatedMessage(updatedMessage); | ||
}) | ||
.catch((error: unknown) => { | ||
console.warn('failed to update message', error); | ||
}); | ||
}, | ||
[update], | ||
); | ||
|
||
const onDeleteMessage = useCallback( | ||
(message: Message) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, thanks for extracting the delete function! |
||
deleteMessage(message, { description: 'deleted by user' }).then((deletedMessage: Message) => { | ||
setMessages((prevMessages) => { | ||
return prevMessages.filter((m) => m.serial !== deletedMessage.serial); | ||
}); | ||
}); | ||
}, | ||
[deleteMessage], | ||
); | ||
|
||
return ( | ||
<div className="flex-1 p:2 sm:p-12 justify-between flex flex-col h-screen"> | ||
<ConnectionStatusComponent /> | ||
|
@@ -192,19 +277,11 @@ export const Chat = (props: { roomId: string; setRoomId: (roomId: string) => voi | |
> | ||
{messages.map((msg) => ( | ||
<MessageComponent | ||
id={msg.serial} | ||
key={msg.serial} | ||
self={msg.clientId === clientId} | ||
message={msg} | ||
onMessageDelete={(msg) => { | ||
deleteMessage(msg, { description: 'deleted by user' }).then((deletedMessage: Message) => { | ||
setMessages((prevMessages) => { | ||
return prevMessages.filter((m) => { | ||
return m.serial !== deletedMessage.serial; | ||
}); | ||
}); | ||
}); | ||
}} | ||
onMessageDelete={onDeleteMessage} | ||
onMessageUpdate={onUpdateMessage} | ||
></MessageComponent> | ||
))} | ||
<div ref={messagesEndRef} /> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,3 +55,11 @@ body { | |
.sent-at-time:hover > .short { | ||
display: none; | ||
} | ||
|
||
.chat-message .buttons { | ||
display: none; | ||
} | ||
Comment on lines
+59
to
+61
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Consider using opacity or visibility for smoother transitions. The current implementation using .chat-message .buttons {
- display: none;
+ visibility: hidden;
+ opacity: 0;
+ transition: opacity 0.2s ease-in-out;
}
.chat-message:hover .buttons {
- display: block;
+ visibility: visible;
+ opacity: 1;
} Additionally, consider adding touch device support since hover states don't work well on mobile: @media (hover: none) {
.chat-message .buttons {
visibility: visible;
opacity: 1;
}
} Also applies to: 63-66 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now we want the layout to shift There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vladvelici Understood, thank you for the clarification. (_/) ✏️ Learnings added
|
||
|
||
.chat-message:hover .buttons { | ||
display: block; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Optimize message ordering algorithm
The current bubble sort implementation for ordering messages is inefficient, especially for large message lists. Consider using array sort with a comparison function.
📝 Committable suggestion