# Unread Chat Messages

**URL:** https://heroiclabs.com/docs/nakama/guides/concepts/unread-messages/
**Summary:** A guide to retrieving unread chat messages and counts on the client.
**Keywords:** unread chat messages, nakama, unread messages
**Categories:** nakama, unread-messages, concepts

---


# Unread Chat Messages

This guide demonstrates how to retrieve the latest unread chat messages in a specific [chat channel](../../../concepts/chat/) for your player, and get the total number of unread messages which can be displayed in the game UI.

![Displaying an unread message count in the game UI]({{< fingerprint_image "/images/pages/nakama/guides/concepts/unread-messages/unread-messages.png" >}})

### Joining the channel

You need to join the appropriate chat channel in order to start retrieving messages. The following example joins a room channel called `global-chat`.

```csharp
var channel = await Socket.JoinChatAsync("global-chat", ChannelType.Room, true);
var channelId = channel.Id;
```

### Cacheable cursor

Nakama's cacheable cursors can be used to store a reference to where in the channel history you last read up to. This value can be stored in Unity's `PlayerPrefs`.

Retrieve the cacheable cursor, defaulting to an empty string if it doesn't already exist.

```csharp
const string MESSAGES_CURSOR = "MESSAGES_CURSOR";
var messagesCursor = PlayerPrefs.GetString(MESSAGES_CURSOR, string.Empty);
```

Use the cacheable cursor to retrieve messages from where you left off. The following will retrieve the next 100 messages from the given cursor.

```csharp
var limit = 100;
var forward = true;
var messageList = await Client.ListChannelMessagesAsync(Session, channel.Id, limit, forward, messagesCursor);
```

### Retrieving all unread messages

The above example only gets up to 100 new messages, but you may want to get all new messages and display a total unread message count. 

To do this you need to make several calls to the `ListChannelMessageAsync` function, passing in an updated cursor each time. How many calls you need to make depends on the `limit` you set and the total number of remaining unread messages, but walking the history this way is fast.

Using a `while` loop you can retrieve unread messages in batches of 100, updating the cursor each time and ending once the `ListChannelMessagesAsync` method returns no more messages.

When all messages have been retrieved you should update the cacheable cursor value in `PlayerPrefs` to ensure that it is correct for the next time you perform this logic.

Below is a full example of a `GetUnreadMessages` method.

```csharp
private async Task<IList<IApiChannelMessage>> GetUnreadMessages(string channelId, int batchSize = 100)
{
    // Create an empty list to store unread messages
    var unreadMessages = new List<IApiChannelMessage>();

    // Get the current value of the cacheable cursor from PlayerPrefs
    var messagesCursor = PlayerPrefs.GetString(MESSAGES_CURSOR, string.Empty);

    var done = false;
    while (!done)
    {
        // Retrieve the next 100 messages
        var messageList = await Client.ListChannelMessagesAsync(Session, channelId, batchSize, true, messagesCursor);

        // Add the messages to the unreadMessages list
        unreadMessages.AddRange(messageList.Messages);

        // Update the cursor ready for the next iteration
        messagesCursor = messageList.CacheableCursor;
        
        // Get the number of messages retrieved this iteration
        var messageCount = messageList.Messages.Count();
        Debug.Log($"Got {messageCount} messages.");
        
        // If the message count this iteration matched the batch size, there may be more messages to get so continue
        if (messageCount == batchSize)
        {
            Debug.Log("Attempting to get more messages.");
            continue;
        }
        
        // If we reached this point, there are no more messages
        // Update the cacheable cursor stored in PlayerPrefs
        PlayerPrefs.SetString(MESSAGES_CURSOR, messagesCursor);

        // We're done
        Debug.Log($"Finished getting a total of {unreadMessages.Count} messages.");
        done = true;
    }
    
    // Return the list of unread messages
    return unreadMessages;
}
```

This function can be called whenever you need to update your game's UI.

```csharp
var channelId = "<SomeChannelId>";
var batchSize = 100;

var unreadMessages = await GetUnreadMessages(channelId, batchSize);
UnreadMessagesText.text = $"You have {unreadMessages.Count} unread messages.";

foreach (var unreadMessage in unreadMessages)
{
    // Add unreadMessage.Content to chat log as appropriate
}
```