Skip to content

Commit 9e71ed2

Browse files
Add new method to fetch unread message counts for user's channels (#185)
* Add new API method returning paginated unread message counts * PubNub kotlin 0.13.0 release --------- Co-authored-by: PubNub Release Bot <120067856+pubnub-release-bot@users.noreply.github.com>
1 parent 39baab4 commit 9e71ed2

File tree

14 files changed

+186
-10
lines changed

14 files changed

+186
-10
lines changed

.pubnub.yml

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: kmp-chat
2-
version: 0.12.1
2+
version: 0.13.0
33
schema: 1
44
scm: github.com/pubnub/kmp-chat
55
sdks:
@@ -21,8 +21,8 @@ sdks:
2121
-
2222
distribution-type: library
2323
distribution-repository: maven
24-
package-name: pubnub-chat-0.12.1
25-
location: https://repo.maven.apache.org/maven2/com/pubnub/pubnub-chat/0.12.1/
24+
package-name: pubnub-chat-0.13.0
25+
location: https://repo.maven.apache.org/maven2/com/pubnub/pubnub-chat/0.13.0/
2626
supported-platforms:
2727
supported-operating-systems:
2828
Android:
@@ -77,6 +77,11 @@ sdks:
7777
license-url: https://github.com/pubnub/kotlin/blob/master/LICENSE
7878
is-required: Required
7979
changelog:
80+
- date: 2025-05-09
81+
version: 0.13.0
82+
changes:
83+
- type: feature
84+
text: "Add new API method returning paginated unread message counts."
8085
- date: 2025-03-21
8186
version: 0.12.1
8287
changes:

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ let package = Package(
1818
targets: [
1919
.binaryTarget(
2020
name: "PubNubChatRemoteBinaryPackage",
21-
url: "https://github.com/pubnub/kmp-chat/releases/download/kotlin-0.12.1/PubNubChat.xcframework.zip",
21+
url: "https://github.com/pubnub/kmp-chat/releases/download/kotlin-0.13.0/PubNubChat.xcframework.zip",
2222
checksum: "3153a4429665fe51861cfdc3b63cdb873260e777dcb41d52f14301698b2b2a91"
2323
)
2424
]

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ You will need the publish and subscribe keys to authenticate your app. Get your
3131
<dependency>
3232
<groupId>com.pubnub</groupId>
3333
<artifactId>pubnub-chat</artifactId>
34-
<version>0.12.1</version>
34+
<version>0.13.0</version>
3535
</dependency>
3636
```
3737

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ SONATYPE_HOST=DEFAULT
1010
SONATYPE_AUTOMATIC_RELEASE=false
1111
GROUP=com.pubnub
1212
POM_PACKAGING=jar
13-
VERSION_NAME=0.12.1
13+
VERSION_NAME=0.13.0
1414

1515

1616
POM_NAME=PubNub Chat SDK

js-chat/tests/channel.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,34 @@ describe("Channel test", () => {
102102
expect(secondPage.messages.length).toBeGreaterThanOrEqual(1)
103103
})
104104

105+
test("should fetch unread messages counts with pagination", async () => {
106+
const channel1 = await createRandomChannel()
107+
const channel2 = await createRandomChannel()
108+
109+
await channel1.invite(chat.currentUser)
110+
await channel2.invite(chat.currentUser)
111+
await channel1.sendText("Some text")
112+
await channel2.sendText("Some text")
113+
114+
const firstResults = await chat.fetchUnreadMessagesCounts({ limit: 1 })
115+
expect(firstResults.countsByChannel.length).toEqual(1)
116+
expect(firstResults.countsByChannel.at(0).count).toEqual(1)
117+
expect(firstResults.countsByChannel.at(0).channel.id in arrayOf(channel1.id, channel2.id)).toBeTruthy()
118+
119+
const secondResults = await chat.fetchUnreadMessagesCounts({ page: { next: firstResults.page.next } })
120+
expect(secondResults.countsByChannel.length).toEqual(1)
121+
expect(secondResults.countsByChannel.at(0).count).toEqual(1)
122+
expect(secondResults.countsByChannel.at(0).channel.id in arrayOf(channel1.id, channel2.id)).toBeTruthy()
123+
124+
const thirdResults = await chat.fetchUnreadMessagesCounts({ page: { next: secondResults.page.next } })
125+
expect(thirdResults.countsByChannel.length).toEqual(0)
126+
127+
await channel1.leave()
128+
await channel2.leave()
129+
await channel1.delete()
130+
await channel2.delete()
131+
})
132+
105133
test("should fail when trying to send a message to a non-existent channel", async () => {
106134
const nonExistentChannel = (await chat.getChannel("non-existing-channel")) as Channel
107135

pubnub-chat-api/api/pubnub-chat-api.api

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ public abstract interface class com/pubnub/chat/Chat {
7777
public abstract fun destroy ()V
7878
public abstract fun emitEvent (Ljava/lang/String;Lcom/pubnub/chat/types/EventContent;Ljava/util/Map;)Lcom/pubnub/kmp/PNFuture;
7979
public static synthetic fun emitEvent$default (Lcom/pubnub/chat/Chat;Ljava/lang/String;Lcom/pubnub/chat/types/EventContent;Ljava/util/Map;ILjava/lang/Object;)Lcom/pubnub/kmp/PNFuture;
80+
public abstract fun fetchUnreadMessagesCounts (Ljava/lang/Integer;Lcom/pubnub/api/models/consumer/objects/PNPage;Ljava/lang/String;Ljava/util/Collection;)Lcom/pubnub/kmp/PNFuture;
81+
public static synthetic fun fetchUnreadMessagesCounts$default (Lcom/pubnub/chat/Chat;Ljava/lang/Integer;Lcom/pubnub/api/models/consumer/objects/PNPage;Ljava/lang/String;Ljava/util/Collection;ILjava/lang/Object;)Lcom/pubnub/kmp/PNFuture;
8082
public abstract fun getChannel (Ljava/lang/String;)Lcom/pubnub/kmp/PNFuture;
8183
public abstract fun getChannels (Ljava/lang/String;Ljava/util/Collection;Ljava/lang/Integer;Lcom/pubnub/api/models/consumer/objects/PNPage;)Lcom/pubnub/kmp/PNFuture;
8284
public static synthetic fun getChannels$default (Lcom/pubnub/chat/Chat;Ljava/lang/String;Ljava/util/Collection;Ljava/lang/Integer;Lcom/pubnub/api/models/consumer/objects/PNPage;ILjava/lang/Object;)Lcom/pubnub/kmp/PNFuture;
@@ -464,6 +466,13 @@ public final class com/pubnub/chat/message/MarkAllMessageAsReadResponse {
464466
public final fun getTotal ()I
465467
}
466468

469+
public final class com/pubnub/chat/message/UnreadMessagesCounts {
470+
public fun <init> (Ljava/util/List;Lcom/pubnub/api/models/consumer/objects/PNPage$PNNext;Lcom/pubnub/api/models/consumer/objects/PNPage$PNPrev;)V
471+
public final fun getCountsByChannel ()Ljava/util/List;
472+
public final fun getNext ()Lcom/pubnub/api/models/consumer/objects/PNPage$PNNext;
473+
public final fun getPrev ()Lcom/pubnub/api/models/consumer/objects/PNPage$PNPrev;
474+
}
475+
467476
public abstract interface class com/pubnub/chat/mutelist/MutedUsersManager {
468477
public abstract fun getMutedUsers ()Ljava/util/Set;
469478
public abstract fun muteUser (Ljava/lang/String;)Lcom/pubnub/kmp/PNFuture;

pubnub-chat-api/src/commonMain/kotlin/com/pubnub/chat/Chat.kt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import com.pubnub.api.models.consumer.push.PNPushRemoveChannelResult
1111
import com.pubnub.chat.config.ChatConfiguration
1212
import com.pubnub.chat.message.GetUnreadMessagesCounts
1313
import com.pubnub.chat.message.MarkAllMessageAsReadResponse
14+
import com.pubnub.chat.message.UnreadMessagesCounts
1415
import com.pubnub.chat.mutelist.MutedUsersManager
1516
import com.pubnub.chat.restrictions.Restriction
1617
import com.pubnub.chat.types.ChannelType
@@ -423,13 +424,35 @@ interface Chat {
423424
*
424425
* @return [PNFuture] containing list of [GetUnreadMessagesCounts]
425426
*/
427+
@Deprecated(
428+
message = "Use `fetchUnreadMessagesCounts()` instead",
429+
level = DeprecationLevel.WARNING,
430+
)
426431
fun getUnreadMessagesCounts(
427432
limit: Int? = null,
428433
page: PNPage? = null,
429434
filter: String? = null,
430435
sort: Collection<PNSortKey<PNMembershipKey>> = listOf(),
431436
): PNFuture<List<GetUnreadMessagesCounts>>
432437

438+
/**
439+
* Returns info on all messages you didn't read on all joined channels. You can display this number on UI in the
440+
* channel list of your chat app.
441+
*
442+
* @param limit Number of objects to return in response. The default (and maximum) value is 100.
443+
* @param page Object used for pagination to define which previous or next result page you want to fetch.
444+
* @param filter Expression used to filter the results. Returns only these channels whose properties satisfy the given expression are returned.
445+
* @param sort A collection to specify the sort order. Available options are id, name, and updated. Use asc or desc.
446+
*
447+
* @return [PNFuture] containing list of [UnreadMessagesCounts]
448+
*/
449+
fun fetchUnreadMessagesCounts(
450+
limit: Int? = null,
451+
page: PNPage? = null,
452+
filter: String? = null,
453+
sort: Collection<PNSortKey<PNMembershipKey>> = listOf(),
454+
): PNFuture<UnreadMessagesCounts>
455+
433456
/**
434457
* Allows you to mark as read all messages you didn't read on all joined channels.
435458
*
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.pubnub.chat.message
2+
3+
import com.pubnub.api.models.consumer.objects.PNPage
4+
5+
/**
6+
* Response wrapper for unread message counts that includes pagination information.
7+
*
8+
* @property countsByChannel List of unread messages for different channels.
9+
* @property next The pagination token for fetching the next page of results, if available.
10+
* @property prev The pagination token for fetching the previous page of results, if available.
11+
*/
12+
class UnreadMessagesCounts(
13+
val countsByChannel: List<GetUnreadMessagesCounts>,
14+
val next: PNPage.PNNext?,
15+
val prev: PNPage.PNPrev?
16+
)

pubnub-chat-impl/src/commonMain/kotlin/com/pubnub/chat/internal/ChatImpl.kt

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ import com.pubnub.chat.internal.utils.cyrb53a
8585
import com.pubnub.chat.membership.MembershipsResponse
8686
import com.pubnub.chat.message.GetUnreadMessagesCounts
8787
import com.pubnub.chat.message.MarkAllMessageAsReadResponse
88+
import com.pubnub.chat.message.UnreadMessagesCounts
8889
import com.pubnub.chat.restrictions.Restriction
8990
import com.pubnub.chat.restrictions.RestrictionType
9091
import com.pubnub.chat.types.ChannelMentionData
@@ -834,14 +835,24 @@ class ChatImpl(
834835
filter: String?,
835836
sort: Collection<PNSortKey<PNMembershipKey>>
836837
): PNFuture<List<GetUnreadMessagesCounts>> {
838+
return fetchUnreadMessagesCounts(limit, page, filter, sort).then {
839+
it.countsByChannel
840+
}
841+
}
842+
843+
override fun fetchUnreadMessagesCounts(
844+
limit: Int?,
845+
page: PNPage?,
846+
filter: String?,
847+
sort: Collection<PNSortKey<PNMembershipKey>>
848+
): PNFuture<UnreadMessagesCounts> {
837849
return currentUser.getMemberships(limit = limit, page = page, filter = filter, sort = sort)
838850
.thenAsync { membershipsResponse: MembershipsResponse ->
839851
val memberships = membershipsResponse.memberships
840852
if (memberships.isEmpty()) {
841-
return@thenAsync emptyList<GetUnreadMessagesCounts>().asFuture()
853+
return@thenAsync UnreadMessagesCounts(emptyList(), null, null).asFuture()
842854
}
843855
val channels = memberships.map { membership -> membership.channel.id }
844-
845856
val channelsTimetoken = memberships.map { membership -> membership.lastReadMessageTimetoken ?: 0 }
846857

847858
pubNub.messageCounts(channels = channels, channelsTimetoken = channelsTimetoken)
@@ -854,10 +865,14 @@ class ChatImpl(
854865
GetUnreadMessagesCounts(
855866
channel = membershipMatchingChannel.channel,
856867
membership = membershipMatchingChannel,
857-
count = messageCount
868+
count = messageCount,
858869
)
859870
}
860-
unreadMessageCounts.filter { unreadMessageCount -> unreadMessageCount.count > 0 }
871+
UnreadMessagesCounts(
872+
countsByChannel = unreadMessageCounts.filter { unreadMessageCount -> unreadMessageCount.count > 0 },
873+
prev = membershipsResponse.prev,
874+
next = membershipsResponse.next
875+
)
861876
}
862877
}
863878
}

pubnub-chat-impl/src/commonTest/kotlin/com/pubnub/kmp/utils/FakeChat.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import com.pubnub.chat.internal.timer.TimerManager
2222
import com.pubnub.chat.internal.timer.createTimerManager
2323
import com.pubnub.chat.message.GetUnreadMessagesCounts
2424
import com.pubnub.chat.message.MarkAllMessageAsReadResponse
25+
import com.pubnub.chat.message.UnreadMessagesCounts
2526
import com.pubnub.chat.mutelist.MutedUsersManager
2627
import com.pubnub.chat.restrictions.Restriction
2728
import com.pubnub.chat.types.ChannelType
@@ -251,6 +252,15 @@ abstract class FakeChat(override val config: ChatConfiguration, override val pub
251252
TODO("Not yet implemented")
252253
}
253254

255+
override fun fetchUnreadMessagesCounts(
256+
limit: Int?,
257+
page: PNPage?,
258+
filter: String?,
259+
sort: Collection<PNSortKey<PNMembershipKey>>
260+
): PNFuture<UnreadMessagesCounts> {
261+
TODO("Not yet implemented")
262+
}
263+
254264
override fun getUnreadMessagesCounts(
255265
limit: Int?,
256266
page: PNPage?,

0 commit comments

Comments
 (0)