Unread messages
PubNub allows you to get a count of messages in channels that were published after a specific timestamp. When users return to their applications, the app can indicate which channels have new messages and also display counts of unread messages in each channel. You can build this feature easily so your users never miss a message and get the best chat experience.
There are two primary ways you can store a user's last-read timetokens:
- Store timetoken cursors locally within the app
- Store timetoken cursors on PubNub using App Context
User ID / UUID
User ID is also referred to as UUID
/uuid
in some APIs and server responses but holds the value of the userId
parameter you set during initialization.
Store cursors locally
With the first option, you'll need to store the timetoken of the last message that was read by the user in a channel. If the user uses multiple channels, you'll need to store multiple timetokens, one per channel.
Store cursors using App Context
With the second option, you can store these timetokens inside of PubNub. The App Context feature enables you to persist membership associations between users and channels, and any custom fields that might be useful for your app. Refer to Channel Metadata for more information on channel memberships.
Set channel cursors
Use the setMemberships
method to store timetokens as custom fields on each user-channel membership on PubNub.
- JavaScript
- Swift
- Objective-C
- C#
pubnub.objects.setMemberships({
channels: [{
id: "channel-1",
custom: {
lastReadTimetoken: "15518041524300120"
},
{
id: "channel-2",
custom: {
lastReadTimetoken: "15518041524300251"
}
}]
});
import PubNubSDK
// Define custom data structure
struct CustomFields: Codable {
let lastReadTimetoken: String
}
// Create memberships with custom fields
let newMembershipChannel1 = PubNubMembershipMetadataBase(
uuidMetadataId: "my_user",
channelMetadataId: "channel-1",
custom: ["lastReadTimetoken": "15518041524300120"]
)
let newMembershipChannel2 = PubNubMembershipMetadataBase(
show all 31 linesNSArray<NSDictionary *> *channels = @[
@{ @"channel": @"my_channel_1", @"custom": @{ @"lastReadTimetoken": @"15518041524300120" } },
@{ @"channel": @"my_channel_2", @"custom": @{ @"lastReadTimetoken": @"15518041524300251" } }
];
self.client.objects().setMemberships()
.uuid(@"uuid")
.channels(channels)
.includeFields(PNMembershipCustomField | PNMembershipChannelField)
.performWithCompletion(^(PNManageMembershipsStatus *status) {
if (!status.isError) {
/**
* UUID's memberships successfully set.
* Result object has following information:
* status.data.memberships - List of UUID's existing memberships.
show all 28 linesList<PNMembership> setMembershipChannelMetadataIdList = new List<PNMembership>();
if (!string.IsNullOrEmpty(seMembershipChannelMetaId))
{
setMembershipChannelMetadataIdList.Add(new PNMembership() { Channel = "my_channel_1", Custom = new Dictionary<string, object>() { { "lastReadTimetoken", "15518041524300120" } } });
setMembershipChannelMetadataIdList.Add(new PNMembership() { Channel = "my_channel_2", Custom = new Dictionary<string, object>() { { "lastReadTimetoken", "15518041524300251" } } });
}
PNResult<PNMembershipsResult> setMembershipsResponse = await pubnub.SetMemberships()
.Uuid("my-userId")
.Channels(setMembershipChannelMetadataIdList)
.Include(new PNMembershipField[] { PNMembershipField.CUSTOM, PNMembershipField.CHANNEL, PNMembershipField.CHANNEL_CUSTOM })
.IncludeCount(true)
.ExecuteAsync();
PNMembershipsResult setMembershipsResult = setMembershipsResponse.Result;
PNStatus status = setMembershipsResponse.Status;
Get channel cursors
When the user returns to the app, use the getMemberships
method to fetch channel memberships for the user along with its custom fields.
- JavaScript
- Swift
- Objective-C
- C#
pubnub.objects.getMemberships({
include: {
customFields: true
}
});
pubnub.fetchMemberships(uuid: "my_user") { result in
switch result {
case let .success(response):
print("The channel memberships for the User ID \(response.memberships)")
if let nextPage = response.next {
print("The next page used for pagination: \(nextPage)")
}
case let .failure(error):
print("Fetch Memberships request failed with error: \(error.localized### Description)")
}
}
self.client.objects().memberships()
.includeFields(PNMembershipCustomField)
.performWithCompletion(^(PNFetchMembershipsResult *result, PNErrorStatus *status) {
});
PNResult<PNGetMembershipsResult> getMembershipsResponse = await pubnub.GetMemberships()
.Include(new PNMembershipField[] { PNMembershipField.CUSTOM })
.IncludeCount(true)
.Page(new PNPageObject() { Next = "", Prev = "" })
.ExecuteAsync();
PNGetMembershipsResult getMembershipsResult = getMembershipsResponse.Result;
PNStatus status = getMembershipsResponse.Status;
The timetokens can then be passed to the messageCounts
method.
Get counts of unread messages
The messageCounts
method returns a count of unread messages in one or more channels. The count returned is the number of messages in Message Persistence with a timetoken value greater than or equal to the passed value in the timetoken
parameter. For more details on retrieving counts of unread messages, refer to Get Message Counts.
- JavaScript
- Swift
- Java
- Unity
pubnub.messageCounts({
channels: ['ch-1', 'ch-2'],
channelTimetokens: ['15518041524300251'],
}, (status, results) => {
// handle status, response
console.log(status);
console.log(results);
});
pubnub.messageCounts(
channels: [
"ch-1": "15526611838554310",
"ch-2": "15526611838554309"
],
channelTimetokens: ["15518041524300251"]
) { result in
switch result {
case let .success(response):
print("Successful Message Count Response: \(response)")
case let .failure(error):
print("Failed Message Count Response: \(error.localizedDescription)")
}
}
Long lastHourTimetoken = (Calendar.getInstance().getTimeInMillis() - TimeUnit.HOURS.toMillis(1)) * 10000L;
pubnub.messageCounts()
.channels(Arrays.asList("ch-1", "ch-2"))
.channelsTimetoken(Arrays.asList(lastHourTimetoken))
.async(result -> {
result.onSuccess(res -> {
for (Map.Entry<String, Long> messageCountEntry : res.getChannels().entrySet()) {
messageCountEntry.getKey(); // the channel name
messageCountEntry.getValue(); // number of messages for that channel
}
}).onFailure(exception -> {
exception.printStackTrace();
});
});
List<string> channelList = new List<string>();
channelList.Add("ch-1");
channelList.Add("ch-2");
pubnub.MessageCounts().Channels(channelList).ChannelsTimetoken(new List<long>{15518041524300251}).Async((result, status) => {
if (!status.Error) {
if((result.Channels != null)){
Debug.Log(string.Format("MessageCounts, {0}", result.Channels.Count));
foreach(KeyValuePair<string, int> kvp in result.Channels){
Debug.Log(string.Format("==kvp.Key {0}, kvp.Value {1} ", kvp.Key, kvp.Value));
}
}
} else {
Debug.Log(status.Error);
Debug.Log(status.ErrorData.Info);
show all 17 lines