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 } }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:
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 messagesfinal response = await client.setUser(user, "{{ chat_user_token }}"); print("There are ${response.me.unreadChannels} unread channels"); print("There are ${response.me.totalUnreadCount} unread messages");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:
channelClient.markRead().enqueue { result -> if (result.isSuccess) { // Messages in the channel marked as read } else { // Handle result.error() } }// mark all messages on a channel as read await channel.markRead();await channel.markRead();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_newnotification.mark_readnotification.mark_unread
These two events include the fields total_unread_count and unread_channels. You can listen to them all at once like this:
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 } } }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}"); });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 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 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 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 couldnt find a user with that ID