Skip to content

Commit

Permalink
Add client events: AddedToChannelAsMember and RemovedFromChannelAsMem…
Browse files Browse the repository at this point in the history
…ber (#146)
  • Loading branch information
sierpinskid authored Jun 11, 2024
1 parent 2b7352f commit f24bae1
Show file tree
Hide file tree
Showing 7 changed files with 329 additions and 53 deletions.
14 changes: 13 additions & 1 deletion Assets/Plugins/StreamChat/Core/IStreamChatClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,26 @@ public interface IStreamChatClient : IDisposable, IStreamChatClientEventsListene
/// Invite to a <see cref="IStreamChannel"/> was rejected
/// </summary>
event ChannelInviteHandler ChannelInviteRejected;

/// <summary>
/// Local user was added to a channel as a member. This event fires only for channels that are not tracked locally.
/// 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.
/// </summary>
event ChannelMemberAddedHandler AddedToChannelAsMember;

/// <summary>
/// Local user was removed from a channel as a member. This event fires only for channels that are not tracked locally.
/// Use this event to get notified when the local user was removed from a channel. For tracked channels use <see cref="IStreamChannel.MemberRemoved"/> event
/// </summary>
event ChannelMemberRemovedHandler RemovedFromChannelAsMember;

/// <summary>
/// Current connection state
/// </summary>
ConnectionState ConnectionState { get; }

/// <summary>
/// Is client connected. Subscribe to <see cref="Connected"/> to get notified when connection is established
/// Is client connected. Subscribe to <see cref="Connected"/> event to get notified when connection is established
/// </summary>
bool IsConnected { get; }

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public interface IStreamChannel : IStreamStatefulModel
/// <summary>
/// Event fired when a <see cref="IStreamChannelMember"/> was added, updated, or removed.
/// </summary>
event StreamChannelMemberAnyChangeHandler MembersChanged;
event StreamChannelMemberAnyChangeHandler MembersChanged; //StreamTodo: typo, this should be MemberChanged

/// <summary>
/// Event fired when visibility of this channel changed. Check <see cref="IStreamChannel.Hidden"/> to know if channel is hidden
Expand Down
89 changes: 58 additions & 31 deletions Assets/Plugins/StreamChat/Core/StreamChatClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,17 @@ namespace StreamChat.Core
//StreamTodo: Handle restoring state after lost connection

public delegate void ChannelInviteHandler(IStreamChannel channel, IStreamUser invitee);


/// <summary>
/// Member added to the channel handler
/// </summary>
public delegate void ChannelMemberAddedHandler(IStreamChannel channel, IStreamChannelMember member);

/// <summary>
/// Member removed from the channel handler
/// </summary>
public delegate void ChannelMemberRemovedHandler(IStreamChannel channel, IStreamChannelMember member);

public sealed class StreamChatClient : IStreamChatClient
{
public event ConnectionMadeHandler Connected;
Expand All @@ -65,6 +75,9 @@ public sealed class StreamChatClient : IStreamChatClient
public event ChannelInviteHandler ChannelInviteReceived;
public event ChannelInviteHandler ChannelInviteAccepted;
public event ChannelInviteHandler ChannelInviteRejected;

public event ChannelMemberAddedHandler AddedToChannelAsMember;
public event ChannelMemberRemovedHandler RemovedFromChannelAsMember;

public const int QueryUsersLimitMaxValue = 30;
public const int QueryUsersOffsetMaxValue = 1000;
Expand Down Expand Up @@ -197,33 +210,9 @@ public Task DisconnectUserAsync()

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

public async Task<IStreamChannel> GetOrCreateChannelWithIdAsync(ChannelType channelType, string channelId,
public Task<IStreamChannel> GetOrCreateChannelWithIdAsync(ChannelType channelType, string channelId,
string name = null, IDictionary<string, object> optionalCustomData = null)
{
StreamAsserts.AssertChannelTypeIsValid(channelType);
StreamAsserts.AssertChannelIdLength(channelId);

var requestBodyDto = new ChannelGetOrCreateRequestInternalDTO
{
Presence = true,
State = true,
Watch = true,
Data = new ChannelRequestInternalDTO
{
Name = name,
},
};

if (optionalCustomData != null && optionalCustomData.Any())
{
requestBodyDto.Data.AdditionalProperties = optionalCustomData?.ToDictionary(x => x.Key, x => x.Value);
}

var channelResponseDto = await InternalLowLevelClient.InternalChannelApi.GetOrCreateChannelAsync(
channelType,
channelId, requestBodyDto);
return _cache.TryCreateOrUpdate(channelResponseDto);
}
=> InternalGetOrCreateChannelWithIdAsync(channelType, channelId, name, presence: true, state: true, watch: true, optionalCustomData);

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

internal StreamChatLowLevelClient InternalLowLevelClient { get; }

// We probably don't want to expose the presence, state, watch params to the public API
internal async Task<IStreamChannel> InternalGetOrCreateChannelWithIdAsync(ChannelType channelType, string channelId,
string name = null, bool presence = true, bool state = true, bool watch = true,
IDictionary<string, object> optionalCustomData = null)
{
StreamAsserts.AssertChannelTypeIsValid(channelType);
StreamAsserts.AssertChannelIdLength(channelId);

var requestBodyDto = new ChannelGetOrCreateRequestInternalDTO
{
Presence = presence,
State = state,
Watch = watch,
Data = new ChannelRequestInternalDTO
{
Name = name,
},
};

if (optionalCustomData != null && optionalCustomData.Any())
{
requestBodyDto.Data.AdditionalProperties = optionalCustomData?.ToDictionary(x => x.Key, x => x.Value);
}

var channelResponseDto = await InternalLowLevelClient.InternalChannelApi.GetOrCreateChannelAsync(
channelType,
channelId, requestBodyDto);
return _cache.TryCreateOrUpdate(channelResponseDto);
}

internal IStreamLocalUserData UpdateLocalUser(OwnUserInternalDTO ownUserInternalDto)
{
Expand Down Expand Up @@ -843,15 +862,23 @@ private void OnMarkReadNotification(NotificationMarkReadEventInternalDTO eventDt
_localUserData.InternalHandleMarkReadNotification(eventDto);
}

private void OnAddedToChannelNotification(NotificationAddedToChannelEventInternalDTO obj)
private void OnAddedToChannelNotification(NotificationAddedToChannelEventInternalDTO eventDto)
{
//StreamTodo: IMPLEMENT
var channel = _cache.TryCreateOrUpdate(eventDto.Channel);
var member = _cache.TryCreateOrUpdate(eventDto.Member);
_cache.TryCreateOrUpdate(eventDto.Member.User);

AddedToChannelAsMember?.Invoke(channel, member);
}

private void OnRemovedFromChannelNotification(
NotificationRemovedFromChannelEventInternalDTO obj)
NotificationRemovedFromChannelEventInternalDTO eventDto)
{
//StreamTodo: IMPLEMENT
var channel = _cache.TryCreateOrUpdate(eventDto.Channel);
var member = _cache.TryCreateOrUpdate(eventDto.Member);
_cache.TryCreateOrUpdate(eventDto.Member.User);

RemovedFromChannelAsMember?.Invoke(channel, member);
}

private void OnInvitedNotification(NotificationInvitedEventInternalDTO eventDto)
Expand Down
161 changes: 161 additions & 0 deletions Assets/Plugins/StreamChat/Samples/EventsSamples.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
using System.Threading.Tasks;
using StreamChat.Core;
using StreamChat.Core.Models;
using StreamChat.Core.StatefulModels;

namespace StreamChat.Samples
{
internal class EventsSamples
{
public async Task QueryChannelsEvents()
{
// Get a single channel
var channel = await Client.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, "my-channel-id");

channel.MessageReceived += OnMessageReceived;
channel.MessageUpdated += OnMessageUpdated;
channel.MessageDeleted += OnMessageDeleted;

channel.ReactionAdded += OnReactionAdded;
channel.ReactionUpdated += OnReactionUpdated;
channel.ReactionRemoved += OnReactionRemoved;

channel.MemberAdded += OnMemberAdded;
channel.MemberRemoved += OnMemberRemoved;
channel.MemberUpdated += OnMemberUpdated;

channel.MembersChanged += OnMembersChanged;
channel.VisibilityChanged += OnVisibilityChanged;
channel.MuteChanged += OnMuteChanged;
channel.Truncated += OnTruncated;
channel.Updated += OnUpdated;
channel.WatcherAdded += OnWatcherAdded;
channel.WatcherRemoved += OnWatcherRemoved;
channel.UserStartedTyping += OnUserStartedTyping;
channel.UserStoppedTyping += OnUserStoppedTyping;
channel.TypingUsersChanged += OnTypingUsersChanged;
}

private void OnMessageReceived(IStreamChannel channel, IStreamMessage message)
{
}

private void OnMessageUpdated(IStreamChannel channel, IStreamMessage message)
{
}

private void OnMessageDeleted(IStreamChannel channel, IStreamMessage message, bool isharddelete)
{
}

private void OnReactionAdded(IStreamChannel channel, IStreamMessage message, StreamReaction reaction)
{
}

private void OnReactionUpdated(IStreamChannel channel, IStreamMessage message, StreamReaction reaction)
{
}

private void OnReactionRemoved(IStreamChannel channel, IStreamMessage message, StreamReaction reaction)
{
}

private void OnMemberAdded(IStreamChannel channel, IStreamChannelMember member)
{
}

private void OnMemberRemoved(IStreamChannel channel, IStreamChannelMember member)
{
}

private void OnMemberUpdated(IStreamChannel channel, IStreamChannelMember member)
{
}

private void OnMembersChanged(IStreamChannel channel, IStreamChannelMember member, OperationType operationType)
{
}

private void OnVisibilityChanged(IStreamChannel channel, bool isVisible)
{
}

private void OnMuteChanged(IStreamChannel channel, bool isMuted)
{
}

private void OnTruncated(IStreamChannel channel)
{
}

private void OnUpdated(IStreamChannel channel)
{
}

private void OnWatcherAdded(IStreamChannel channel, IStreamUser user)
{
}

private void OnWatcherRemoved(IStreamChannel channel, IStreamUser user)
{
}

private void OnUserStartedTyping(IStreamChannel channel, IStreamUser user)
{
}

private void OnUserStoppedTyping(IStreamChannel channel, IStreamUser user)
{
}

private void OnTypingUsersChanged(IStreamChannel channel)
{
}

public void ClientEvents()
{
Client.AddedToChannelAsMember += OnAddedToChannelAsMember;
Client.RemovedFromChannelAsMember += OnRemovedFromChannel;
}

private void OnAddedToChannelAsMember(IStreamChannel channel, IStreamChannelMember member)
{
// channel - new channel to which local user was just added
// member - object containing channel membership information
}

private void OnRemovedFromChannel(IStreamChannel channel, IStreamChannelMember member)
{
// channel - channel from which local user was removed
// member - object containing channel membership information
}

public void ConnectionEvents()
{
Client.Connected += OnConnected;
Client.Disconnected += OnDisconnected;
Client.ConnectionStateChanged += OnConnectionStateChanged;
}

private void OnConnectionStateChanged(ConnectionState previous, ConnectionState current)
{
}

private void OnDisconnected()
{
}

private void OnConnected(IStreamLocalUserData localUserData)
{
}

public void Unsubscribe()
{
Client.Connected -= OnConnected;
Client.Disconnected -= OnDisconnected;
Client.ConnectionStateChanged -= OnConnectionStateChanged;
}

private IStreamChatClient Client { get; } = StreamChatClient.CreateDefaultClient();
}
}
3 changes: 3 additions & 0 deletions Assets/Plugins/StreamChat/Samples/EventsSamples.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using NUnit.Framework;
using StreamChat.Core;
using StreamChat.Core.Exceptions;
using StreamChat.Core.InternalDTO.Requests;
using StreamChat.Core.Requests;
using StreamChat.Core.StatefulModels;
using StreamChat.Libs.Auth;
Expand Down Expand Up @@ -48,11 +49,11 @@ protected static IEnumerable<AuthCredentials> OtherAdminUsersCredentials
/// <summary>
/// Create temp channel with random id that will be removed in [TearDown]
/// </summary>
protected async Task<IStreamChannel> CreateUniqueTempChannelAsync(string name = null)
protected async Task<IStreamChannel> CreateUniqueTempChannelAsync(string name = null, bool watch = true)
{
var channelId = "random-channel-11111-" + Guid.NewGuid();

var channelState = await Client.GetOrCreateChannelWithIdAsync(ChannelType.Messaging, channelId, name);
var channelState = await Client.InternalGetOrCreateChannelWithIdAsync(ChannelType.Messaging, channelId, name, watch: watch);
_tempChannels.Add(channelState);
return channelState;
}
Expand Down Expand Up @@ -210,28 +211,32 @@ private static async Task ConnectAndExecuteAsync(Func<Task> test)
throw new AggregateException($"Failed all attempts. Last Exception: {exceptions.Last().Message} ", exceptions);
}
}

private async Task DeleteTempChannelsAsync()
{
if (_tempChannels.Count == 0)
{
return;
}

try
{
await Client.DeleteMultipleChannelsAsync(_tempChannels, isHardDelete: true);
}
catch (StreamApiException streamApiException)
for (int i = 0; i < 5; i++)
{
if (streamApiException.Code == StreamApiException.RateLimitErrorHttpStatusCode)
try
{
await Task.Delay(500);
await Client.DeleteMultipleChannelsAsync(_tempChannels, isHardDelete: true);
}
catch (StreamApiException streamApiException)
{
if (streamApiException.Code == StreamApiException.RateLimitErrorHttpStatusCode)
{
await Task.Delay(1000);
}

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

await Client.DeleteMultipleChannelsAsync(_tempChannels, isHardDelete: true);
break;
}

_tempChannels.Clear();
Expand Down
Loading

0 comments on commit f24bae1

Please sign in to comment.