IVS Chat Client Messaging SDK: JavaScript Tutorial Part 2: Messages and Events
This second (and last) part of the tutorial is broken up into several sections:
Note: In some cases, code examples for JavaScript and TypeScript are identical, so they are combined.
For full SDK documentation, start with
Amazon IVS Chat Client Messaging SDK (here in
the Amazon IVS Chat User Guide) and the Chat Client Messaging:
SDK for JavaScript Reference
Prerequisite
Be sure you have completed Part 1 of this tutorial, Chat Rooms.
Subscribe to Chat Message Events
The ChatRoom
instance uses events to communicate when events occur in a chat
room. To start implementing the chat experience, you need to show your users when others send
a message in the room to which they're connected.
Here, you subscribe to chat message events. Later, we’ll show you how to update a message list you create, which updates with every message/event.
In your App
, inside the useEffect
hook, subscribe to all message
events:
// App.tsx / App.jsx useEffect(() => { // ... const unsubscribeOnMessageReceived = room.addListener('message', (message) => {}); return () => { // ... unsubscribeOnMessageReceived(); }; }, []);
Show Received Messages
Receiving messages is a core part of the chat experience. Using the Chat JS SDK, you can set up your code to easily receive events from other users connected to a chat room.
Later, we’ll show you how to perform actions in a chat room that leverage the components you create here.
In your App
, define a state named messages
with a
ChatMessage
array type named messages
:
Next, in the message
listener function, append message
to the
messages
array:
// App.jsx / App.tsx // ... const unsubscribeOnMessageReceived = room.addListener('message', (message) => { setMessages((msgs) => [...msgs, message]); }); // ...
Below we step through the tasks to show received messages:
Creating a Message Component
The Message
component is responsible for rendering the contents of a
message received by your chat room. In this section, you create a messages component for
rendering individual chat messages in the App
.
Create a new file in the src
directory and name it Message
.
Pass in the ChatMessage
type for this component, and pass the
content
string from ChatMessage
properties to display message
text received from chat-room message listeners. In the Project Navigator, go to
Message
.
Tip: Use this component to store different properties that you want to render in your message rows; for example, avatar URLs, user names, and timestamps of when the message was sent.
Recognizing Messages Sent by the Current User
To recognize the message sent by the current user, we modify the code and create a React
context for storing the userId
of the current user.
Create a new file in the src
directory and name it
UserContext
:
Note: Here we used the useState
hook to store the userId
value. In the future you can use setUserId
to change user context or for login
purposes.
Next, replace userId
in the first parameter passed to
tokenProvider
, using the previously created context:
// App.jsx / App.tsx // ... import { useUserContext } from './UserContext'; // ... export default function App() { const [messages, setMessages] = useState<ChatMessage[]>([]); const { userId } = useUserContext(); const [room] = useState( () => new ChatRoom({ regionOrUrl: process.env.REGION, tokenProvider: () => tokenProvider(userId, ['SEND_MESSAGE']), }), ); // ... }
In your Message
component, use the UserContext
created before,
declare the isMine
variable, match the sender’s userId
with the
userId
from the context, and apply different styles of messages for the
current user.
Creating a Message List Component
The MessageList
component is responsible for displaying a chat room's
conversation over time. The MessageList
file is the container that holds all
our messages. Message
is one row in MessageList
.
Create a new file in the src
directory and name it
MessageList
. Define Props
with messages
of type
ChatMessage
array. Inside the body, map our messages
property
and pass Props
to your Message
component.
Rendering a List of Chat Messages
Now bring your new MessageList
into your main App
component:
// App.jsx / App.tsx import { MessageList } from './MessageList'; // ... return ( <div style={{ display: 'flex', flexDirection: 'column', padding: 10 }}> <h4>Connection State: {connectionState}</h4> <MessageList messages={messages} /> <div style={{ flexDirection: 'row', display: 'flex', width: '100%', backgroundColor: 'red' }}> <MessageInput value={messageToSend} onValueChange={setMessageToSend} /> <SendButton disabled={isSendDisabled} onPress={onMessageSend} /> </div> </div> ); // ...
All the puzzle pieces are now in place for your App
to start rendering
messages received by your chat room. Continue below to see how to perform actions in a chat
room that leverage the components you have created.
Perform Actions in a Chat Room
Sending messages and performing moderator actions within a chat room are some of the
primary ways you interact with a chat room. Here you will learn how to use various
ChatRequest
objects to perform common actions in Chatterbox, such as sending a
message, deleting a message, and disconnecting other users.
All actions in a chat room follow a common pattern: for every action you perform in a chat room, there is a corresponding request object. For each request there is a corresponding response object that you receive on request confirmation.
As long as your users are granted the correct permissions when you create a chat token, they can successfully perform the corresponding action(s) using the request objects to see what requests you can perform in a chat room.
Below, we explain how to send a message and delete a message.
Sending a Message
The SendMessageRequest
class enables sending messages in a chat room. Here,
you modify your App
to send a message request using the component you created
in Create a Message Input (in Part 1 of
this tutorial).
To start, define a new boolean property named isSending
with the
useState
hook. Use this new property to toggle the disabled state of your
button
HTML element, using the isSendDisabled
constant. In the
event handler for your SendButton
, clear the value for
messageToSend
and set isSending
to true.
Since you will be making an API call from this button, adding
the isSending
boolean helps prevent multiple API calls from occuring at the
same time, by disabling user interactions on your SendButton
until the
request is complete.
// App.jsx / App.tsx // ... const [isSending, setIsSending] = useState(false); // ... const onMessageSend = () => { setIsSending(true); setMessageToSend(''); }; // ... const isSendDisabled = connectionState !== 'connected' || isSending; // ...
Prepare the request by creating a new SendMessageRequest
instance, passing
message content to the constructor. After setting the isSending
and
messageToSend
states, call the sendMessage
method, which sends
the request to the chat room. Finally, clear the isSending
flag on receiving
confirmation or rejection of the request.
Give Chatterbox a run: try sending a message by drafting one with your
MessageInput
and tapping your SendButton
. You should see your
sent message rendered within the MessageList
that you created earlier.
Deleting a Message
To delete a message from a chat room, you need to have the proper capability.
Capabilities are granted during the initialization of the chat token that you use when
authenticating to a chat room. For the purposes of this section, the ServerApp
from Set Up a Local Authentication/Authorization
Server (in Part 1 of this tutorial) lets you specify moderator capabilities. This
is done in your app using the tokenProvider
object that you created in Build a Token Provider (also in Part
1).
Here you modify your Message
by adding a function to delete the
message.
First, open the App.tsx
and add the DELETE_MESSAGE
capability.
(capabilities
is the second parameter of your tokenProvider
function.)
Note: This is how your ServerApp
informs the IVS Chat APIs that the user
being associated with the resulting chat token can delete messages in a chat room. In a
real-world situation you probably will have more complex backend logic to manage user
capabilities in your server app's infrastructure.
In the next steps, you update your Message
to display a delete
button.
Open Message
and define a new boolean state named isDeleting
using the useState
hook with an initial value of false
. Using this
state, update the contents of your Button
to be different depending on the
current state of isDeleting
. Disable your button when isDeleting
is true; this prevents you from attempting to make two delete message requests at the same
time.
Define a new function called onDelete
that accepts a string as one of its
parameters and returns Promise
. In the body of your Button
‘s
action closure, use setIsDeleting
to toggle your isDeleting
boolean before and after a call to onDelete
. For the string parameter, pass in
your component message ID.
Next, you update your MessageList
to reflect the latest changes to your
Message
component.
Open MessageList
and define a new function called onDelete
that accepts a string as a parameter and returns Promise
. Update your
Message
and pass it through the properties of Message
. The
string parameter in your new closure will be the ID of the message that you want to delete,
which gets passed from your Message
.
Next, you update your App
to reflect the latest changes to your
MessageList
.
In App
, define a function named onDeleteMessage
and pass it to
the MessageList onDelete
property:
Prepare a request by creating a new instance of DeleteMessageRequest
,
passing the relevant message ID to the constructor parameter, and call
deleteMessage
that accepts the prepared request above:
Next, you update your messages
state to reflect a new list of messages that
omits the message you just deleted.
In the useEffect
hook, listen for the messageDelete
event and
update your messages
state array by deleting the message with a matching ID to
the message
parameter.
Note: The messageDelete
event might be raised for messages being deleted by
the current user or any other user in the room. Handling it in the event handler (instead of
next to the deleteMessage
request) allows you to unify delete-message
handling.
// App.jsx / App.tsx // ... const unsubscribeOnMessageDeleted = room.addListener('messageDelete', (deleteMessageEvent) => { setMessages((prev) => prev.filter((message) => message.id !== deleteMessageEvent.id)); }); return () => { // ... unsubscribeOnMessageDeleted(); }; // ...
You are now able to delete users from a chat room in your chat app.
Next Steps
As an experiment, try implementing other actions in a room like disconnecting another user.