Skip to content

Commit f24bae1

Browse files
authored
Add client events: AddedToChannelAsMember and RemovedFromChannelAsMember (#146)
1 parent 2b7352f commit f24bae1

File tree

7 files changed

+329
-53
lines changed

7 files changed

+329
-53
lines changed

Assets/Plugins/StreamChat/Core/IStreamChatClient.cs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,26 @@ public interface IStreamChatClient : IDisposable, IStreamChatClientEventsListene
5757
/// Invite to a <see cref="IStreamChannel"/> was rejected
5858
/// </summary>
5959
event ChannelInviteHandler ChannelInviteRejected;
60+
61+
/// <summary>
62+
/// Local user was added to a channel as a member. This event fires only for channels that are not tracked locally.
63+
/// Use this event to get notified when the local user is added to a new channel. For tracked channels, use the <see cref="IStreamChannel.MemberAdded"/> event.
64+
/// </summary>
65+
event ChannelMemberAddedHandler AddedToChannelAsMember;
66+
67+
/// <summary>
68+
/// Local user was removed from a channel as a member. This event fires only for channels that are not tracked locally.
69+
/// Use this event to get notified when the local user was removed from a channel. For tracked channels use <see cref="IStreamChannel.MemberRemoved"/> event
70+
/// </summary>
71+
event ChannelMemberRemovedHandler RemovedFromChannelAsMember;
6072

6173
/// <summary>
6274
/// Current connection state
6375
/// </summary>
6476
ConnectionState ConnectionState { get; }
6577

6678
/// <summary>
67-
/// Is client connected. Subscribe to <see cref="Connected"/> to get notified when connection is established
79+
/// Is client connected. Subscribe to <see cref="Connected"/> event to get notified when connection is established
6880
/// </summary>
6981
bool IsConnected { get; }
7082

Assets/Plugins/StreamChat/Core/StatefulModels/IStreamChannel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public interface IStreamChannel : IStreamStatefulModel
6262
/// <summary>
6363
/// Event fired when a <see cref="IStreamChannelMember"/> was added, updated, or removed.
6464
/// </summary>
65-
event StreamChannelMemberAnyChangeHandler MembersChanged;
65+
event StreamChannelMemberAnyChangeHandler MembersChanged; //StreamTodo: typo, this should be MemberChanged
6666

6767
/// <summary>
6868
/// Event fired when visibility of this channel changed. Check <see cref="IStreamChannel.Hidden"/> to know if channel is hidden

Assets/Plugins/StreamChat/Core/StreamChatClient.cs

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,17 @@ namespace StreamChat.Core
4949
//StreamTodo: Handle restoring state after lost connection
5050

5151
public delegate void ChannelInviteHandler(IStreamChannel channel, IStreamUser invitee);
52-
52+
53+
/// <summary>
54+
/// Member added to the channel handler
55+
/// </summary>
56+
public delegate void ChannelMemberAddedHandler(IStreamChannel channel, IStreamChannelMember member);
57+
58+
/// <summary>
59+
/// Member removed from the channel handler
60+
/// </summary>
61+
public delegate void ChannelMemberRemovedHandler(IStreamChannel channel, IStreamChannelMember member);
62+
5363
public sealed class StreamChatClient : IStreamChatClient
5464
{
5565
public event ConnectionMadeHandler Connected;
@@ -65,6 +75,9 @@ public sealed class StreamChatClient : IStreamChatClient
6575
public event ChannelInviteHandler ChannelInviteReceived;
6676
public event ChannelInviteHandler ChannelInviteAccepted;
6777
public event ChannelInviteHandler ChannelInviteRejected;
78+
79+
public event ChannelMemberAddedHandler AddedToChannelAsMember;
80+
public event ChannelMemberRemovedHandler RemovedFromChannelAsMember;
6881

6982
public const int QueryUsersLimitMaxValue = 30;
7083
public const int QueryUsersOffsetMaxValue = 1000;
@@ -197,33 +210,9 @@ public Task DisconnectUserAsync()
197210

198211
public bool IsLocalUser(IStreamUser user) => LocalUserData.User == user;
199212

200-
public async Task<IStreamChannel> GetOrCreateChannelWithIdAsync(ChannelType channelType, string channelId,
213+
public Task<IStreamChannel> GetOrCreateChannelWithIdAsync(ChannelType channelType, string channelId,
201214
string name = null, IDictionary<string, object> optionalCustomData = null)
202-
{
203-
StreamAsserts.AssertChannelTypeIsValid(channelType);
204-
StreamAsserts.AssertChannelIdLength(channelId);
205-
206-
var requestBodyDto = new ChannelGetOrCreateRequestInternalDTO
207-
{
208-
Presence = true,
209-
State = true,
210-
Watch = true,
211-
Data = new ChannelRequestInternalDTO
212-
{
213-
Name = name,
214-
},
215-
};
216-
217-
if (optionalCustomData != null && optionalCustomData.Any())
218-
{
219-
requestBodyDto.Data.AdditionalProperties = optionalCustomData?.ToDictionary(x => x.Key, x => x.Value);
220-
}
221-
222-
var channelResponseDto = await InternalLowLevelClient.InternalChannelApi.GetOrCreateChannelAsync(
223-
channelType,
224-
channelId, requestBodyDto);
225-
return _cache.TryCreateOrUpdate(channelResponseDto);
226-
}
215+
=> InternalGetOrCreateChannelWithIdAsync(channelType, channelId, name, presence: true, state: true, watch: true, optionalCustomData);
227216

228217
public async Task<IStreamChannel> GetOrCreateChannelWithMembersAsync(ChannelType channelType,
229218
IEnumerable<IStreamUser> members, IDictionary<string, object> optionalCustomData = null)
@@ -579,6 +568,36 @@ void IStreamChatClientEventsListener.Destroy()
579568
void IStreamChatClientEventsListener.Update() => InternalLowLevelClient.Update(_timeService.DeltaTime);
580569

581570
internal StreamChatLowLevelClient InternalLowLevelClient { get; }
571+
572+
// We probably don't want to expose the presence, state, watch params to the public API
573+
internal async Task<IStreamChannel> InternalGetOrCreateChannelWithIdAsync(ChannelType channelType, string channelId,
574+
string name = null, bool presence = true, bool state = true, bool watch = true,
575+
IDictionary<string, object> optionalCustomData = null)
576+
{
577+
StreamAsserts.AssertChannelTypeIsValid(channelType);
578+
StreamAsserts.AssertChannelIdLength(channelId);
579+
580+
var requestBodyDto = new ChannelGetOrCreateRequestInternalDTO
581+
{
582+
Presence = presence,
583+
State = state,
584+
Watch = watch,
585+
Data = new ChannelRequestInternalDTO
586+
{
587+
Name = name,
588+
},
589+
};
590+
591+
if (optionalCustomData != null && optionalCustomData.Any())
592+
{
593+
requestBodyDto.Data.AdditionalProperties = optionalCustomData?.ToDictionary(x => x.Key, x => x.Value);
594+
}
595+
596+
var channelResponseDto = await InternalLowLevelClient.InternalChannelApi.GetOrCreateChannelAsync(
597+
channelType,
598+
channelId, requestBodyDto);
599+
return _cache.TryCreateOrUpdate(channelResponseDto);
600+
}
582601

583602
internal IStreamLocalUserData UpdateLocalUser(OwnUserInternalDTO ownUserInternalDto)
584603
{
@@ -843,15 +862,23 @@ private void OnMarkReadNotification(NotificationMarkReadEventInternalDTO eventDt
843862
_localUserData.InternalHandleMarkReadNotification(eventDto);
844863
}
845864

846-
private void OnAddedToChannelNotification(NotificationAddedToChannelEventInternalDTO obj)
865+
private void OnAddedToChannelNotification(NotificationAddedToChannelEventInternalDTO eventDto)
847866
{
848-
//StreamTodo: IMPLEMENT
867+
var channel = _cache.TryCreateOrUpdate(eventDto.Channel);
868+
var member = _cache.TryCreateOrUpdate(eventDto.Member);
869+
_cache.TryCreateOrUpdate(eventDto.Member.User);
870+
871+
AddedToChannelAsMember?.Invoke(channel, member);
849872
}
850873

851874
private void OnRemovedFromChannelNotification(
852-
NotificationRemovedFromChannelEventInternalDTO obj)
875+
NotificationRemovedFromChannelEventInternalDTO eventDto)
853876
{
854-
//StreamTodo: IMPLEMENT
877+
var channel = _cache.TryCreateOrUpdate(eventDto.Channel);
878+
var member = _cache.TryCreateOrUpdate(eventDto.Member);
879+
_cache.TryCreateOrUpdate(eventDto.Member.User);
880+
881+
RemovedFromChannelAsMember?.Invoke(channel, member);
855882
}
856883

857884
private void OnInvitedNotification(NotificationInvitedEventInternalDTO eventDto)
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
using System.Threading.Tasks;
2+
using StreamChat.Core;
3+
using StreamChat.Core.Models;
4+
using StreamChat.Core.StatefulModels;
5+
6+
namespace StreamChat.Samples
7+
{
8+
internal class EventsSamples
9+
{
10+
public async Task QueryChannelsEvents()
11+
{
12+
// Get a single channel
13+
var channel = await Client.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, "my-channel-id");
14+
15+
channel.MessageReceived += OnMessageReceived;
16+
channel.MessageUpdated += OnMessageUpdated;
17+
channel.MessageDeleted += OnMessageDeleted;
18+
19+
channel.ReactionAdded += OnReactionAdded;
20+
channel.ReactionUpdated += OnReactionUpdated;
21+
channel.ReactionRemoved += OnReactionRemoved;
22+
23+
channel.MemberAdded += OnMemberAdded;
24+
channel.MemberRemoved += OnMemberRemoved;
25+
channel.MemberUpdated += OnMemberUpdated;
26+
27+
channel.MembersChanged += OnMembersChanged;
28+
channel.VisibilityChanged += OnVisibilityChanged;
29+
channel.MuteChanged += OnMuteChanged;
30+
channel.Truncated += OnTruncated;
31+
channel.Updated += OnUpdated;
32+
channel.WatcherAdded += OnWatcherAdded;
33+
channel.WatcherRemoved += OnWatcherRemoved;
34+
channel.UserStartedTyping += OnUserStartedTyping;
35+
channel.UserStoppedTyping += OnUserStoppedTyping;
36+
channel.TypingUsersChanged += OnTypingUsersChanged;
37+
}
38+
39+
private void OnMessageReceived(IStreamChannel channel, IStreamMessage message)
40+
{
41+
}
42+
43+
private void OnMessageUpdated(IStreamChannel channel, IStreamMessage message)
44+
{
45+
}
46+
47+
private void OnMessageDeleted(IStreamChannel channel, IStreamMessage message, bool isharddelete)
48+
{
49+
}
50+
51+
private void OnReactionAdded(IStreamChannel channel, IStreamMessage message, StreamReaction reaction)
52+
{
53+
}
54+
55+
private void OnReactionUpdated(IStreamChannel channel, IStreamMessage message, StreamReaction reaction)
56+
{
57+
}
58+
59+
private void OnReactionRemoved(IStreamChannel channel, IStreamMessage message, StreamReaction reaction)
60+
{
61+
}
62+
63+
private void OnMemberAdded(IStreamChannel channel, IStreamChannelMember member)
64+
{
65+
}
66+
67+
private void OnMemberRemoved(IStreamChannel channel, IStreamChannelMember member)
68+
{
69+
}
70+
71+
private void OnMemberUpdated(IStreamChannel channel, IStreamChannelMember member)
72+
{
73+
}
74+
75+
private void OnMembersChanged(IStreamChannel channel, IStreamChannelMember member, OperationType operationType)
76+
{
77+
}
78+
79+
private void OnVisibilityChanged(IStreamChannel channel, bool isVisible)
80+
{
81+
}
82+
83+
private void OnMuteChanged(IStreamChannel channel, bool isMuted)
84+
{
85+
}
86+
87+
private void OnTruncated(IStreamChannel channel)
88+
{
89+
}
90+
91+
private void OnUpdated(IStreamChannel channel)
92+
{
93+
}
94+
95+
private void OnWatcherAdded(IStreamChannel channel, IStreamUser user)
96+
{
97+
}
98+
99+
private void OnWatcherRemoved(IStreamChannel channel, IStreamUser user)
100+
{
101+
}
102+
103+
private void OnUserStartedTyping(IStreamChannel channel, IStreamUser user)
104+
{
105+
}
106+
107+
private void OnUserStoppedTyping(IStreamChannel channel, IStreamUser user)
108+
{
109+
}
110+
111+
private void OnTypingUsersChanged(IStreamChannel channel)
112+
{
113+
}
114+
115+
public void ClientEvents()
116+
{
117+
Client.AddedToChannelAsMember += OnAddedToChannelAsMember;
118+
Client.RemovedFromChannelAsMember += OnRemovedFromChannel;
119+
}
120+
121+
private void OnAddedToChannelAsMember(IStreamChannel channel, IStreamChannelMember member)
122+
{
123+
// channel - new channel to which local user was just added
124+
// member - object containing channel membership information
125+
}
126+
127+
private void OnRemovedFromChannel(IStreamChannel channel, IStreamChannelMember member)
128+
{
129+
// channel - channel from which local user was removed
130+
// member - object containing channel membership information
131+
}
132+
133+
public void ConnectionEvents()
134+
{
135+
Client.Connected += OnConnected;
136+
Client.Disconnected += OnDisconnected;
137+
Client.ConnectionStateChanged += OnConnectionStateChanged;
138+
}
139+
140+
private void OnConnectionStateChanged(ConnectionState previous, ConnectionState current)
141+
{
142+
}
143+
144+
private void OnDisconnected()
145+
{
146+
}
147+
148+
private void OnConnected(IStreamLocalUserData localUserData)
149+
{
150+
}
151+
152+
public void Unsubscribe()
153+
{
154+
Client.Connected -= OnConnected;
155+
Client.Disconnected -= OnDisconnected;
156+
Client.ConnectionStateChanged -= OnConnectionStateChanged;
157+
}
158+
159+
private IStreamChatClient Client { get; } = StreamChatClient.CreateDefaultClient();
160+
}
161+
}

Assets/Plugins/StreamChat/Samples/EventsSamples.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Plugins/StreamChat/Tests/StatefulClient/BaseStateIntegrationTests.cs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using NUnit.Framework;
99
using StreamChat.Core;
1010
using StreamChat.Core.Exceptions;
11+
using StreamChat.Core.InternalDTO.Requests;
1112
using StreamChat.Core.Requests;
1213
using StreamChat.Core.StatefulModels;
1314
using StreamChat.Libs.Auth;
@@ -48,11 +49,11 @@ protected static IEnumerable<AuthCredentials> OtherAdminUsersCredentials
4849
/// <summary>
4950
/// Create temp channel with random id that will be removed in [TearDown]
5051
/// </summary>
51-
protected async Task<IStreamChannel> CreateUniqueTempChannelAsync(string name = null)
52+
protected async Task<IStreamChannel> CreateUniqueTempChannelAsync(string name = null, bool watch = true)
5253
{
5354
var channelId = "random-channel-11111-" + Guid.NewGuid();
5455

55-
var channelState = await Client.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, channelId, name);
56+
var channelState = await Client.InternalGetOrCreateChannelWithIdAsync(ChannelType.Messaging, channelId, name, watch: watch);
5657
_tempChannels.Add(channelState);
5758
return channelState;
5859
}
@@ -210,28 +211,32 @@ private static async Task ConnectAndExecuteAsync(Func<Task> test)
210211
throw new AggregateException($"Failed all attempts. Last Exception: {exceptions.Last().Message} ", exceptions);
211212
}
212213
}
213-
214+
214215
private async Task DeleteTempChannelsAsync()
215216
{
216217
if (_tempChannels.Count == 0)
217218
{
218219
return;
219220
}
220221

221-
try
222-
{
223-
await Client.DeleteMultipleChannelsAsync(_tempChannels, isHardDelete: true);
224-
}
225-
catch (StreamApiException streamApiException)
222+
for (int i = 0; i < 5; i++)
226223
{
227-
if (streamApiException.Code == StreamApiException.RateLimitErrorHttpStatusCode)
224+
try
228225
{
229-
await Task.Delay(500);
226+
await Client.DeleteMultipleChannelsAsync(_tempChannels, isHardDelete: true);
230227
}
228+
catch (StreamApiException streamApiException)
229+
{
230+
if (streamApiException.Code == StreamApiException.RateLimitErrorHttpStatusCode)
231+
{
232+
await Task.Delay(1000);
233+
}
231234

232-
Debug.Log($"Try {nameof(DeleteTempChannelsAsync)} again due to exception: " + streamApiException);
235+
Debug.Log($"Attempt {i} failed. Try {nameof(DeleteTempChannelsAsync)} again due to exception: " + streamApiException);
236+
continue;
237+
}
233238

234-
await Client.DeleteMultipleChannelsAsync(_tempChannels, isHardDelete: true);
239+
break;
235240
}
236241

237242
_tempChannels.Clear();

0 commit comments

Comments
 (0)