const user = await client.connectUser({ id: 'myid' }, token);
// response.me.total_unread_count returns the unread count
// response.me.unread_channels returns the count of channels with unread messages
Unread
The most common use case for client-level events is unread counts. Here’s an example of a complete unread count integration for your chat app. As a first step we get the unread count when the user connects:
final response = await client.setUser(user, "{{ chat_user_token }}");
print("There are ${response.me.unreadChannels} unread channels");
print("There are ${response.me.totalUnreadCount} unread messages");
client.connectUser(User("user-id"), "{{ chat_user_token }}").enqueue { result ->
if (result.isSuccess) {
val user = result.data().user
val unreadChannels = user.unreadChannels
val totalUnreadCount = user.totalUnreadCount
}
}
let currentUserController = client.currentUserController()
class CurrentUserDelegate: CurrentChatUserControllerDelegate {
func currentUserController(_ controller: CurrentChatUserController,
didChangeCurrentUserUnreadCount: UnreadCount) {
// handle unread count change
}
}
// We need to keep a strong reference to delegate
// since controller only keeps a weak reference
let delegate = CurrentUserDelegate()
currentUserController.delegate = delegate
currentUserController.synchronize()
Client->ConnectUser(
User,
TEXT("{{ chat_user_token }}"),
[](const FOwnUser& UserRef)
{
const int32 TotalUnreadCount = UserRef.TotalUnreadCount;
const int32 UnreadChannels = UserRef.UnreadChannels;
});
User user = new User();
user.setId("user-id");
client.connectUser(user, "{{ chat_user_token }}").enqueue(result -> {
if (result.isSuccess()) {
User userRes = result.data().getUser();
int unreadChannels = userRes.getUnreadChannels();
int totalUnreadCount = userRes.getTotalUnreadCount();
}
});
public async Task GetUnreadCounts()
{
// Once user is connected you can access unread counts via IStreamLocalUserData
var localUserData = Client.LocalUserData;
Debug.Log(localUserData.UnreadChannels);
Debug.Log(localUserData.TotalUnreadCount);
// It's also returned by the ConnectUserAsync method
var localUserData2 = await Client.ConnectUserAsync("api_key", "user_id", "user_token");
// And also returned by the Connected event
Client.Connected += ClientOnConnected;
// All above examples returned the same IStreamLocalUserData object which represents the local user connected to the Stream Chat server
}
private void ClientOnConnected(IStreamLocalUserData localUserData)
{
}
By default the UI component SDKs (React, React Native, …) mark messages as read automatically when the channel is visible. You can also make the call manually like this:
// mark all messages on a channel as read
await channel.markRead();
await channel.markRead();
channelClient.markRead().enqueue { result ->
if (result.isSuccess) {
// Messages in the channel marked as read
} else {
// Handle result.error()
}
}
let channelController = client.channelController(for: .init(type: .messaging, id: "general"))
channelController.markRead()
Channel->MarkRead();
channelClient.markRead().enqueue(result -> {
if (result.isSuccess()) {
// Messages in the channel marked as read
} else {
// Handle result.error()
}
});
await message.MarkMessageAsLastReadAsync();
While you’re using the app, the unread count can change. A user can be added to a channel, a new message can be created, or the user can mark the messages as seen on another tab/device.
Unread counts are only stored and returned at connection time for channel members.
The markRead
function can also be executed server-side by passing a user ID as shown in the example below:
// mark all messages on a channel as read (server side)
await channel.markRead({ user_id: 'foo' });
// mark all messages on a channel as read (server side)
$mark = $channel->markRead('user-id');
It’s also possible to mark an already read message as unread:
await channel.markUnread({message_id: "<message_id>"})
channelController.markUnread(from: "message-id") { result in
// …
}
The mark unread operation can also be executed server-side by passing a user ID:
await channel.markUnread({message_id: "<message_id>", user_id: "<user_id>"})
To support updating the unread count in real-time, you can listen to these events:
notification.message_new
notification.mark_read
notification.mark_unread
These two events include the fields total_unread_count
and unread_channels
. You can listen to them all at once like this:
client.on((event) => {
if (event.total_unread_count !== undefined) {
console.log(event.total_unread_count);
}
if (event.unread_channels !== undefined) {
console.log(event.unread_channels);
}
});
client.on().where((Event event) => event.unreadChannels != null).listen((Event event) {
print("Unread channels count changed to:${event.unreadChannels}");
});
client.on().where((Event event) => event.totalUnreadCount != null).listen((Event event) {
print("Unread messages count changed to:${event.totalUnreadCount}");
});
channelClient.subscribeFor(
NewMessageEvent::class,
NotificationMessageNewEvent::class,
MarkAllReadEvent::class,
NotificationMarkReadEvent::class
) { event ->
when (event) {
is NewMessageEvent -> {
val unreadChannels = event.unreadChannels
val totalUnreadCount = event.totalUnreadCount
}
is NotificationMessageNewEvent -> {
val unreadChannels = event.unreadChannels
val totalUnreadCount = event.totalUnreadCount
}
is MarkAllReadEvent -> {
val unreadChannels = event.unreadChannels
val totalUnreadCount = event.totalUnreadCount
}
is NotificationMarkReadEvent -> {
val unreadChannels = event.unreadChannels
val totalUnreadCount = event.totalUnreadCount
}
}
}
let currentUserController = client.currentUserController()
class CurrentUserDelegate: CurrentChatUserControllerDelegate {
func currentUserController(_ controller: CurrentChatUserController,
didChangeCurrentUserUnreadCount: UnreadCount) {
// handle unread count change
}
}
// We need to keep a strong reference to delegate
// since controller only keeps a weak reference
let delegate = CurentUserDelegate()
currentUserController.delegate = delegate
currentUserController.synchronize()
Client->On<FNotificationMessageNewEvent>(
[](const FNotificationMessageNewEvent& Event)
{
const int32 TotalUnreadCount = Event.TotalUnreadCount;
const int32 UnreadChannels = Event.UnreadChannels;
});
Client->On<FNotificationMarkReadEvent>(
[](const FNotificationMarkReadEvent& Event)
{
const int32 TotalUnreadCount = Event.TotalUnreadCount;
const int32 UnreadChannels = Event.UnreadChannels;
});
channelClient.subscribeFor(
new Class[]{
NewMessageEvent.class,
NotificationMessageNewEvent.class,
MarkAllReadEvent.class,
NotificationMarkReadEvent.class
},
event -> {
if (event instanceof NewMessageEvent) {
NewMessageEvent newMessageEvent = (NewMessageEvent) event;
Integer unreadChannels = newMessageEvent.getUnreadChannels();
Integer totalUnreadCount = newMessageEvent.getTotalUnreadCount();
} else if (event instanceof NotificationMessageNewEvent) {
NotificationMessageNewEvent notificationMessageNewEvent = (NotificationMessageNewEvent) event;
Integer unreadChannels = notificationMessageNewEvent.getUnreadChannels();
Integer totalUnreadCount = notificationMessageNewEvent.getTotalUnreadCount();
} else if (event instanceof MarkAllReadEvent) {
MarkAllReadEvent markAllReadEvent = (MarkAllReadEvent) event;
Integer unreadChannels = markAllReadEvent.getUnreadChannels();
Integer totalUnreadCount = markAllReadEvent.getTotalUnreadCount();
} else if (event instanceof NotificationMarkReadEvent) {
NotificationMarkReadEvent notificationMarkReadEvent = (NotificationMarkReadEvent) event;
Integer unreadChannels = notificationMarkReadEvent.getUnreadChannels();
Integer totalUnreadCount = notificationMarkReadEvent.getTotalUnreadCount();
}
}
);
// Will be implemented soon, raise a GitHub issue if you need this feature https://github.com/GetStream/stream-chat-unity/issues/
Fetch Unread API
The unread endpoint can fetch unread counts server-side, eliminating the need for a client-side connection. It can also be used client-side without requiring a persistent connection to the chat service. This can be useful for including an unread count in notifications or for gently polling when a user loads the application to keep the client up to date without loading up the entire chat.
A user_id whose unread count is fetched through this method is automatically counted as a Monthly Active User. This may affect your bill.
const response = await client.getUnreadCount(userID);
console.log(response.total_unread_count); // total unread count for user
console.log(response.channels); // distribution of unread counts across channels
console.log(response.channel_type); // distribution of unread counts across channel types
console.log(response.total_unread_threads_count) // total unread threads
console.log(response.threads) // list of unread counts per thread
response = client.unread_counts(userID)
print(response["total_unread_count"]) # total unread count for user
print(response["channels"]) # distribution of unread counts across channels
print(response["channel_type"]) # distribution of unread counts across channel types
print(response["total_unread_threads_count"]) # total unread threads
print(response["threads"]) # list of unread counts per thread
$response = $client->unreadCounts(userID);
echo $response["total_unread_count"]; // total unread count for user
echo $response["channels"]; // distribution of unread counts across channels
echo $response["channel_type"]; // distribution of unread counts across channel types
echo $response["total_unread_threads_count"]; // total unread threads
echo $response["threads"]; // list of unread counts per thread
resp, err := client.UnreadCounts(context.Background(), userID)
if err != nil {
panic(err)
}
fmt.Println(resp.TotalUnreadCount)
fmt.Println(resp.Channels)
fmt.Println(resp.ChannelType)
fmt.Println(resp.TotalUnreadThreadsCount)
fmt.Println(resp.Threads)
var current = await Client.GetLatestUnreadCountsAsync();
Debug.Log(current.TotalUnreadCount); // Total unread messages
Debug.Log(current.TotalUnreadThreadsCount); // Total unread threads
foreach (var unreadChannel in current.UnreadChannels)
{
Debug.Log(unreadChannel.ChannelCid); // CID of the channel with unread messages
Debug.Log(unreadChannel.UnreadCount); // Count of unread messages
Debug.Log(unreadChannel.LastRead); // Datetime of the last read message
}
foreach (var unreadChannelByType in current.UnreadChannelsByType)
{
Debug.Log(unreadChannelByType.ChannelType); // Channel type
Debug.Log(unreadChannelByType.ChannelCount); // How many channels of this type have unread messages
Debug.Log(unreadChannelByType.UnreadCount); // How many unread messages in all channels of this type
}
foreach (var unreadThread in current.UnreadThreads)
{
Debug.Log(unreadThread.ParentMessageId); // Message ID of the parent message for this thread
Debug.Log(unreadThread.LastReadMessageId); // Last read message in this thread
Debug.Log(unreadThread.UnreadCount); // Count of unread messages
Debug.Log(unreadThread.LastRead); // Datetime of the last read message
}
This endpoint will return the last 100 unread channels, they are sorted by last_message_at.
Batch Fetch Unread API
The batch unread endpoint works the same way as the non-batch version with the exception that it can handle multiple user IDs at once and that it is restricted to server-side only.
const response = await client.getUnreadCountBatch([userID1, userID2]);
console.log(response.counts_by_user[userID1].total_unread_count); // total unread count for userID1
console.log(response.counts_by_user[userID1].channels); // distribution of unread counts across channels for userID1
console.log(response.counts_by_user[userID1].channel_type); // distribution of unread counts across channel types for userID1
console.log(response.counts_by_user[userID1].total_unread_threads_count) // total unread threads count for userID1
console.log(response.counts_by_user[userID1].threads) // list of unread counts per thread for userID1
response = client.unread_counts_batch([userID1, userID2])
print(response["counts_by_user"][userID1]["total_unread_count"]) # total unread count for userID1
print(response["counts_by_user"][userID1]["channels"]) # distribution of unread counts across channels for userID1
print(response["counts_by_user"][userID1]["channel_type"]) # distribution of unread counts across channel types for userID1
print(response["counts_by_user"][userID1]["total_unread_threads_count"]) # total unread threads count for userID1
print(response["counts_by_user"][userID1]["threads"]) # list of unread counts per thread for userID1
$response = $client->unreadCountsBatch([userID1, userID2]);
echo $response["counts_by_user"][userID1]["total_unread_count"]; // total unread count for userID1
echo $response["counts_by_user"][userID1]["channels"]; // distribution of unread counts across channels for userID1
echo $response["counts_by_user"][userID1]["channel_type"]; // distribution of unread counts across channel types for userID1
echo $response["counts_by_user"][userID1]["total_unread_threads_count"]) // total unread threads count for userID1
echo $response["counts_by_user"][userID1]["threads"]) // list of unread counts per thread for userID1
resp, err := client.UnreadCountsBatch(context.Background(), []string{userID1, userID2})
if err != nil {
panic(err)
}
fmt.Println(resp.CountsByUser[userID1].TotalUnreadCount)
fmt.Println(resp.CountsByUser[userID1].Channels)
fmt.Println(resp.CountsByUser[userID1].ChannelType)
fmt.Println(resp.CountsByUser[userID1].TotalUnreadThreadsCount)
fmt.Println(resp.CountsByUser[userID1].Threads)
If a user ID is not returned in the response then the API couldnt find a user with that ID