Web JS SDK - How Do You Hook Onto Incoming Messages?

I’ve been going through the web SDK start guide and can connect, create and join a channel, and send message to the channel (maybe?) with the following:

let openChannelObject;
sendbirdObject.connect('myusername', ()=>{
    const channelUrl = "sendbird_open_channel_10376_88531c010cacb6efkasdfj2380b49a483fjd";
    sendbirdObject.OpenChannel.getChannel(channelUrl, (openChannel, error) => {
      console.log(openChannel);
      openChannel.enter((response, error) => {
        openChannel.sendUserMessage('test', (message) => {
          console.log(message);
        });
      });
      openChannelObject = openChannel;
    });
  });

But how do I listen for new messages in the channel I’ve joined? The documentation makes no mention of this. So something like:

openChannel.listenToChannel((newMessage, error) => {
    console.log(newMessage);
});

Also, in the block code above where I connect to my channel, I have my sendUserMessage() method nested in three callback functions. What’s the best way to create a globally available custom send() method in the window scope? I created an openChannelObject variable outside of the callbacks, but that object returns null even after I assigned the openChannel variable to it inside of the callbacks.

Hey @diggtydo,

Welcome to the Sendbird Community!

You’re looking for Event Handlers. Here is a link to register event handlers for the channel.

You’re looking for the onMessageReceived() event handler to listen for incoming messages.

In terms of your code block, the instance of Sendbird is a singleton and can be accessed by doing something like const sb = SendBird.getInstance(). Then you can access your Sendbird session from anywhere. You simply just have to get the current instance.

For example, in my personal application that I built for testing, I have an actions file that contains a lot of the sendbird methods, which I then implement like this:

export const sendUserMessage = (channel, params) => {
  return new Promise((resolve, reject) => {
    channel.sendUserMessage(params, (message, error) => {
      if (error) {
        reject(error);
      }

      resolve(message);
    });
  });
};

That method is called in my Redux Thunk Action like this:

export const sendUserMessage = createAsyncThunk(
  'chat/sendUserMessage',
  async (messageText, { getState }) => {
    try {
      const url = getState().chat.activeChannel.channel.url;
      const params = SendbirdChatActions.createUserMessageParams();
      params.message = messageText;

      const channel = await SendbirdChatActions.getGroupChannel(url);
      const message = await SendbirdChatActions.sendUserMessage(channel, params);

      return JSON.stringify(message);
    } catch (error) {
      console.error(error);
    }
  }
);

Please note that I’m not a developer so my implementation is by no means best practice. I would definitely recommend looking at our Sample applications to get ideas on how our Engineering team might implement these methods. GitHub - sendbird/SendBird-JavaScript: A guide of the installation and functions of Sendbird Chat, UIKit, and SyncManager for JavaScript samples.

In your first code block, can you show how you would pass an instance of the channel you want into the sendUserMessage() method via the singleton Sendbird object? Something that looks:

import sendUserMessage from 'sendUserMessage';

// How do we assign channel here via the Sendbird instance?
let channel = ?;
let params = {...};
sendUserMessage(myChannelObject, params);

My implementation relies heavily on Redux for state management, and may not work for you. I have my channel stored in my state, which I use to grab the channel_url and then I use that to get the channel object. You can see this in my second code block:

Getting channel_url from state:

const url = getState().chat.activeChannel.channel.url;

Getting channel object from Sendbird:

const channel = await SendbirdChatActions.getGroupChannel(url);

I highly recommend you take a look at the sample applications I linked in my earlier post as most of them do not rely on Redux for state management.

I was able to get the send message stuff to work. Going back to hooking onto event handlers as messages are added to a conversation, is the onMessageReceived hook supposed to be tripped when I send a message to the channel myself? Should my callback for this be called if I send a message to the channel?

This is what my listener looks like right now:

let channelHandler = new sendbirdObject.ChannelHandler();
  channelHandler.onMessageReceived = (channel, message) => {
    console.log(channel);
    console.log(message);
  }
sendbirdObject.addChannelHandler('GLOBAL_HANDLER', channelHandler);

Do you see any problems with this implementation? Specifically, I put down GLOBAL_HANDER for the first parameter of addChannelHandler. Is this the correct value to put there?

You should not expect to see the onMessageReceived() handler fired when you send a message. As you’re not receiving a message. The other users in the channel who are receiving the new message would see the callback fired.

The UNIQUE_HANDLER_ID is a unique identifier you should declare to register multiple concurrent handlers. So if you’re only registering one handler for the user, then GLOBAL is fine. I would like to note that there are both channel handlers and user handlers, so you may want to take that into account with your UNIQUE_HANDLER_ID.