/ SDKs / JavaScript
SDKs
Chat SDKs JavaScript v4
Chat SDKs JavaScript
Chat SDKs
JavaScript
Version 4

Collection migration guide

Copy link

Sendbird Chat SDK v4 for Javascript has introduced group channel collection and message collection to enhance efficiency and improve developer experience. This guide assists you in transitioning from low-level interface implementations to more efficient, collection-based solutions. We strongly recommend integrating collections into your existing codebase for improved efficiency and user experience.

Key benefits of using collection

Copy link
  1. View-driven interface: The collection interface organizes handler methods and event callbacks around the currently displayed view. Event callbacks are triggered only for the channels or messages that are currently being displayed.

  2. Effortless event handling: By assigning a collection handler, the data source for your channel and message views are automatically updated when an event is triggered. You can set your handler, and the collection handles the event updates to your data source.

  3. Automated updates upon reconnection: Collections handle channel and message updates automatically upon app reconnection, eliminating the need for manual intervention.

  4. Local caching capabilities: Collections also handle local caching of chat data for offline messaging.


Implementing group channel collection

Copy link

The following section further explains how to implement and use group channel collection.

Step 1 Create a group channel collection instance

Copy link

To use the group channel collection interfaces, a GroupChannelCollection instance must be created first.

JavaScriptTypeScript
const filter = new GroupChannelfilter();
filter.includeEmpty = true;

const groupChannelCollection = sb.groupChannel.createGroupChannelCollection({
  filter,
  order: GroupChannelListOrder.LATEST_LAST_MESSAGE,
});

Step 2 Retrieve a list of group channels

Copy link

You can load more channels by using the loadMore() method of GroupChannelCollection created in step 1. You don't need to create a GroupChannelListQuery instance and utilize the query to load the next page anymore.

With collection

Copy link
JavaScriptTypeScript
// Check whether there are more channels to load before calling loadMore().
if (groupChannelCollection.hasMore) {
  const moreGroupChannels = await groupChannelCollection.loadMore();
  // use received channels or groupChannelcollection.channels
}

Without collection

Copy link
JavaScriptTypeScript
const query = sb.groupChannel.createMyGroupChannelListQuery({
  includeEmpty: true,
  order: GroupChannelListOrder.LATEST_LAST_MESSAGE,
  ...
});


const channels = await query.next();

Step 3 Handle group channel events

Copy link

With collections, numerous group channel event callbacks have been consolidated into three streamlined GroupChannelCollectionEventHandler methods. The new methods are designed to only pass events that are relevant to the group channel list view.

Example

Copy link

You're managing the channel list view and want to show a typing indicator when a member starts typing and update the last message in real-time.

Before collections, you had to implement different GroupChannelHandler methods to keep up with these changes.

With group channel collection, however, all changes from events are already applied to updatedChannels in GroupChannelCollectionHandler. Now, all you need to do is refresh your channel list with the updated channels as shown in the example code below.

With collection

Copy link
JavaScriptTypeScript
groupChannelCollection.setGroupChannelCollectionHandler({
  onChannelsAdded: (context, channels) => {
    ...
  },
  onChannelsUpdated: (context, channels) => {
    // `updatedChannels` carries latest typing status and last message for channels that have had updates.
  },
  onChannelsDeleted: (context, channels) => {
    ...
  }
});

Without collection

Copy link
JavaScriptTypeScript
const groupChannelHandler = new GroupChannelHandler({
  ...
  onTypingStatusUpdated: (channel) => {
    // update channel
  }
  ...
});

sb.groupChannel.addGroupChannelHandler(EVENT_KEY, groupChannelHandler);

Handle specific events

Copy link

To react to specific events, such as when a member starts typing, check context.source. The following code is an example of triggering an alert when a member starts typing.

JavaScriptTypeScript
groupChannelCollection.setGroupChannelCollectionHandler({
  onChannelsAdded: (context, channels) => {
    ...
  },
  onChannelsUpdated: (context, channels) => {
    if (context.source === GroupChannelEventSource.EVENT_CHANNEL_TYPING_STATUS_UPDATE) {
      // update typing status.
    }
  },
  onChannelsDeleted: (context, channels) => {
    ...
  }
});

To learn more, see a full list of GroupChannelEventSource.

Step 4 Keep cached channels synced with the Sendbird server

Copy link

When a user is online, all data associated with the group channels they are a member of are automatically updated. However, when a user is disconnected from the Sendbird server and reconnects later, data associated with group channels need to be updated.

Previously, you had to call sb.groupChannel.getMyGroupChannelChangeLogsByTimestamp(ts, params) or sb.groupChannel.getMyGroupChannelChangeLogsByToken(token, params) to manually update the data upon reconnection.

With collections, however, it automatically keeps your cached channels' information up-to-date without requiring additional code implementation. If you wish to be notified when this update is completed in order to do any custom updates, you can check GroupChannelEventSource.SYNC_CHANNEL_CHANGELOGS inside each GroupChannelCollectionHandler.

With collection

Copy link
JavaScriptTypeScript
groupChannelCollection.setGroupChannelCollectionHandler({
  onChannelsAdded: (context, channels) => {
    if (context.source === GroupChannelEventSource.SYNC_CHANNEL_CHANGELOGS) {
      // add channels
    }
  },
  onChannelsUpdated: (context, channels) => {
    if (context.source === GroupChannelEventSource.SYNC_CHANNEL_CHANGELOGS) {
      // update channels
    }
  },
  onChannelsDeleted: (context, channels) => {
    if (context.source === GroupChannelEventSource.SYNC_CHANNEL_CHANGELOGS) {
      // delete channels
    }
  }
});

Without collection

Copy link
JavaScriptTypeScript
const connectionHandler = new ConnectionHandler();

handler.onReconnectSucceeded = () => {
  const channelChangelogs = await sb.groupChannel.getMyGroupChannelChangeLogsByTimestamp(TIMESTAMP);

  const updatedChannels = channelChangelogs.updatedChannels;
  // update channels using updatedChannels

  const deletedChannelUrls = channelChangelogs.deletedChannelUrls;
  // delete channels using deletedChannels

  const hasMore = channelChangelogs.hasMore;
  const token = channelChangelogs.token;
};
...
sendbird.addConnectionHandler(EVENT_KEY, handler);

Implementing message collection

Copy link

The following section explains how to implement and use message collection.

Step 1 Create a message collection instance

Copy link

To use message collection interfaces, a MessageCollection instance must be created first. You can do so by calling the createMessageCollection() method. Then, you must start the collection to load the first batch of messages through initialize().

JavaScriptTypeScript
const filter = new MessageFilter();
const limit = 100;
const startingPoint = Date.now();
const collection = channel.createMessageCollection({
    filter,
    limit,
    startingPoint,
});
collection.initialize(MessageCollectionInitPolicy.CACHE_AND_REPLACE_BY_API)
  .onCacheResult((err, messages) => {
      // Messages are retrieved from the local cache.
      // They might be too outdated or far from the startingPoint.
  })
  .onApiResult((err, messages) => {
      // Messages are retrieved from the Sendbird server through API.
      // According to MessageCollectionInitPolicy.CACHE_AND_REPLACE_BY_API,
      // the existing data source needs to be cleared
      // before adding retrieved messages to the local cache.
  });

// ...

Step 2 Retrieve a list of messages

Copy link

You can load more messages by using the hasNext or hasPrevious method of MessageCollection created in step 1. Previously, you had to call either getMessagesByTimestamp() or getMessagesByMessageId() and manually manage the data source for the message list after loading.

With collection

Copy link
JavaScriptTypeScript
if (messageCollection.hasNext) {
  const nextMessages = await messageCollection.loadNext();
}

if (messageCollection.hasPrevious) {
  const prevMessages = await messageCollection.loadPrevious();
}

Without collection

Copy link
JavaScriptTypeScript
const messages = await groupChannel.getMessagesByTimestamp(TimeStamp, {
  prevResultSize: prevSize,
  nextResultSize: nextSize,
});
    
// A list of previous and next messages of a specified timestamp is successfully retrieved.
// Through the messages returned,
// you can access and display the data of each message from the result list.

const messages = await groupChannel.getMessagesByMessageId(messageId, {
  prevResultSize: prevSize,
  nextResultSize: nextSize,
});

// A list of previous and next messages of a specified message ID is successfully retrieved.
// Through the messages returned,
// you can access and display the data of each message from the result list.

Step 3 Handle channel events

Copy link

With collections, numerous message event callbacks have been consolidated into six streamlined MessageCollectionEventHandler methods. Step 3 explains how to handle events on the currently displayed group channel.

Example of group channel event

Copy link

If you wanted to handle events when new members join or leave a channel, you had to implement two separate GroupChannelHandler methods shown in the code below to handle the events. With message collection, however, all changes from both events are already applied to the updatedChannel parameter in the MessageCollectionEventHandler. All you need to do is refresh with the updated channels as shown in the example code below.

With collection

Copy link
JavaScriptTypeScript
groupChannel.setMessagecollectionHandler({
  onChannelUpdated: (context, channel) => {
    // update channel with latest member data.
  },
  ...
})

Without collection

Copy link
JavaScriptTypeScript
const groupChannelHandler = new GroupChannelHandler({
  ...
  onUserJoined: (channel, user) => {
    // update channel with latest member data.
  },
  onUserLeft: (channel, user) => {
    // update channel with latest member data.
  },
  ...
});

sb.groupChannel.addGroupChannelHandler(EVENT_KEY, groupChannelHandler);

Handle specific events

Copy link

For use cases where you need to specifically check whether a member has joined or left a channel separately, you can check context.source.

JavaScriptTypeScript
groupChannel.setMessagecollectionHandler({
  onChannelUpdated: (context, channel) => {
    // update channel with latest member data.
    switch(context.source) {
      case GroupChannelEventSource.EVENT_CHANNEL_JOINED:
        // update channel with latest member data.
      break;
      case GroupChannelEventSource.EVENT_CHANNEL_LEFT:
        // update channel with latest member data.
      break;
    }
  },
  ...
})

To learn more, see a full list of GroupChannelEventSource.

Step 4 Handle message events

Copy link

With collection, you can wait for an event on the MessageCollectionEventHandler without specifying a MessageRequestHandler. Each message event can be handled accordingly depending on their messages status by utilizing the source property included in the MessageContext.

With collection

Copy link
JavaScriptTypeScript
groupChannel.sendUserMessage({
  message: 'hello',
});

groupChannel.setMessageCollectionHandler({
  ...
  onMessagesAdded: (context, channel, messages) => {
    switch(context.source) {
      case MessageEventSource.EVENT_MESSAGE_SENT_PENDING:
        // add pending message to View
      break;
    }
  },
  onMessagesUpdated: (context, channel, messages) => {
    switch(context.source) {
      case MessageEventSource.EVENT_MESSAGE_SENT_SUCCESS:
        // message sent successfully
      break;
      case MessageEventSource.EVENT_MESSAGE_SENT_FAILED:
        // Failed to message sent
      break;
      case MessageEventSource.LOCAL_MESSAGE_CANCELED:
        // Cancel message sending
      break;
      case MessageEventSource.LOCAL_MESSAGE_FAILED:
        // Auto-Resend Fail.
      break;
      case MessageEventSource.LOCAL_MESSAGE_RESEND_STARTED:
        // Resend Start
      break;
    }
  },
  ...
})

Without collection

Copy link
JavaScriptTypeScript
groupChannel.sendUserMessage({
  message: 'hello',
})
.onPending((message) => {
  // Add the view with the pending message.
})
.onSucceeded((message) => {
  // message is successfully sent.
  // Update the view with the succeeded message.
})
.onFailed((error, failedMessage) => {
  // message failed to be sent, handle error 
});

Other message events

Copy link

Previously, different events that affect your message list data had to be handled separately. For example, to update your message list data with the latest message update events, reaction events, and poll update events, you had to use three different GroupChannelHandler handler methods - onMessageUpdated, onReactionUpdated, and onPollUpdated. In each, you had to separately implement how to handle your message list.

Using message collection, you can handle all message events by simply updating your message data through updatedMessages of MessageCollectionEventHandler. This is because updatedMessages already carries all the updated information from each message update, reaction, and poll update event.

With collection

Copy link
JavaScriptTypeScript
groupChannel.setMessageCollectionHandler({
  ...
  onMessagesUpdated: (context, channel, messages) => {
    // update message
  },
  ...
})

Without collection

Copy link
JavaScriptTypeScript
const groupChannelHandler = new GroupChannelHandler({
  ...
  onMessageUpdated: (channel, message) => {
    // update message
  },
  onReactionUpdated: (channel, reactionEvent) => {
    // update message Reaction
  },
  onPollUpdated:(channel, event) => {
    // update poll
  }
  ...
});

sb.groupChannel.addGroupChannelHandler(EVENT_KEY, groupChannelHandler);

Handle specific events

Copy link

To react to specific events, such as displaying an alert when a poll has been updated, check MessageContext.source.

JavaScriptTypeScript
groupChannel.setMessageCollectionHandler({
  ...
  onMessagesUpdated: (context, channel, messages) => {
    switch(context.source) {
      case MessageEventSource.EVENT_POLL_UPDATED:
        // update poll
      break;
    }
  },
  ...
})

To learn more, see a full list of MessageEventSource.

Step 5 Keep cached channel and messages synced with the Sendbird server

Copy link

When a user is online, all data associated with the group channels they are a member of and its messages are automatically updated. However, when a user is disconnected from the Sendbird server and reconnects later, data associated with group channels need to be updated.

Refresh cached channel upon reconnection

Copy link

With message collection, it automatically keeps your cached channel information up-to-date without requiring additional code implementation.

Without collection

Copy link
JavaScriptTypeScript
// Previously, you needed to call channel.refresh() manually upon reconnection.

const connectionHandler = new ConnectionHandler();
handler.onReconnectSucceeded = () => {
  const refreshedChannel = await groupChannel.refresh();
};
...
sendbird.addConnectionHandler(EVENT_KEY, handler);

Refresh cached messages upon reconnection

Copy link

Previously, to keep your cached messages up-to-date, you had to manually call either getMessageChangeLogsSinceTimestamp() or getMessageChangeLogsSinceToken() upon reconnection to the Sendbird server.

With message collection, however, it automatically fetches the changelogs of message internally. Then, it passes the results onto MessageCollectionEventHandler.onMessagesUpdated or MessageCollectionEventHandler.onMessagesDeleted with context.source set as MessageEventSource.SYNC_MESSAGE_CHANGELOGS.

With collection

Copy link
JavaScriptTypeScript
groupChannel.setMessageCollectionHandler({
  ...
  onMessagesAdded: (context, channel, messages) => {
    switch(context.source) {
      case MessageEventSource.SYNC_MESSAGE_CHANGELOGS:
        // add messages
      break;
    }
  },
  onMessagesUpdated: (context, channel, messages) => {
    switch(context.source) {
      case MessageEventSource.SYNC_MESSAGE_CHANGELOGS:
        // update messages
      break;
    }
  },
  onMessagesDeleted: (
    context,
    channel,
    messageIds,
    messages) => {
    switch(context.source) {
      case MessageEventSource.SYNC_MESSAGE_CHANGELOGS:
        // delete messages
        // It is recommended to use messages not messageIds to delete messages.
      break;
    }
  },
});

Without collection

Copy link
JavaScriptTypeScript
const connectionHandler = new ConnectionHandler();

handler.onReconnectSucceeded = () => {
  const messageChangeLogs = await groupChannel.getMessageChangeLogsSinceToken(TOKEN, {
    replyType: REPLY_TYPE,
    includeParentMessageInfo: INCLUDE_PARENT_MESSAGE_INFO,
    includeThreadInfo: INCLUDE_THREAD_INFO,
  });

  const updatedMessages = messageChangeLogs.updatedMessages;
  // update message

  const deletedMessageIds = messageChangeLogs.deletedMessageIds;
  // delete message

  const hasMore = messageChangeLogs.hasMore;
  const token = messageChangeLogs.token;

  --- or ---

  const messageChangeLogs = await groupChannel.getMessageChangeLogsSinceTimestamp(TIMESTAMP, {
    replyType: REPLY_TYPE,
    includeParentMessageInfo: INCLUDE_PARENT_MESSAGE_INFO,
    includeThreadInfo: INCLUDE_THREAD_INFO,
  });

  const updatedMessages = messageChangeLogs.updatedMessages;
  // update message

  const deletedMessageIds = messageChangeLogs.deletedMessageIds;
  // delete message

  const hasMore = messageChangeLogs.hasMore;
  const token = messageChangeLogs.token;
};

Collection event sources

Copy link

The following table shows a list of GroupChannelEventSource and MessageEventSource that trigger GroupChannelCollectionHandler and MessageCollectionHandler methods to be called. The event sources are passed to ChannelContext and MessageContext inside delegate methods.

List of group channel event sources

Copy link
NameEvent description

GroupChannelEventSource.EVENT_CHANNEL_MUTED

A user has been muted in a group channel.

GroupChannelEventSource.EVENT_CHANNEL_UNMUTED

A user has been unmuted in a group channel.

GroupChannelEventSource.EVENT_CHANNEL_BANNED

A user has been banned from a group channel.

GroupChannelEventSource.EVENT_CHANNEL_UNBANNED

A user has been unbanned from a group channel.

GroupChannelEventSource.EVENT_CHANNEL_FROZEN

A group channel has been frozen.

GroupChannelEventSource.EVENT_CHANNEL_UNFROZEN

A group channel has been unfrozen.

GroupChannelEventSource.EVENT_CHANNEL_UPDATED

A group channel has the following changes:
- The last message has been updated.
- The unread count of a group channel has been updated.
- A new message has been pinned.
- A pinned message has been updated.
- A group channel has been unhidden.

GroupChannelEventSource.EVENT_CHANNEL_DELETED

A group channel has been deleted.

GroupChannelEventSource.EVENT_CHANNEL_METADATA_CREATED

Metadata for a group channel has been created.

GroupChannelEventSource.EVENT_CHANNEL_METADATA_UPDATED

Metadata for a group channel has been updated.

GroupChannelEventSource.EVENT_CHANNEL_METADATA_DELETED

Metadata for a group channel has been deleted.

GroupChannelEventSource.EVENT_CHANNEL_METACOUNTER_CREATED

A metacounter for a group channel has been created.

GroupChannelEventSource.EVENT_CHANNEL_METACOUNTER_UPDATED

A metacounter for a group channel has been updated.

GroupChannelEventSource.EVENT_CHANNEL_METACOUNTER_DELETED

A metacounter for a group channel has been deleted.

GroupChannelEventSource.EVENT_CHANNEL_OPERATOR_UPDATED

A channel operator has been updated.

GroupChannelEventSource.EVENT_CHANNEL_READ

A message has been read by a reader who is not the currentUser.

GroupChannelEventSource.EVENT_CHANNEL_DELIVERED

The delivery status of a message sent to a group channel has been updated.

GroupChannelEventSource.EVENT_CHANNEL_TYPING_STATUS_UPDATE

A user starts typing a message to a group channel.

GroupChannelEventSource.EVENT_CHANNEL_MEMBER_COUNT_UPDATED

The member count of one or more group channels has changed.

GroupChannelEventSource.EVENT_CHANNEL_INVITED

A user has been invited to a group channel.

GroupChannelEventSource.EVENT_CHANNEL_DECLINED_INVITE

A user has declined an invitation to a group channel.

GroupChannelEventSource.EVENT_CHANNEL_JOINED

A user has joined a group channel.

GroupChannelEventSource.EVENT_CHANNEL_LEFT

A user has left a group channel.

GroupChannelEventSource.EVENT_CHANNEL_HIDDEN

A group channel has been hidden.

GroupChannelEventSource.EVENT_CHANNEL_UNHIDDEN

A group channel has been unhidden.

GroupChannelEventSource.SYNC_CHANNEL_CHANGELOGS

Channel changelog has been received.

GroupChannelEventSource.REFRESH_CHANNEL

A group channel has been refreshed through startMessageCollection() method.

List of message event sources

Copy link
NameEvent description

MessageEventSource.EVENT_MESSAGE_RECEIVED

A message has been received in a group channel.

MessageEventSource.EVENT_MESSAGE_UPDATED

A message has been updated in a group channel.

MessageEventSource.EVENT_MESSAGE_DELETED

A message has been deleted in a group channel.

MessageEventSource.EVENT_MESSAGE_THREADINFO_UPDATED

A reply message has been created or deleted from a thread in a group channel.

MessageEventSource.EVENT_PINNED_MESSAGE_UPDATED

A message has been pinned or unpinned in a group channel.

MessageEventSource.EVENT_POLL_UPDATED

A poll has been updated in a group channel.

MessageEventSource.EVENT_POLL_VOTED

A user has voted on a poll in a group channel.

MessageEventSource.SYNC_MESSAGE_CHANGELOGS

Message changelog has been received in the following cases:
- The messageCollection is created.
- Client app is initially connected.
- Client app is reconnected after disconnection.

MessageEventSource.SYNC_MESSAGE_FILL

A message gap has been filled.

MessageEventSource.EVENT_MESSAGE_SENT_PENDING

A message is pending to be sent.

MessageEventSource.EVENT_MESSAGE_SENT_FAILED

An attempt to send a message in a group channel has failed.

MessageEventSource.LOCAL_MESSAGE_CANCELED

A message sent in a group channel has been canceled.

MessageEventSource.LOCAL_MESSAGE_RESEND_STARTED

A failed message has been resent in a group channel.

MessageEventSource.EVENT_MESSAGE_SENT_SUCCESS

A message has been sent in a group channel.

MessageEventSource.EVENT_MESSAGE_SENT_SUCCESS

A message has been sent in a group channel.

MessageEventSource.SYNC_POLL_CHANGELOGS

Poll changelog has been received.