Skip to content

Commit

Permalink
Add query channels sort buider (#80)
Browse files Browse the repository at this point in the history
* Create QueryChannelsAsync overload with ChannelSortObject param to control result sorting

* Add comments + code samples

* add missing compiler flag

* Make filters parameter optional in IStreamChatClient.QueryChannelsAsync
  • Loading branch information
sierpinskid authored Jan 11, 2023
1 parent db2b5f4 commit 10ada48
Show file tree
Hide file tree
Showing 18 changed files with 424 additions and 60 deletions.
34 changes: 20 additions & 14 deletions Assets/Plugins/StreamChat/Core/IStreamChatClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading;
using System.Threading.Tasks;
using StreamChat.Core.LowLevelClient;
using StreamChat.Core.QueryBuilders.Sort;
using StreamChat.Core.Requests;
using StreamChat.Core.Responses;
using StreamChat.Core.StatefulModels;
Expand Down Expand Up @@ -35,7 +36,7 @@ public interface IStreamChatClient : IDisposable, IStreamChatClientEventsListene
/// Event fired when connection state with Stream Chat server has changed
/// </summary>
event ConnectionChangeHandler ConnectionStateChanged;

/// <summary>
/// Channel was deleted
/// </summary>
Expand All @@ -45,12 +46,12 @@ public interface IStreamChatClient : IDisposable, IStreamChatClientEventsListene
/// Current connection state
/// </summary>
ConnectionState ConnectionState { get; }

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

/// <summary>
/// If true it means that client initiated connection and is waiting for the Stream server to confirm the connection. Subscribe to <see cref="Connected"/> to get notified when connection is established
/// </summary>
Expand All @@ -73,7 +74,7 @@ public interface IStreamChatClient : IDisposable, IStreamChatClientEventsListene
/// Next time since startup the client will attempt to reconnect to the Stream Server.
/// </summary>
double? NextReconnectTime { get; }

/// <summary>
/// Low level client. Use it if you want to bypass the stateful client and execute low level requests directly.
/// </summary>
Expand Down Expand Up @@ -102,7 +103,7 @@ Task<IStreamLocalUserData> ConnectUserAsync(AuthCredentials userAuthCredentials,
/// <remarks>https://getstream.io/chat/docs/unity/unity_client_overview/?language=unity#auth-credentials</remarks>
Task<IStreamLocalUserData> ConnectUserAsync(string apiKey, string userId, string userAuthToken,
CancellationToken cancellationToken = default);

/// <summary>
///
/// </summary>
Expand Down Expand Up @@ -144,15 +145,20 @@ Task<IStreamChannel> GetOrCreateChannelWithMembersAsync(ChannelType channelType,
IDictionary<string, object> optionalCustomData = null);

/// <summary>
/// Query <see cref="IStreamChannel"/> with provided filters
/// Query <see cref="IStreamChannel"/> with optional: filters, sorting, and pagination
/// </summary>
/// <param name="filters">[Optional] Filters</param>
/// <param name="sort">[Optional] Sort object. You can chain multiple sorting fields</param>
/// <param name="limit">[Optional] How many records to return. Think about it as "records per page"</param>
/// <param name="offset">[Optional] How many records to skip. Example: if Limit is 30, the offset for 2nd page is 30, for 3rd page is 60, etc.</param>
Task<IEnumerable<IStreamChannel>> QueryChannelsAsync(IDictionary<string, object> filters = null,
ChannelSortObject sort = null, int limit = 30, int offset = 0);

/// <summary>
/// Query <see cref="IStreamUser"/>
/// </summary>
/// <param name="filters"></param>
/// <param name="limit">How many records to return. Think about it as "records per page"</param>
/// <param name="offset">How many records to skip. Example: if Limit is 30, the offset for 2nd page is 30, for 3rd page is 60, etc.</param>
/// <returns></returns>
Task<IEnumerable<IStreamChannel>> QueryChannelsAsync(IDictionary<string, object> filters, int limit = 30, int offset = 0);

//StreamTodo: missing descriptions
Task<IEnumerable<IStreamUser>> QueryUsersAsync(IDictionary<string, object> filters);

/// <summary>
Expand All @@ -168,7 +174,7 @@ Task<IEnumerable<StreamUserBanInfo>> QueryBannedUsersAsync(
/// <param name="userRequests"></param>
/// <returns></returns>
Task<IEnumerable<IStreamUser>> UpsertUsers(IEnumerable<StreamUserUpsertRequest> userRequests);

/// <summary>
/// Mute channels with optional duration in milliseconds
/// </summary>
Expand All @@ -194,9 +200,9 @@ Task<StreamDeleteChannelsResponse> DeleteMultipleChannelsAsync(IEnumerable<IStre
/// <param name="users">Users to mute</param>
/// <param name="timeoutMinutes">Optional timeout. Without timeout users will stay muted indefinitely</param>
Task MuteMultipleUsersAsync(IEnumerable<IStreamUser> users, int? timeoutMinutes = default);

Task DisconnectUserAsync();

bool IsLocalUser(IStreamUser messageUser);
}
}
3 changes: 3 additions & 0 deletions Assets/Plugins/StreamChat/Core/QueryBuilders.meta

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

3 changes: 3 additions & 0 deletions Assets/Plugins/StreamChat/Core/QueryBuilders/Sort.meta

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

47 changes: 47 additions & 0 deletions Assets/Plugins/StreamChat/Core/QueryBuilders/Sort/ChannelSort.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System.Collections.Generic;
using StreamChat.Core.StatefulModels;

namespace StreamChat.Core.QueryBuilders.Sort
{
/// <summary>
/// Factory for creating for <see cref="IStreamChannel"/> query <see cref="IStreamChatClient.QueryChannelsAsync(IDictionary{string,object}, ChannelSortObject, int, int)"/> sort object
/// </summary>
public static class ChannelSort
{
/// <summary>
/// Sort in ascending order meaning from lowest to highest value of the specified field
/// </summary>
/// <param name="fieldName">Field name to sort by</param>
public static ChannelSortObject OrderByAscending(ChannelSortFieldName fieldName)
{
var instance = new ChannelSortObject();
instance.OrderByAscending(fieldName);
return instance;
}

/// <summary>
/// Sort in descending order meaning from highest to lowest value of the specified field
/// </summary>
/// <param name="fieldName">Field name to sort by</param>
public static ChannelSortObject OrderByDescending(ChannelSortFieldName fieldName)
{
var instance = new ChannelSortObject();
instance.OrderByDescending(fieldName);
return instance;
}

/// <summary>
/// Sort in descending order meaning from highest to lowest value of the specified field
/// </summary>
/// <param name="fieldName">Field name to sort by</param>
public static ChannelSortObject ThenByAscending(this ChannelSortObject sort, ChannelSortFieldName fieldName)
=> sort.OrderByAscending(fieldName);

/// <summary>
/// Sort in descending order meaning from highest to lowest value of the specified field
/// </summary>
/// <param name="fieldName">Field name to sort by</param>
public static ChannelSortObject ThenByDescending(this ChannelSortObject sort, ChannelSortFieldName fieldName)
=> sort.OrderByDescending(fieldName);
}
}

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
@@ -0,0 +1,19 @@
using System.Collections.Generic;
using StreamChat.Core.StatefulModels;

namespace StreamChat.Core.QueryBuilders.Sort
{
/// <summary>
/// Field names available for <see cref="IStreamChannel"/> query: <see cref="IStreamChatClient.QueryChannelsAsync(IDictionary{string,object}, ChannelSortObject, int, int)"/>
/// </summary>
public enum ChannelSortFieldName
{
LastUpdated,
LastMessageAt,
UpdatedAt,
CreatedAt,
MemberCount,
UnreadCount,
HasUnread
}
}

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
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using StreamChat.Core.StatefulModels;

namespace StreamChat.Core.QueryBuilders.Sort
{
/// <summary>
/// Sort object for <see cref="IStreamChannel"/> query: <see cref="IStreamChatClient.QueryChannelsAsync(IDictionary{string,object}, ChannelSortObject, int, int)"/>
/// </summary>
public sealed class ChannelSortObject : QuerySort<ChannelSortObject, ChannelSortFieldName>
{
protected override ChannelSortObject Instance => this;

protected override string ToUnderlyingFieldName(ChannelSortFieldName fieldName)
{
switch (fieldName)
{
case ChannelSortFieldName.LastUpdated: return "last_updated";
case ChannelSortFieldName.LastMessageAt: return "last_message_at";
case ChannelSortFieldName.UpdatedAt: return "updated_at";
case ChannelSortFieldName.CreatedAt: return "created_at";
case ChannelSortFieldName.MemberCount: return "member_count";
case ChannelSortFieldName.UnreadCount: return "unread_count ";
case ChannelSortFieldName.HasUnread: return "has_unread";
default:
throw new ArgumentOutOfRangeException(nameof(fieldName), fieldName, null);
}
}
}
}

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

63 changes: 63 additions & 0 deletions Assets/Plugins/StreamChat/Core/QueryBuilders/Sort/QuerySort.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System.Collections.Generic;
using StreamChat.Core.InternalDTO.Requests;

namespace StreamChat.Core.QueryBuilders.Sort
{
/// <summary>
/// Base class for query sort objects
/// </summary>
public abstract class QuerySort<TSortType, TFieldType> where TSortType : QuerySort<TSortType, TFieldType>
{
/// <summary>
/// Order by field in an ascending order
/// </summary>
/// <param name="fieldName"></param>
/// <returns></returns>
internal TSortType OrderByAscending(TFieldType fieldName)
{
Instance._order.Add((fieldName, AscendingOrder));
return Instance;
}

/// <summary>
///
/// </summary>
/// <param name="fieldName"></param>
/// <returns></returns>
internal TSortType OrderByDescending(TFieldType fieldName)
{
Instance._order.Add((fieldName, DescendingOrder));
return Instance;
}

internal List<SortParamRequestInternalDTO> ToSortParamRequestList()
{
if (_order.Count == 0)
{
return null;
}

var sortParams = new List<SortParamRequestInternalDTO>();

foreach (var entry in _order)
{
sortParams.Add(new SortParamRequestInternalDTO
{
Direction = entry.Direction,
Field = ToUnderlyingFieldName(entry.Field),
});
}

return sortParams;
}

protected abstract TSortType Instance { get; }

protected abstract string ToUnderlyingFieldName(TFieldType field);

private const int AscendingOrder = 1;
private const int DescendingOrder = -1;

private readonly List<(TFieldType Field, int Direction)> _order = new List<(TFieldType Field, int Direction)>();
}
}

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

Loading

0 comments on commit 10ada48

Please sign in to comment.