fix tests

Signed-off-by: Sahil Kumar <[email protected]>
This commit is contained in:
Sahil Kumar
2021-04-19 17:43:11 +05:30
parent af135ecb9c
commit 07196eab96
4 changed files with 339 additions and 139 deletions
+105 -105
View File
@@ -75,7 +75,7 @@ class Channel {
/// Channel configuration /// Channel configuration
ChannelConfig? get config { ChannelConfig? get config {
_checkInitialized(); _checkInitialized();
return state?._channelState?.channel?.config; return state?._channelState.channel?.config;
} }
/// Channel configuration as a stream /// Channel configuration as a stream
@@ -87,7 +87,7 @@ class Channel {
/// Channel user creator /// Channel user creator
User? get createdBy { User? get createdBy {
_checkInitialized(); _checkInitialized();
return state?._channelState?.channel?.createdBy; return state?._channelState.channel?.createdBy;
} }
/// Channel user creator as a stream /// Channel user creator as a stream
@@ -99,7 +99,7 @@ class Channel {
/// Channel frozen status /// Channel frozen status
bool? get frozen { bool? get frozen {
_checkInitialized(); _checkInitialized();
return state?._channelState?.channel?.frozen; return state?._channelState.channel?.frozen;
} }
/// Channel frozen status as a stream /// Channel frozen status as a stream
@@ -111,7 +111,7 @@ class Channel {
/// Channel creation date /// Channel creation date
DateTime? get createdAt { DateTime? get createdAt {
_checkInitialized(); _checkInitialized();
return state?._channelState?.channel?.createdAt; return state?._channelState.channel?.createdAt;
} }
/// Channel creation date as a stream /// Channel creation date as a stream
@@ -124,7 +124,7 @@ class Channel {
DateTime? get lastMessageAt { DateTime? get lastMessageAt {
_checkInitialized(); _checkInitialized();
return state?._channelState?.channel?.lastMessageAt; return state?._channelState.channel?.lastMessageAt;
} }
/// Channel last message date as a stream /// Channel last message date as a stream
@@ -138,7 +138,7 @@ class Channel {
DateTime? get updatedAt { DateTime? get updatedAt {
_checkInitialized(); _checkInitialized();
return state?._channelState?.channel?.updatedAt; return state?._channelState.channel?.updatedAt;
} }
/// Channel updated date as a stream /// Channel updated date as a stream
@@ -152,7 +152,7 @@ class Channel {
DateTime? get deletedAt { DateTime? get deletedAt {
_checkInitialized(); _checkInitialized();
return state?._channelState?.channel?.deletedAt; return state?._channelState.channel?.deletedAt;
} }
/// Channel deletion date as a stream /// Channel deletion date as a stream
@@ -166,7 +166,7 @@ class Channel {
int? get memberCount { int? get memberCount {
_checkInitialized(); _checkInitialized();
return state?._channelState?.channel?.memberCount; return state?._channelState.channel?.memberCount;
} }
/// Channel member count as a stream /// Channel member count as a stream
@@ -177,20 +177,20 @@ class Channel {
} }
/// Channel id /// Channel id
String? get id => state?._channelState?.channel?.id ?? _id; String? get id => state?._channelState.channel?.id ?? _id;
/// Channel cid /// Channel cid
String? get cid => state?._channelState?.channel?.cid ?? _cid; String? get cid => state?._channelState.channel?.cid ?? _cid;
/// Channel team /// Channel team
String? get team { String? get team {
_checkInitialized(); _checkInitialized();
return state?._channelState?.channel?.team; return state?._channelState.channel?.team;
} }
/// Channel extra data /// Channel extra data
Map<String, dynamic>? get extraData => Map<String, dynamic>? get extraData =>
state?._channelState?.channel?.extraData ?? _extraData; state?._channelState.channel?.extraData ?? _extraData;
/// Channel extra data as a stream /// Channel extra data as a stream
Stream<Map<String, dynamic>?>? get extraDataStream { Stream<Map<String, dynamic>?>? get extraDataStream {
@@ -341,14 +341,15 @@ class Channel {
/// Send a [message] to this channel. /// Send a [message] to this channel.
/// Waits for a [_messageAttachmentsUploadCompleter] to complete /// Waits for a [_messageAttachmentsUploadCompleter] to complete
/// before actually sending the message. /// before actually sending the message.
Future<SendMessageResponse?> sendMessage(Message message) async { Future<SendMessageResponse> sendMessage(Message message) async {
_checkInitialized();
// Cancelling previous completer in case it's called again in the process // Cancelling previous completer in case it's called again in the process
// Eg. Updating the message while the previous call is in progress. // Eg. Updating the message while the previous call is in progress.
_messageAttachmentsUploadCompleter _messageAttachmentsUploadCompleter
.remove(message.id) .remove(message.id)
?.completeError('Message Cancelled'); ?.completeError('Message Cancelled');
final quotedMessage = state?.messages.firstWhereOrNull( final quotedMessage = state!.messages.firstWhereOrNull(
(m) => m.id == message.quotedMessageId, (m) => m.id == message.quotedMessageId,
); );
// ignore: parameter_assignments // ignore: parameter_assignments
@@ -365,7 +366,7 @@ class Channel {
).toList(), ).toList(),
); );
state?.addMessage(message); state!.addMessage(message);
try { try {
if (message.attachments.any((it) => !it.uploadState.isSuccess) == true) { if (message.attachments.any((it) => !it.uploadState.isSuccess) == true) {
@@ -384,11 +385,11 @@ class Channel {
} }
final response = await _client.sendMessage(message, id!, type!); final response = await _client.sendMessage(message, id!, type!);
state?.addMessage(response.message); state!.addMessage(response.message);
return response; return response;
} catch (error) { } catch (error) {
if (error is DioError && error.type != DioErrorType.response) { if (error is DioError && error.type != DioErrorType.response) {
state?.retryQueue?.add([message]); state!.retryQueue?.add([message]);
} }
rethrow; rethrow;
} }
@@ -397,7 +398,7 @@ class Channel {
/// Updates the [message] in this channel. /// Updates the [message] in this channel.
/// Waits for a [_messageAttachmentsUploadCompleter] to complete /// Waits for a [_messageAttachmentsUploadCompleter] to complete
/// before actually updating the message. /// before actually updating the message.
Future<UpdateMessageResponse?> updateMessage(Message message) async { Future<UpdateMessageResponse> updateMessage(Message message) async {
// Cancelling previous completer in case it's called again in the process // Cancelling previous completer in case it's called again in the process
// Eg. Updating the message while the previous call is in progress. // Eg. Updating the message while the previous call is in progress.
_messageAttachmentsUploadCompleter _messageAttachmentsUploadCompleter
@@ -452,7 +453,7 @@ class Channel {
} }
/// Deletes the [message] from the channel. /// Deletes the [message] from the channel.
Future<EmptyResponse?> deleteMessage(Message message) async { Future<EmptyResponse> deleteMessage(Message message) async {
// Directly deleting the local messages which are not yet sent to server // Directly deleting the local messages which are not yet sent to server
if (message.status == MessageSendingStatus.sending || if (message.status == MessageSendingStatus.sending ||
message.status == MessageSendingStatus.failed) { message.status == MessageSendingStatus.failed) {
@@ -493,7 +494,7 @@ class Channel {
} }
/// Pins provided message /// Pins provided message
Future<UpdateMessageResponse?> pinMessage( Future<UpdateMessageResponse> pinMessage(
Message message, Message message,
Object? timeoutOrExpirationDate, Object? timeoutOrExpirationDate,
) { ) {
@@ -523,11 +524,11 @@ class Channel {
} }
/// Unpins provided message /// Unpins provided message
Future<UpdateMessageResponse?> unpinMessage(Message message) => Future<UpdateMessageResponse> unpinMessage(Message message) =>
updateMessage(message.copyWith(pinned: false)); updateMessage(message.copyWith(pinned: false));
/// Send a file to this channel /// Send a file to this channel
Future<SendFileResponse?> sendFile( Future<SendFileResponse> sendFile(
AttachmentFile file, { AttachmentFile file, {
ProgressCallback? onSendProgress, ProgressCallback? onSendProgress,
CancelToken? cancelToken, CancelToken? cancelToken,
@@ -543,7 +544,7 @@ class Channel {
} }
/// Send an image to this channel /// Send an image to this channel
Future<SendImageResponse?> sendImage( Future<SendImageResponse> sendImage(
AttachmentFile file, { AttachmentFile file, {
ProgressCallback? onSendProgress, ProgressCallback? onSendProgress,
CancelToken? cancelToken, CancelToken? cancelToken,
@@ -559,7 +560,7 @@ class Channel {
} }
/// A message search. /// A message search.
Future<SearchMessagesResponse?> search({ Future<SearchMessagesResponse> search({
String? query, String? query,
Map<String, dynamic>? messageFilters, Map<String, dynamic>? messageFilters,
List<SortOption>? sort, List<SortOption>? sort,
@@ -578,7 +579,7 @@ class Channel {
); );
/// Delete a file from this channel /// Delete a file from this channel
Future<EmptyResponse?> deleteFile( Future<EmptyResponse> deleteFile(
String url, { String url, {
CancelToken? cancelToken, CancelToken? cancelToken,
}) { }) {
@@ -592,7 +593,7 @@ class Channel {
} }
/// Delete an image from this channel /// Delete an image from this channel
Future<EmptyResponse?> deleteImage( Future<EmptyResponse> deleteImage(
String url, { String url, {
CancelToken? cancelToken, CancelToken? cancelToken,
}) { }) {
@@ -616,12 +617,13 @@ class Channel {
/// Send a reaction to this channel /// Send a reaction to this channel
/// Set [enforceUnique] to true to remove the existing user reaction /// Set [enforceUnique] to true to remove the existing user reaction
Future<SendReactionResponse?> sendReaction( Future<SendReactionResponse> sendReaction(
Message message, Message message,
String type, { String type, {
Map<String, dynamic> extraData = const {}, Map<String, dynamic> extraData = const {},
bool enforceUnique = false, bool enforceUnique = false,
}) async { }) async {
_checkInitialized();
final messageId = message.id; final messageId = message.id;
final now = DateTime.now(); final now = DateTime.now();
final user = _client.state.user; final user = _client.state.user;
@@ -686,7 +688,7 @@ class Channel {
} }
/// Delete a reaction from this channel /// Delete a reaction from this channel
Future<EmptyResponse?> deleteReaction( Future<EmptyResponse> deleteReaction(
Message message, Reaction reaction) async { Message message, Reaction reaction) async {
final type = reaction.type; final type = reaction.type;
final user = _client.state.user; final user = _client.state.user;
@@ -730,7 +732,7 @@ class Channel {
} }
/// Edit the channel custom data /// Edit the channel custom data
Future<UpdateChannelResponse?> update( Future<UpdateChannelResponse> update(
Map<String, dynamic> channelData, [ Map<String, dynamic> channelData, [
Message? updateMessage, Message? updateMessage,
]) async { ]) async {
@@ -743,40 +745,40 @@ class Channel {
} }
/// Edit the channel custom data /// Edit the channel custom data
Future<PartialUpdateChannelResponse?> updatePartial( Future<PartialUpdateChannelResponse> updatePartial(
Map<String, dynamic> channelData) async { Map<String, dynamic> channelData) async {
final response = await _client.patch(_channelURL, data: channelData); final response = await _client.patch(_channelURL, data: channelData);
return _client.decode(response.data, PartialUpdateChannelResponse.fromJson); return _client.decode(response.data, PartialUpdateChannelResponse.fromJson);
} }
/// Delete this channel. Messages are permanently removed. /// Delete this channel. Messages are permanently removed.
Future<EmptyResponse?> delete() async { Future<EmptyResponse> delete() async {
final response = await _client.delete(_channelURL); final response = await _client.delete(_channelURL);
return _client.decode(response.data, EmptyResponse.fromJson); return _client.decode(response.data, EmptyResponse.fromJson);
} }
/// Removes all messages from the channel /// Removes all messages from the channel
Future<EmptyResponse?> truncate() async { Future<EmptyResponse> truncate() async {
final response = await _client.post('$_channelURL/truncate'); final response = await _client.post('$_channelURL/truncate');
return _client.decode(response.data, EmptyResponse.fromJson); return _client.decode(response.data, EmptyResponse.fromJson);
} }
/// Accept invitation to the channel /// Accept invitation to the channel
Future<AcceptInviteResponse?> acceptInvite([Message? message]) async { Future<AcceptInviteResponse> acceptInvite([Message? message]) async {
final res = await _client.post(_channelURL, final res = await _client.post(_channelURL,
data: {'accept_invite': true, 'message': message?.toJson()}); data: {'accept_invite': true, 'message': message?.toJson()});
return _client.decode(res.data, AcceptInviteResponse.fromJson); return _client.decode(res.data, AcceptInviteResponse.fromJson);
} }
/// Reject invitation to the channel /// Reject invitation to the channel
Future<RejectInviteResponse?> rejectInvite([Message? message]) async { Future<RejectInviteResponse> rejectInvite([Message? message]) async {
final res = await _client.post(_channelURL, final res = await _client.post(_channelURL,
data: {'reject_invite': true, 'message': message?.toJson()}); data: {'reject_invite': true, 'message': message?.toJson()});
return _client.decode(res.data, RejectInviteResponse.fromJson); return _client.decode(res.data, RejectInviteResponse.fromJson);
} }
/// Add members to the channel /// Add members to the channel
Future<AddMembersResponse?> addMembers( Future<AddMembersResponse> addMembers(
List<String> memberIds, [ List<String> memberIds, [
Message? message, Message? message,
]) async { ]) async {
@@ -788,7 +790,7 @@ class Channel {
} }
/// Invite members to the channel /// Invite members to the channel
Future<InviteMembersResponse?> inviteMembers( Future<InviteMembersResponse> inviteMembers(
List<String> memberIds, [ List<String> memberIds, [
Message? message, Message? message,
]) async { ]) async {
@@ -800,7 +802,7 @@ class Channel {
} }
/// Remove members from the channel /// Remove members from the channel
Future<RemoveMembersResponse?> removeMembers( Future<RemoveMembersResponse> removeMembers(
List<String> memberIds, [ List<String> memberIds, [
Message? message, Message? message,
]) async { ]) async {
@@ -836,7 +838,7 @@ class Channel {
Message? oldMessage; Message? oldMessage;
if (oldIndex != -1) { if (oldIndex != -1) {
oldMessage = state!.messages[oldIndex]; oldMessage = state!.messages[oldIndex];
state!.updateChannelState(state!._channelState!.copyWith( state!.updateChannelState(state!._channelState.copyWith(
messages: state?.messages?..remove(oldMessage), messages: state?.messages?..remove(oldMessage),
)); ));
} else { } else {
@@ -863,7 +865,7 @@ class Channel {
} }
/// Mark all channel messages as read /// Mark all channel messages as read
Future<EmptyResponse?> markRead() async { Future<EmptyResponse> markRead() async {
_checkInitialized(); _checkInitialized();
client.state.totalUnreadCount = max( client.state.totalUnreadCount = max(
0, (client.state.totalUnreadCount ?? 0) - (state!.unreadCount ?? 0)); 0, (client.state.totalUnreadCount ?? 0) - (state!.unreadCount ?? 0));
@@ -908,7 +910,7 @@ class Channel {
} }
/// Stop watching the channel /// Stop watching the channel
Future<EmptyResponse?> stopWatching() async { Future<EmptyResponse> stopWatching() async {
final response = await _client.post( final response = await _client.post(
'$_channelURL/stop-watching', '$_channelURL/stop-watching',
data: {}, data: {},
@@ -949,7 +951,7 @@ class Channel {
} }
/// List the reactions for a message in the channel /// List the reactions for a message in the channel
Future<QueryReactionsResponse?> getReactions( Future<QueryReactionsResponse> getReactions(
String messageID, String messageID,
PaginationParams options, PaginationParams options,
) async { ) async {
@@ -982,7 +984,7 @@ class Channel {
} }
/// Retrieves a list of messages by ID /// Retrieves a list of messages by ID
Future<TranslateMessageResponse?> translateMessage( Future<TranslateMessageResponse> translateMessage(
String messageId, String messageId,
String language, String language,
) async { ) async {
@@ -999,16 +1001,16 @@ class Channel {
} }
/// Creates a new channel /// Creates a new channel
Future<ChannelState>? create() async => query(options: { Future<ChannelState> create() async => query(options: {
'watch': false, 'watch': false,
'state': false, 'state': false,
'presence': false, 'presence': false,
})!; });
/// Query the API, get messages, members or other channel fields /// Query the API, get messages, members or other channel fields
/// Set [preferOffline] to true to avoid the api call if the data is already /// Set [preferOffline] to true to avoid the api call if the data is already
/// in the offline storage /// in the offline storage
Future<ChannelState>? query({ Future<ChannelState> query({
Map<String, dynamic> options = const {}, Map<String, dynamic> options = const {},
PaginationParams? messagesPagination, PaginationParams? messagesPagination,
PaginationParams? membersPagination, PaginationParams? membersPagination,
@@ -1079,7 +1081,7 @@ class Channel {
} }
/// Query channel members /// Query channel members
Future<QueryMembersResponse?> queryMembers({ Future<QueryMembersResponse> queryMembers({
Map<String, dynamic>? filter, Map<String, dynamic>? filter,
List<SortOption>? sort, List<SortOption>? sort,
PaginationParams? pagination, PaginationParams? pagination,
@@ -1108,7 +1110,7 @@ class Channel {
} }
/// Mutes the channel /// Mutes the channel
Future<EmptyResponse?> mute({Duration? expiration}) async { Future<EmptyResponse> mute({Duration? expiration}) async {
final response = await _client.post('/moderation/mute/channel', data: { final response = await _client.post('/moderation/mute/channel', data: {
'channel_cid': cid, 'channel_cid': cid,
if (expiration != null) 'expiration': expiration.inMilliseconds, if (expiration != null) 'expiration': expiration.inMilliseconds,
@@ -1117,7 +1119,7 @@ class Channel {
} }
/// Unmutes the channel /// Unmutes the channel
Future<EmptyResponse?> unmute() async { Future<EmptyResponse> unmute() async {
final response = await _client.post('/moderation/unmute/channel', data: { final response = await _client.post('/moderation/unmute/channel', data: {
'channel_cid': cid, 'channel_cid': cid,
}); });
@@ -1125,7 +1127,7 @@ class Channel {
} }
/// Bans a user from the channel /// Bans a user from the channel
Future<EmptyResponse?> banUser( Future<EmptyResponse> banUser(
String userID, String userID,
Map<String, dynamic> options, Map<String, dynamic> options,
) async { ) async {
@@ -1139,7 +1141,7 @@ class Channel {
} }
/// Remove the ban for a user in the channel /// Remove the ban for a user in the channel
Future<EmptyResponse?> unbanUser(String userID) async { Future<EmptyResponse> unbanUser(String userID) async {
_checkInitialized(); _checkInitialized();
return _client.unbanUser(userID, { return _client.unbanUser(userID, {
'type': type, 'type': type,
@@ -1148,7 +1150,7 @@ class Channel {
} }
/// Shadow bans a user from the channel /// Shadow bans a user from the channel
Future<EmptyResponse?> shadowBan( Future<EmptyResponse> shadowBan(
String userID, String userID,
Map<String, dynamic> options, Map<String, dynamic> options,
) async { ) async {
@@ -1162,7 +1164,7 @@ class Channel {
} }
/// Remove the shadow ban for a user in the channel /// Remove the shadow ban for a user in the channel
Future<EmptyResponse?> removeShadowBan(String userID) async { Future<EmptyResponse> removeShadowBan(String userID) async {
_checkInitialized(); _checkInitialized();
return _client.removeShadowBan(userID, { return _client.removeShadowBan(userID, {
'type': type, 'type': type,
@@ -1173,7 +1175,7 @@ class Channel {
/// Hides the channel from [StreamChatClient.queryChannels] for the user /// Hides the channel from [StreamChatClient.queryChannels] for the user
/// until a message is added If [clearHistory] is set to true - all messages /// until a message is added If [clearHistory] is set to true - all messages
/// will be removed for the user /// will be removed for the user
Future<EmptyResponse?> hide({bool clearHistory = false}) async { Future<EmptyResponse> hide({bool clearHistory = false}) async {
_checkInitialized(); _checkInitialized();
final response = await _client final response = await _client
.post('$_channelURL/hide', data: {'clear_history': clearHistory}); .post('$_channelURL/hide', data: {'clear_history': clearHistory});
@@ -1190,7 +1192,7 @@ class Channel {
} }
/// Removes the hidden status for the channel /// Removes the hidden status for the channel
Future<EmptyResponse?> show() async { Future<EmptyResponse> show() async {
_checkInitialized(); _checkInitialized();
final response = await _client.post('$_channelURL/show'); final response = await _client.post('$_channelURL/show');
return _client.decode(response.data, EmptyResponse.fromJson); return _client.decode(response.data, EmptyResponse.fromJson);
@@ -1257,9 +1259,10 @@ class Channel {
void _checkInitialized() { void _checkInitialized() {
assert( assert(
!_initializedCompleter.isCompleted, _initializedCompleter.isCompleted,
"Channel $cid hasn't been initialized yet. Make sure to call .watch()" "Channel $cid hasn't been initialized yet. Make sure to call .watch()"
' or to instantiate the client using [Channel.fromState]'); ' or to instantiate the client using [Channel.fromState]',
);
} }
} }
@@ -1413,9 +1416,9 @@ class ChannelClientState {
/// This flag should be managed by UI sdks. /// This flag should be managed by UI sdks.
/// When false, any new message (received by WebSocket event /// When false, any new message (received by WebSocket event
/// - [EventType.messageNew]) will not be pushed on to message list. /// - [EventType.messageNew]) will not be pushed on to message list.
bool? get isUpToDate => _isUpToDateController.value; bool get isUpToDate => _isUpToDateController.value!;
set isUpToDate(bool? isUpToDate) => _isUpToDateController.add(isUpToDate); set isUpToDate(bool isUpToDate) => _isUpToDateController.add(isUpToDate);
/// [isUpToDate] flag count as a stream /// [isUpToDate] flag count as a stream
Stream<bool?> get isUpToDateStream => _isUpToDateController.stream; Stream<bool?> get isUpToDateStream => _isUpToDateController.stream;
@@ -1483,9 +1486,9 @@ class ChannelClientState {
addMessage(message); addMessage(message);
if (message.pinned == true) { if (message.pinned == true) {
_channelState = _channelState!.copyWith( _channelState = _channelState.copyWith(
pinnedMessages: [ pinnedMessages: [
..._channelState!.pinnedMessages, ..._channelState.pinnedMessages,
message, message,
], ],
); );
@@ -1522,7 +1525,7 @@ class ChannelClientState {
/// Add a message to this channel /// Add a message to this channel
void addMessage(Message message) { void addMessage(Message message) {
if (message.parentId == null || message.showInChannel == true) { if (message.parentId == null || message.showInChannel == true) {
final newMessages = List<Message>.from(_channelState!.messages); final newMessages = List<Message>.from(_channelState.messages);
final oldIndex = newMessages.indexWhere((m) => m.id == message.id); final oldIndex = newMessages.indexWhere((m) => m.id == message.id);
if (oldIndex != -1) { if (oldIndex != -1) {
Message? m; Message? m;
@@ -1537,9 +1540,9 @@ class ChannelClientState {
newMessages.add(message); newMessages.add(message);
} }
_channelState = _channelState!.copyWith( _channelState = _channelState.copyWith(
messages: newMessages, messages: newMessages,
channel: _channelState!.channel!.copyWith( channel: _channelState.channel?.copyWith(
lastMessageAt: message.createdAt, lastMessageAt: message.createdAt,
), ),
); );
@@ -1551,7 +1554,7 @@ class ChannelClientState {
} }
void _listenReadEvents() { void _listenReadEvents() {
if (_channel.config?.readEvents == false) { if (_channelState.channel?.config.readEvents == false) {
return; return;
} }
@@ -1563,7 +1566,7 @@ class ChannelClientState {
) )
.listen( .listen(
(event) { (event) {
final readList = List<Read>.from(_channelState?.read ?? []); final readList = List<Read>.from(_channelState.read);
final userReadIndex = final userReadIndex =
read?.indexWhere((r) => r.user.id == event.user!.id); read?.indexWhere((r) => r.user.id == event.user!.id);
@@ -1577,7 +1580,7 @@ class ChannelClientState {
lastRead: event.createdAt!, lastRead: event.createdAt!,
unreadMessages: event.totalUnreadCount!, unreadMessages: event.totalUnreadCount!,
)); ));
_channelState = _channelState!.copyWith(read: readList); _channelState = _channelState.copyWith(read: readList);
} }
}, },
), ),
@@ -1585,22 +1588,22 @@ class ChannelClientState {
} }
/// Channel message list /// Channel message list
List<Message> get messages => _channelState!.messages; List<Message> get messages => _channelState.messages;
/// Channel message list as a stream /// Channel message list as a stream
Stream<List<Message>?> get messagesStream => Stream<List<Message>?> get messagesStream =>
channelStateStream.map((cs) => cs!.messages); channelStateStream.map((cs) => cs!.messages);
/// Channel pinned message list /// Channel pinned message list
List<Message>? get pinnedMessages => _channelState!.pinnedMessages.toList(); List<Message>? get pinnedMessages => _channelState.pinnedMessages.toList();
/// Channel pinned message list as a stream /// Channel pinned message list as a stream
Stream<List<Message>?> get pinnedMessagesStream => Stream<List<Message>?> get pinnedMessagesStream =>
channelStateStream.map((cs) => cs!.pinnedMessages.toList()); channelStateStream.map((cs) => cs!.pinnedMessages.toList());
/// Get channel last message /// Get channel last message
Message? get lastMessage => _channelState!.messages.isNotEmpty == true Message? get lastMessage => _channelState.messages.isNotEmpty == true
? _channelState!.messages.last ? _channelState.messages.last
: null; : null;
/// Get channel last message /// Get channel last message
@@ -1608,7 +1611,7 @@ class ChannelClientState {
.map((event) => event?.isNotEmpty == true ? event!.last : null); .map((event) => event?.isNotEmpty == true ? event!.last : null);
/// Channel members list /// Channel members list
List<Member> get members => _channelState!.members List<Member> get members => _channelState.members
.map((e) => e.copyWith(user: _channel.client.state.users[e.user!.id])) .map((e) => e.copyWith(user: _channel.client.state.users[e.user!.id]))
.toList(); .toList();
@@ -1622,14 +1625,14 @@ class ChannelClientState {
); );
/// Channel watcher count /// Channel watcher count
int? get watcherCount => _channelState!.watcherCount; int? get watcherCount => _channelState.watcherCount;
/// Channel watcher count as a stream /// Channel watcher count as a stream
Stream<int?> get watcherCountStream => Stream<int?> get watcherCountStream =>
channelStateStream.map((cs) => cs!.watcherCount); channelStateStream.map((cs) => cs!.watcherCount);
/// Channel watchers list /// Channel watchers list
List<User> get watchers => _channelState!.watchers List<User> get watchers => _channelState.watchers
.map((e) => _channel.client.state.users[e.id] ?? e) .map((e) => _channel.client.state.users[e.id] ?? e)
.toList(); .toList();
@@ -1642,7 +1645,7 @@ class ChannelClientState {
); );
/// Channel read list /// Channel read list
List<Read>? get read => _channelState!.read; List<Read>? get read => _channelState.read;
/// Channel read list as a stream /// Channel read list as a stream
Stream<List<Read>?> get readStream => Stream<List<Read>?> get readStream =>
@@ -1693,7 +1696,7 @@ class ChannelClientState {
/// Delete all channel messages /// Delete all channel messages
void truncate() { void truncate() {
_channelState = _channelState!.copyWith( _channelState = _channelState.copyWith(
messages: [], messages: [],
); );
} }
@@ -1704,24 +1707,22 @@ class ChannelClientState {
void updateChannelState(ChannelState updatedState) { void updateChannelState(ChannelState updatedState) {
final newMessages = <Message>[ final newMessages = <Message>[
...updatedState.messages, ...updatedState.messages,
..._channelState?.messages ..._channelState.messages
.where((m) => .where((m) =>
updatedState.messages updatedState.messages
.any((newMessage) => newMessage.id == m.id) != .any((newMessage) => newMessage.id == m.id) !=
true) true)
.toList() ?? .toList(),
[],
]..sort(_sortByCreatedAt as int Function(Message, Message)?); ]..sort(_sortByCreatedAt as int Function(Message, Message)?);
final newWatchers = <User>[ final newWatchers = <User>[
...updatedState.watchers, ...updatedState.watchers,
..._channelState?.watchers ..._channelState.watchers
.where((w) => .where((w) =>
updatedState.watchers updatedState.watchers
.any((newWatcher) => newWatcher.id == w.id) != .any((newWatcher) => newWatcher.id == w.id) !=
true) true)
.toList() ?? .toList(),
[],
]; ];
final newMembers = <Member>[ final newMembers = <Member>[
@@ -1730,20 +1731,19 @@ class ChannelClientState {
final newReads = <Read>[ final newReads = <Read>[
...updatedState.read, ...updatedState.read,
..._channelState?.read ..._channelState.read
.where((r) => .where((r) =>
updatedState.read updatedState.read
.any((newRead) => newRead.user.id == r.user.id) != .any((newRead) => newRead.user.id == r.user.id) !=
true) true)
.toList() ?? .toList(),
[],
]; ];
_checkExpiredAttachmentMessages(updatedState); _checkExpiredAttachmentMessages(updatedState);
_channelState = _channelState!.copyWith( _channelState = _channelState.copyWith(
messages: newMessages, messages: newMessages,
channel: _channelState!.channel?.merge(updatedState.channel), channel: _channelState.channel?.merge(updatedState.channel),
watchers: newWatchers, watchers: newWatchers,
watcherCount: updatedState.watcherCount, watcherCount: updatedState.watcherCount,
members: newMembers, members: newMembers,
@@ -1765,7 +1765,7 @@ class ChannelClientState {
} }
/// The channel state related to this client /// The channel state related to this client
ChannelState? get _channelState => _channelStateController.value; ChannelState get _channelState => _channelStateController.value!;
/// The channel state related to this client as a stream /// The channel state related to this client as a stream
Stream<ChannelState?> get channelStateStream => Stream<ChannelState?> get channelStateStream =>
@@ -1773,11 +1773,11 @@ class ChannelClientState {
/// The channel state related to this client /// The channel state related to this client
ChannelState? get channelState => _channelStateController.value; ChannelState? get channelState => _channelStateController.value;
late BehaviorSubject<ChannelState?> _channelStateController; late BehaviorSubject<ChannelState> _channelStateController;
final Debounce _debouncedUpdatePersistenceChannelState; final Debounce _debouncedUpdatePersistenceChannelState;
set _channelState(ChannelState? v) { set _channelState(ChannelState v) {
_channelStateController.add(v); _channelStateController.add(v);
_debouncedUpdatePersistenceChannelState.call([v]); _debouncedUpdatePersistenceChannelState.call([v]);
} }
@@ -1812,7 +1812,7 @@ class ChannelClientState {
final Map<User?, DateTime> _typings = {}; final Map<User?, DateTime> _typings = {};
void _listenTypingEvents() { void _listenTypingEvents() {
if (_channel.config?.typingEvents == false) { if (_channelState.channel?.config.typingEvents == false) {
return; return;
} }
@@ -1867,7 +1867,7 @@ class ChannelClientState {
late Timer _cleaningTimer; late Timer _cleaningTimer;
void _startCleaning() { void _startCleaning() {
if (_channel.config?.typingEvents == false) { if (_channelState.channel?.config.typingEvents == false) {
return; return;
} }
@@ -1899,7 +1899,7 @@ class ChannelClientState {
)) ))
.toList(); .toList();
updateChannelState(_channelState!.copyWith( updateChannelState(_channelState.copyWith(
pinnedMessages: pinnedMessages!.where(_pinIsValid()).toList(), pinnedMessages: pinnedMessages!.where(_pinIsValid()).toList(),
messages: expiredMessages, messages: expiredMessages,
)); ));
@@ -12,11 +12,11 @@ class Reaction {
this.messageId, this.messageId,
DateTime? createdAt, DateTime? createdAt,
required this.type, required this.type,
required this.user, this.user,
String? userId, String? userId,
this.score = 0, this.score = 0,
this.extraData, this.extraData,
}) : userId = userId ?? user.id, }) : userId = userId ?? user?.id,
createdAt = createdAt ?? DateTime.now(); createdAt = createdAt ?? DateTime.now();
/// Create a new instance from a json /// Create a new instance from a json
@@ -38,7 +38,7 @@ class Reaction {
/// The user that sent the reaction /// The user that sent the reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final User user; final User? user;
/// The score of the reaction (ie. number of reactions sent) /// The score of the reaction (ie. number of reactions sent)
@JsonKey(defaultValue: 0) @JsonKey(defaultValue: 0)
@@ -13,7 +13,9 @@ Reaction _$ReactionFromJson(Map<String, dynamic> json) {
? null ? null
: DateTime.parse(json['created_at'] as String), : DateTime.parse(json['created_at'] as String),
type: json['type'] as String, type: json['type'] as String,
user: User.fromJson(json['user'] as Map<String, dynamic>), user: json['user'] == null
? null
: User.fromJson(json['user'] as Map<String, dynamic>),
userId: json['user_id'] as String?, userId: json['user_id'] as String?,
score: json['score'] as int? ?? 0, score: json['score'] as int? ?? 0,
extraData: json['extra_data'] as Map<String, dynamic>?, extraData: json['extra_data'] as Map<String, dynamic>?,
@@ -38,6 +38,17 @@ void main() {
final channelClient = client.channel('messaging', id: 'testid'); final channelClient = client.channel('messaging', id: 'testid');
final message = Message(text: 'hey', id: 'test'); final message = Message(text: 'hey', id: 'test');
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState()),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
when( when(
() => mockDio.post<String>( () => mockDio.post<String>(
'/channels/messaging/testid/message', '/channels/messaging/testid/message',
@@ -45,7 +56,7 @@ void main() {
), ),
).thenAnswer( ).thenAnswer(
(_) async => Response( (_) async => Response(
data: '{}', data: jsonEncode({'message': message}),
statusCode: 200, statusCode: 200,
requestOptions: FakeRequestOptions(), requestOptions: FakeRequestOptions(),
), ),
@@ -227,6 +238,17 @@ void main() {
); );
final channelClient = client.channel(channelType, id: channelId); final channelClient = client.channel(channelType, id: channelId);
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState()),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
when(() => mockUploader.sendFile(file, channelId, channelType)) when(() => mockUploader.sendFile(file, channelId, channelType))
.thenAnswer((_) async => SendFileResponse()); .thenAnswer((_) async => SendFileResponse());
@@ -255,6 +277,17 @@ void main() {
); );
final channelClient = client.channel(channelType, id: channelId); final channelClient = client.channel(channelType, id: channelId);
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState()),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
when(() => mockUploader.sendImage(image, channelId, channelType)) when(() => mockUploader.sendImage(image, channelId, channelType))
.thenAnswer((_) async => SendImageResponse()); .thenAnswer((_) async => SendImageResponse());
@@ -278,6 +311,17 @@ void main() {
final channelClient = client.channel('messaging', id: 'testid'); final channelClient = client.channel('messaging', id: 'testid');
const url = 'url'; const url = 'url';
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState()),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
when( when(
() => mockDio.delete<String>( () => mockDio.delete<String>(
'/channels/messaging/testid/file', '/channels/messaging/testid/file',
@@ -311,6 +355,17 @@ void main() {
final channelClient = client.channel('messaging', id: 'testid'); final channelClient = client.channel('messaging', id: 'testid');
const url = 'url'; const url = 'url';
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState()),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
when( when(
() => mockDio.delete<String>( () => mockDio.delete<String>(
'/channels/messaging/testid/image', '/channels/messaging/testid/image',
@@ -357,6 +412,17 @@ void main() {
final channelClient = client.channel('messaging', id: 'testid'); final channelClient = client.channel('messaging', id: 'testid');
final message = Message(text: 'Hello', id: 'test'); final message = Message(text: 'Hello', id: 'test');
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState()),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
when( when(
() => mockDio.post<String>( () => mockDio.post<String>(
'/messages/${message.id}', '/messages/${message.id}',
@@ -364,7 +430,7 @@ void main() {
), ),
).thenAnswer( ).thenAnswer(
(_) async => Response( (_) async => Response(
data: '{}', data: jsonEncode({'message': message}),
statusCode: 200, statusCode: 200,
requestOptions: FakeRequestOptions(), requestOptions: FakeRequestOptions(),
), ),
@@ -391,6 +457,17 @@ void main() {
final channelClient = client.channel('messaging', id: 'testid'); final channelClient = client.channel('messaging', id: 'testid');
final message = Message(text: 'Hello', id: 'test'); final message = Message(text: 'Hello', id: 'test');
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState()),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
when( when(
() => mockDio.post<String>( () => mockDio.post<String>(
'/messages/${message.id}', '/messages/${message.id}',
@@ -398,7 +475,7 @@ void main() {
), ),
).thenAnswer( ).thenAnswer(
(_) async => Response( (_) async => Response(
data: '{}', data: jsonEncode({'message': message}),
statusCode: 200, statusCode: 200,
requestOptions: FakeRequestOptions(), requestOptions: FakeRequestOptions(),
), ),
@@ -567,14 +644,30 @@ void main() {
tokenProvider: (_) async => '', tokenProvider: (_) async => '',
); );
client.state.user = OwnUser(id: 'test-id'); final user = OwnUser(id: 'test-id');
final channelClient = client.channel('messaging', id: 'testid'); client.state.user = user;
final message = Message(id: 'messageid');
const reactionType = 'test'; const reactionType = 'test';
final reaction = Reaction(type: reactionType);
final channelClient = client.channel('messaging', id: 'testid');
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer(
(_) async => Response(
data: '{}',
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
);
await channelClient.watch();
when( when(
() => mockDio.post<String>( () => mockDio.post<String>(
'/messages/messageid/reaction', '/messages/${message.id}/reaction',
data: { data: {
'reaction': { 'reaction': {
'type': reactionType, 'type': reactionType,
@@ -584,16 +677,17 @@ void main() {
), ),
).thenAnswer( ).thenAnswer(
(_) async => Response( (_) async => Response(
data: '{}', data: jsonEncode({
'message': message,
'reaction': reaction,
}),
statusCode: 200, statusCode: 200,
requestOptions: FakeRequestOptions(), requestOptions: FakeRequestOptions(),
), ),
); );
await channelClient.sendReaction( await channelClient.sendReaction(
Message( message,
id: 'messageid',
),
reactionType, reactionType,
); );
@@ -697,26 +791,44 @@ void main() {
tokenProvider: (_) async => '', tokenProvider: (_) async => '',
); );
final channelClient = client.channel('messaging', id: 'testid'); final channelClient = client.channel('messaging', id: 'testid');
final members = ['vishal']; final channelModel = ChannelModel(cid: 'messaging:testid');
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState(channel: channelModel)),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
final members = [Member(userId: 'vishal')];
final memberIds = members.map((e) => e.userId!).toList();
final message = Message(text: 'test'); final message = Message(text: 'test');
when( when(
() => mockDio.post<String>( () => mockDio.post<String>(
'/channels/messaging/testid', '/channels/messaging/testid',
data: {'add_members': members, 'message': message.toJson()}, data: {'add_members': memberIds, 'message': message.toJson()},
), ),
).thenAnswer( ).thenAnswer(
(_) async => Response( (_) async => Response(
data: '{}', data: jsonEncode({
'members': members,
'message': message,
'channel': channelModel,
}),
statusCode: 200, statusCode: 200,
requestOptions: FakeRequestOptions(), requestOptions: FakeRequestOptions(),
), ),
); );
await channelClient.addMembers(members, message); await channelClient.addMembers(memberIds, message);
verify(() => mockDio.post<String>('/channels/messaging/testid', verify(() => mockDio.post<String>('/channels/messaging/testid',
data: {'add_members': members, 'message': message.toJson()})) data: {'add_members': memberIds, 'message': message.toJson()}))
.called(1); .called(1);
}); });
@@ -732,6 +844,19 @@ void main() {
tokenProvider: (_) async => '', tokenProvider: (_) async => '',
); );
final channelClient = client.channel('messaging', id: 'testid'); final channelClient = client.channel('messaging', id: 'testid');
final channelModel = ChannelModel(cid: 'messaging:testid');
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState(channel: channelModel)),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
final message = Message(text: 'test'); final message = Message(text: 'test');
when( when(
@@ -741,7 +866,10 @@ void main() {
), ),
).thenAnswer( ).thenAnswer(
(_) async => Response( (_) async => Response(
data: '{}', data: jsonEncode({
'message': message,
'channel': channelModel,
}),
statusCode: 200, statusCode: 200,
requestOptions: FakeRequestOptions(), requestOptions: FakeRequestOptions(),
), ),
@@ -2080,6 +2208,19 @@ void main() {
tokenProvider: (_) async => '', tokenProvider: (_) async => '',
); );
final channelClient = client.channel('messaging', id: 'testid'); final channelClient = client.channel('messaging', id: 'testid');
final channelModel = ChannelModel(cid: 'messaging:testid');
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState(channel: channelModel)),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
final message = Message(text: 'test'); final message = Message(text: 'test');
when( when(
@@ -2092,7 +2233,10 @@ void main() {
), ),
).thenAnswer( ).thenAnswer(
(_) async => Response( (_) async => Response(
data: '{}', data: jsonEncode({
'channel': channelModel,
'message': message,
}),
statusCode: 200, statusCode: 200,
requestOptions: FakeRequestOptions(), requestOptions: FakeRequestOptions(),
), ),
@@ -2179,6 +2323,19 @@ void main() {
tokenProvider: (_) async => '', tokenProvider: (_) async => '',
); );
final channelClient = client.channel('messaging', id: 'testid'); final channelClient = client.channel('messaging', id: 'testid');
final channelModel = ChannelModel(cid: 'messaging:testid');
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState(channel: channelModel)),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
final message = Message(text: 'test'); final message = Message(text: 'test');
when( when(
@@ -2188,7 +2345,10 @@ void main() {
), ),
).thenAnswer( ).thenAnswer(
(_) async => Response( (_) async => Response(
data: '{}', data: jsonEncode({
'message': message,
'channel': channelModel,
}),
statusCode: 200, statusCode: 200,
requestOptions: FakeRequestOptions(), requestOptions: FakeRequestOptions(),
), ),
@@ -2213,26 +2373,45 @@ void main() {
tokenProvider: (_) async => '', tokenProvider: (_) async => '',
); );
final channelClient = client.channel('messaging', id: 'testid'); final channelClient = client.channel('messaging', id: 'testid');
final members = ['vishal']; final channelModel = ChannelModel(cid: 'messaging:testid');
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState(channel: channelModel)),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
final members = [Member(userId: 'vishal')];
final memberIds = members.map((e) => e.userId!).toList();
final message = Message(text: 'test'); final message = Message(text: 'test');
when( when(
() => mockDio.post<String>( () => mockDio.post<String>(
'/channels/messaging/testid', '/channels/messaging/testid',
data: {'invites': members, 'message': message.toJson()}, data: {'invites': memberIds, 'message': message.toJson()},
), ),
).thenAnswer( ).thenAnswer(
(_) async => Response( (_) async => Response(
data: '{}', data: jsonEncode({
'members': members,
'message': message,
'channel': channelModel,
}),
statusCode: 200, statusCode: 200,
requestOptions: FakeRequestOptions(), requestOptions: FakeRequestOptions(),
), ),
); );
await channelClient.inviteMembers(members, message); await channelClient.inviteMembers(memberIds, message);
verify(() => mockDio.post<String>('/channels/messaging/testid', verify(() => mockDio.post<String>('/channels/messaging/testid',
data: {'invites': members, 'message': message.toJson()})).called(1); data: {'invites': memberIds, 'message': message.toJson()}))
.called(1);
}); });
test('removeMembers', () async { test('removeMembers', () async {
@@ -2247,27 +2426,46 @@ void main() {
tokenProvider: (_) async => '', tokenProvider: (_) async => '',
); );
final channelClient = client.channel('messaging', id: 'testid'); final channelClient = client.channel('messaging', id: 'testid');
final members = ['vishal']; final channelModel = ChannelModel(cid: 'messaging:testid');
when(() => mockDio.post<String>(
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: jsonEncode(ChannelState(channel: channelModel)),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
final members = [Member(userId: 'vishal')];
final memberIds = members.map((e) => e.userId!).toList();
final message = Message(text: 'test'); final message = Message(text: 'test');
when( when(
() => mockDio.post<String>( () => mockDio.post<String>(
'/channels/messaging/testid', '/channels/messaging/testid',
data: {'remove_members': members, 'message': message.toJson()}, data: {'remove_members': memberIds, 'message': message.toJson()},
), ),
).thenAnswer( ).thenAnswer(
(_) async => Response( (_) async => Response(
data: '{}', data: jsonEncode({
'members': members,
'message': message,
'channel': channelModel,
}),
statusCode: 200, statusCode: 200,
requestOptions: FakeRequestOptions(), requestOptions: FakeRequestOptions(),
), ),
); );
await channelClient.removeMembers(members, message); await channelClient.removeMembers(memberIds, message);
verify(() => mockDio.post<String>('/channels/messaging/testid', verify(() => mockDio.post<String>('/channels/messaging/testid', data: {
data: {'remove_members': members, 'message': message.toJson()})) 'remove_members': memberIds,
.called(1); 'message': message.toJson()
})).called(1);
}); });
test('hide', () async { test('hide', () async {