SyncManager JS v1
SyncManager JS
SyncManager
JS
Home
/
SyncManager
/
JavaScript
/
Local caching

Message collection

A MessageCollection retrieves data from both the local cache and Sendbird server and lets you quickly create a chat view without missing any message-related updates. This page explains how to create a chat view using the collection and serves as a migration guide.


Create a collection

Create a MessageCollection through the createMessageCollection() method. Use the new sendbird.MessageFilter() to set the message order and then, specify the starting point of the message list in the chat view using the collection's builder.

Local cachingSyncManager
const messageFilter = new sendbird.MessageFilter();
messageFilter.messageType = MESSAGE_TYPE;
let messageCollection = channel.createMessageCollection()
  .setFilter(messageFilter)
  .setStartingPoint(STARTING_POINT)
  .setLimit(LIMIT)
  .build();

Message events

Use the messageCollectionHandler to determine how the client app reacts to message-related events.

A new addition to local caching is the onHugeGapDetected. If more than 300 messages are missing in the local cache compared to the remote server, Sendbird Chat SDK determines that there is a huge gap. For more information, see Gap and synchronization.

The following table shows when to call each event handler.

HandlerCalled when

onMessageAdded

- A new message is created as a real-time event.
- New messages are fetched through changelog sync.

onMessageDeleted

- A message is deleted as a real-time event.
- Message deletion is detected during changelog sync.
- The value of the messageFilter setter changes.

onMessageUpdated

- A message is updated as a real-time event.
- Message update is detected during changelog sync.
- The sending status of a pending message changes.

onChannelUpdated

- The channel information that is included in the user's current chat view is updated as a real-time event.
-Channel info update is detected during changelog sync.

onChannelDeleted

- The current channel is deleted as a real-time event.
- Channel deletion is detected during changelog sync.
- In both cases, the entire view should be disposed of.

onHugeGapDetected

- A huge gap is detected through background sync. In this case, you need to dispose of the view and create a new messageCollection instance.

SyncManager's onSucceededMessageEvent()should be changed as shown in the code below.

Local cachingSyncManager
messageCollection.setMessageCollectionHandler({
   /**
    * SyncManager's following events should be handled through the onMessagesAdded in local caching:
    *   1. onSucceededMessageEvent()'s `insert` action.
    *   2. onPendingMessageEvent()'s `insert` action when sending a message.
    */
  onMessagesAdded: (context, channel, messages) => {
    // ...
  },
  /**
    * SyncManager's following events should be handled through the onMessagesUpdated in local caching:
    *   1. onSucceededMessageEvent()'s `update` action.
    *   2. onPendingMessageEvent()'s `insert` action when resending a failed message.
    *   3. onFailedMessageEvent()'s `insert` action when sending or resending a message failed.
    */
  onMessagesUpdated: (context, channel, messages) => {
    // ...
  },
  /**
    * SyncManager's following events should be handled through the onMessagesDeleted in local caching:
    *   1. onSucceededMessageEvent()'s `remove` action.
    *   2. onFailedMessageEvent()'s `remove` action.
    */
  onMessagesDeleted: (context, channel, messages) => {
    // ...
  },
    /**
    * The onChannelEvents()'s update action should be handled through the onChannelUpdated in local caching.
    */
  onChannelUpdated: (context, channel) => {
    // ...
  },
  /**
    * The onChannelEvent()'s remove action should be handled through the onChannelDeleted() in local caching.
    */
  onChannelDeleted: (context, channel) => {
    // ...
  },
   /**
    * The onHugeGapDetected is called when the SDK detects more than 300 messages missing when connected.
    * The current message collection should be disposed and a new MessageCollection is created.
    */
  onHugeGapDetected: () => {
    // ...
  },
});

SyncManager's messageCollectionHandler.onNewMessage which notifies the client app of new messages in a channel should also be changed as shown in the code below.

Local cachingSyncManager
const channelHandler = new sendbird.ChannelHandler();
channelHandler.onMessageReceived = (channel, message) => {
  // There's no new message for this channel.
};
sendbird.addChannelHandler(CHANNEL_HANDLER_KEY, channelHandler);

List messages

SyncManager's fetchSucceededMessages() method retrieves messages from the local cache and delivers them to the MessageCollectionHandler. In local caching, the MessageCollection can retrieve messages through four new interfaces, hasPrevious(), hasNext(), loadPrevious(), and loadNext().

Unlike the GroupChannelCollection, pagination works in both directions for messages because messages can be shown in either chronological or reverse chronological order depending on how you set the value of the startingPoint.

MethodDescription

hasPrevious()

- Checks if there are more messages to load from the previous page.
- Called whenever a user scroll hits the top of the chat view.

loadPrevious()

- If hasPrevious() is true, retrieves messages from the local cache to show in the view.
- Called whenever a user scroll hits the top of the chat view.

hasNext()

- Checks if there are more messages to load in the next page.
- Called whenever a user scroll hits the bottom of the chat view.

loadNext()

- If hasNext() is true, retrieves messages from the local cache to show in the view.
- Called whenever a user scroll hits the bottom of the chat view.

Policy

In a MessageCollection, the initialization is dictated by the MessageCollectionInitPolicy. The MessageCollectionInitPolicy determines how initialization deals with the message data retrieved from the local cache and API calls. Because we only support CACHE_AND_REPLACE_BY_API at this time, you should clear all messages in the local cache before adding messages from the remote server. Messages will first be retrieved from the cached list using the .onCacheResult(). Next, the .onApiResult() calls the API result list which then replaces the cached message list with messages received from the API call.

Local cachingSyncManager
// Initialize messages from the startingPoint.
sendbird.setErrorFirstCallback(true);
let initPolicy = sendbird.MessageCollection.MessageCollectionInitPolicy.CACHE_AND_REPLACE_BY_API;
messageCollection.initialize(initPolicy)
    // Messages will be retrieved from the local cache.
    // They might be too outdated compared to the startingPoint.
  .onCacheResult((error, messages) => {
    if (error) {
      // Handle error.
    }
    // ...
  })
  .onApiResult((error, messages) => {
    if (error) {
      // Handle error.
    }
    // ...
  });

// Next direction
if (messageCollection.hasNext) {
  messageCollection.loadNext()
    .then(messages => {
      // ...
    })
    .catch(error => {
      // Handle error.
    });
}

// Previous direction
if (messageCollection.hasPrevious) {
  messageCollection.loadPrevious()
    .then(messages => {
      // ...
    })
    .catch(error => {
      // Handle error.
    });
}

A viewpoint is a timestamp option that sets the starting point of the current user's view. This viewpoint can be reset anytime by calling the resetViewpointTimestamp() method. SyncManager's resetViewpointTimestamp is used to reset the timestamp of the current message collection to another specified time. To reset a viewpoint timestamp in local caching, you should dispose of the current message collection and create a new one.

Local cachingSyncManager
messageCollection.dispose();
messageCollection = channel.createMessageCollection()
  .setFilter(messageFilter)
  .setStartingPoint(messageCollection.startingPoint)
  .setLimit(LIMIT)
  .build();
messageCollection.initialize(MESSAGE_COLLECTION_INIT_POLICY)
  .onCacheResult((error, messages) => {
    if (error) {
      // Handle error.
    }
    // ...
  })
  .onApiResult((error, messages) => {
    if (error) {
      // Handle error.
    }
    // ...
  });

Send a message

In local caching, the result of sending a message is handled internally through the MessageCollectionHandler. First, pending messages are delivered to local caching's MessageCollectionHandler.onMessagesAdded(). Whether the message succeeds or fails in sending, the result will be delivered to the MessageCollectionHandler.onMessagesUpdated(). Thus, in local caching, it is not necessary to manually input the send status using a onSent() callback from the sendUserMessage() and the sendFileMessage() as in SyncManager.

Note: Don't add the pending, succeeded or failed message objects of the sendMessage() callback to your message list data source. This can cause duplicate messages in the chat view.

Local cachingSyncManager
// User Message
channel.sendUserMessage(USER_MESSAGE_PARAMS, (error, message) => {
});

// File Message
channel.sendFileMessage(FILE_MESSAGE_PARAMS, (error, message) => {
});

Resend a failed message

In local caching, the result of resending a message is handled internally through the MessageCollectionHandler. First, pending messages are delivered to local caching's MessageCollectionHandler.onMessagesUpdated(). Whether the message has succeeded or failed in sending, the result will be delivered to the MessageCollectionHandler.onMessagesUpdated(). Thus, in local caching, it is not necessary to manually input the send status using a onSent() callback from the resendMessage() as in SyncManager.

Note:Don't add the pending, succeeded or failed message objects of the resendMessage() callback to your message list data source. This can cause duplicate messages in the chat view.

Local cachingSyncManager
// User Message
channel.resendUserMessage(failedMessage, (error, message) => {
});

// File Message
channel.resendFileMessage(failedMessage, (error, message) => {
});

// Pending Messages
//This should be called after the messageCollection.start(with:cacheResultHandler:apiResultHandler:).
const pendingMessages = messageCollection.pendingMessages;

// Failed Messages
//This should be called after the messageCollection.start(with:cacheResultHandler:apiResultHandler:).
const failedMessages = messageCollection.failedMessages;

Update a message

SyncManager uses the MessageCollections updateMessage() callback to manually update a message in the collection after updating the same message in a channel. In local caching, however, updating messages is handled internally, and the result is delivered to the MessageCollectionHandler.onMessagesUpdated().

Local cachingSyncManager
channel.updateUserMessage(message.messageId, userMessageParams, (error, message) => {
});

Delete a message

There are two cases for deleting a message:

Deleting a sent message.

In SyncManager, the process of delivering the status of message deletion through the deleteMessage() is required. However, this process isn't needed in local caching.

Deleting a failed message.

The process is the same for both SyncManager and local caching where the failed message object is deleted explicitly from the local cache. In SyncManager, failed messages are deleted through the deleteMessage(). In local caching, the same can be done through the removeFailedMessages().

Local cachingSyncManager
// 1. Deleting a succeeded message.
channel.deleteMessage(message, error => {
  /**
   * The result will be delivered to the `MessageCollectionHandler.onMessagesDeleted()`.
   */
});

// 2-1. Deleting a failed message. Delete it from the collection only.
messageCollection.removeFailedMessages(failedMessages)
  .then(requestIds => {
    // ...
  })
  .catch(error => {
    // Handle error.
  });

// 2-2. Deleting all failed messages. Delete it from the collection only.
messageCollection.removeAllFailedMessages()
  .then(() => {
    // ...
  })
  .catch(error => {
    // Handle error.
  });

Dispose of the collection

SyncManager's MessageCollection has a remove() method that clears all the messages managed by the collection and stops the synchronization processes in the collection instance.

On the other hand, local caching uses the dispose() method to clear the existing chat view. You should call this method when the current user leaves the channel or you need to create a new collection because a huge gap has been detected by the onHugeGapDetected().

Local cachingSyncManager
messageCollection.dispose();

Other methods

SyncManager's messageCollection.messageCount is used to count the number of succeeded messages in the collection. In local caching, it should be updated as shown in the code below.

Local cachingSyncManager
// Provides a getter for the current message list in the message collection.
const messageCount = messageCollection.succeededMessages.length;