Skip to content

Commit 3bba1cd

Browse files
Implement the "pending" status for Memberships created from Invites (#21)
* Implement the "pending" status for Memberships created from Invites * add code snippet for GetInvitees() * Add filter argument to GetInvitees * Also add filter to GetInvitees on Unity side * PubNub SDK v1.2.0 release. * fix pubnub.yml --------- Co-authored-by: PubNub Release Bot <[email protected]>
1 parent 95d7a0c commit 3bba1cd

File tree

12 files changed

+155
-17
lines changed

12 files changed

+155
-17
lines changed

.pubnub.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,15 @@
11
---
2-
version: v1.1.1
2+
version: v1.2.0
33
changelog:
4+
- date: 2025-11-24
5+
version: v1.2.0
6+
changes:
7+
- type: feature
8+
text: "Memberships generated from Invite methods now have Status pending and can be filtered out in GetMemberships()."
9+
- type: feature
10+
text: "Added new overloads for update related methods and events so that they now use ChatEntityChangeType to convey the type of update."
11+
- type: improvement
12+
text: "Changed SetListening(...) and AddListenerTo(...) methods to align with other Chat SDKs. New methods are use the Stream and StreamOn naming convetions."
413
- date: 2025-11-06
514
version: v1.1.1
615
changes:

c-sharp-chat/PubnubChatApi/PubNubChatApi.Tests/ChannelTests.cs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,35 @@ public async Task TestGetMemberships()
169169
Assert.That(memberships.Memberships.Count, Is.GreaterThanOrEqualTo(1));
170170
}
171171

172+
[Test]
173+
public async Task TestGetInvitees()
174+
{
175+
var channel = TestUtils.AssertOperation(await chat.CreatePublicConversation());
176+
await channel.Invite(user);
177+
await Task.Delay(3500);
178+
var invitees = TestUtils.AssertOperation(await channel.GetInvitees());
179+
Assert.True(invitees.Memberships.Any(x => x.UserId == user.Id && x.ChannelId == channel.Id && x.MembershipData.Status == "pending"));
180+
181+
//Cleanup
182+
await channel.Delete();
183+
}
184+
185+
[Test]
186+
public async Task TestInviteAndJoin()
187+
{
188+
var channel = TestUtils.AssertOperation(await chat.CreatePublicConversation());
189+
await channel.Invite(user);
190+
await Task.Delay(3500);
191+
var invitees = TestUtils.AssertOperation(await channel.GetInvitees());
192+
Assert.True(invitees.Memberships.Any(x => x.UserId == user.Id && x.ChannelId == channel.Id && x.MembershipData.Status == "pending"));
193+
await channel.Join();
194+
await Task.Delay(3500);
195+
invitees = TestUtils.AssertOperation(await channel.GetInvitees());
196+
Assert.False(invitees.Memberships.Any());
197+
var members = TestUtils.AssertOperation(await channel.GetMemberships());
198+
Assert.True(members.Memberships.Any(x => x.UserId == user.Id && x.ChannelId == channel.Id && x.MembershipData.Status != "pending"));
199+
}
200+
172201
[Test]
173202
public async Task TestStartTyping()
174203
{

c-sharp-chat/PubnubChatApi/PubNubChatApi.Tests/MembershipTests.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,13 @@ public async Task TestUpdateMemberships()
8585
[Test]
8686
public async Task TestInvite()
8787
{
88-
var testChannel = TestUtils.AssertOperation(await chat.CreateGroupConversation([user], "test_invite_group_channel")).CreatedChannel;
88+
var testChannel = TestUtils.AssertOperation(await chat.CreateGroupConversation([user], Guid.NewGuid().ToString())).CreatedChannel;
8989
var testUser = await chat.GetOrCreateUser("test_invite_user");
9090
var returnedMembership = TestUtils.AssertOperation(await testChannel.Invite(testUser));
91-
Assert.True(returnedMembership.ChannelId == testChannel.Id && returnedMembership.UserId == testUser.Id);
91+
Assert.True(returnedMembership.ChannelId == testChannel.Id && returnedMembership.UserId == testUser.Id && returnedMembership.MembershipData.Status == "pending");
92+
93+
//Cleanup
94+
await testChannel.Delete();
9295
}
9396

9497
[Test]
@@ -106,6 +109,7 @@ public async Task TestInviteMultiple()
106109
returnedMemberships.Count == 2 &&
107110
returnedMemberships.Any(x => x.UserId == secondUser.Id && x.ChannelId == testChannel.Id) &&
108111
returnedMemberships.Any(x => x.UserId == thirdUser.Id && x.ChannelId == testChannel.Id));
112+
Assert.True(returnedMemberships.All(x => x.MembershipData.Status == "pending"));
109113
}
110114

111115
[Test]

c-sharp-chat/PubnubChatApi/PubnubChatApi/Entities/Channel.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1148,7 +1148,7 @@ public async Task<ChatOperationResult<List<string>>> WhoIsPresent()
11481148
/// <param name="sort">The sort parameter.</param>
11491149
/// <param name="limit">The maximum amount of the memberships received.</param>
11501150
/// <param name="page">The page object for pagination.</param>
1151-
/// <returns>A ChatOperationResult containing the list of the <c>Membership</c> objects.</returns>
1151+
/// <returns>A ChatOperationResult containing a wrapper object with the list of the <c>Membership</c> objects.</returns>
11521152
/// <example>
11531153
/// <code>
11541154
/// var channel = //...
@@ -1166,6 +1166,40 @@ public async Task<ChatOperationResult<MembersResponseWrapper>> GetMemberships(st
11661166
return await chat.GetChannelMemberships(Id, filter, sort, limit, page).ConfigureAwait(false);
11671167
}
11681168

1169+
/// <summary>
1170+
/// Gets the list of the <c>Membership</c> objects which have a Status of "pending".
1171+
/// <para>
1172+
/// Gets the list of the <c>Membership</c> objects that represent the users that are invited
1173+
/// to the channel.
1174+
/// </para>
1175+
/// </summary>
1176+
/// <param name="filter">The filter parameter. Note that it will always contain "status == \"pending\"" in the end request.</param>
1177+
/// <param name="sort">The sort parameter.</param>
1178+
/// <param name="limit">The maximum amount of the memberships received.</param>
1179+
/// <param name="page">The page object for pagination.</param>
1180+
/// <returns>A ChatOperationResult containing a wrapper object with the list of the <c>Membership</c> objects.</returns>
1181+
/// <example>
1182+
/// <code>
1183+
/// var channel = //...
1184+
/// var result = await channel.GetInvitees(limit: 10);
1185+
/// var invites = result.Result.Memberships;
1186+
/// foreach (var invited in invites) {
1187+
/// Console.WriteLine($"Invited user: {invited.UserId}");
1188+
/// }
1189+
/// </code>
1190+
/// </example>
1191+
/// <seealso cref="Membership"/>
1192+
public async Task<ChatOperationResult<MembersResponseWrapper>> GetInvitees(string filter = "", string sort = "", int limit = 0,
1193+
PNPageObject page = null)
1194+
{
1195+
var finalFilter = "status == \"pending\"";
1196+
if (!string.IsNullOrEmpty(filter))
1197+
{
1198+
finalFilter = $"{filter} && {finalFilter}";
1199+
}
1200+
return await chat.GetChannelMemberships(Id, finalFilter, sort, limit, page).ConfigureAwait(false);
1201+
}
1202+
11691203
/// <summary>
11701204
/// Asynchronously gets the <c>Message</c> object for the given timetoken sent from this <c>Channel</c>.
11711205
/// </summary>

c-sharp-chat/PubnubChatApi/PubnubChatApi/Entities/Chat.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -357,10 +357,10 @@ public async Task<ChatOperationResult<Membership>> InviteToChannel(string channe
357357
new()
358358
{
359359
Channel = channelId,
360+
Status = "pending"
360361
//TODO: these too here?
361362
//TODO: again, should ChatMembershipData from Create(...)Channel also be passed here?
362363
/*Custom = ,
363-
Status = ,
364364
Type = */
365365
}
366366
}).ExecuteAsync().ConfigureAwait(false);
@@ -380,7 +380,10 @@ public async Task<ChatOperationResult<Membership>> InviteToChannel(string channe
380380
var inviteEventPayload = $"{{\"channelType\": \"{channel.Result.Type}\", \"channelId\": {channelId}}}";
381381
await EmitEvent(PubnubChatEventType.Invite, userId, inviteEventPayload).ConfigureAwait(false);
382382

383-
var newMembership = new Membership(this, userId, channelId, new ChatMembershipData());
383+
var newMembership = new Membership(this, userId, channelId, new ChatMembershipData()
384+
{
385+
Status = "pending"
386+
});
384387
await newMembership.SetLastReadMessageTimeToken(ChatUtils.TimeTokenNow()).ConfigureAwait(false);
385388

386389
result.Result = newMembership;
@@ -413,7 +416,7 @@ public async Task<ChatOperationResult<List<Membership>>> InviteMultipleToChannel
413416
PNChannelMemberField.UUID_STATUS
414417
})
415418
//TODO: again, should ChatMembershipData from Create(...)Channel also be passed here?
416-
.Uuids(users.Select(x => new PNChannelMember() { Custom = x.CustomData, Uuid = x.Id }).ToList())
419+
.Uuids(users.Select(x => new PNChannelMember() { Custom = x.CustomData, Uuid = x.Id, Status = "pending"}).ToList())
417420
.ExecuteAsync().ConfigureAwait(false);
418421

419422
if (result.RegisterOperation(inviteResponse))

c-sharp-chat/PubnubChatApi/PubnubChatApi/Entities/Data/ChatMembershipData.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ namespace PubnubChatApi
1515
public class ChatMembershipData
1616
{
1717
public Dictionary<string, object> CustomData { get; set; } = new();
18-
public string Status { get; set; }
19-
public string Type { get; set; }
18+
public string Status { get; set; } = string.Empty;
19+
public string Type { get; set; } = string.Empty;
2020

2121
public static implicit operator ChatMembershipData(PNChannelMembersItemResult membersItem)
2222
{

unity-chat/PubnubChatUnity/Assets/PubnubChat/Runtime/PubnubChatApi/Entities/Channel.cs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1148,7 +1148,7 @@ public async Task<ChatOperationResult<List<string>>> WhoIsPresent()
11481148
/// <param name="sort">The sort parameter.</param>
11491149
/// <param name="limit">The maximum amount of the memberships received.</param>
11501150
/// <param name="page">The page object for pagination.</param>
1151-
/// <returns>A ChatOperationResult containing the list of the <c>Membership</c> objects.</returns>
1151+
/// <returns>A ChatOperationResult containing a wrapper object with the list of the <c>Membership</c> objects.</returns>
11521152
/// <example>
11531153
/// <code>
11541154
/// var channel = //...
@@ -1166,6 +1166,40 @@ public async Task<ChatOperationResult<MembersResponseWrapper>> GetMemberships(st
11661166
return await chat.GetChannelMemberships(Id, filter, sort, limit, page).ConfigureAwait(false);
11671167
}
11681168

1169+
/// <summary>
1170+
/// Gets the list of the <c>Membership</c> objects which have a Status of "pending".
1171+
/// <para>
1172+
/// Gets the list of the <c>Membership</c> objects that represent the users that are invited
1173+
/// to the channel.
1174+
/// </para>
1175+
/// </summary>
1176+
/// <param name="filter">The filter parameter. Note that it will always contain "status == \"pending\"" in the end request.</param>
1177+
/// <param name="sort">The sort parameter.</param>
1178+
/// <param name="limit">The maximum amount of the memberships received.</param>
1179+
/// <param name="page">The page object for pagination.</param>
1180+
/// <returns>A ChatOperationResult containing a wrapper object with the list of the <c>Membership</c> objects.</returns>
1181+
/// <example>
1182+
/// <code>
1183+
/// var channel = //...
1184+
/// var result = await channel.GetInvitees(limit: 10);
1185+
/// var invites = result.Result.Memberships;
1186+
/// foreach (var invited in invites) {
1187+
/// Console.WriteLine($"Invited user: {invited.UserId}");
1188+
/// }
1189+
/// </code>
1190+
/// </example>
1191+
/// <seealso cref="Membership"/>
1192+
public async Task<ChatOperationResult<MembersResponseWrapper>> GetInvitees(string filter = "", string sort = "", int limit = 0,
1193+
PNPageObject page = null)
1194+
{
1195+
var finalFilter = "status == \"pending\"";
1196+
if (!string.IsNullOrEmpty(filter))
1197+
{
1198+
finalFilter = $"{filter} && {finalFilter}";
1199+
}
1200+
return await chat.GetChannelMemberships(Id, finalFilter, sort, limit, page).ConfigureAwait(false);
1201+
}
1202+
11691203
/// <summary>
11701204
/// Asynchronously gets the <c>Message</c> object for the given timetoken sent from this <c>Channel</c>.
11711205
/// </summary>

unity-chat/PubnubChatUnity/Assets/PubnubChat/Runtime/PubnubChatApi/Entities/Chat.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -357,10 +357,10 @@ public async Task<ChatOperationResult<Membership>> InviteToChannel(string channe
357357
new()
358358
{
359359
Channel = channelId,
360+
Status = "pending"
360361
//TODO: these too here?
361362
//TODO: again, should ChatMembershipData from Create(...)Channel also be passed here?
362363
/*Custom = ,
363-
Status = ,
364364
Type = */
365365
}
366366
}).ExecuteAsync().ConfigureAwait(false);
@@ -380,7 +380,10 @@ public async Task<ChatOperationResult<Membership>> InviteToChannel(string channe
380380
var inviteEventPayload = $"{{\"channelType\": \"{channel.Result.Type}\", \"channelId\": {channelId}}}";
381381
await EmitEvent(PubnubChatEventType.Invite, userId, inviteEventPayload).ConfigureAwait(false);
382382

383-
var newMembership = new Membership(this, userId, channelId, new ChatMembershipData());
383+
var newMembership = new Membership(this, userId, channelId, new ChatMembershipData()
384+
{
385+
Status = "pending"
386+
});
384387
await newMembership.SetLastReadMessageTimeToken(ChatUtils.TimeTokenNow()).ConfigureAwait(false);
385388

386389
result.Result = newMembership;
@@ -413,7 +416,7 @@ public async Task<ChatOperationResult<List<Membership>>> InviteMultipleToChannel
413416
PNChannelMemberField.UUID_STATUS
414417
})
415418
//TODO: again, should ChatMembershipData from Create(...)Channel also be passed here?
416-
.Uuids(users.Select(x => new PNChannelMember() { Custom = x.CustomData, Uuid = x.Id }).ToList())
419+
.Uuids(users.Select(x => new PNChannelMember() { Custom = x.CustomData, Uuid = x.Id, Status = "pending"}).ToList())
417420
.ExecuteAsync().ConfigureAwait(false);
418421

419422
if (result.RegisterOperation(inviteResponse))

unity-chat/PubnubChatUnity/Assets/PubnubChat/Runtime/PubnubChatApi/Entities/Data/ChatMembershipData.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ namespace PubnubChatApi
1515
public class ChatMembershipData
1616
{
1717
public Dictionary<string, object> CustomData { get; set; } = new();
18-
public string Status { get; set; }
19-
public string Type { get; set; }
18+
public string Status { get; set; } = string.Empty;
19+
public string Type { get; set; } = string.Empty;
2020

2121
public static implicit operator ChatMembershipData(PNChannelMembersItemResult membersItem)
2222
{

unity-chat/PubnubChatUnity/Assets/PubnubChat/Runtime/UnityChatPNSDKSource.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace PubnubChatApi
55
{
66
public class UnityChatPNSDKSource : IPNSDKSource
77
{
8-
private const string build = "1.1.1";
8+
private const string build = "1.2.0";
99

1010
private string GetPlatformString()
1111
{

0 commit comments

Comments
 (0)