Merge branch 'develop' of https://github.com/GetStream/stream-chat-flutter into feat/capabilities

 Conflicts:
	packages/stream_chat/CHANGELOG.md
	packages/stream_chat_flutter/CHANGELOG.md
This commit is contained in:
Deven Joshi
2021-12-03 15:24:09 +05:30
56 changed files with 744 additions and 376 deletions
+20 -1
View File
@@ -4,6 +4,25 @@
- `ChannelModel` now supplies individual user capabilities.
🐞 Fixed
- [[#799]](https://github.com/GetStream/stream-chat-flutter/issues/799) Fixed `totalUnreadCount` is not updating when
app is resumed from background mode
## 3.3.0
✅ Added
- Extra properties added to `PaginationParams` to aid in fetching messages.
- Added hard delete functionality.
🐞 Fixed
- `closeConnection()` now uses `normalClosure` status when closing websocket.
- Fixed local unread count indicator increasing for thread replies.
- Fixed user presence indicator not updating correctly.
- `ChannelEvent.membersCount` defaults to 0 avoiding parsing errors due to missing `members_count` field.
## 3.2.0
🐞 Fixed
@@ -726,4 +745,4 @@
## 0.0.2
- first beta version
- first beta version
+2 -2
View File
@@ -233,10 +233,10 @@ class _MessageViewState extends State<MessageView> {
),
),
),
)
),
],
),
)
),
],
);
}
@@ -648,7 +648,7 @@ class Channel {
}
/// Deletes the [message] from the channel.
Future<EmptyResponse> deleteMessage(Message message) async {
Future<EmptyResponse> deleteMessage(Message message, {bool? hard}) async {
// Directly deleting the local messages which are not yet sent to server
if (message.status == MessageSendingStatus.sending ||
message.status == MessageSendingStatus.failed) {
@@ -675,7 +675,7 @@ class Channel {
state?.addMessage(message);
final response = await _client.deleteMessage(message.id);
final response = await _client.deleteMessage(message.id, hard: hard);
state?.addMessage(message.copyWith(status: MessageSendingStatus.sent));
@@ -1466,8 +1466,6 @@ class ChannelClientState {
_listenMemberRemoved();
_computeUnread();
_startCleaning();
_startCleaningPinnedMessages();
@@ -1490,15 +1488,6 @@ class ChannelClientState {
final _subscriptions = <StreamSubscription>[];
void _computeUnread() {
final userRead = channelState.read.firstWhereOrNull(
(r) => r.user.id == _channel._client.state.currentUser?.id,
);
if (userRead != null && userRead.unreadMessages > 0) {
unreadCount = userRead.unreadMessages;
}
}
void _checkExpiredAttachmentMessages(ChannelState channelState) async {
final expiredAttachmentMessagesId = channelState.messages
.where((m) =>
@@ -1603,7 +1592,7 @@ class ChannelClientState {
message.createdAt.isBefore(
DateTime.now().subtract(
const Duration(
seconds: 1,
seconds: 5,
),
),
),
@@ -1663,7 +1652,11 @@ class ChannelClientState {
void _listenMessageDeleted() {
_subscriptions.add(_channel.on(EventType.messageDeleted).listen((event) {
final message = event.message!;
addMessage(message);
if (event.hardDelete == true) {
removeMessage(message, hardDelete: true);
} else {
addMessage(message);
}
}));
}
@@ -1718,7 +1711,7 @@ class ChannelClientState {
}
/// Remove a [message] from this [channelState].
void removeMessage(Message message) {
void removeMessage(Message message, {bool hardDelete = false}) {
final parentId = message.parentId;
// i.e. it's a thread message
// 1. Remove the thread message
@@ -1740,7 +1733,10 @@ class ChannelClientState {
} else {
// Remove regular message
final allMessages = [...messages];
if (allMessages.remove(message)) {
if (hardDelete) {
allMessages.removeWhere((e) => e.id == message.id);
_channelState = _channelState.copyWith(messages: allMessages);
} else if (allMessages.remove(message)) {
_channelState = _channelState.copyWith(messages: allMessages);
}
}
@@ -1843,15 +1839,34 @@ class ChannelClientState {
/// Channel read list as a stream.
Stream<List<Read>> get readStream => channelStateStream.map((cs) => cs.read);
final BehaviorSubject<int> _unreadCountController = BehaviorSubject.seeded(0);
bool _isCurrentUserRead(Read read) =>
read.user.id == _channel._client.state.currentUser!.id;
set unreadCount(int value) => _unreadCountController.add(value);
/// Channel read for the logged in user.
Read? get currentUserRead => read.firstWhereOrNull(_isCurrentUserRead);
/// Channel read for the logged in user as a stream.
Stream<Read?> get currentUserReadStream =>
readStream.map((read) => read.firstWhereOrNull(_isCurrentUserRead));
/// Unread count getter as a stream.
Stream<int> get unreadCountStream => _unreadCountController.stream.distinct();
Stream<int> get unreadCountStream =>
currentUserReadStream.map((read) => read?.unreadMessages ?? 0);
/// Unread count getter.
int get unreadCount => _unreadCountController.value;
int get unreadCount => currentUserRead?.unreadMessages ?? 0;
/// Setter for unread count.
set unreadCount(int count) {
final reads = [..._channelState.read];
final currentUserReadIndex = reads.indexWhere(_isCurrentUserRead);
if (currentUserReadIndex < 0) return;
reads[currentUserReadIndex] =
reads[currentUserReadIndex].copyWith(unreadMessages: count);
_channelState = _channelState.copyWith(read: reads);
}
bool _countMessageAsUnread(Message message) {
final userId = _channel.client.state.currentUser?.id;
@@ -1860,10 +1875,13 @@ class ChannelClientState {
(m) => m.user.id == message.user?.id,
) !=
null;
final isThreadMessage = message.parentId != null;
return !message.silent &&
!message.shadowed &&
message.user?.id != userId &&
!userIsMuted;
!userIsMuted &&
!isThreadMessage;
}
/// Update threads with updated information about messages.
@@ -2108,7 +2126,6 @@ class ChannelClientState {
/// Call this method to dispose this object.
void dispose() {
_debouncedUpdatePersistenceChannelState.cancel();
_unreadCountController.close();
_retryQueue.dispose();
_subscriptions.forEach((s) => s.cancel());
_channelStateController.close();
@@ -395,6 +395,9 @@ class StreamChatClient {
}
void _handleHealthCheckEvent(Event event) {
final user = event.me;
if (user != null) state.currentUser = user;
final connectionId = event.connectionId;
if (connectionId != null) {
_connectionIdManager.setConnectionId(connectionId);
@@ -1213,8 +1216,14 @@ class StreamChatClient {
);
/// Deletes the given message
Future<EmptyResponse> deleteMessage(String messageId) =>
_chatApi.message.deleteMessage(messageId);
Future<EmptyResponse> deleteMessage(String messageId, {bool? hard}) async {
final response =
await _chatApi.message.deleteMessage(messageId, hard: hard);
if (hard == true) {
await _chatPersistenceClient?.deleteMessageById(messageId);
}
return response;
}
/// Get a message by [messageId]
Future<GetMessageResponse> getMessage(String messageId) =>
@@ -1432,6 +1441,7 @@ class ClientState {
.listen((Event event) async {
final eventChannel = event.channel!;
await _client.chatPersistenceClient?.deleteChannels([eventChannel.cid]);
channels[eventChannel.cid]?.dispose();
channels = channels..remove(eventChannel.cid);
}));
}
@@ -80,10 +80,16 @@ class MessageApi {
/// Deletes the given [messageId]
Future<EmptyResponse> deleteMessage(
String messageId,
) async {
String messageId, {
bool? hard,
}) async {
final response = await _client.delete(
'/messages/$messageId',
queryParameters: hard != null
? {
'hard': hard,
}
: null,
);
return EmptyResponse.fromJson(response.data);
}
@@ -60,8 +60,11 @@ class PaginationParams extends Equatable {
/// ```
const PaginationParams({
this.limit = 10,
this.before = 10,
this.after = 10,
this.offset,
this.next,
this.idAround,
this.greaterThan,
this.greaterThanOrEqual,
this.lessThan,
@@ -78,12 +81,22 @@ class PaginationParams extends Equatable {
/// The amount of items requested from the APIs.
final int limit;
/// The amount of items requested before message ID from the APIs.
final int before;
/// The amount of items requested after message ID from the APIs.
final int after;
/// The offset of requesting items.
final int? offset;
/// A key used to paginate.
final String? next;
/// Message ID to fetch messages around
@JsonKey(name: 'id_around')
final String? idAround;
/// Filter on ids greater than the given value.
@JsonKey(name: 'id_gt')
final String? greaterThan;
@@ -106,7 +119,10 @@ class PaginationParams extends Equatable {
/// Creates a copy of [PaginationParams] with specified attributes overridden.
PaginationParams copyWith({
int? limit,
int? before,
int? after,
int? offset,
String? idAround,
String? next,
String? greaterThan,
String? greaterThanOrEqual,
@@ -115,7 +131,10 @@ class PaginationParams extends Equatable {
}) =>
PaginationParams(
limit: limit ?? this.limit,
before: before ?? this.before,
after: limit ?? this.after,
offset: offset ?? this.offset,
idAround: idAround ?? this.idAround,
next: next ?? this.next,
greaterThan: greaterThan ?? this.greaterThan,
greaterThanOrEqual: greaterThanOrEqual ?? this.greaterThanOrEqual,
@@ -126,8 +145,11 @@ class PaginationParams extends Equatable {
@override
List<Object?> get props => [
limit,
before,
after,
offset,
next,
idAround,
greaterThan,
greaterThanOrEqual,
lessThan,
@@ -21,8 +21,11 @@ Map<String, dynamic> _$SortOptionToJson<T>(SortOption<T> instance) =>
PaginationParams _$PaginationParamsFromJson(Map<String, dynamic> json) =>
PaginationParams(
limit: json['limit'] as int? ?? 10,
before: json['before'] as int? ?? 10,
after: json['after'] as int? ?? 10,
offset: json['offset'] as int?,
next: json['next'] as String?,
idAround: json['id_around'] as String?,
greaterThan: json['id_gt'] as String?,
greaterThanOrEqual: json['id_gte'] as String?,
lessThan: json['id_lt'] as String?,
@@ -32,6 +35,8 @@ PaginationParams _$PaginationParamsFromJson(Map<String, dynamic> json) =>
Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
final val = <String, dynamic>{
'limit': instance.limit,
'before': instance.before,
'after': instance.after,
};
void writeNotNull(String key, dynamic value) {
@@ -42,6 +47,7 @@ Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
writeNotNull('offset', instance.offset);
writeNotNull('next', instance.next);
writeNotNull('id_around', instance.idAround);
writeNotNull('id_gt', instance.greaterThan);
writeNotNull('id_gte', instance.greaterThanOrEqual);
writeNotNull('id_lt', instance.lessThan);
@@ -14,7 +14,7 @@ final _privateConstructorUsedError = UnsupportedError(
'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more informations: https://github.com/rrousselGit/freezed#custom-getters-and-methods');
UploadState _$UploadStateFromJson(Map<String, dynamic> json) {
switch (json['runtimeType'] as String?) {
switch (json['runtimeType']) {
case 'preparing':
return Preparing.fromJson(json);
case 'inProgress':
@@ -55,7 +55,7 @@ class _$UploadStateTearOff {
);
}
UploadState fromJson(Map<String, Object> json) {
UploadState fromJson(Map<String, Object?> json) {
return UploadState.fromJson(json);
}
}
@@ -153,11 +153,14 @@ class _$PreparingCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
/// @nodoc
@JsonSerializable()
class _$Preparing implements Preparing {
const _$Preparing();
const _$Preparing({String? $type}) : $type = $type ?? 'preparing';
factory _$Preparing.fromJson(Map<String, dynamic> json) =>
_$$PreparingFromJson(json);
@JsonKey(name: 'runtimeType')
final String $type;
@override
String toString() {
return 'UploadState.preparing()';
@@ -165,7 +168,8 @@ class _$Preparing implements Preparing {
@override
bool operator ==(dynamic other) {
return identical(this, other) || (other is Preparing);
return identical(this, other) ||
(other.runtimeType == runtimeType && other is Preparing);
}
@override
@@ -247,7 +251,7 @@ class _$Preparing implements Preparing {
@override
Map<String, dynamic> toJson() {
return _$$PreparingToJson(this)..['runtimeType'] = 'preparing';
return _$$PreparingToJson(this);
}
}
@@ -295,7 +299,9 @@ class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
/// @nodoc
@JsonSerializable()
class _$InProgress implements InProgress {
const _$InProgress({required this.uploaded, required this.total});
const _$InProgress(
{required this.uploaded, required this.total, String? $type})
: $type = $type ?? 'inProgress';
factory _$InProgress.fromJson(Map<String, dynamic> json) =>
_$$InProgressFromJson(json);
@@ -305,6 +311,9 @@ class _$InProgress implements InProgress {
@override
final int total;
@JsonKey(name: 'runtimeType')
final String $type;
@override
String toString() {
return 'UploadState.inProgress(uploaded: $uploaded, total: $total)';
@@ -313,19 +322,15 @@ class _$InProgress implements InProgress {
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other is InProgress &&
(other.runtimeType == runtimeType &&
other is InProgress &&
(identical(other.uploaded, uploaded) ||
const DeepCollectionEquality()
.equals(other.uploaded, uploaded)) &&
(identical(other.total, total) ||
const DeepCollectionEquality().equals(other.total, total)));
other.uploaded == uploaded) &&
(identical(other.total, total) || other.total == total));
}
@override
int get hashCode =>
runtimeType.hashCode ^
const DeepCollectionEquality().hash(uploaded) ^
const DeepCollectionEquality().hash(total);
int get hashCode => Object.hash(runtimeType, uploaded, total);
@JsonKey(ignore: true)
@override
@@ -408,7 +413,7 @@ class _$InProgress implements InProgress {
@override
Map<String, dynamic> toJson() {
return _$$InProgressToJson(this)..['runtimeType'] = 'inProgress';
return _$$InProgressToJson(this);
}
}
@@ -419,8 +424,8 @@ abstract class InProgress implements UploadState {
factory InProgress.fromJson(Map<String, dynamic> json) =
_$InProgress.fromJson;
int get uploaded => throw _privateConstructorUsedError;
int get total => throw _privateConstructorUsedError;
int get uploaded;
int get total;
@JsonKey(ignore: true)
$InProgressCopyWith<InProgress> get copyWith =>
throw _privateConstructorUsedError;
@@ -445,11 +450,14 @@ class _$SuccessCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
/// @nodoc
@JsonSerializable()
class _$Success implements Success {
const _$Success();
const _$Success({String? $type}) : $type = $type ?? 'success';
factory _$Success.fromJson(Map<String, dynamic> json) =>
_$$SuccessFromJson(json);
@JsonKey(name: 'runtimeType')
final String $type;
@override
String toString() {
return 'UploadState.success()';
@@ -457,7 +465,8 @@ class _$Success implements Success {
@override
bool operator ==(dynamic other) {
return identical(this, other) || (other is Success);
return identical(this, other) ||
(other.runtimeType == runtimeType && other is Success);
}
@override
@@ -539,7 +548,7 @@ class _$Success implements Success {
@override
Map<String, dynamic> toJson() {
return _$$SuccessToJson(this)..['runtimeType'] = 'success';
return _$$SuccessToJson(this);
}
}
@@ -581,7 +590,8 @@ class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
/// @nodoc
@JsonSerializable()
class _$Failed implements Failed {
const _$Failed({required this.error});
const _$Failed({required this.error, String? $type})
: $type = $type ?? 'failed';
factory _$Failed.fromJson(Map<String, dynamic> json) =>
_$$FailedFromJson(json);
@@ -589,6 +599,9 @@ class _$Failed implements Failed {
@override
final String error;
@JsonKey(name: 'runtimeType')
final String $type;
@override
String toString() {
return 'UploadState.failed(error: $error)';
@@ -597,14 +610,13 @@ class _$Failed implements Failed {
@override
bool operator ==(dynamic other) {
return identical(this, other) ||
(other is Failed &&
(identical(other.error, error) ||
const DeepCollectionEquality().equals(other.error, error)));
(other.runtimeType == runtimeType &&
other is Failed &&
(identical(other.error, error) || other.error == error));
}
@override
int get hashCode =>
runtimeType.hashCode ^ const DeepCollectionEquality().hash(error);
int get hashCode => Object.hash(runtimeType, error);
@JsonKey(ignore: true)
@override
@@ -687,7 +699,7 @@ class _$Failed implements Failed {
@override
Map<String, dynamic> toJson() {
return _$$FailedToJson(this)..['runtimeType'] = 'failed';
return _$$FailedToJson(this);
}
}
@@ -696,7 +708,7 @@ abstract class Failed implements UploadState {
factory Failed.fromJson(Map<String, dynamic> json) = _$Failed.fromJson;
String get error => throw _privateConstructorUsedError;
String get error;
@JsonKey(ignore: true)
$FailedCopyWith<Failed> get copyWith => throw _privateConstructorUsedError;
}
@@ -22,31 +22,42 @@ Map<String, dynamic> _$AttachmentFileToJson(AttachmentFile instance) =>
'size': instance.size,
};
_$Preparing _$$PreparingFromJson(Map<String, dynamic> json) => _$Preparing();
_$Preparing _$$PreparingFromJson(Map<String, dynamic> json) => _$Preparing(
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$$PreparingToJson(_$Preparing instance) =>
<String, dynamic>{};
<String, dynamic>{
'runtimeType': instance.$type,
};
_$InProgress _$$InProgressFromJson(Map<String, dynamic> json) => _$InProgress(
uploaded: json['uploaded'] as int,
total: json['total'] as int,
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$$InProgressToJson(_$InProgress instance) =>
<String, dynamic>{
'uploaded': instance.uploaded,
'total': instance.total,
'runtimeType': instance.$type,
};
_$Success _$$SuccessFromJson(Map<String, dynamic> json) => _$Success();
_$Success _$$SuccessFromJson(Map<String, dynamic> json) => _$Success(
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$$SuccessToJson(_$Success instance) =>
<String, dynamic>{};
Map<String, dynamic> _$$SuccessToJson(_$Success instance) => <String, dynamic>{
'runtimeType': instance.$type,
};
_$Failed _$$FailedFromJson(Map<String, dynamic> json) => _$Failed(
error: json['error'] as String,
$type: json['runtimeType'] as String?,
);
Map<String, dynamic> _$$FailedToJson(_$Failed instance) => <String, dynamic>{
'error': instance.error,
'runtimeType': instance.$type,
};
@@ -27,6 +27,7 @@ class Event {
this.channelId,
this.channelType,
this.parentId,
this.hardDelete,
this.extraData = const {},
this.isLocal = true,
}) : createdAt = createdAt?.toUtc() ?? DateTime.now().toUtc();
@@ -91,6 +92,10 @@ class Event {
@JsonKey(defaultValue: false)
final bool isLocal;
/// This is true if the message has been hard deleted
@JsonKey(includeIfNull: false)
final bool? hardDelete;
/// Map of custom channel extraData
final Map<String, Object?> extraData;
@@ -113,6 +118,7 @@ class Event {
'channel_id',
'channel_type',
'parent_id',
'hard_delete',
'is_local',
];
@@ -139,6 +145,7 @@ class Event {
int? unreadChannels,
bool? online,
String? parentId,
bool? hardDelete,
Map<String, Object?>? extraData,
}) =>
Event(
@@ -158,6 +165,7 @@ class Event {
channelId: channelId ?? this.channelId,
channelType: channelType ?? this.channelType,
parentId: parentId ?? this.parentId,
hardDelete: hardDelete ?? this.hardDelete,
extraData: extraData ?? this.extraData,
isLocal: isLocal,
);
@@ -181,7 +189,7 @@ class EventChannel extends ChannelModel {
required DateTime createdAt,
required DateTime updatedAt,
DateTime? deletedAt,
required int memberCount,
int memberCount = 0,
Map<String, Object?>? extraData,
int cooldown = 0,
String? team,
@@ -37,30 +37,42 @@ Event _$EventFromJson(Map<String, dynamic> json) => Event(
channelId: json['channel_id'] as String?,
channelType: json['channel_type'] as String?,
parentId: json['parent_id'] as String?,
hardDelete: json['hard_delete'] as bool?,
extraData: json['extra_data'] as Map<String, dynamic>? ?? const {},
isLocal: json['is_local'] as bool? ?? false,
);
Map<String, dynamic> _$EventToJson(Event instance) => <String, dynamic>{
'type': instance.type,
'cid': instance.cid,
'channel_id': instance.channelId,
'channel_type': instance.channelType,
'connection_id': instance.connectionId,
'created_at': instance.createdAt.toIso8601String(),
'me': instance.me?.toJson(),
'user': instance.user?.toJson(),
'message': instance.message?.toJson(),
'channel': instance.channel?.toJson(),
'member': instance.member?.toJson(),
'reaction': instance.reaction?.toJson(),
'total_unread_count': instance.totalUnreadCount,
'unread_channels': instance.unreadChannels,
'online': instance.online,
'parent_id': instance.parentId,
'is_local': instance.isLocal,
'extra_data': instance.extraData,
};
Map<String, dynamic> _$EventToJson(Event instance) {
final val = <String, dynamic>{
'type': instance.type,
'cid': instance.cid,
'channel_id': instance.channelId,
'channel_type': instance.channelType,
'connection_id': instance.connectionId,
'created_at': instance.createdAt.toIso8601String(),
'me': instance.me?.toJson(),
'user': instance.user?.toJson(),
'message': instance.message?.toJson(),
'channel': instance.channel?.toJson(),
'member': instance.member?.toJson(),
'reaction': instance.reaction?.toJson(),
'total_unread_count': instance.totalUnreadCount,
'unread_channels': instance.unreadChannels,
'online': instance.online,
'parent_id': instance.parentId,
'is_local': instance.isLocal,
};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('hard_delete', instance.hardDelete);
val['extra_data'] = instance.extraData;
return val;
}
EventChannel _$EventChannelFromJson(Map<String, dynamic> json) => EventChannel(
members: (json['members'] as List<dynamic>?)
@@ -82,7 +94,7 @@ EventChannel _$EventChannelFromJson(Map<String, dynamic> json) => EventChannel(
deletedAt: json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
memberCount: json['member_count'] as int,
memberCount: json['member_count'] as int? ?? 0,
extraData: json['extra_data'] as Map<String, dynamic>?,
cooldown: json['cooldown'] as int? ?? 0,
team: json['team'] as String?,
@@ -1,3 +1,4 @@
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';
import 'package:stream_chat/src/core/models/user.dart';
@@ -5,9 +6,9 @@ part 'read.g.dart';
/// The class that defines a read event
@JsonSerializable()
class Read {
class Read extends Equatable {
/// Constructor used for json serialization
Read({
const Read({
required this.lastRead,
required this.user,
this.unreadMessages = 0,
@@ -39,4 +40,11 @@ class Read {
user: user ?? this.user,
unreadMessages: unreadMessages ?? this.unreadMessages,
);
@override
List<Object?> get props => [
lastRead,
user,
unreadMessages,
];
}
@@ -179,5 +179,14 @@ class User extends Equatable {
);
@override
List<Object?> get props => [id, role];
List<Object?> get props => [
id,
role,
lastActive,
online,
extraData,
banned,
teams,
language,
];
}
@@ -82,7 +82,6 @@ abstract class ChatPersistenceClient {
members: data[0] as List<Member>,
// ignore: cast_nullable_to_non_nullable
read: data[1] as List<Read>,
// ignore: cast_nullable_to_non_nullable
channel: data[2] as ChannelModel?,
// ignore: cast_nullable_to_non_nullable
messages: data[3] as List<Message>,
@@ -121,7 +121,8 @@ class WebSocket with TimerHelper {
_logger?.info('Closing connection with $baseUrl');
if (_webSocketChannel != null) {
_unsubscribeFromWebSocketChannel();
_webSocketChannel?.sink.close(status.goingAway);
_webSocketChannel?.sink
.close(_manuallyClosed ? status.normalClosure : status.goingAway);
_webSocketChannel = null;
}
}
@@ -309,7 +310,10 @@ class WebSocket with TimerHelper {
Event? event;
try {
event = Event.fromJson(jsonData);
} catch (_) {}
} catch (e, stk) {
_logger?.warning('Error parsing an event: $e');
_logger?.warning('Stack trace: $stk');
}
if (event == null) return;
+1 -1
View File
@@ -3,4 +3,4 @@ import 'package:stream_chat/src/client/client.dart';
/// Current package version
/// Used in [StreamChatClient] to build the `x-stream-client` header
// ignore: constant_identifier_names
const PACKAGE_VERSION = '3.2.0';
const PACKAGE_VERSION = '3.3.0';
+4 -4
View File
@@ -1,7 +1,7 @@
name: stream_chat
homepage: https://getstream.io/
description: The official Dart client for Stream Chat, a service for building chat applications.
version: 3.2.0
version: 3.3.0
repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
@@ -13,10 +13,10 @@ dependencies:
collection: ^1.15.0
dio: ^4.0.0
equatable: ^2.0.0
freezed_annotation: ^0.15.0
freezed_annotation: ^1.0.0
http_parser: ^4.0.0
jose: ^0.3.2
json_annotation: ^4.0.1
json_annotation: ^4.3.0
logging: ^1.0.1
meta: ^1.3.0
mime: ^1.0.0
@@ -28,7 +28,7 @@ dependencies:
dev_dependencies:
build_runner: ^2.0.1
dart_code_metrics: ^4.4.0
freezed: ^0.15.0+1
freezed: ^1.0.0
json_serializable: ^6.0.1
mocktail: ^0.2.0
test: ^1.17.12
@@ -123,27 +123,6 @@ void main() {
verify(() => logger.severe(any())).called(greaterThan(0));
});
test('`.lock` should lock the dio client', () async {
final client = StreamHttpClient('api-key');
expect(client.httpClient.interceptors.requestLock.locked, isFalse);
client.lock();
expect(client.httpClient.interceptors.requestLock.locked, isTrue);
});
test('`.unlock` should unlock the dio client', () async {
final client = StreamHttpClient('api-key');
expect(client.httpClient.interceptors.requestLock.locked, isFalse);
client.lock();
expect(client.httpClient.interceptors.requestLock.locked, isTrue);
client.unlock();
expect(client.httpClient.interceptors.requestLock.locked, isFalse);
});
test('`.clear` should clear and unlock the dio client', () async {
final client = StreamHttpClient('api-key')..clear();
expect(client.httpClient.interceptors.requestLock.locked, isFalse);
});
test('`.close` should close the dio client', () async {
final client = StreamHttpClient('api-key')..close(force: true);
try {
@@ -70,13 +70,19 @@ void main() {
expect(
newReaction.extraData, {'updated_at': '2020-01-28T22:17:31.108742Z'});
final newUserCreateTime = DateTime.now();
newReaction = reaction.copyWith(
type: 'lol',
createdAt: DateTime.parse('2021-01-28T22:17:31.108742Z'),
extraData: {},
messageId: 'test',
score: 2,
user: User(id: 'test'),
user: User(
id: 'test',
createdAt: newUserCreateTime,
updatedAt: newUserCreateTime,
),
userId: 'test',
);
@@ -88,12 +94,21 @@ void main() {
expect(newReaction.extraData, {});
expect(newReaction.messageId, 'test');
expect(newReaction.score, 2);
expect(newReaction.user, User(id: 'test'));
expect(
newReaction.user,
User(
id: 'test',
createdAt: newUserCreateTime,
updatedAt: newUserCreateTime,
),
);
expect(newReaction.userId, 'test');
});
test('merge', () {
final reaction = Reaction.fromJson(jsonFixture('reaction.json'));
final newUserCreateTime = DateTime.now();
final newReaction = reaction.merge(
Reaction(
type: 'lol',
@@ -101,7 +116,11 @@ void main() {
extraData: {},
messageId: 'test',
score: 2,
user: User(id: 'test'),
user: User(
id: 'test',
createdAt: newUserCreateTime,
updatedAt: newUserCreateTime,
),
userId: 'test',
),
);
@@ -114,7 +133,14 @@ void main() {
expect(newReaction.extraData, {});
expect(newReaction.messageId, 'test');
expect(newReaction.score, 2);
expect(newReaction.user, User(id: 'test'));
expect(
newReaction.user,
User(
id: 'test',
createdAt: newUserCreateTime,
updatedAt: newUserCreateTime,
),
);
expect(newReaction.userId, 'test');
});
});