// Step 1: Get initial unread counts when connecting
const user = await client.connectUser({ id: "myid" }, token);
console.log(user.me.total_unread_count); // total unread messages
console.log(user.me.unread_channels); // number of channels with unread messages
console.log(user.me.unread_threads); // number of unread threads
// Step 2: Listen to events for real-time updates
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);
}
});Unread Counts
The following unread counts are provided by Stream
- A total count of unread messages
- Number of unread channels
- A count of unread threads
- Unread @mentions
- Unread messages per channel
- Unread @mentions per channel
- Unread counts by team
- Unread counts by channel type
Unread counts are first fetched when a user connects. After that they are updated by events. (new message, mark read, delete message, delete channel etc.)
Reading Unread Counts
Unread counts are returned when a user connects. After that, you can listen to events to keep them updated in real-time.
// Step 1: Get initial unread counts when connecting
final response = await client.connectUser(user, "{{ chat_user_token }}");
print("There are ${response.me.unreadChannels} unread channels");
print("There are ${response.me.totalUnreadCount} unread messages");
// Step 2: Listen to events for real-time updates
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}");
});// Step 1: Get initial unread counts when connecting
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
}
}
// Step 2: Listen to events for real-time updates
client.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
}
}
}// Use the currentUserController to get unread counts and listen for changes
let currentUserController = client.currentUserController()
class CurrentUserDelegate: CurrentChatUserControllerDelegate {
func currentUserController(_ controller: CurrentChatUserController,
didChangeCurrentUserUnreadCount: UnreadCount) {
// Handle unread count change (both initial and real-time updates)
}
}
// Keep a strong reference to delegate since controller only keeps a weak reference
let delegate = CurrentUserDelegate()
currentUserController.delegate = delegate
currentUserController.synchronize()// Step 1: Get initial unread counts when connecting
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();
}
});
// Step 2: Listen to events for real-time updates
client.subscribeFor(
new Class[]{
NewMessageEvent.class,
NotificationMessageNewEvent.class,
MarkAllReadEvent.class,
NotificationMarkReadEvent.class
},
event -> {
if (event instanceof NewMessageEvent) {
NewMessageEvent e = (NewMessageEvent) event;
Integer unreadChannels = e.getUnreadChannels();
Integer totalUnreadCount = e.getTotalUnreadCount();
} else if (event instanceof NotificationMessageNewEvent) {
NotificationMessageNewEvent e = (NotificationMessageNewEvent) event;
Integer unreadChannels = e.getUnreadChannels();
Integer totalUnreadCount = e.getTotalUnreadCount();
}
// Handle other event types similarly
}
);// Step 1: Get initial unread counts when connecting
var localUserData = await Client.ConnectUserAsync("api_key", "user_id", "user_token");
Debug.Log(localUserData.UnreadChannels);
Debug.Log(localUserData.TotalUnreadCount);
// You can also access unread counts via Client.LocalUserData after connection
// Or subscribe to the Connected event for real-time updates
Client.Connected += (IStreamLocalUserData userData) =>
{
Debug.Log(userData.UnreadChannels);
Debug.Log(userData.TotalUnreadCount);
};Note that the higher level SDKs offer convenient endpoints for this. Hooks on react, stateflow on Android etc. So you only need to use the events manually if you’re using plain JS.
Unread Counts - Server side
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 threadresponse = 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 threadresp, 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
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 userID1response = 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 userID1resp, 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 couldn’t find a user with that ID
Mark Read
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();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>" });Mark All As Read
You can mark all channels as read for a user like this:
// client-side
await client.markAllRead();
// mark all as read for one user server-side
await serverSideClient.markAllRead({ user: { id: "myid" } });// mark all messages on all channels as read for one user, server-side
$client->markAllRead('user-id');await client.markRead();let channelListController = client.channelListController(query: .init(filter: .containMembers(userIds: ["joe"])))
channelListController.markAllRead()client.markAllRead().enqueue { result ->
if (result.isSuccess) {
//Handle success
} else {
//Handle failure
}
}Client->MarkAllRead();client.markAllRead().enqueue((result) -> {
if (result.isSuccess()) {
//Handle success
} else {
//Handle failure
}
});// Mark this message as last read
await message.MarkMessageAsLastReadAsync();
// Mark whole channel as read
await channel.MarkChannelReadAsync();Read State - Showing how far other users have read
When you retrieve a channel from the API (e.g. using query channels), the read state for all members is included in the response. This allows you to display which messages are read by each user. For each member, we include the last time they marked the channel as read.
const channel = client.channel("messaging", "test");
await channel.watch();
console.log(channel.state.read);
//{ '2fe6019c-872f-482a-989e-ecf4f786501b':
// { user:
// {
// id: '2fe6019c-872f-482a-989e-ecf4f786501b',
// role: 'user',
// created_at: '2019-04-24T13:09:19.664378Z',
// updated_at: '2019-04-24T13:09:23.784642Z',
// last_active: '2019-04-24T13:09:23.781641Z',
// online: true
// },
// last_read: '2019-04-24T13:09:21.623Z',
// }
//}final response = await channel.watch();
// readState is the list of read states for each user on the channel
List<Read> readState = response.read;let channelController = client.channelController(for: .init(type: .messaging, id: "general"))
channelController.synchronize() { error in
if error == nil {
channelController.channel?.reads
}
}// Get channel
val queryChannelRequest = QueryChannelRequest().withState()
client.queryChannel(
channelType = "channel-type",
channelId = "channel-id",
request = queryChannelRequest,
).enqueue { result ->
if (result.isSuccess) {
// readState is the list of read states for each user on the channel
val readState: List<ChannelUserRead> = result.data().read
} else {
// Handle result.error()
}
}const FChannelProperties Properties{TEXT("channel-type"), TEXT("channel-id")};
Client->WatchChannel(
Properties,
[](UChatChannel* Channel)
{
// ReadState is the list of read states for each user on the channel
const TArray<FRead>& ReadState = Channel->State.Read;
});// Get channel
QueryChannelRequest queryChannelRequest = new QueryChannelRequest().withState();
client.queryChannel("channel-type", "channel-id", queryChannelRequest).enqueue((result) -> {
if (result.isSuccess()) {
// readState is the list of read states for each user on the channel
List<ChannelUserRead> readState = result.data().getRead();
} else {
// Handle result.error()
}
});// Every channel maintains a full list of read state for each channel member
foreach (var read in channel.Read)
{
Debug.Log(read.User); // User
Debug.Log(read.UnreadMessages); // How many unread messages
Debug.Log(read.LastRead); // Last read date
}Unread Messages Per Channel
You can retrieve the count of unread messages for the current user on a channel like this:
channel.countUnread();channel.unreadCount();let channelController = client.channelController(for: .init(type: .messaging, id: "general"))
class ChannelDelegate: ChatChannelControllerDelegate {
func channelController(_ channelController: ChatChannelController,
didUpdateChannel channel: EntityChange<ChatChannel>) {
channelController.channel?.unreadCount
}
}
channelController.delegate = ChannelDelegate()
channelController.synchronize()// Get channel
val queryChannelRequest = QueryChannelRequest().withState()
client.queryChannel(
channelType = "channel-type",
channelId = "channel-id",
request = queryChannelRequest,
).enqueue { result ->
if (result.isSuccess) {
// Unread count for current user
val unreadCount = result.data().unreadCount
} else {
// Handle result.error()
}
}Channel->State.UnreadCount();// Get channel
QueryChannelRequest queryChannelRequest = new QueryChannelRequest().withState();
client.queryChannel("channel-type", "channel-id", queryChannelRequest).enqueue((result) -> {
if (result.isSuccess()) {
// Unread count for current user
Integer unreadCount = result.data().getUnreadCount();
} else {
// Handle result.error()
}
});// Every channel maintains a full list of read state for each channel member
foreach (var read in channel.Read)
{
Debug.Log(read.User); // User
Debug.Log(read.UnreadMessages); // How many unread messages
Debug.Log(read.LastRead); // Last read date
}Unread Mentions Per Channel
You can retrieve the count of unread messages mentioning the current user on a channel like this:
channel.countUnreadMentions();let channelController = client.channelController(for: .init(type: .messaging, id: "general"))
class ChannelDelegate: ChatChannelControllerDelegate {
func channelController(_ channelController: ChatChannelController,
didUpdateChannel channel: EntityChange<ChatChannel>) {
channelController.channel?.unreadCount.mentionedMessages
}
}
channelController.delegate = ChannelDelegate()
channelController.synchronize()// Get channel
val queryChannelRequest = QueryChannelRequest().withState()
val currentUser = client.getCurrentUser()
if (currentUser == null) {
// Handle user not connected state
return
}
client.queryChannel(
channelType = "channel-type",
channelId = "channel-id",
request = queryChannelRequest,
).enqueue { result ->
if (result.isSuccess) {
// Unread mentions
val channel = result.data()
val unreadCount = channel.countUnreadMentionsForUser(currentUser)
} else {
// Handle result.error()
}
}// Get channel
QueryChannelRequest queryChannelRequest = new QueryChannelRequest().withState();
User currentUser = client.getCurrentUser();
if (currentUser == null) {
// Handle user not connected state
return;
}
client.queryChannel("channel-type", "channel-id", queryChannelRequest).enqueue((result) -> {
if (result.isSuccess()) {
// Unread mentions
Channel channel = result.data();
Integer unreadCount = ChannelExtensionKt.countUnreadMentionsForUser(channel, currentUser);
} else {
// Handle result.error()
}
});// Will be implemented soon, raise a GitHub issue if you need this feature https://github.com/GetStream/stream-chat-unity/issues/