Merge pull request #389 from GetStream/llc-nnbd

feat: Llc nnbd
This commit is contained in:
Salvatore Giordano
2021-04-20 10:33:52 +02:00
committed by GitHub
75 changed files with 2387 additions and 2236 deletions
+1 -2
View File
@@ -4,5 +4,4 @@ targets:
json_serializable:
options:
explicit_to_json: true
field_rename: snake
any_map: true
field_rename: snake
+20 -17
View File
@@ -44,9 +44,9 @@ class StreamExample extends StatelessWidget {
/// To initialize this example, an instance of
/// [client] and [channel] is required.
const StreamExample({
Key key,
@required this.client,
@required this.channel,
Key? key,
required this.client,
required this.channel,
}) : super(key: key);
/// Instance of [StreamChatClient] we created earlier.
@@ -69,28 +69,31 @@ class StreamExample extends StatelessWidget {
/// containing the channel name and a [MessageView] displaying recent messages.
class HomeScreen extends StatelessWidget {
/// [HomeScreen] is constructed using the [Channel] we defined earlier.
const HomeScreen({Key key, @required this.channel}) : super(key: key);
const HomeScreen({
Key? key,
required this.channel,
}) : super(key: key);
/// Channel object containing the [Channel.id] we'd like to observe.
final Channel channel;
@override
Widget build(BuildContext context) {
final messages = channel.state.channelStateStream;
final messages = channel.state!.channelStateStream;
return Scaffold(
appBar: AppBar(
title: Text('Channel: ${channel.id}'),
),
body: SafeArea(
child: StreamBuilder<ChannelState>(
child: StreamBuilder<ChannelState?>(
stream: messages,
builder: (
BuildContext context,
AsyncSnapshot<ChannelState> snapshot,
AsyncSnapshot<ChannelState?> snapshot,
) {
if (snapshot.hasData && snapshot.data != null) {
return MessageView(
messages: snapshot.data.messages.reversed.toList(),
messages: snapshot.data!.messages.reversed.toList(),
channel: channel,
);
} else if (snapshot.hasError) {
@@ -119,9 +122,9 @@ class HomeScreen extends StatelessWidget {
class MessageView extends StatefulWidget {
/// Message takes the latest list of messages and the current channel.
const MessageView({
Key key,
@required this.messages,
@required this.channel,
Key? key,
required this.messages,
required this.channel,
}) : super(key: key);
/// List of messages sent in the given channel.
@@ -135,8 +138,8 @@ class MessageView extends StatefulWidget {
}
class _MessageViewState extends State<MessageView> {
TextEditingController _controller;
ScrollController _scrollController;
late final TextEditingController _controller;
late final ScrollController _scrollController;
List<Message> get _messages => widget.messages;
@@ -174,12 +177,12 @@ class _MessageViewState extends State<MessageView> {
reverse: true,
itemBuilder: (BuildContext context, int index) {
final item = _messages[index];
if (item.user.id == widget.channel.client.uid) {
if (item.user?.id == widget.channel.client.uid) {
return Align(
alignment: Alignment.centerRight,
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(item.text),
child: Text(item.text ?? ''),
),
);
} else {
@@ -187,7 +190,7 @@ class _MessageViewState extends State<MessageView> {
alignment: Alignment.centerLeft,
child: Padding(
padding: const EdgeInsets.all(8),
child: Text(item.text),
child: Text(item.text ?? ''),
),
);
}
@@ -246,5 +249,5 @@ class _MessageViewState extends State<MessageView> {
/// Helper extension for quickly retrieving
/// the current user id from a [StreamChatClient].
extension on StreamChatClient {
String get uid => state.user.id;
String get uid => state.user!.id;
}
+1 -1
View File
@@ -5,7 +5,7 @@ publish_to: "none"
version: 1.0.0+1
environment:
sdk: ">=2.7.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
cupertino_icons: ^1.0.0
File diff suppressed because it is too large Load Diff
+12 -11
View File
@@ -34,7 +34,7 @@ class SortOption<T> {
/// Sorting field Comparator required for offline sorting
@JsonKey(ignore: true)
final Comparator<T> comparator;
final Comparator<T>? comparator;
/// Serialize model to json
Map<String, dynamic> toJson() => _$SortOptionToJson(this);
@@ -70,31 +70,31 @@ class PaginationParams {
/// Filter on ids greater than the given value.
@JsonKey(name: 'id_gt')
final String greaterThan;
final String? greaterThan;
/// Filter on ids greater than or equal to the given value.
@JsonKey(name: 'id_gte')
final String greaterThanOrEqual;
final String? greaterThanOrEqual;
/// Filter on ids smaller than the given value.
@JsonKey(name: 'id_lt')
final String lessThan;
final String? lessThan;
/// Filter on ids smaller than or equal to the given value.
@JsonKey(name: 'id_lte')
final String lessThanOrEqual;
final String? lessThanOrEqual;
/// Serialize model to json
Map<String, dynamic> toJson() => _$PaginationParamsToJson(this);
/// Creates a copy of [PaginationParams] with specified attributes overridden.
PaginationParams copyWith({
int limit,
int offset,
String greaterThan,
String greaterThanOrEqual,
String lessThan,
String lessThanOrEqual,
int? limit,
int? offset,
String? greaterThan,
String? greaterThanOrEqual,
String? lessThan,
String? lessThanOrEqual,
}) =>
PaginationParams(
limit: limit ?? this.limit,
@@ -106,6 +106,7 @@ class PaginationParams {
);
@override
@JsonKey(ignore: true)
int get hashCode =>
runtimeType.hashCode ^
limit.hashCode ^
@@ -13,7 +13,10 @@ Map<String, dynamic> _$SortOptionToJson<T>(SortOption<T> instance) =>
};
Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
final val = <String, dynamic>{};
final val = <String, dynamic>{
'limit': instance.limit,
'offset': instance.offset,
};
void writeNotNull(String key, dynamic value) {
if (value != null) {
@@ -21,8 +24,6 @@ Map<String, dynamic> _$PaginationParamsToJson(PaginationParams instance) {
}
}
writeNotNull('limit', instance.limit);
writeNotNull('offset', instance.offset);
writeNotNull('id_gt', instance.greaterThan);
writeNotNull('id_gte', instance.greaterThanOrEqual);
writeNotNull('id_lt', instance.lessThan);
+69 -50
View File
@@ -13,14 +13,15 @@ import 'package:stream_chat/src/models/user.dart';
part 'responses.g.dart';
class _BaseResponse {
String duration;
String? duration;
}
/// Model response for [StreamChatClient.resync] api call
@JsonSerializable(createToJson: false)
class SyncResponse extends _BaseResponse {
/// The list of events
List<Event> events;
@JsonKey(defaultValue: [])
late List<Event> events;
/// Create a new instance from a json
static SyncResponse fromJson(Map<String, dynamic> json) =>
@@ -31,7 +32,8 @@ class SyncResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class QueryChannelsResponse extends _BaseResponse {
/// List of channels state returned by the query
List<ChannelState> channels;
@JsonKey(defaultValue: [])
late List<ChannelState> channels;
/// Create a new instance from a json
static QueryChannelsResponse fromJson(Map<String, dynamic> json) =>
@@ -41,8 +43,8 @@ class QueryChannelsResponse extends _BaseResponse {
/// Model response for [StreamChatClient.queryChannels] api call
@JsonSerializable(createToJson: false)
class TranslateMessageResponse extends _BaseResponse {
/// List of channels state returned by the query
TranslatedMessage message;
/// Translated message
late TranslatedMessage message;
/// Create a new instance from a json
static TranslateMessageResponse fromJson(Map<String, dynamic> json) =>
@@ -53,7 +55,8 @@ class TranslateMessageResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class QueryMembersResponse extends _BaseResponse {
/// List of channels state returned by the query
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Create a new instance from a json
static QueryMembersResponse fromJson(Map<String, dynamic> json) =>
@@ -64,7 +67,8 @@ class QueryMembersResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class QueryUsersResponse extends _BaseResponse {
/// List of users returned by the query
List<User> users;
@JsonKey(defaultValue: [])
late List<User> users;
/// Create a new instance from a json
static QueryUsersResponse fromJson(Map<String, dynamic> json) =>
@@ -75,7 +79,8 @@ class QueryUsersResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class QueryReactionsResponse extends _BaseResponse {
/// List of reactions returned by the query
List<Reaction> reactions;
@JsonKey(defaultValue: [])
late List<Reaction> reactions;
/// Create a new instance from a json
static QueryReactionsResponse fromJson(Map<String, dynamic> json) =>
@@ -86,7 +91,8 @@ class QueryReactionsResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class QueryRepliesResponse extends _BaseResponse {
/// List of messages returned by the api call
List<Message> messages;
@JsonKey(defaultValue: [])
late List<Message> messages;
/// Create a new instance from a json
static QueryRepliesResponse fromJson(Map<String, dynamic> json) =>
@@ -97,7 +103,8 @@ class QueryRepliesResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class ListDevicesResponse extends _BaseResponse {
/// List of user devices
List<Device> devices;
@JsonKey(defaultValue: [])
late List<Device> devices;
/// Create a new instance from a json
static ListDevicesResponse fromJson(Map<String, dynamic> json) =>
@@ -108,7 +115,7 @@ class ListDevicesResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class SendFileResponse extends _BaseResponse {
/// The url of the uploaded file
String file;
late String file;
/// Create a new instance from a json
static SendFileResponse fromJson(Map<String, dynamic> json) =>
@@ -119,7 +126,7 @@ class SendFileResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class SendImageResponse extends _BaseResponse {
/// The url of the uploaded file
String file;
late String file;
/// Create a new instance from a json
static SendImageResponse fromJson(Map<String, dynamic> json) =>
@@ -130,10 +137,10 @@ class SendImageResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class SendReactionResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
late Message message;
/// The reaction created by the api call
Reaction reaction;
late Reaction reaction;
/// Create a new instance from a json
static SendReactionResponse fromJson(Map<String, dynamic> json) =>
@@ -144,10 +151,10 @@ class SendReactionResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class ConnectGuestUserResponse extends _BaseResponse {
/// Guest user access token
String accessToken;
late String accessToken;
/// Guest user
User user;
late User user;
/// Create a new instance from a json
static ConnectGuestUserResponse fromJson(Map<String, dynamic> json) =>
@@ -158,7 +165,8 @@ class ConnectGuestUserResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class UpdateUsersResponse extends _BaseResponse {
/// Updated users
Map<String, User> users;
@JsonKey(defaultValue: {})
late Map<String, User> users;
/// Create a new instance from a json
static UpdateUsersResponse fromJson(Map<String, dynamic> json) =>
@@ -169,7 +177,7 @@ class UpdateUsersResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class UpdateMessageResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
late Message message;
/// Create a new instance from a json
static UpdateMessageResponse fromJson(Map<String, dynamic> json) =>
@@ -180,7 +188,7 @@ class UpdateMessageResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class SendMessageResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
late Message message;
/// Create a new instance from a json
static SendMessageResponse fromJson(Map<String, dynamic> json) =>
@@ -191,15 +199,15 @@ class SendMessageResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class GetMessageResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
late Message message;
/// Channel of the message
ChannelModel channel;
ChannelModel? channel;
/// Create a new instance from a json
static GetMessageResponse fromJson(Map<String, dynamic> json) {
final res = _$GetMessageResponseFromJson(json);
final jsonChannel = res.message?.extraData?.remove('channel');
final jsonChannel = res.message.extraData.remove('channel');
if (jsonChannel != null) {
res.channel = ChannelModel.fromJson(jsonChannel);
}
@@ -211,7 +219,8 @@ class GetMessageResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class SearchMessagesResponse extends _BaseResponse {
/// List of messages returned by the api call
List<GetMessageResponse> results;
@JsonKey(defaultValue: [])
late List<GetMessageResponse> results;
/// Create a new instance from a json
static SearchMessagesResponse fromJson(Map<String, dynamic> json) =>
@@ -222,7 +231,8 @@ class SearchMessagesResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class GetMessagesByIdResponse extends _BaseResponse {
/// Message returned by the api call
List<Message> messages;
@JsonKey(defaultValue: [])
late List<Message> messages;
/// Create a new instance from a json
static GetMessagesByIdResponse fromJson(Map<String, dynamic> json) =>
@@ -233,13 +243,13 @@ class GetMessagesByIdResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class UpdateChannelResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
List<Member>? members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static UpdateChannelResponse fromJson(Map<String, dynamic> json) =>
@@ -250,10 +260,10 @@ class UpdateChannelResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class PartialUpdateChannelResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
List<Member>? members;
/// Create a new instance from a json
static PartialUpdateChannelResponse fromJson(Map<String, dynamic> json) =>
@@ -264,13 +274,14 @@ class PartialUpdateChannelResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class InviteMembersResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static InviteMembersResponse fromJson(Map<String, dynamic> json) =>
@@ -281,13 +292,14 @@ class InviteMembersResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class RemoveMembersResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static RemoveMembersResponse fromJson(Map<String, dynamic> json) =>
@@ -298,7 +310,7 @@ class RemoveMembersResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class SendActionResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static SendActionResponse fromJson(Map<String, dynamic> json) =>
@@ -309,13 +321,14 @@ class SendActionResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class AddMembersResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static AddMembersResponse fromJson(Map<String, dynamic> json) =>
@@ -326,13 +339,14 @@ class AddMembersResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class AcceptInviteResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static AcceptInviteResponse fromJson(Map<String, dynamic> json) =>
@@ -343,13 +357,14 @@ class AcceptInviteResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class RejectInviteResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// Channel members
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static RejectInviteResponse fromJson(Map<String, dynamic> json) =>
@@ -368,19 +383,23 @@ class EmptyResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class ChannelStateResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
late ChannelModel channel;
/// List of messages returned by the api call
List<Message> messages;
@JsonKey(defaultValue: [])
late List<Message> messages;
/// Channel members
List<Member> members;
@JsonKey(defaultValue: [])
late List<Member> members;
/// Number of users watching the channel
int watcherCount;
@JsonKey(defaultValue: 0)
late int watcherCount;
/// List of read states
List<Read> read;
@JsonKey(defaultValue: [])
late List<Read> read;
/// Create a new instance from a json
static ChannelStateResponse fromJson(Map<String, dynamic> json) =>
+170 -290
View File
@@ -6,394 +6,274 @@ part of 'responses.dart';
// JsonSerializableGenerator
// **************************************************************************
SyncResponse _$SyncResponseFromJson(Map json) {
SyncResponse _$SyncResponseFromJson(Map<String, dynamic> json) {
return SyncResponse()
..duration = json['duration'] as String
..events = (json['events'] as List)
?.map((e) => e == null
? null
: Event.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..events = (json['events'] as List<dynamic>?)
?.map((e) => Event.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
QueryChannelsResponse _$QueryChannelsResponseFromJson(Map json) {
QueryChannelsResponse _$QueryChannelsResponseFromJson(
Map<String, dynamic> json) {
return QueryChannelsResponse()
..duration = json['duration'] as String
..channels = (json['channels'] as List)
?.map((e) => e == null ? null : ChannelState.fromJson(e as Map))
?.toList();
..duration = json['duration'] as String?
..channels = (json['channels'] as List<dynamic>?)
?.map((e) => ChannelState.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
TranslateMessageResponse _$TranslateMessageResponseFromJson(Map json) {
TranslateMessageResponse _$TranslateMessageResponseFromJson(
Map<String, dynamic> json) {
return TranslateMessageResponse()
..duration = json['duration'] as String
..message = json['message'] == null
? null
: TranslatedMessage.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
..duration = json['duration'] as String?
..message =
TranslatedMessage.fromJson(json['message'] as Map<String, dynamic>);
}
QueryMembersResponse _$QueryMembersResponseFromJson(Map json) {
QueryMembersResponse _$QueryMembersResponseFromJson(Map<String, dynamic> json) {
return QueryMembersResponse()
..duration = json['duration'] as String
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
QueryUsersResponse _$QueryUsersResponseFromJson(Map json) {
QueryUsersResponse _$QueryUsersResponseFromJson(Map<String, dynamic> json) {
return QueryUsersResponse()
..duration = json['duration'] as String
..users = (json['users'] as List)
?.map((e) => e == null
? null
: User.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..users = (json['users'] as List<dynamic>?)
?.map((e) => User.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
QueryReactionsResponse _$QueryReactionsResponseFromJson(Map json) {
QueryReactionsResponse _$QueryReactionsResponseFromJson(
Map<String, dynamic> json) {
return QueryReactionsResponse()
..duration = json['duration'] as String
..reactions = (json['reactions'] as List)
?.map((e) => e == null
? null
: Reaction.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..reactions = (json['reactions'] as List<dynamic>?)
?.map((e) => Reaction.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
QueryRepliesResponse _$QueryRepliesResponseFromJson(Map json) {
QueryRepliesResponse _$QueryRepliesResponseFromJson(Map<String, dynamic> json) {
return QueryRepliesResponse()
..duration = json['duration'] as String
..messages = (json['messages'] as List)
?.map((e) => e == null
? null
: Message.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..messages = (json['messages'] as List<dynamic>?)
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
ListDevicesResponse _$ListDevicesResponseFromJson(Map json) {
ListDevicesResponse _$ListDevicesResponseFromJson(Map<String, dynamic> json) {
return ListDevicesResponse()
..duration = json['duration'] as String
..devices = (json['devices'] as List)
?.map((e) => e == null
? null
: Device.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..devices = (json['devices'] as List<dynamic>?)
?.map((e) => Device.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
SendFileResponse _$SendFileResponseFromJson(Map json) {
SendFileResponse _$SendFileResponseFromJson(Map<String, dynamic> json) {
return SendFileResponse()
..duration = json['duration'] as String
..duration = json['duration'] as String?
..file = json['file'] as String;
}
SendImageResponse _$SendImageResponseFromJson(Map json) {
SendImageResponse _$SendImageResponseFromJson(Map<String, dynamic> json) {
return SendImageResponse()
..duration = json['duration'] as String
..duration = json['duration'] as String?
..file = json['file'] as String;
}
SendReactionResponse _$SendReactionResponseFromJson(Map json) {
SendReactionResponse _$SendReactionResponseFromJson(Map<String, dynamic> json) {
return SendReactionResponse()
..duration = json['duration'] as String
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..reaction = json['reaction'] == null
? null
: Reaction.fromJson((json['reaction'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
..duration = json['duration'] as String?
..message = Message.fromJson(json['message'] as Map<String, dynamic>)
..reaction = Reaction.fromJson(json['reaction'] as Map<String, dynamic>);
}
ConnectGuestUserResponse _$ConnectGuestUserResponseFromJson(Map json) {
ConnectGuestUserResponse _$ConnectGuestUserResponseFromJson(
Map<String, dynamic> json) {
return ConnectGuestUserResponse()
..duration = json['duration'] as String
..duration = json['duration'] as String?
..accessToken = json['access_token'] as String
..user = json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
..user = User.fromJson(json['user'] as Map<String, dynamic>);
}
UpdateUsersResponse _$UpdateUsersResponseFromJson(Map json) {
UpdateUsersResponse _$UpdateUsersResponseFromJson(Map<String, dynamic> json) {
return UpdateUsersResponse()
..duration = json['duration'] as String
..users = (json['users'] as Map)?.map(
(k, e) => MapEntry(
k as String,
e == null
? null
: User.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
))),
);
..duration = json['duration'] as String?
..users = (json['users'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, User.fromJson(e as Map<String, dynamic>)),
) ??
{};
}
UpdateMessageResponse _$UpdateMessageResponseFromJson(Map json) {
UpdateMessageResponse _$UpdateMessageResponseFromJson(
Map<String, dynamic> json) {
return UpdateMessageResponse()
..duration = json['duration'] as String
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
..duration = json['duration'] as String?
..message = Message.fromJson(json['message'] as Map<String, dynamic>);
}
SendMessageResponse _$SendMessageResponseFromJson(Map json) {
SendMessageResponse _$SendMessageResponseFromJson(Map<String, dynamic> json) {
return SendMessageResponse()
..duration = json['duration'] as String
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
..duration = json['duration'] as String?
..message = Message.fromJson(json['message'] as Map<String, dynamic>);
}
GetMessageResponse _$GetMessageResponseFromJson(Map json) {
GetMessageResponse _$GetMessageResponseFromJson(Map<String, dynamic> json) {
return GetMessageResponse()
..duration = json['duration'] as String
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..duration = json['duration'] as String?
..message = Message.fromJson(json['message'] as Map<String, dynamic>)
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: ChannelModel.fromJson(json['channel'] as Map<String, dynamic>);
}
SearchMessagesResponse _$SearchMessagesResponseFromJson(Map json) {
SearchMessagesResponse _$SearchMessagesResponseFromJson(
Map<String, dynamic> json) {
return SearchMessagesResponse()
..duration = json['duration'] as String
..results = (json['results'] as List)
?.map((e) => e == null ? null : GetMessageResponse.fromJson(e as Map))
?.toList();
..duration = json['duration'] as String?
..results = (json['results'] as List<dynamic>?)
?.map((e) => GetMessageResponse.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson(Map json) {
GetMessagesByIdResponse _$GetMessagesByIdResponseFromJson(
Map<String, dynamic> json) {
return GetMessagesByIdResponse()
..duration = json['duration'] as String
..messages = (json['messages'] as List)
?.map((e) => e == null
? null
: Message.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..messages = (json['messages'] as List<dynamic>?)
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
UpdateChannelResponse _$UpdateChannelResponseFromJson(Map json) {
UpdateChannelResponse _$UpdateChannelResponseFromJson(
Map<String, dynamic> json) {
return UpdateChannelResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList()
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
PartialUpdateChannelResponse _$PartialUpdateChannelResponseFromJson(Map json) {
PartialUpdateChannelResponse _$PartialUpdateChannelResponseFromJson(
Map<String, dynamic> json) {
return PartialUpdateChannelResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList();
}
InviteMembersResponse _$InviteMembersResponseFromJson(Map json) {
InviteMembersResponse _$InviteMembersResponseFromJson(
Map<String, dynamic> json) {
return InviteMembersResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
RemoveMembersResponse _$RemoveMembersResponseFromJson(Map json) {
RemoveMembersResponse _$RemoveMembersResponseFromJson(
Map<String, dynamic> json) {
return RemoveMembersResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
SendActionResponse _$SendActionResponseFromJson(Map json) {
SendActionResponse _$SendActionResponseFromJson(Map<String, dynamic> json) {
return SendActionResponse()
..duration = json['duration'] as String
..duration = json['duration'] as String?
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
AddMembersResponse _$AddMembersResponseFromJson(Map json) {
AddMembersResponse _$AddMembersResponseFromJson(Map<String, dynamic> json) {
return AddMembersResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
AcceptInviteResponse _$AcceptInviteResponseFromJson(Map json) {
AcceptInviteResponse _$AcceptInviteResponseFromJson(Map<String, dynamic> json) {
return AcceptInviteResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
RejectInviteResponse _$RejectInviteResponseFromJson(Map json) {
RejectInviteResponse _$RejectInviteResponseFromJson(Map<String, dynamic> json) {
return RejectInviteResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..message = json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
));
: Message.fromJson(json['message'] as Map<String, dynamic>);
}
EmptyResponse _$EmptyResponseFromJson(Map json) {
return EmptyResponse()..duration = json['duration'] as String;
EmptyResponse _$EmptyResponseFromJson(Map<String, dynamic> json) {
return EmptyResponse()..duration = json['duration'] as String?;
}
ChannelStateResponse _$ChannelStateResponseFromJson(Map json) {
ChannelStateResponse _$ChannelStateResponseFromJson(Map<String, dynamic> json) {
return ChannelStateResponse()
..duration = json['duration'] as String
..channel = json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
))
..messages = (json['messages'] as List)
?.map((e) => e == null
? null
: Message.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..members = (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList()
..watcherCount = json['watcher_count'] as int
..read = (json['read'] as List)
?.map((e) => e == null
? null
: Read.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList();
..duration = json['duration'] as String?
..channel = ChannelModel.fromJson(json['channel'] as Map<String, dynamic>)
..messages = (json['messages'] as List<dynamic>?)
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..members = (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[]
..watcherCount = json['watcher_count'] as int? ?? 0
..read = (json['read'] as List<dynamic>?)
?.map((e) => Read.fromJson(e as Map<String, dynamic>))
.toList() ??
[];
}
@@ -1,4 +1,3 @@
import 'package:meta/meta.dart';
import 'package:stream_chat/src/client.dart';
import 'package:stream_chat/src/exceptions.dart';
@@ -6,30 +5,30 @@ import 'package:stream_chat/src/exceptions.dart';
class RetryPolicy {
/// Instantiate a new RetryPolicy
RetryPolicy({
@required this.shouldRetry,
@required this.retryTimeout,
this.attempt,
required this.shouldRetry,
required this.retryTimeout,
this.attempt = 0,
});
/// The number of attempts tried so far
int attempt = 0;
/// This function evaluates if we should retry the failure
final bool Function(StreamChatClient client, int attempt, ApiError apiError)
final bool Function(StreamChatClient client, int attempt, ApiError? apiError)
shouldRetry;
/// In the case that we want to retry a failed request the retryTimeout
/// method is called to determine the timeout
final Duration Function(
StreamChatClient client, int attempt, ApiError apiError) retryTimeout;
StreamChatClient client, int attempt, ApiError? apiError) retryTimeout;
/// Creates a copy of [RetryPolicy] with specified attributes overridden.
RetryPolicy copyWith({
bool Function(StreamChatClient client, int attempt, ApiError apiError)
bool Function(StreamChatClient client, int attempt, ApiError? apiError)?
shouldRetry,
Duration Function(StreamChatClient client, int attempt, ApiError apiError)
Duration Function(StreamChatClient client, int attempt, ApiError? apiError)?
retryTimeout,
int attempt,
int? attempt,
}) =>
RetryPolicy(
retryTimeout: retryTimeout ?? this.retryTimeout,
@@ -2,7 +2,6 @@ import 'dart:async';
import 'package:collection/collection.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:stream_chat/src/api/channel.dart';
import 'package:stream_chat/src/api/retry_policy.dart';
import 'package:stream_chat/src/event_type.dart';
@@ -14,7 +13,7 @@ import 'package:stream_chat/stream_chat.dart';
class RetryQueue {
/// Instantiate a new RetryQueue object
RetryQueue({
@required this.channel,
required this.channel,
this.logger,
}) {
_retryPolicy = channel.client.retryPolicy;
@@ -28,14 +27,14 @@ class RetryQueue {
final Channel channel;
/// The logger associated to this queue
final Logger logger;
final Logger? logger;
final _subscriptions = <StreamSubscription>[];
void _listenConnectionRecovered() {
_subscriptions
.add(channel.client.on(EventType.connectionRecovered).listen((event) {
if (!_isRetrying && event.online) {
if (!_isRetrying && event.online!) {
_startRetrying();
}
}));
@@ -43,12 +42,13 @@ class RetryQueue {
final HeapPriorityQueue<Message> _messageQueue = HeapPriorityQueue(_byDate);
bool _isRetrying = false;
RetryPolicy _retryPolicy;
RetryPolicy? _retryPolicy;
/// Add a list of messages
void add(List<Message> messages) {
logger?.info('added ${messages.length} messages');
final messageList = _messageQueue.toList();
_messageQueue.addAll(messages
.where((element) => !messageList.any((m) => m.id == element.id)));
@@ -60,7 +60,7 @@ class RetryQueue {
Future<void> _startRetrying() async {
logger?.info('start retrying');
_isRetrying = true;
final retryPolicy = _retryPolicy.copyWith(attempt: 0);
final retryPolicy = _retryPolicy!.copyWith(attempt: 0);
while (_messageQueue.isNotEmpty) {
final message = _messageQueue.first;
@@ -72,7 +72,7 @@ class RetryQueue {
logger?.info('now ${_messageQueue.length} messages in the queue');
retryPolicy.attempt = 0;
} catch (error) {
ApiError apiError;
ApiError? apiError;
if (error is DioError) {
if (error.type == DioErrorType.response) {
_messageQueue.remove(message);
@@ -84,7 +84,7 @@ class RetryQueue {
);
} else if (error is ApiError) {
apiError = error;
if (apiError.status?.toString()?.startsWith('4') == true) {
if (apiError.status?.toString().startsWith('4') == true) {
_messageQueue.remove(message);
return;
}
@@ -101,6 +101,7 @@ class RetryQueue {
}
retryPolicy.attempt++;
final timeout = retryPolicy.retryTimeout(
channel.client,
retryPolicy.attempt,
@@ -112,13 +113,13 @@ class RetryQueue {
_isRetrying = false;
}
void _sendFailedEvent(Message message) {
final newStatus = message.status == MessageSendingStatus.sending
void _sendFailedEvent(Message? message) {
final newStatus = message!.status == MessageSendingStatus.sending
? MessageSendingStatus.failed
: (message.status == MessageSendingStatus.updating
? MessageSendingStatus.failed_update
: MessageSendingStatus.failed_delete);
channel.state.addMessage(message.copyWith(
channel.state!.addMessage(message.copyWith(
status: newStatus,
));
}
@@ -141,20 +142,24 @@ class RetryQueue {
final messageList = _messageQueue.toList();
if (event.message != null) {
final messageIndex =
messageList.indexWhere((m) => m.id == event.message.id);
messageList.indexWhere((m) => m.id == event.message!.id);
if (messageIndex == -1 &&
[
MessageSendingStatus.failed_update,
MessageSendingStatus.failed,
MessageSendingStatus.failed_delete,
].contains(event.message.status)) {
].contains(event.message!.status)) {
logger?.info('add message from events');
add([event.message]);
final m = event.message;
if (m != null) {
add([m]);
}
} else if (messageIndex != -1 &&
[
MessageSendingStatus.sent,
null,
].contains(event.message.status)) {
].contains(event.message!.status)) {
_messageQueue.remove(messageList[messageIndex]);
}
}
@@ -171,10 +176,14 @@ class RetryQueue {
final date1 = _getMessageDate(m1);
final date2 = _getMessageDate(m2);
if (date1 == null || date2 == null) {
return 0;
}
return date1.compareTo(date2);
}
static DateTime _getMessageDate(Message m1) {
static DateTime? _getMessageDate(Message m1) {
switch (m1.status) {
case MessageSendingStatus.failed_delete:
case MessageSendingStatus.deleting:
@@ -3,5 +3,5 @@ import 'package:web_socket_channel/web_socket_channel.dart';
/// Html version of websocket implementation
/// Used in Flutter web version
WebSocketChannel connectWebSocket(String url, {Iterable<String> protocols}) =>
WebSocketChannel connectWebSocket(String url, {Iterable<String>? protocols}) =>
HtmlWebSocketChannel.connect(url, protocols: protocols);
@@ -3,5 +3,5 @@ import 'package:web_socket_channel/web_socket_channel.dart';
/// IO version of websocket implementation
/// Used in Flutter mobile version
WebSocketChannel connectWebSocket(String url, {Iterable<String> protocols}) =>
WebSocketChannel connectWebSocket(String url, {Iterable<String>? protocols}) =>
IOWebSocketChannel.connect(url, protocols: protocols);
@@ -3,7 +3,7 @@ import 'package:web_socket_channel/web_socket_channel.dart';
/// Stub version of websocket implementation
/// Used just for conditional library import
WebSocketChannel connectWebSocket(String url,
{Iterable<String> protocols,
Map<String, dynamic> headers,
Duration pingInterval}) =>
{Iterable<String>? protocols,
Map<String, dynamic>? headers,
Duration? pingInterval}) =>
throw UnimplementedError();
+36 -38
View File
@@ -16,8 +16,8 @@ typedef EventHandler = void Function(Event);
/// Typedef used for connecting to a websocket. Method returns a
/// [WebSocketChannel] and accepts a connection [url] and an optional
/// [Iterable] of `protocols`.
typedef ConnectWebSocket = WebSocketChannel Function(String url,
{Iterable<String> protocols});
typedef ConnectWebSocket = WebSocketChannel Function(String? url,
{Iterable<String>? protocols});
// TODO: parse error even
// TODO: if parsing an error into an event fails we should not hide the
@@ -27,11 +27,11 @@ class WebSocket {
/// Creates a new websocket
/// To connect the WS call [connect]
WebSocket({
@required this.baseUrl,
this.user,
this.connectParams,
this.connectPayload,
this.handler,
required this.baseUrl,
required this.user,
required this.handler,
this.connectParams = const {},
this.connectPayload = const {},
this.logger,
this.connectFunc,
this.reconnectionMonitorInterval = 1,
@@ -78,12 +78,12 @@ class WebSocket {
final EventHandler handler;
/// A WS specific logger instance
final Logger logger;
final Logger? logger;
/// Connection function
/// Used only for testing purpose
@visibleForTesting
final ConnectWebSocket connectFunc;
final ConnectWebSocket? connectFunc;
/// Interval of the reconnection monitor timer
/// This checks that it received a new event in the last
@@ -107,43 +107,43 @@ class WebSocket {
_connectionStatusController.add(status);
/// The current connection status value
ConnectionStatus get connectionStatus => _connectionStatusController.value;
ConnectionStatus? get connectionStatus => _connectionStatusController.value;
/// This notifies of connection status changes
Stream<ConnectionStatus> get connectionStatusStream =>
_connectionStatusController.stream;
String _path;
late final String _path;
int _retryAttempt = 1;
WebSocketChannel _channel;
Timer _healthCheck, _reconnectionMonitor;
DateTime _lastEventAt;
late WebSocketChannel _channel;
Timer? _healthCheck, _reconnectionMonitor;
DateTime? _lastEventAt;
bool _manuallyDisconnected = false;
bool _connecting = false;
bool _reconnecting = false;
Event _decodeEvent(String source) => Event.fromJson(json.decode(source));
Completer<Event> _connectionCompleter = Completer<Event>();
Completer<Event?> _connectionCompleter = Completer<Event?>();
/// Connect the WS using the parameters passed in the constructor
Future<Event> connect() {
Future<Event?> connect() async {
_manuallyDisconnected = false;
if (_connecting) {
logger.severe('already connecting');
logger?.severe('already connecting');
return null;
}
_connecting = true;
_connectionStatus = ConnectionStatus.connecting;
logger.info('connecting to $_path');
logger?.info('connecting to $_path');
_channel =
connectFunc?.call(_path) ?? WebSocketChannel.connect(Uri.parse(_path));
_channel.stream.listen(
(data) {
(data) async {
final jsonData = json.decode(data);
if (jsonData['error'] != null) {
return _onConnectionError(jsonData['error']);
@@ -153,9 +153,7 @@ class WebSocket {
onError: (error, stacktrace) {
_onConnectionError(error, stacktrace);
},
onDone: () {
_onDone();
},
onDone: _onDone,
);
return _connectionCompleter.future;
}
@@ -166,7 +164,7 @@ class WebSocket {
return;
}
logger.info('connection closed | closeCode: ${_channel.closeCode} | '
logger?.info('connection closed | closeCode: ${_channel.closeCode} | '
'closedReason: ${_channel.closeReason}');
if (!_reconnecting) {
@@ -180,10 +178,10 @@ class WebSocket {
}
final event = _decodeEvent(data);
logger.info('received new event: $data');
logger?.info('received new event: $data');
if (_lastEventAt == null) {
logger.info('connection estabilished');
logger?.info('connection estabilished');
_connecting = false;
_reconnecting = false;
_lastEventAt = DateTime.now();
@@ -204,9 +202,9 @@ class WebSocket {
}
Future<void> _onConnectionError(error, [stacktrace]) async {
logger..severe('error connecting')..severe(error);
logger?..severe('error connecting')..severe(error);
if (stacktrace != null) {
logger.severe(stacktrace);
logger?.severe(stacktrace);
}
_connecting = false;
@@ -225,7 +223,7 @@ class WebSocket {
void _reconnectionTimer(_) {
final now = DateTime.now();
if (_lastEventAt != null &&
now.difference(_lastEventAt).inSeconds > reconnectionMonitorTimeout) {
now.difference(_lastEventAt!).inSeconds > reconnectionMonitorTimeout) {
_channel.sink.close();
}
}
@@ -244,18 +242,18 @@ class WebSocket {
return;
}
if (_connecting) {
logger.info('already connecting');
logger?.info('already connecting');
return;
}
logger.info('reconnecting..');
logger?.info('reconnecting..');
_cancelTimers();
try {
await connect();
} catch (e) {
logger.log(Level.SEVERE, e.toString());
logger?.log(Level.SEVERE, e.toString());
}
await Future.delayed(
Duration(seconds: min(_retryAttempt * 5, 25)),
@@ -267,7 +265,7 @@ class WebSocket {
}
Future<void> _reconnect() async {
logger.info('reconnect');
logger?.info('reconnect');
if (!_reconnecting) {
_reconnecting = true;
_connectionStatus = ConnectionStatus.connecting;
@@ -279,20 +277,20 @@ class WebSocket {
void _cancelTimers() {
_lastEventAt = null;
if (_healthCheck != null) {
_healthCheck.cancel();
_healthCheck!.cancel();
}
if (_reconnectionMonitor != null) {
_reconnectionMonitor.cancel();
_reconnectionMonitor!.cancel();
}
}
void _healthCheckTimer(_) {
logger.info('sending health.check');
logger?.info('sending health.check');
_channel.sink.add("{'type': 'health.check'}");
}
void _startHealthCheck() {
logger.info('start health check monitor');
logger?.info('start health check monitor');
_healthCheck = Timer.periodic(
Duration(seconds: healthCheckInterval),
@@ -311,13 +309,13 @@ class WebSocket {
if (_manuallyDisconnected) {
return;
}
logger.info('disconnecting');
logger?.info('disconnecting');
_connectionCompleter = Completer();
_cancelTimers();
_reconnecting = false;
_manuallyDisconnected = true;
_connectionStatus = ConnectionStatus.disconnected;
await _connectionStatusController.close();
return _channel.sink.close();
await _channel.sink.close();
}
}
@@ -1,8 +1,8 @@
import 'package:dio/dio.dart';
import 'package:stream_chat/src/api/responses.dart';
import 'package:stream_chat/src/client.dart';
import 'package:stream_chat/src/models/attachment_file.dart';
import 'package:stream_chat/src/extensions/string_extension.dart';
import 'package:stream_chat/src/models/attachment_file.dart';
/// Class responsible for uploading images and files to a given channel
abstract class AttachmentFileUploader {
@@ -15,8 +15,8 @@ abstract class AttachmentFileUploader {
AttachmentFile image,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
});
/// Uploads a [file] to the given channel.
@@ -28,8 +28,8 @@ abstract class AttachmentFileUploader {
AttachmentFile file,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
});
/// Deletes a image using its [url] from the given channel.
@@ -40,7 +40,7 @@ abstract class AttachmentFileUploader {
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
CancelToken? cancelToken,
});
/// Deletes a file using its [url] from the given channel.
@@ -51,7 +51,7 @@ abstract class AttachmentFileUploader {
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
CancelToken? cancelToken,
});
}
@@ -67,22 +67,22 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
AttachmentFile file,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
}) async {
final filename = file.path?.split('/')?.last ?? file.name;
final mimeType = filename.mimeType;
final filename = file.path?.split('/').last ?? file.name;
final mimeType = filename?.mimeType;
MultipartFile multiPartFile;
MultipartFile? multiPartFile;
if (file.path != null) {
multiPartFile = await MultipartFile.fromFile(
file.path,
file.path!,
filename: filename,
contentType: mimeType,
);
} else if (file.bytes != null) {
multiPartFile = MultipartFile.fromBytes(
file.bytes,
file.bytes!,
filename: filename,
contentType: mimeType,
);
@@ -104,22 +104,22 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
AttachmentFile file,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
}) async {
final filename = file.path?.split('/')?.last ?? file.name;
final mimeType = filename.mimeType;
final filename = file.path?.split('/').last ?? file.name;
final mimeType = filename?.mimeType;
MultipartFile multiPartFile;
MultipartFile? multiPartFile;
if (file.path != null) {
multiPartFile = await MultipartFile.fromFile(
file.path,
file.path!,
filename: filename,
contentType: mimeType,
);
} else if (file.bytes != null) {
multiPartFile = MultipartFile.fromBytes(
file.bytes,
file.bytes!,
filename: filename,
contentType: mimeType,
);
@@ -141,7 +141,7 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
CancelToken? cancelToken,
}) async {
final response = await _client.delete(
'/channels/$channelType/$channelId/image',
@@ -156,7 +156,7 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
CancelToken? cancelToken,
}) async {
final response = await _client.delete(
'/channels/$channelType/$channelId/file',
+200 -188
View File
@@ -38,7 +38,7 @@ typedef DecoderFunction<T> = T Function(Map<String, dynamic>);
/// A function which can be used to request a Stream Chat API token from your
/// own backend server. Function requires a single [userId].
typedef TokenProvider = Future<String> Function(String userId);
typedef TokenProvider = Future<String> Function(String? userId);
/// Provider used to send push notifications.
enum PushProvider {
@@ -82,58 +82,59 @@ class StreamChatClient {
this.tokenProvider,
this.baseURL = _defaultBaseURL,
this.logLevel = Level.WARNING,
this.logHandlerFunction,
LogHandlerFunction? logHandlerFunction,
Duration connectTimeout = const Duration(seconds: 6),
Duration receiveTimeout = const Duration(seconds: 6),
Dio httpClient,
RetryPolicy retryPolicy,
Dio? httpClient,
RetryPolicy? retryPolicy,
this.attachmentFileUploader,
}) {
_retryPolicy = retryPolicy ??
RetryPolicy(
retryTimeout:
(StreamChatClient client, int attempt, ApiError error) =>
(StreamChatClient client, int attempt, ApiError? error) =>
Duration(seconds: 1 * attempt),
shouldRetry: (StreamChatClient client, int attempt, ApiError error) =>
attempt < 5,
shouldRetry:
(StreamChatClient client, int attempt, ApiError? error) =>
attempt < 5,
);
attachmentFileUploader ??= StreamAttachmentFileUploader(this);
state = ClientState(this);
_setupLogger();
_setupLogger(logHandlerFunction);
_setupDio(httpClient, receiveTimeout, connectTimeout);
logger.info('instantiating new client');
}
set chatPersistenceClient(ChatPersistenceClient value) {
set chatPersistenceClient(ChatPersistenceClient? value) {
_originalChatPersistenceClient = value;
}
ChatPersistenceClient _originalChatPersistenceClient;
ChatPersistenceClient? _originalChatPersistenceClient;
/// Chat persistence client
ChatPersistenceClient get chatPersistenceClient => _chatPersistenceClient;
ChatPersistenceClient? get chatPersistenceClient => _chatPersistenceClient;
ChatPersistenceClient _chatPersistenceClient;
ChatPersistenceClient? _chatPersistenceClient;
/// Attachment uploader
AttachmentFileUploader attachmentFileUploader;
AttachmentFileUploader? attachmentFileUploader;
/// Whether the chat persistence is available or not
bool get persistenceEnabled => _chatPersistenceClient != null;
RetryPolicy _retryPolicy;
RetryPolicy? _retryPolicy;
bool _synced = false;
/// The retry policy options getter
RetryPolicy get retryPolicy => _retryPolicy;
RetryPolicy? get retryPolicy => _retryPolicy;
/// This client state
ClientState state;
late ClientState state;
/// By default the Chat client will write all messages with level Warn or
/// Error to stdout.
@@ -169,7 +170,7 @@ class StreamChatClient {
/// final client = StreamChatClient("stream-chat-api-key",
/// logHandlerFunction: myLogHandlerFunction);
///```
LogHandlerFunction logHandlerFunction;
late LogHandlerFunction logHandlerFunction;
/// Your project Stream Chat api key.
/// Find your API keys here https://getstream.io/dashboard/
@@ -184,7 +185,7 @@ class StreamChatClient {
/// The token will be the return value of the function.
/// It's used by the client to refresh the token once expired or to connect
/// the user without a predefined token using [connectUserWithProvider].
final TokenProvider tokenProvider;
final TokenProvider? tokenProvider;
/// [Dio] httpClient
/// It's be chosen because it's easy to use and supports interesting features
@@ -195,8 +196,8 @@ class StreamChatClient {
static const _defaultBaseURL = 'chat-us-east-1.stream-io-api.com';
static const _tokenExpiredErrorCode = 40;
StreamSubscription<ConnectionStatus> _connectionStatusSubscription;
Future<void> Function(ConnectionStatus) _connectionStatusHandler;
StreamSubscription<ConnectionStatus>? _connectionStatusSubscription;
Future<void> Function(ConnectionStatus)? _connectionStatusHandler;
final BehaviorSubject<Event> _controller = BehaviorSubject<Event>();
@@ -211,7 +212,7 @@ class StreamChatClient {
_wsConnectionStatusController.add(status);
/// The current status value of the websocket connection
ConnectionStatus get wsConnectionStatus =>
ConnectionStatus? get wsConnectionStatus =>
_wsConnectionStatusController.value;
/// This notifies the connection status of the websocket connection.
@@ -220,19 +221,19 @@ class StreamChatClient {
_wsConnectionStatusController.stream;
/// The current user token
String token;
String? token;
/// The id of the current websocket connection
String get connectionId => _connectionId;
String? get connectionId => _connectionId;
bool _anonymous = false;
String _connectionId;
WebSocket _ws;
String? _connectionId;
late WebSocket _ws;
bool get _hasConnectionId => _connectionId != null;
void _setupDio(
Dio httpClient,
Dio? httpClient,
Duration receiveTimeout,
Duration connectTimeout,
) {
@@ -267,9 +268,8 @@ class StreamChatClient {
var stringData = options.data.toString();
if (options.data is FormData) {
final multiPart = (options.data as FormData).files[0]?.value;
stringData =
'${multiPart?.filename} - ${multiPart?.contentType}';
final multiPart = (options.data as FormData).files[0].value;
stringData = '${multiPart.filename} - ${multiPart.contentType}';
}
logger.info('''
@@ -301,11 +301,11 @@ class StreamChatClient {
if (tokenProvider != null) {
httpClient.lock();
final userId = state.user.id;
final userId = state.user!.id;
await _disconnect();
final newToken = await tokenProvider(userId);
final newToken = await tokenProvider!(userId);
await Future.delayed(const Duration(seconds: 4));
token = newToken;
@@ -341,13 +341,11 @@ class StreamChatClient {
),
),
);
} catch (err) {
} on DioError {
handler.reject(err);
}
}
}
return err;
}
LogHandlerFunction _getDefaultLogHandler() {
@@ -373,14 +371,14 @@ class StreamChatClient {
) =>
Logger.detached(name)
..level = logLevel
..onRecord.listen(logHandlerFunction ?? _getDefaultLogHandler());
..onRecord.listen(logHandlerFunction);
void _setupLogger() {
void _setupLogger(LogHandlerFunction? logHandlerFunction) {
logger.level = logLevel;
logHandlerFunction ??= _getDefaultLogHandler();
this.logHandlerFunction = logHandlerFunction ?? _getDefaultLogHandler();
logger.onRecord.listen(logHandlerFunction);
logger.onRecord.listen(this.logHandlerFunction);
logger.info('logger setup');
}
@@ -395,7 +393,7 @@ class StreamChatClient {
await _wsConnectionStatusController.close();
}
Map<String, String> get _httpHeaders => {
Map<String, String?> get _httpHeaders => {
'Authorization': token,
'stream-auth-type': _authType,
'X-Stream-Client': _userAgent,
@@ -405,12 +403,12 @@ class StreamChatClient {
/// Set the current user, this triggers a connection to the API.
/// It returns a [Future] that resolves when the connection is setup.
@Deprecated('Use `connectUser` instead. Will be removed in Future releases')
Future<Event> setUser(User user, String token) => connectUser(user, token);
Future<Event?> setUser(User user, String token) => connectUser(user, token);
/// Connects the current user, this triggers a connection to the API.
/// It returns a [Future] that resolves when the connection is setup.
Future<Event> connectUser(User user, String token) async {
if (_connectCompleter != null && !_connectCompleter.isCompleted) {
Future<Event?> connectUser(User? user, String? token) async {
if (_connectCompleter != null && !_connectCompleter!.isCompleted) {
logger.warning('Already connecting');
throw Exception('Already connecting');
}
@@ -418,15 +416,23 @@ class StreamChatClient {
_connectCompleter = Completer();
logger.info('connect user');
if (user == null) {
final e = Error();
_connectCompleter!
.completeError(e, StackTrace.fromString('No user provided.'));
throw e;
}
state.user = OwnUser.fromJson(user.toJson());
this.token = token;
_anonymous = false;
return connect().then((event) {
_connectCompleter.complete(event);
_connectCompleter!.complete(event);
return event;
}).catchError((e, s) {
_connectCompleter.completeError(e, s);
_connectCompleter!.completeError(e, s);
throw e;
});
}
@@ -436,28 +442,29 @@ class StreamChatClient {
@Deprecated(
'Use `connectUserWithProvider` instead. Will be removed in Future releases',
)
Future<Event> setUserWithProvider(User user) => connectUserWithProvider(user);
Future<Event?> setUserWithProvider(User user) =>
connectUserWithProvider(user);
/// Connects the current user using the [tokenProvider] to fetch the token.
/// It returns a [Future] that resolves when the connection is setup.
Future<Event> connectUserWithProvider(User user) async {
Future<Event?> connectUserWithProvider(User user) async {
if (tokenProvider == null) {
throw Exception('''
TokenProvider must be provided in the constructor in order to use `connectUserWithProvider` method.
Use `connectUser` providing a token.
''');
}
final token = await tokenProvider(user.id);
final token = await tokenProvider!(user.id);
return connectUser(user, token);
}
/// Stream of [Event] coming from websocket connection
/// Pass an eventType as parameter in order to filter just a type of event
Stream<Event> on([
String eventType,
String eventType2,
String eventType3,
String eventType4,
String? eventType,
String? eventType2,
String? eventType3,
String? eventType4,
]) =>
stream.where((event) =>
eventType == null ||
@@ -475,9 +482,10 @@ class StreamChatClient {
}
if (!event.isLocal) {
if (_synced && event.createdAt != null) {
final createdAt = event.createdAt;
if (_synced && createdAt != null) {
await _chatPersistenceClient?.updateConnectionInfo(event);
await _chatPersistenceClient?.updateLastSyncAt(event.createdAt);
await _chatPersistenceClient?.updateLastSyncAt(createdAt);
}
}
@@ -491,10 +499,10 @@ class StreamChatClient {
_controller.add(event);
}
Completer<Event> _connectCompleter;
Completer<Event>? _connectCompleter;
/// Connect the client websocket
Future<Event> connect() async {
Future<Event?> connect() async {
logger.info('connecting');
if (wsConnectionStatus == ConnectionStatus.connecting) {
logger.warning('Already connecting');
@@ -510,20 +518,20 @@ class StreamChatClient {
if (_originalChatPersistenceClient != null) {
_chatPersistenceClient = _originalChatPersistenceClient;
await _chatPersistenceClient.connect(state.user.id);
await _chatPersistenceClient!.connect(state.user!.id);
}
_ws = WebSocket(
baseUrl: baseURL,
user: state.user,
user: state.user!,
connectParams: {
'api_key': apiKey,
'authorization': token,
'authorization': token!,
'stream-auth-type': _authType,
'X-Stream-Client': _userAgent,
},
connectPayload: {
'user_id': state.user.id,
'user_id': state.user!.id,
'server_determines_connection_id': true,
},
handler: handleEvent,
@@ -540,7 +548,7 @@ class StreamChatClient {
);
if (status == ConnectionStatus.connected) {
handleEvent(Event(
handleEvent(const Event(
type: EventType.connectionRecovered,
online: true,
));
@@ -548,7 +556,7 @@ class StreamChatClient {
// ignore: unawaited_futures
queryChannelsOnline(filter: {
'cid': {
'\$in': state.channels.keys.toList(),
'\$in': state.channels!.keys.toList(),
},
}).then(
(_) async {
@@ -567,8 +575,10 @@ class StreamChatClient {
var event = await _chatPersistenceClient?.getConnectionInfo();
await _ws.connect().then((e) async {
await _chatPersistenceClient?.updateConnectionInfo(e);
event = e;
if (e != null) {
await _chatPersistenceClient?.updateConnectionInfo(e);
event = e;
}
await resync();
}).catchError((err, stacktrace) {
logger.severe('error connecting ws', err, stacktrace);
@@ -582,7 +592,7 @@ class StreamChatClient {
}
/// Get the events missed while offline to sync the offline storage
Future<void> resync([List<String> cids]) async {
Future<void> resync([List<String>? cids]) async {
final lastSyncAt = await _chatPersistenceClient?.getLastSyncAt();
if (lastSyncAt == null) {
@@ -608,7 +618,7 @@ class StreamChatClient {
SyncResponse.fromJson,
);
res.events.sort((a, b) => a.createdAt.compareTo(b.createdAt));
res.events.sort((a, b) => a.createdAt!.compareTo(b.createdAt!));
res.events.forEach((element) {
logger
@@ -625,26 +635,26 @@ class StreamChatClient {
}
}
String _asMap(sort) => sort?.map((s) => s.toJson().toString())?.join('');
String? _asMap(sort) => sort?.map((s) => s.toJson().toString())?.join('');
final _queryChannelsStreams = <String, Future<List<Channel>>>{};
/// Requests channels with a given query.
Stream<List<Channel>> queryChannels({
Map<String, dynamic> filter,
List<SortOption<ChannelModel>> sort,
Map<String, dynamic> options,
Map<String, dynamic>? filter,
List<SortOption<ChannelModel>>? sort,
Map<String, dynamic>? options,
PaginationParams paginationParams = const PaginationParams(),
int messageLimit,
int? messageLimit,
bool waitForConnect = true,
}) async* {
final hash = base64.encode(utf8.encode(
'$filter${_asMap(sort)}$options${paginationParams?.toJson()}'
'$filter${_asMap(sort)}$options${paginationParams.toJson()}'
'$messageLimit',
));
if (_queryChannelsStreams.containsKey(hash)) {
yield await _queryChannelsStreams[hash];
yield await _queryChannelsStreams[hash]!;
} else {
final channels = await queryChannelsOffline(
filter: filter,
@@ -674,17 +684,17 @@ class StreamChatClient {
/// Requests channels with a given query from the API.
Future<List<Channel>> queryChannelsOnline({
@required Map<String, dynamic> filter,
List<SortOption<ChannelModel>> sort,
Map<String, dynamic> options,
int messageLimit,
required Map<String, dynamic>? filter,
List<SortOption<ChannelModel>>? sort,
Map<String, dynamic>? options,
int? messageLimit,
PaginationParams paginationParams = const PaginationParams(),
bool waitForConnect = true,
}) async {
if (waitForConnect) {
if (_connectCompleter != null && !_connectCompleter.isCompleted) {
if (_connectCompleter != null && !_connectCompleter!.isCompleted) {
logger.info('awaiting connection completer');
await _connectCompleter.future;
await _connectCompleter!.future;
}
if (wsConnectionStatus != ConnectionStatus.connected) {
throw Exception(
@@ -716,9 +726,7 @@ class StreamChatClient {
payload.addAll(options);
}
if (paginationParams != null) {
payload.addAll(paginationParams.toJson());
}
payload.addAll(paginationParams.toJson());
final response = await get(
'/channels',
@@ -732,7 +740,7 @@ class StreamChatClient {
QueryChannelsResponse.fromJson,
);
if ((res.channels ?? []).isEmpty && (paginationParams?.offset ?? 0) == 0) {
if (res.channels.isEmpty && paginationParams.offset == 0) {
logger.warning(
'''
We could not find any channel for this query.
@@ -751,15 +759,14 @@ class StreamChatClient {
state._updateUsers(users);
logger.info('Got ${res.channels?.length} channels from api');
logger.info('Got ${res.channels.length} channels from api');
final updateData = _mapChannelStateToChannel(channels);
await _chatPersistenceClient?.updateChannelQueries(
filter,
channels.map((c) => c.channel.cid).toList(),
clearQueryCache:
paginationParams?.offset == null || paginationParams.offset == 0,
filter ?? {},
channels.map((c) => c.channel!.cid).toList(),
clearQueryCache: paginationParams.offset == 0,
);
state.channels = updateData.key;
@@ -768,36 +775,34 @@ class StreamChatClient {
/// Requests channels with a given query from the Persistence client.
Future<List<Channel>> queryChannelsOffline({
@required Map<String, dynamic> filter,
@required List<SortOption<ChannelModel>> sort,
required Map<String, dynamic>? filter,
required List<SortOption<ChannelModel>>? sort,
PaginationParams paginationParams = const PaginationParams(),
}) async {
final offlineChannels = await _chatPersistenceClient?.getChannelStates(
final offlineChannels = (await _chatPersistenceClient?.getChannelStates(
filter: filter,
sort: sort,
paginationParams: paginationParams,
);
))!;
final updatedData = _mapChannelStateToChannel(offlineChannels);
state.channels = updatedData.key;
return updatedData.value;
}
MapEntry<Map<String, Channel>, List<Channel>> _mapChannelStateToChannel(
MapEntry<Map<String?, Channel>, List<Channel>> _mapChannelStateToChannel(
List<ChannelState> channelStates,
) {
final channels = {...state.channels ?? {}};
final newChannels = <Channel>[];
if (channelStates != null) {
for (final channelState in channelStates) {
final channel = channels[channelState.channel.cid];
if (channel != null) {
channel.state?.updateChannelState(channelState);
newChannels.add(channel);
} else {
final newChannel = Channel.fromState(this, channelState);
channels[newChannel.cid] = newChannel;
newChannels.add(newChannel);
}
for (final channelState in channelStates) {
final channel = channels[channelState.channel!.cid];
if (channel != null) {
channel.state!.updateChannelState(channelState);
newChannels.add(channel);
} else {
final newChannel = Channel.fromState(this, channelState);
channels[newChannel.cid] = newChannel;
newChannels.add(newChannel);
}
}
return MapEntry(channels, newChannels);
@@ -817,7 +822,7 @@ class StreamChatClient {
/// Handy method to make http GET request with error parsing.
Future<Response<String>> get(
String path, {
Map<String, dynamic> queryParameters,
Map<String, dynamic>? queryParameters,
}) async {
try {
final response = await httpClient.get<String>(
@@ -835,8 +840,8 @@ class StreamChatClient {
Future<Response<String>> post(
String path, {
dynamic data,
ProgressCallback onSendProgress,
CancelToken cancelToken,
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
}) async {
try {
final response = await httpClient.post<String>(
@@ -855,8 +860,8 @@ class StreamChatClient {
/// Handy method to make http DELETE request with error parsing.
Future<Response<String>> delete(
String path, {
Map<String, dynamic> queryParameters,
CancelToken cancelToken,
Map<String, dynamic>? queryParameters,
CancelToken? cancelToken,
}) async {
try {
final response = await httpClient.delete<String>(
@@ -874,7 +879,7 @@ class StreamChatClient {
/// Handy method to make http PATCH request with error parsing.
Future<Response<String>> patch(
String path, {
Map<String, dynamic> queryParameters,
Map<String, dynamic>? queryParameters,
dynamic data,
}) async {
try {
@@ -893,7 +898,7 @@ class StreamChatClient {
/// Handy method to make http PUT request with error parsing.
Future<Response<String>> put(
String path, {
Map<String, dynamic> queryParameters,
Map<String, dynamic>? queryParameters,
dynamic data,
}) async {
try {
@@ -910,12 +915,10 @@ class StreamChatClient {
}
/// Used to log errors and stacktrace in case of bad json deserialization
T decode<T>(String j, DecoderFunction<T> decoderFunction) {
T decode<T>(String? j, DecoderFunction<T> decoderFunction) {
try {
if (j == null) {
return null;
}
return decoderFunction(json.decode(j));
final data = j ?? '{}';
return decoderFunction(json.decode(data));
} catch (error, stacktrace) {
logger.severe('Error decoding response', error, stacktrace);
rethrow;
@@ -927,7 +930,7 @@ class StreamChatClient {
String get _userAgent => 'stream-chat-dart-client-${CurrentPlatform.name}-'
'${PACKAGE_VERSION.split('+')[0]}';
Map<String, String> get _commonQueryParams => {
Map<String, String?> get _commonQueryParams => {
'user_id': state.user?.id,
'api_key': apiKey,
'connection_id': _connectionId,
@@ -937,13 +940,13 @@ class StreamChatClient {
/// the API. It returns a [Future] that resolves when the connection is setup.
@Deprecated(
'Use `connectAnonymousUser` instead. Will be removed in Future releases')
Future<Event> setAnonymousUser() => connectAnonymousUser();
Future<Event?> setAnonymousUser() => connectAnonymousUser();
/// Connects the current user with an anonymous id, this triggers a connection
/// to the API. It returns a [Future] that resolves when the connection is
/// setup.
Future<Event> connectAnonymousUser() async {
if (_connectCompleter != null && !_connectCompleter.isCompleted) {
Future<Event?> connectAnonymousUser() async {
if (_connectCompleter != null && !_connectCompleter!.isCompleted) {
logger.warning('Already connecting');
throw Exception('Already connecting');
}
@@ -955,10 +958,10 @@ class StreamChatClient {
state.user = OwnUser(id: uuid.v4());
return connect().then((event) {
_connectCompleter.complete(event);
_connectCompleter!.complete(event);
return event;
}).catchError((e, s) {
_connectCompleter.completeError(e, s);
_connectCompleter!.completeError(e, s);
throw e;
});
}
@@ -967,16 +970,17 @@ class StreamChatClient {
/// It returns a [Future] that resolves when the connection is setup.
@Deprecated(
'Use `connectGuestUser` instead. Will be removed in Future releases')
Future<Event> setGuestUser(User user) => connectGuestUser(user);
Future<Event?> setGuestUser(User user) => connectGuestUser(user);
/// Connects the current user as guest, this triggers a connection to the API.
/// It returns a [Future] that resolves when the connection is setup.
Future<Event> connectGuestUser(User user) async {
Future<Event?> connectGuestUser(User user) async {
_anonymous = true;
final response = await post('/guest', data: {'user': user.toJson()})
.then((res) => decode<ConnectGuestUserResponse>(
res.data, ConnectGuestUserResponse.fromJson))
.whenComplete(() => _anonymous = false);
return connectUser(
response.user,
response.accessToken,
@@ -1009,16 +1013,16 @@ class StreamChatClient {
Future<void> _disconnect() async {
logger.info('Client disconnecting');
await _ws?.disconnect();
await _ws.disconnect();
await _connectionStatusSubscription?.cancel();
}
/// Requests users with a given query.
Future<QueryUsersResponse> queryUsers({
Map<String, dynamic> filter,
List<SortOption> sort,
Map<String, dynamic> options,
PaginationParams pagination,
Map<String, dynamic>? filter,
List<SortOption>? sort,
Map<String, dynamic>? options,
PaginationParams? pagination,
}) async {
final defaultOptions = {
'presence': _hasConnectionId,
@@ -1049,7 +1053,7 @@ class StreamChatClient {
QueryUsersResponse.fromJson,
);
state?._updateUsers(response.users);
state._updateUsers(response.users);
return response;
}
@@ -1057,13 +1061,13 @@ class StreamChatClient {
/// A message search.
Future<SearchMessagesResponse> search(
Map<String, dynamic> filters, {
String query,
List<SortOption> sort,
PaginationParams paginationParams,
Map<String, dynamic> messageFilters,
String? query,
List<SortOption>? sort,
PaginationParams? paginationParams,
Map<String, dynamic>? messageFilters,
}) async {
assert(() {
if (filters == null || filters.isEmpty) {
if (filters.isEmpty) {
throw ArgumentError('`filters` cannot be set as null or empty');
}
if (query == null && messageFilters == null) {
@@ -1098,10 +1102,10 @@ class StreamChatClient {
AttachmentFile file,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
}) =>
attachmentFileUploader.sendFile(
attachmentFileUploader!.sendFile(
file,
channelId,
channelType,
@@ -1114,10 +1118,10 @@ class StreamChatClient {
AttachmentFile image,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
}) =>
attachmentFileUploader.sendImage(
attachmentFileUploader!.sendImage(
image,
channelId,
channelType,
@@ -1130,9 +1134,9 @@ class StreamChatClient {
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
CancelToken? cancelToken,
}) =>
attachmentFileUploader.deleteFile(
attachmentFileUploader!.deleteFile(
url,
channelId,
channelType,
@@ -1144,9 +1148,9 @@ class StreamChatClient {
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
CancelToken? cancelToken,
}) =>
attachmentFileUploader.deleteImage(
attachmentFileUploader!.deleteImage(
url,
channelId,
channelType,
@@ -1188,13 +1192,13 @@ class StreamChatClient {
/// Returns a channel client with the given type, id and custom data.
Channel channel(
String type, {
String id,
Map<String, dynamic> extraData,
String? id,
Map<String, dynamic>? extraData,
}) {
if (type != null &&
id != null &&
state.channels?.containsKey('$type:$id') == true) {
return state.channels['$type:$id'];
if (id != null && state.channels?.containsKey('$type:$id') == true) {
if (state.channels!['$type:$id'] != null) {
return state.channels!['$type:$id'] as Channel;
}
}
return Channel(this, type, id, extraData);
@@ -1323,7 +1327,10 @@ class StreamChatClient {
/// Sends the message to the given channel
Future<SendMessageResponse> sendMessage(
Message message, String channelId, String channelType) async {
Message message,
String channelId,
String channelType,
) async {
final response = await post(
'/channels/$channelType/$channelId/message',
data: {'message': message.toJson()},
@@ -1359,14 +1366,13 @@ class StreamChatClient {
) {
assert(() {
if (timeoutOrExpirationDate is! DateTime &&
timeoutOrExpirationDate is! num &&
timeoutOrExpirationDate != null) {
timeoutOrExpirationDate is! num) {
throw ArgumentError('Invalid timeout or Expiration date');
}
return true;
}(), 'Check whether time out is valid');
DateTime pinExpires;
DateTime? pinExpires;
if (timeoutOrExpirationDate is DateTime) {
pinExpires = timeoutOrExpirationDate.toUtc();
} else if (timeoutOrExpirationDate is num) {
@@ -1395,12 +1401,12 @@ class ClientState {
.map((e) => e.me)
.listen((user) {
_userController.add(user);
if (user.totalUnreadCount != null) {
_totalUnreadCountController.add(user.totalUnreadCount);
if (user?.totalUnreadCount != null) {
_totalUnreadCountController.add(user?.totalUnreadCount);
}
if (user.unreadChannels != null) {
_unreadChannelsController.add(user.unreadChannels);
if (user?.unreadChannels != null) {
_unreadChannelsController.add(user?.unreadChannels);
}
}),
_client
@@ -1425,23 +1431,27 @@ class ClientState {
final _subscriptions = <StreamSubscription>[];
/// Used internally for optimistic update of unread count
set totalUnreadCount(int unreadCount) {
_totalUnreadCountController?.add(unreadCount ?? 0);
set totalUnreadCount(int? unreadCount) {
_totalUnreadCountController.add(unreadCount ?? 0);
}
void _listenChannelHidden() {
_subscriptions.add(_client.on(EventType.channelHidden).listen((event) {
_client.chatPersistenceClient?.deleteChannels([event.cid]);
final cid = event.cid;
if (cid != null) {
_client.chatPersistenceClient?.deleteChannels([cid]);
}
if (channels != null) {
channels = channels..removeWhere((cid, ch) => cid == event.cid);
channels = channels?..removeWhere((cid, ch) => cid == event.cid);
}
}));
}
void _listenUserUpdated() {
_subscriptions.add(_client.on(EventType.userUpdated).listen((event) {
if (event.user.id == user.id) {
user = OwnUser.fromJson(event.user.toJson());
if (event.user!.id == user!.id) {
user = OwnUser.fromJson(event.user!.toJson());
}
_updateUser(event.user);
}));
@@ -1455,10 +1465,10 @@ class ClientState {
EventType.notificationChannelDeleted,
)
.listen((Event event) async {
final eventChannel = event.channel;
final eventChannel = event.channel!;
await _client.chatPersistenceClient?.deleteChannels([eventChannel.cid]);
if (channels != null) {
channels = channels..remove(eventChannel.cid);
channels = channels?..remove(eventChannel.cid);
}
}));
}
@@ -1466,61 +1476,63 @@ class ClientState {
final StreamChatClient _client;
/// Update user information
set user(OwnUser user) {
set user(OwnUser? user) {
_userController.add(user);
}
void _updateUsers(List<User> userList) {
void _updateUsers(List<User?> userList) {
final newUsers = {
...users ?? {},
for (var user in userList) user.id: user,
...users,
for (var user in userList) user!.id: user,
};
_usersController.add(newUsers);
}
void _updateUser(User user) => _updateUsers([user]);
void _updateUser(User? user) => _updateUsers([user]);
/// The current user
OwnUser get user => _userController.value;
OwnUser? get user => _userController.value;
/// The current user as a stream
Stream<OwnUser> get userStream => _userController.stream;
Stream<OwnUser?> get userStream => _userController.stream;
/// The current user
Map<String, User> get users => _usersController.value;
Map<String?, User?> get users =>
_usersController.value as Map<String?, User?>;
/// The current user as a stream
Stream<Map<String, User>> get usersStream => _usersController.stream;
Stream<Map<String?, User?>> get usersStream => _usersController.stream;
/// The current unread channels count
int get unreadChannels => _unreadChannelsController.value;
int? get unreadChannels => _unreadChannelsController.value;
/// The current unread channels count as a stream
Stream<int> get unreadChannelsStream => _unreadChannelsController.stream;
Stream<int?> get unreadChannelsStream => _unreadChannelsController.stream;
/// The current total unread messages count
int get totalUnreadCount => _totalUnreadCountController.value;
int? get totalUnreadCount => _totalUnreadCountController.value;
/// The current total unread messages count as a stream
Stream<int> get totalUnreadCountStream => _totalUnreadCountController.stream;
Stream<int?> get totalUnreadCountStream => _totalUnreadCountController.stream;
/// The current list of channels in memory as a stream
Stream<Map<String, Channel>> get channelsStream => _channelsController.stream;
Stream<Map<String?, Channel>?> get channelsStream =>
_channelsController.stream;
/// The current list of channels in memory
Map<String, Channel> get channels => _channelsController.value;
Map<String?, Channel>? get channels => _channelsController.value;
set channels(Map<String, Channel> v) {
set channels(Map<String?, Channel>? v) {
_channelsController.add(v);
}
final BehaviorSubject<Map<String, Channel>> _channelsController =
final BehaviorSubject<Map<String?, Channel>?> _channelsController =
BehaviorSubject.seeded({});
final BehaviorSubject<OwnUser> _userController = BehaviorSubject();
final BehaviorSubject<Map<String, User>> _usersController =
final BehaviorSubject<OwnUser?> _userController = BehaviorSubject();
final BehaviorSubject<Map<String?, User?>> _usersController =
BehaviorSubject.seeded({});
final BehaviorSubject<int> _unreadChannelsController = BehaviorSubject();
final BehaviorSubject<int> _totalUnreadCountController = BehaviorSubject();
final BehaviorSubject<int?> _unreadChannelsController = BehaviorSubject();
final BehaviorSubject<int?> _totalUnreadCountController = BehaviorSubject();
/// Call this method to dispose this object
void dispose() {
@@ -1528,7 +1540,7 @@ class ClientState {
_userController.close();
_unreadChannelsController.close();
_totalUnreadCountController.close();
channels.values.forEach((c) => c.dispose());
channels!.values.forEach((c) => c.dispose());
_channelsController.close();
}
}
@@ -20,7 +20,7 @@ abstract class ChatPersistenceClient {
/// Get stored replies by messageId
Future<List<Message>> getReplies(
String parentId, {
PaginationParams options,
PaginationParams? options,
});
/// Get stored connection event
@@ -53,20 +53,20 @@ abstract class ChatPersistenceClient {
/// for filtering out messages
Future<List<Message>> getMessagesByCid(
String cid, {
PaginationParams messagePagination,
PaginationParams? messagePagination,
});
/// Get stored pinned [Message]s by providing channel [cid]
Future<List<Message>> getPinnedMessagesByCid(
String cid, {
PaginationParams messagePagination,
PaginationParams? messagePagination,
});
/// Get [ChannelState] data by providing channel [cid]
Future<ChannelState> getChannelStateByCid(
String cid, {
PaginationParams messagePagination,
PaginationParams pinnedMessagePagination,
PaginationParams? messagePagination,
PaginationParams? pinnedMessagePagination,
}) async {
final data = await Future.wait([
getMembersByCid(cid),
@@ -76,11 +76,11 @@ abstract class ChatPersistenceClient {
getPinnedMessagesByCid(cid, messagePagination: pinnedMessagePagination),
]);
return ChannelState(
members: data[0],
read: data[1],
channel: data[2],
messages: data[3],
pinnedMessages: data[4],
members: data[0] as List<Member>,
read: data[1] as List<Read>,
channel: data[2] as ChannelModel,
messages: data[3] as List<Message>,
pinnedMessages: data[4] as List<Message>,
);
}
@@ -89,9 +89,9 @@ abstract class ChatPersistenceClient {
/// Optionally, pass [filter], [sort], [paginationParams]
/// for filtering out states.
Future<List<ChannelState>> getChannelStates({
Map<String, dynamic> filter,
List<SortOption<ChannelModel>> sort = const [],
PaginationParams paginationParams,
Map<String, dynamic>? filter,
List<SortOption<ChannelModel>>? sort = const [],
PaginationParams? paginationParams,
});
/// Update list of channel queries.
@@ -180,8 +180,11 @@ abstract class ChatPersistenceClient {
.map((m) => m.id)
.toList(growable: false));
final cleanedChannelStates =
channelStates.where((it) => it.channel != null);
final deleteMembers = deleteMembersByCids(
channelStates.map((it) => it.channel.cid).toList(growable: false),
cleanedChannelStates.map((it) => it.channel!.cid).toList(growable: false),
);
await Future.wait([
@@ -189,58 +192,57 @@ abstract class ChatPersistenceClient {
deleteMembers,
]);
final channels =
channelStates.map((it) => it.channel).where((it) => it != null);
final channels = cleanedChannelStates
.map((it) => it.channel)
.where((it) => it != null) as Iterable<ChannelModel>;
final reactions = channelStates
.expand((it) => it.messages)
.expand((it) => [
final reactions =
cleanedChannelStates.expand((it) => it.messages).expand((it) => [
if (it.ownReactions != null)
...it.ownReactions.where((r) => r.userId != null),
...it.ownReactions!.where((r) => r.userId != null),
if (it.latestReactions != null)
...it.latestReactions.where((r) => r.userId != null)
])
.where((it) => it != null);
...it.latestReactions!.where((r) => r.userId != null),
]);
final users = channelStates
final users = cleanedChannelStates
.map((cs) => [
cs.channel?.createdBy,
...cs.messages
?.map((m) => [
.map((m) => [
m.user,
if (m.latestReactions != null)
...m.latestReactions.map((r) => r.user),
...m.latestReactions!.map((r) => r.user),
if (m.ownReactions != null)
...m.ownReactions.map((r) => r.user),
...m.ownReactions!.map((r) => r.user),
])
?.expand((v) => v),
if (cs.read != null) ...cs.read.map((r) => r.user),
if (cs.members != null) ...cs.members.map((m) => m.user),
.expand((v) => v),
...cs.read.map((r) => r.user),
...cs.members.map((m) => m.user),
])
.expand((it) => it)
.where((it) => it != null);
.where((it) => it != null) as Iterable<User>;
final updateMessagesFuture = channelStates.map((it) {
final cid = it.channel.cid;
final messages = it.messages.where((it) => it != null);
final updateMessagesFuture = cleanedChannelStates.map((it) {
final cid = it.channel!.cid;
final messages = it.messages;
return updateMessages(cid, messages.toList(growable: false));
}).toList(growable: false);
final updatePinnedMessagesFuture = channelStates.map((it) {
final cid = it.channel.cid;
final messages = it.pinnedMessages.where((it) => it != null);
final updatePinnedMessagesFuture = cleanedChannelStates.map((it) {
final cid = it.channel!.cid;
final messages = it.pinnedMessages;
return updatePinnedMessages(cid, messages.toList(growable: false));
}).toList(growable: false);
final updateReadsFuture = channelStates.map((it) {
final cid = it.channel.cid;
final reads = it.read?.where((it) => it != null) ?? [];
final updateReadsFuture = cleanedChannelStates.map((it) {
final cid = it.channel!.cid;
final reads = it.read;
return updateReads(cid, reads.toList(growable: false));
}).toList(growable: false);
final updateMembersFuture = channelStates.map((it) {
final cid = it.channel.cid;
final members = it.members.where((it) => it != null);
final updateMembersFuture = cleanedChannelStates.map((it) {
final cid = it.channel!.cid;
final members = it.members;
return updateMembers(cid, members.toList(growable: false));
}).toList(growable: false);
+8 -8
View File
@@ -4,25 +4,25 @@ import 'dart:convert';
class ApiError extends Error {
/// Creates a new ApiError instance using the response body and status code
ApiError(this.body, this.status) : jsonData = _decode(body) {
if (jsonData != null && jsonData.containsKey('code')) {
_code = jsonData['code'];
if (jsonData != null && jsonData!.containsKey('code')) {
_code = jsonData!['code'];
}
}
/// Raw body of the response
final String body;
final String? body;
/// Json parsed body
final Map<String, dynamic> jsonData;
final Map<String, dynamic>? jsonData;
/// Http status code of the response
final int status;
final int? status;
/// Stream specific error code
int get code => _code;
int _code;
int? get code => _code;
int? _code;
static Map<String, dynamic> _decode(String body) {
static Map<String, dynamic>? _decode(String? body) {
try {
if (body == null) {
return null;
@@ -1,6 +1,6 @@
/// Useful extension functions for [Map]
extension MapX on Map {
extension MapX<K, V> on Map<K, V> {
/// Returns a new map with null keys or values removed
Map<String, dynamic> get nullProtected =>
{...this}..removeWhere((key, value) => key == null || value == null);
Map<K, V> get nullProtected =>
Map.from(this)..removeWhere((key, value) => key == null || value == null);
}
@@ -10,7 +10,7 @@ extension RateLimit on Function {
Duration wait, {
bool leading = false,
bool trailing = true,
Duration maxWait,
Duration? maxWait,
}) =>
Debounce(
this,
@@ -40,7 +40,7 @@ Debounce debounce(
Duration wait, {
bool leading = false,
bool trailing = true,
Duration maxWait,
Duration? maxWait,
}) =>
Debounce(
func,
@@ -121,13 +121,13 @@ class Debounce {
Duration wait, {
bool leading = false,
bool trailing = true,
Duration maxWait,
Duration? maxWait,
}) : _leading = leading,
_trailing = trailing,
_wait = wait?.inMilliseconds ?? 0,
_wait = wait.inMilliseconds,
_maxing = maxWait != null {
if (_maxing) {
_maxWait = math.max(maxWait.inMilliseconds, _wait);
_maxWait = math.max(maxWait!.inMilliseconds, _wait);
}
}
@@ -137,15 +137,15 @@ class Debounce {
final int _wait;
final bool _maxing;
int _maxWait;
List<Object> _lastArgs;
Map<Symbol, Object> _lastNamedArgs;
Timer _timer;
int _lastCallTime;
Object _result;
int _lastInvokeTime = 0;
late int _maxWait;
List<Object?>? _lastArgs;
Map<Symbol, Object>? _lastNamedArgs;
Timer? _timer;
int? _lastCallTime;
Object? _result;
int? _lastInvokeTime = 0;
Object _invokeFunc(int time) {
Object? _invokeFunc(int? time) {
final args = _lastArgs;
final namedArgs = _lastNamedArgs;
_lastArgs = _lastNamedArgs = null;
@@ -154,11 +154,11 @@ class Debounce {
}
Timer _startTimer(Function pendingFunc, int wait) =>
Timer(Duration(milliseconds: wait), pendingFunc);
Timer(Duration(milliseconds: wait), pendingFunc as void Function());
bool _shouldInvoke(int time) {
final timeSinceLastCall = time - (_lastCallTime ?? double.nan);
final timeSinceLastInvoke = time - _lastInvokeTime;
final timeSinceLastInvoke = time - _lastInvokeTime!;
// Either this is the first call, activity has stopped and we're at the
// trailing edge, the system time has gone backwards and we're treating
@@ -169,7 +169,7 @@ class Debounce {
(_maxing && timeSinceLastInvoke >= _maxWait);
}
Object _trailingEdge(int time) {
Object? _trailingEdge(int time) {
_timer = null;
// Only invoke if we have `lastArgs` which means `func` has been
@@ -182,8 +182,8 @@ class Debounce {
}
int _remainingWait(int time) {
final timeSinceLastCall = time - _lastCallTime;
final timeSinceLastInvoke = time - _lastInvokeTime;
final timeSinceLastCall = time - _lastCallTime!;
final timeSinceLastInvoke = time - _lastInvokeTime!;
final timeWaiting = _wait - timeSinceLastCall;
return _maxing
@@ -201,7 +201,7 @@ class Debounce {
}
}
Object _leadingEdge(int time) {
Object? _leadingEdge(int? time) {
// Reset any `maxWait` timer.
_lastInvokeTime = time;
// Start the timer for the trailing edge.
@@ -218,7 +218,7 @@ class Debounce {
}
/// Immediately invokes all the remaining delayed functions.
Object flush() {
Object? flush() {
final now = DateTime.now().millisecondsSinceEpoch;
return _timer == null ? _result : _trailingEdge(now);
}
@@ -228,15 +228,15 @@ class Debounce {
/// Calls/invokes this class like a function.
/// Pass [args] and [namedArgs] to be used while invoking [_func].
Object call(
Object? call(
List<dynamic> args, {
Map<Symbol, dynamic> namedArgs,
Map<Symbol, dynamic>? namedArgs,
}) {
final time = DateTime.now().millisecondsSinceEpoch;
final isInvoking = _shouldInvoke(time);
_lastArgs = args;
_lastNamedArgs = namedArgs;
_lastNamedArgs = namedArgs as Map<Symbol, Object>?;
_lastCallTime = time;
if (isInvoking) {
@@ -323,13 +323,13 @@ class Throttle {
void cancel() => _debounce.cancel();
/// Immediately invokes all the remaining delayed functions.
Object flush() => _debounce.flush();
Object? flush() => _debounce.flush();
/// True if there are functions remaining to get invoked.
bool get isPending => _debounce.isPending;
/// Calls/invokes this class like a function.
/// Pass [args] and [namedArgs] to be used while invoking `func`.
Object call(List<dynamic> args, {Map<Symbol, dynamic> namedArgs}) =>
Object? call(List<dynamic> args, {Map<Symbol, dynamic>? namedArgs}) =>
_debounce.call(args, namedArgs: namedArgs);
}
@@ -4,12 +4,15 @@ import 'package:mime/mime.dart';
/// Useful extension functions for [String]
extension StringX on String {
/// Returns the mime type from the passed file name.
http_parser.MediaType get mimeType {
if (this == null) return null;
http_parser.MediaType? get mimeType {
if (toLowerCase().endsWith('heic')) {
return http_parser.MediaType.parse('image/heic');
} else {
return http_parser.MediaType.parse(lookupMimeType(this));
final mimeType = lookupMimeType(this);
if (mimeType == null) {
return null;
}
return http_parser.MediaType.parse(mimeType);
}
}
}
@@ -6,7 +6,13 @@ part 'action.g.dart';
@JsonSerializable()
class Action {
/// Constructor used for json serialization
Action({this.name, this.style, this.text, this.type, this.value});
Action({
required this.name,
this.style = 'default',
required this.text,
required this.type,
this.value,
});
/// Create a new instance from a json
factory Action.fromJson(Map<String, dynamic> json) => _$ActionFromJson(json);
@@ -15,6 +21,7 @@ class Action {
final String name;
/// The style of the action
@JsonKey(defaultValue: 'default')
final String style;
/// The test of the action
@@ -24,7 +31,7 @@ class Action {
final String type;
/// The value of the action
final String value;
final String? value;
/// Serialize to json
Map<String, dynamic> toJson() => _$ActionToJson(this);
@@ -6,13 +6,13 @@ part of 'action.dart';
// JsonSerializableGenerator
// **************************************************************************
Action _$ActionFromJson(Map json) {
Action _$ActionFromJson(Map<String, dynamic> json) {
return Action(
name: json['name'] as String,
style: json['style'] as String,
style: json['style'] as String? ?? 'default',
text: json['text'] as String,
type: json['type'] as String,
value: json['value'] as String,
value: json['value'] as String?,
);
}
@@ -14,10 +14,10 @@ part 'attachment.g.dart';
class Attachment extends Equatable {
/// Constructor used for json serialization
Attachment({
String id,
String? id,
this.type,
this.titleLink,
String title,
String? title,
this.thumbUrl,
this.text,
this.pretext,
@@ -32,17 +32,19 @@ class Attachment extends Equatable {
this.authorLink,
this.authorIcon,
this.assetUrl,
this.actions,
List<Action>? actions,
this.extraData,
this.file,
UploadState uploadState,
UploadState? uploadState,
}) : id = id ?? const Uuid().v4(),
title = title ?? file?.name,
localUri = file?.path != null ? Uri.parse(file.path) : null,
uploadState = uploadState ??
((assetUrl != null || imageUrl != null)
? const UploadState.success()
: const UploadState.preparing());
localUri = file?.path != null ? Uri.parse(file!.path!) : null,
actions = actions ?? [] {
this.uploadState = uploadState ??
((assetUrl != null || imageUrl != null)
? const UploadState.success()
: const UploadState.preparing());
}
/// Create a new instance from a json
factory Attachment.fromJson(Map<String, dynamic> json) =>
@@ -56,59 +58,60 @@ class Attachment extends Equatable {
///The attachment type based on the URL resource. This can be: audio,
///image or video
final String type;
final String? type;
///The link to which the attachment message points to.
final String titleLink;
final String? titleLink;
/// The attachment title
final String title;
final String? title;
/// The URL to the attached file thumbnail. You can use this to represent the
/// attached link.
final String thumbUrl;
final String? thumbUrl;
/// The attachment text. It will be displayed in the channel next to the
/// original message.
final String text;
final String? text;
/// Optional text that appears above the attachment block
final String pretext;
final String? pretext;
/// The original URL that was used to scrape this attachment.
final String ogScrapeUrl;
final String? ogScrapeUrl;
/// The URL to the attached image. This is present for URL pointing to an
/// image article (eg. Unsplash)
final String imageUrl;
final String footerIcon;
final String footer;
final String? imageUrl;
final String? footerIcon;
final String? footer;
final dynamic fields;
final String fallback;
final String color;
final String? fallback;
final String? color;
/// The name of the author.
final String authorName;
final String authorLink;
final String authorIcon;
final String? authorName;
final String? authorLink;
final String? authorIcon;
/// The URL to the audio, video or image related to the URL.
final String assetUrl;
final String? assetUrl;
/// Actions from a command
@JsonKey(defaultValue: [])
final List<Action> actions;
final Uri localUri;
final Uri? localUri;
/// The file present inside this attachment.
final AttachmentFile file;
final AttachmentFile? file;
/// The current upload state of the attachment
final UploadState uploadState;
late final UploadState uploadState;
/// Map of custom channel extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
final Map<String, dynamic>? extraData;
/// The attachment ID.
///
@@ -147,37 +150,37 @@ class Attachment extends Equatable {
];
/// Serialize to json
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$AttachmentToJson(this), topLevelFields)
..removeWhere((key, value) => dbSpecificTopLevelFields.contains(key));
Map<String, dynamic> toJson() =>
Serialization.moveFromExtraDataToRoot(_$AttachmentToJson(this))
..removeWhere((key, value) => dbSpecificTopLevelFields.contains(key));
/// Serialize to db data
Map<String, dynamic> toData() => Serialization.moveFromExtraDataToRoot(
_$AttachmentToJson(this), topLevelFields + dbSpecificTopLevelFields);
Map<String, dynamic> toData() =>
Serialization.moveFromExtraDataToRoot(_$AttachmentToJson(this));
Attachment copyWith({
String id,
String type,
String titleLink,
String title,
String thumbUrl,
String text,
String pretext,
String ogScrapeUrl,
String imageUrl,
String footerIcon,
String footer,
String? id,
String? type,
String? titleLink,
String? title,
String? thumbUrl,
String? text,
String? pretext,
String? ogScrapeUrl,
String? imageUrl,
String? footerIcon,
String? footer,
dynamic fields,
String fallback,
String color,
String authorName,
String authorLink,
String authorIcon,
String assetUrl,
List<Action> actions,
AttachmentFile file,
UploadState uploadState,
Map<String, dynamic> extraData,
String? fallback,
String? color,
String? authorName,
String? authorLink,
String? authorIcon,
String? assetUrl,
List<Action>? actions,
AttachmentFile? file,
UploadState? uploadState,
Map<String, dynamic>? extraData,
}) =>
Attachment(
id: id ?? this.id,
@@ -205,7 +208,7 @@ class Attachment extends Equatable {
);
@override
List<Object> get props => [
List<Object?> get props => [
id,
type,
titleLink,
@@ -6,46 +6,37 @@ part of 'attachment.dart';
// JsonSerializableGenerator
// **************************************************************************
Attachment _$AttachmentFromJson(Map json) {
Attachment _$AttachmentFromJson(Map<String, dynamic> json) {
return Attachment(
id: json['id'] as String,
type: json['type'] as String,
titleLink: json['title_link'] as String,
title: json['title'] as String,
thumbUrl: json['thumb_url'] as String,
text: json['text'] as String,
pretext: json['pretext'] as String,
ogScrapeUrl: json['og_scrape_url'] as String,
imageUrl: json['image_url'] as String,
footerIcon: json['footer_icon'] as String,
footer: json['footer'] as String,
id: json['id'] as String?,
type: json['type'] as String?,
titleLink: json['title_link'] as String?,
title: json['title'] as String?,
thumbUrl: json['thumb_url'] as String?,
text: json['text'] as String?,
pretext: json['pretext'] as String?,
ogScrapeUrl: json['og_scrape_url'] as String?,
imageUrl: json['image_url'] as String?,
footerIcon: json['footer_icon'] as String?,
footer: json['footer'] as String?,
fields: json['fields'],
fallback: json['fallback'] as String,
color: json['color'] as String,
authorName: json['author_name'] as String,
authorLink: json['author_link'] as String,
authorIcon: json['author_icon'] as String,
assetUrl: json['asset_url'] as String,
actions: (json['actions'] as List)
?.map((e) => e == null
? null
: Action.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
),
fallback: json['fallback'] as String?,
color: json['color'] as String?,
authorName: json['author_name'] as String?,
authorLink: json['author_link'] as String?,
authorIcon: json['author_icon'] as String?,
assetUrl: json['asset_url'] as String?,
actions: (json['actions'] as List<dynamic>?)
?.map((e) => Action.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
extraData: json['extra_data'] as Map<String, dynamic>?,
file: json['file'] == null
? null
: AttachmentFile.fromJson((json['file'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: AttachmentFile.fromJson(json['file'] as Map<String, dynamic>),
uploadState: json['upload_state'] == null
? null
: UploadState.fromJson((json['upload_state'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: UploadState.fromJson(json['upload_state'] as Map<String, dynamic>),
);
}
@@ -75,10 +66,10 @@ Map<String, dynamic> _$AttachmentToJson(Attachment instance) {
writeNotNull('author_link', instance.authorLink);
writeNotNull('author_icon', instance.authorIcon);
writeNotNull('asset_url', instance.assetUrl);
writeNotNull('actions', instance.actions?.map((e) => e?.toJson())?.toList());
val['actions'] = instance.actions.map((e) => e.toJson()).toList();
writeNotNull('file', instance.file?.toJson());
writeNotNull('upload_state', instance.uploadState?.toJson());
val['upload_state'] = instance.uploadState.toJson();
writeNotNull('extra_data', instance.extraData);
writeNotNull('id', instance.id);
val['id'] = instance.id;
return val;
}
@@ -8,18 +8,21 @@ part 'attachment_file.g.dart';
/// Union class to hold various [UploadState] of a attachment.
@freezed
abstract class UploadState with _$UploadState {
class UploadState with _$UploadState {
/// Preparing state of the union
const factory UploadState.preparing() = Preparing;
/// InProgress state of the union
const factory UploadState.inProgress({int uploaded, int total}) = InProgress;
const factory UploadState.inProgress({
required int uploaded,
required int total,
}) = InProgress;
/// Success state of the union
const factory UploadState.success() = Success;
/// Failed state of the union
const factory UploadState.failed({@required String error}) = Failed;
const factory UploadState.failed({required String error}) = Failed;
/// Creates a new instance from a json
factory UploadState.fromJson(Map<String, dynamic> json) =>
@@ -27,7 +30,7 @@ abstract class UploadState with _$UploadState {
}
/// Helper extension for UploadState
extension UploadStateX on UploadState {
extension UploadStateX on UploadState? {
/// Returns true if state is [Preparing]
bool get isPreparing => this is Preparing;
@@ -41,9 +44,15 @@ extension UploadStateX on UploadState {
bool get isFailed => this is Failed;
}
Uint8List _fromString(String bytes) => Uint8List.fromList(bytes.codeUnits);
Uint8List? _fromString(String? bytes) {
if (bytes == null) return null;
return Uint8List.fromList(bytes.codeUnits);
}
String _toString(Uint8List bytes) => String.fromCharCodes(bytes);
String? _toString(Uint8List? bytes) {
if (bytes == null) return null;
return String.fromCharCodes(bytes);
}
/// The class that contains the information about an attachment file
@JsonSerializable()
@@ -54,7 +63,10 @@ class AttachmentFile {
this.name,
this.bytes,
this.size,
});
}) : assert(
path != null || bytes != null,
'Either path or bytes should be != null',
);
/// Create a new instance from a json
factory AttachmentFile.fromJson(Map<String, dynamic> json) =>
@@ -65,21 +77,21 @@ class AttachmentFile {
/// ```
/// final File myFile = File(platformFile.path);
/// ```
final String path;
final String? path;
/// File name including its extension.
final String name;
final String? name;
/// Byte data for this file. Particularly useful if you want to manipulate
/// its data or easily upload to somewhere else.
@JsonKey(toJson: _toString, fromJson: _fromString)
final Uint8List bytes;
final Uint8List? bytes;
/// The file size in bytes.
final int size;
final int? size;
/// File extension for this file.
String get extension => name?.split('.')?.last;
String? get extension => name?.split('.').last;
/// Serialize to json
Map<String, dynamic> toJson() => _$AttachmentFileToJson(this);
@@ -1,5 +1,5 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// ignore_for_file: deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies
// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides
part of 'attachment_file.dart';
@@ -8,6 +8,10 @@ part of 'attachment_file.dart';
// **************************************************************************
T _$identity<T>(T value) => value;
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) {
case 'preparing':
@@ -28,74 +32,72 @@ UploadState _$UploadStateFromJson(Map<String, dynamic> json) {
class _$UploadStateTearOff {
const _$UploadStateTearOff();
// ignore: unused_element
Preparing preparing() {
return const Preparing();
}
// ignore: unused_element
InProgress inProgress({int uploaded, int total}) {
InProgress inProgress({required int uploaded, required int total}) {
return InProgress(
uploaded: uploaded,
total: total,
);
}
// ignore: unused_element
Success success() {
return const Success();
}
// ignore: unused_element
Failed failed({@required String error}) {
Failed failed({required String error}) {
return Failed(
error: error,
);
}
// ignore: unused_element
UploadState fromJson(Map<String, Object> json) {
return UploadState.fromJson(json);
}
}
/// @nodoc
// ignore: unused_element
const $UploadState = _$UploadStateTearOff();
/// @nodoc
mixin _$UploadState {
@optionalTypeArgs
TResult when<TResult extends Object>({
@required TResult preparing(),
@required TResult inProgress(int uploaded, int total),
@required TResult success(),
@required TResult failed(String error),
});
TResult when<TResult extends Object?>({
required TResult Function() preparing,
required TResult Function(int uploaded, int total) inProgress,
required TResult Function() success,
required TResult Function(String error) failed,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object>({
TResult preparing(),
TResult inProgress(int uploaded, int total),
TResult success(),
TResult failed(String error),
@required TResult orElse(),
});
TResult maybeWhen<TResult extends Object?>({
TResult Function()? preparing,
TResult Function(int uploaded, int total)? inProgress,
TResult Function()? success,
TResult Function(String error)? failed,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object>({
@required TResult preparing(Preparing value),
@required TResult inProgress(InProgress value),
@required TResult success(Success value),
@required TResult failed(Failed value),
});
TResult map<TResult extends Object?>({
required TResult Function(Preparing value) preparing,
required TResult Function(InProgress value) inProgress,
required TResult Function(Success value) success,
required TResult Function(Failed value) failed,
}) =>
throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object>({
TResult preparing(Preparing value),
TResult inProgress(InProgress value),
TResult success(Success value),
TResult failed(Failed value),
@required TResult orElse(),
});
Map<String, dynamic> toJson();
TResult maybeMap<TResult extends Object?>({
TResult Function(Preparing value)? preparing,
TResult Function(InProgress value)? inProgress,
TResult Function(Success value)? success,
TResult Function(Failed value)? failed,
required TResult orElse(),
}) =>
throw _privateConstructorUsedError;
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
}
/// @nodoc
@@ -154,29 +156,24 @@ class _$Preparing implements Preparing {
@override
@optionalTypeArgs
TResult when<TResult extends Object>({
@required TResult preparing(),
@required TResult inProgress(int uploaded, int total),
@required TResult success(),
@required TResult failed(String error),
TResult when<TResult extends Object?>({
required TResult Function() preparing,
required TResult Function(int uploaded, int total) inProgress,
required TResult Function() success,
required TResult Function(String error) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return preparing();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object>({
TResult preparing(),
TResult inProgress(int uploaded, int total),
TResult success(),
TResult failed(String error),
@required TResult orElse(),
TResult maybeWhen<TResult extends Object?>({
TResult Function()? preparing,
TResult Function(int uploaded, int total)? inProgress,
TResult Function()? success,
TResult Function(String error)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (preparing != null) {
return preparing();
}
@@ -185,29 +182,24 @@ class _$Preparing implements Preparing {
@override
@optionalTypeArgs
TResult map<TResult extends Object>({
@required TResult preparing(Preparing value),
@required TResult inProgress(InProgress value),
@required TResult success(Success value),
@required TResult failed(Failed value),
TResult map<TResult extends Object?>({
required TResult Function(Preparing value) preparing,
required TResult Function(InProgress value) inProgress,
required TResult Function(Success value) success,
required TResult Function(Failed value) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return preparing(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object>({
TResult preparing(Preparing value),
TResult inProgress(InProgress value),
TResult success(Success value),
TResult failed(Failed value),
@required TResult orElse(),
TResult maybeMap<TResult extends Object?>({
TResult Function(Preparing value)? preparing,
TResult Function(InProgress value)? inProgress,
TResult Function(Success value)? success,
TResult Function(Failed value)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (preparing != null) {
return preparing(this);
}
@@ -245,12 +237,18 @@ class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
@override
$Res call({
Object uploaded = freezed,
Object total = freezed,
Object? uploaded = freezed,
Object? total = freezed,
}) {
return _then(InProgress(
uploaded: uploaded == freezed ? _value.uploaded : uploaded as int,
total: total == freezed ? _value.total : total as int,
uploaded: uploaded == freezed
? _value.uploaded
: uploaded // ignore: cast_nullable_to_non_nullable
as int,
total: total == freezed
? _value.total
: total // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
@@ -259,7 +257,7 @@ class _$InProgressCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
/// @nodoc
class _$InProgress implements InProgress {
const _$InProgress({this.uploaded, this.total});
const _$InProgress({required this.uploaded, required this.total});
factory _$InProgress.fromJson(Map<String, dynamic> json) =>
_$_$InProgressFromJson(json);
@@ -298,29 +296,24 @@ class _$InProgress implements InProgress {
@override
@optionalTypeArgs
TResult when<TResult extends Object>({
@required TResult preparing(),
@required TResult inProgress(int uploaded, int total),
@required TResult success(),
@required TResult failed(String error),
TResult when<TResult extends Object?>({
required TResult Function() preparing,
required TResult Function(int uploaded, int total) inProgress,
required TResult Function() success,
required TResult Function(String error) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return inProgress(uploaded, total);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object>({
TResult preparing(),
TResult inProgress(int uploaded, int total),
TResult success(),
TResult failed(String error),
@required TResult orElse(),
TResult maybeWhen<TResult extends Object?>({
TResult Function()? preparing,
TResult Function(int uploaded, int total)? inProgress,
TResult Function()? success,
TResult Function(String error)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (inProgress != null) {
return inProgress(uploaded, total);
}
@@ -329,29 +322,24 @@ class _$InProgress implements InProgress {
@override
@optionalTypeArgs
TResult map<TResult extends Object>({
@required TResult preparing(Preparing value),
@required TResult inProgress(InProgress value),
@required TResult success(Success value),
@required TResult failed(Failed value),
TResult map<TResult extends Object?>({
required TResult Function(Preparing value) preparing,
required TResult Function(InProgress value) inProgress,
required TResult Function(Success value) success,
required TResult Function(Failed value) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return inProgress(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object>({
TResult preparing(Preparing value),
TResult inProgress(InProgress value),
TResult success(Success value),
TResult failed(Failed value),
@required TResult orElse(),
TResult maybeMap<TResult extends Object?>({
TResult Function(Preparing value)? preparing,
TResult Function(InProgress value)? inProgress,
TResult Function(Success value)? success,
TResult Function(Failed value)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (inProgress != null) {
return inProgress(this);
}
@@ -365,15 +353,17 @@ class _$InProgress implements InProgress {
}
abstract class InProgress implements UploadState {
const factory InProgress({int uploaded, int total}) = _$InProgress;
const factory InProgress({required int uploaded, required int total}) =
_$InProgress;
factory InProgress.fromJson(Map<String, dynamic> json) =
_$InProgress.fromJson;
int get uploaded;
int get total;
int get uploaded => throw _privateConstructorUsedError;
int get total => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$InProgressCopyWith<InProgress> get copyWith;
$InProgressCopyWith<InProgress> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
@@ -416,29 +406,24 @@ class _$Success implements Success {
@override
@optionalTypeArgs
TResult when<TResult extends Object>({
@required TResult preparing(),
@required TResult inProgress(int uploaded, int total),
@required TResult success(),
@required TResult failed(String error),
TResult when<TResult extends Object?>({
required TResult Function() preparing,
required TResult Function(int uploaded, int total) inProgress,
required TResult Function() success,
required TResult Function(String error) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return success();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object>({
TResult preparing(),
TResult inProgress(int uploaded, int total),
TResult success(),
TResult failed(String error),
@required TResult orElse(),
TResult maybeWhen<TResult extends Object?>({
TResult Function()? preparing,
TResult Function(int uploaded, int total)? inProgress,
TResult Function()? success,
TResult Function(String error)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (success != null) {
return success();
}
@@ -447,29 +432,24 @@ class _$Success implements Success {
@override
@optionalTypeArgs
TResult map<TResult extends Object>({
@required TResult preparing(Preparing value),
@required TResult inProgress(InProgress value),
@required TResult success(Success value),
@required TResult failed(Failed value),
TResult map<TResult extends Object?>({
required TResult Function(Preparing value) preparing,
required TResult Function(InProgress value) inProgress,
required TResult Function(Success value) success,
required TResult Function(Failed value) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return success(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object>({
TResult preparing(Preparing value),
TResult inProgress(InProgress value),
TResult success(Success value),
TResult failed(Failed value),
@required TResult orElse(),
TResult maybeMap<TResult extends Object?>({
TResult Function(Preparing value)? preparing,
TResult Function(InProgress value)? inProgress,
TResult Function(Success value)? success,
TResult Function(Failed value)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (success != null) {
return success(this);
}
@@ -506,10 +486,13 @@ class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
@override
$Res call({
Object error = freezed,
Object? error = freezed,
}) {
return _then(Failed(
error: error == freezed ? _value.error : error as String,
error: error == freezed
? _value.error
: error // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
@@ -518,7 +501,7 @@ class _$FailedCopyWithImpl<$Res> extends _$UploadStateCopyWithImpl<$Res>
/// @nodoc
class _$Failed implements Failed {
const _$Failed({@required this.error}) : assert(error != null);
const _$Failed({required this.error});
factory _$Failed.fromJson(Map<String, dynamic> json) =>
_$_$FailedFromJson(json);
@@ -550,29 +533,24 @@ class _$Failed implements Failed {
@override
@optionalTypeArgs
TResult when<TResult extends Object>({
@required TResult preparing(),
@required TResult inProgress(int uploaded, int total),
@required TResult success(),
@required TResult failed(String error),
TResult when<TResult extends Object?>({
required TResult Function() preparing,
required TResult Function(int uploaded, int total) inProgress,
required TResult Function() success,
required TResult Function(String error) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return failed(error);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object>({
TResult preparing(),
TResult inProgress(int uploaded, int total),
TResult success(),
TResult failed(String error),
@required TResult orElse(),
TResult maybeWhen<TResult extends Object?>({
TResult Function()? preparing,
TResult Function(int uploaded, int total)? inProgress,
TResult Function()? success,
TResult Function(String error)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (failed != null) {
return failed(error);
}
@@ -581,29 +559,24 @@ class _$Failed implements Failed {
@override
@optionalTypeArgs
TResult map<TResult extends Object>({
@required TResult preparing(Preparing value),
@required TResult inProgress(InProgress value),
@required TResult success(Success value),
@required TResult failed(Failed value),
TResult map<TResult extends Object?>({
required TResult Function(Preparing value) preparing,
required TResult Function(InProgress value) inProgress,
required TResult Function(Success value) success,
required TResult Function(Failed value) failed,
}) {
assert(preparing != null);
assert(inProgress != null);
assert(success != null);
assert(failed != null);
return failed(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object>({
TResult preparing(Preparing value),
TResult inProgress(InProgress value),
TResult success(Success value),
TResult failed(Failed value),
@required TResult orElse(),
TResult maybeMap<TResult extends Object?>({
TResult Function(Preparing value)? preparing,
TResult Function(InProgress value)? inProgress,
TResult Function(Success value)? success,
TResult Function(Failed value)? failed,
required TResult orElse(),
}) {
assert(orElse != null);
if (failed != null) {
return failed(this);
}
@@ -617,11 +590,11 @@ class _$Failed implements Failed {
}
abstract class Failed implements UploadState {
const factory Failed({@required String error}) = _$Failed;
const factory Failed({required String error}) = _$Failed;
factory Failed.fromJson(Map<String, dynamic> json) = _$Failed.fromJson;
String get error;
String get error => throw _privateConstructorUsedError;
@JsonKey(ignore: true)
$FailedCopyWith<Failed> get copyWith;
$FailedCopyWith<Failed> get copyWith => throw _privateConstructorUsedError;
}
@@ -6,12 +6,12 @@ part of 'attachment_file.dart';
// JsonSerializableGenerator
// **************************************************************************
AttachmentFile _$AttachmentFileFromJson(Map json) {
AttachmentFile _$AttachmentFileFromJson(Map<String, dynamic> json) {
return AttachmentFile(
path: json['path'] as String,
name: json['name'] as String,
bytes: _fromString(json['bytes'] as String),
size: json['size'] as int,
path: json['path'] as String?,
name: json['name'] as String?,
bytes: _fromString(json['bytes'] as String?),
size: json['size'] as int?,
);
}
@@ -23,14 +23,14 @@ Map<String, dynamic> _$AttachmentFileToJson(AttachmentFile instance) =>
'size': instance.size,
};
_$Preparing _$_$PreparingFromJson(Map json) {
_$Preparing _$_$PreparingFromJson(Map<String, dynamic> json) {
return _$Preparing();
}
Map<String, dynamic> _$_$PreparingToJson(_$Preparing instance) =>
<String, dynamic>{};
_$InProgress _$_$InProgressFromJson(Map json) {
_$InProgress _$_$InProgressFromJson(Map<String, dynamic> json) {
return _$InProgress(
uploaded: json['uploaded'] as int,
total: json['total'] as int,
@@ -43,14 +43,14 @@ Map<String, dynamic> _$_$InProgressToJson(_$InProgress instance) =>
'total': instance.total,
};
_$Success _$_$SuccessFromJson(Map json) {
_$Success _$_$SuccessFromJson(Map<String, dynamic> json) {
return _$Success();
}
Map<String, dynamic> _$_$SuccessToJson(_$Success instance) =>
<String, dynamic>{};
_$Failed _$_$FailedFromJson(Map json) {
_$Failed _$_$FailedFromJson(Map<String, dynamic> json) {
return _$Failed(
error: json['error'] as String,
);
@@ -1,5 +1,6 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:stream_chat/src/models/command.dart';
part 'channel_config.g.dart';
/// The class that contains the information about the configuration of a channel
@@ -7,35 +8,38 @@ part 'channel_config.g.dart';
class ChannelConfig {
/// Constructor used for json serialization
ChannelConfig({
this.automod,
this.commands,
this.connectEvents,
this.createdAt,
this.updatedAt,
this.maxMessageLength,
this.messageRetention,
this.mutes,
this.name,
this.reactions,
this.readEvents,
this.replies,
this.search,
this.typingEvents,
this.uploads,
this.urlEnrichment,
});
this.automod = 'flag',
this.commands = const [],
this.connectEvents = false,
DateTime? createdAt,
DateTime? updatedAt,
this.maxMessageLength = 0,
this.messageRetention = '',
this.mutes = false,
this.reactions = false,
this.readEvents = false,
this.replies = false,
this.search = false,
this.typingEvents = false,
this.uploads = false,
this.urlEnrichment = false,
}) : createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
/// Create a new instance from a json
factory ChannelConfig.fromJson(Map<String, dynamic> json) =>
_$ChannelConfigFromJson(json);
/// Moderation configuration
@JsonKey(defaultValue: 'flag')
final String automod;
/// List of available commands
@JsonKey(defaultValue: [])
final List<Command> commands;
/// True if the channel should send connect events
@JsonKey(defaultValue: false)
final bool connectEvents;
/// Date of channel creation
@@ -45,36 +49,43 @@ class ChannelConfig {
final DateTime updatedAt;
/// Max channel message length
@JsonKey(defaultValue: 0)
final int maxMessageLength;
/// Duration of message retention
@JsonKey(defaultValue: '')
final String messageRetention;
/// True if users can be muted
@JsonKey(defaultValue: false)
final bool mutes;
/// Name of the channel
final String name;
/// True if reaction are active for this channel
@JsonKey(defaultValue: false)
final bool reactions;
/// True if readEvents are active for this channel
@JsonKey(defaultValue: false)
final bool readEvents;
/// True if reply message are active for this channel
@JsonKey(defaultValue: false)
final bool replies;
/// True if it's possible to perform a search in this channel
@JsonKey(defaultValue: false)
final bool search;
/// True if typing events should be sent for this channel
@JsonKey(defaultValue: false)
final bool typingEvents;
/// True if it's possible to upload files to this channel
@JsonKey(defaultValue: false)
final bool uploads;
/// True if urls appears as attachments
@JsonKey(defaultValue: false)
final bool urlEnrichment;
/// Serialize to json
@@ -6,48 +6,43 @@ part of 'channel_config.dart';
// JsonSerializableGenerator
// **************************************************************************
ChannelConfig _$ChannelConfigFromJson(Map json) {
ChannelConfig _$ChannelConfigFromJson(Map<String, dynamic> json) {
return ChannelConfig(
automod: json['automod'] as String,
commands: (json['commands'] as List)
?.map((e) => e == null
? null
: Command.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
connectEvents: json['connect_events'] as bool,
automod: json['automod'] as String? ?? 'flag',
commands: (json['commands'] as List<dynamic>?)
?.map((e) => Command.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
connectEvents: json['connect_events'] as bool? ?? false,
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
updatedAt: json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String),
maxMessageLength: json['max_message_length'] as int,
messageRetention: json['message_retention'] as String,
mutes: json['mutes'] as bool,
name: json['name'] as String,
reactions: json['reactions'] as bool,
readEvents: json['read_events'] as bool,
replies: json['replies'] as bool,
search: json['search'] as bool,
typingEvents: json['typing_events'] as bool,
uploads: json['uploads'] as bool,
urlEnrichment: json['url_enrichment'] as bool,
maxMessageLength: json['max_message_length'] as int? ?? 0,
messageRetention: json['message_retention'] as String? ?? '',
mutes: json['mutes'] as bool? ?? false,
reactions: json['reactions'] as bool? ?? false,
readEvents: json['read_events'] as bool? ?? false,
replies: json['replies'] as bool? ?? false,
search: json['search'] as bool? ?? false,
typingEvents: json['typing_events'] as bool? ?? false,
uploads: json['uploads'] as bool? ?? false,
urlEnrichment: json['url_enrichment'] as bool? ?? false,
);
}
Map<String, dynamic> _$ChannelConfigToJson(ChannelConfig instance) =>
<String, dynamic>{
'automod': instance.automod,
'commands': instance.commands?.map((e) => e?.toJson())?.toList(),
'commands': instance.commands.map((e) => e.toJson()).toList(),
'connect_events': instance.connectEvents,
'created_at': instance.createdAt?.toIso8601String(),
'updated_at': instance.updatedAt?.toIso8601String(),
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
'max_message_length': instance.maxMessageLength,
'message_retention': instance.messageRetention,
'mutes': instance.mutes,
'name': instance.name,
'reactions': instance.reactions,
'read_events': instance.readEvents,
'replies': instance.replies,
@@ -10,20 +10,29 @@ part 'channel_model.g.dart';
class ChannelModel {
/// Constructor used for json serialization
ChannelModel({
this.id,
this.type,
this.cid,
this.config,
String? id,
String? type,
String? cid,
ChannelConfig? config,
this.createdBy,
this.frozen,
this.frozen = false,
this.lastMessageAt,
this.createdAt,
this.updatedAt,
DateTime? createdAt,
DateTime? updatedAt,
this.deletedAt,
this.memberCount,
this.memberCount = 0,
this.extraData,
this.team,
});
}) : config = config ?? ChannelConfig(),
createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now(),
assert(
cid != null || (id != null && type != null),
'provide either a cid or an id and type',
),
id = id ?? cid!.split(':')[1],
type = type ?? cid!.split(':')[0],
cid = cid ?? '$type:$id';
/// Create a new instance from a json
factory ChannelModel.fromJson(Map<String, dynamic> json) =>
@@ -46,15 +55,15 @@ class ChannelModel {
/// The user that created this channel
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final User createdBy;
final User? createdBy;
/// True if this channel is frozen
@JsonKey(includeIfNull: false)
@JsonKey(includeIfNull: false, defaultValue: false)
final bool frozen;
/// The date of the last message
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime lastMessageAt;
final DateTime? lastMessageAt;
/// The date of channel creation
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@@ -66,19 +75,20 @@ class ChannelModel {
/// The date of channel deletion
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime deletedAt;
final DateTime? deletedAt;
/// The count of this channel members
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false, toJson: Serialization.readOnly, defaultValue: 0)
final int memberCount;
/// Map of custom channel extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
final Map<String, dynamic>? extraData;
/// The team the channel belongs to
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String team;
final String? team;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -98,30 +108,29 @@ class ChannelModel {
];
/// Shortcut for channel name
String get name =>
extraData?.containsKey('name') == true ? extraData['name'] : cid;
String? get name =>
extraData?.containsKey('name') == true ? extraData!['name'] : cid;
/// Serialize to json
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$ChannelModelToJson(this),
topLevelFields,
);
/// Creates a copy of [ChannelModel] with specified attributes overridden.
ChannelModel copyWith({
String id,
String type,
String cid,
ChannelConfig config,
User createdBy,
bool frozen,
DateTime lastMessageAt,
DateTime createdAt,
DateTime updatedAt,
DateTime deletedAt,
int memberCount,
Map<String, dynamic> extraData,
String team,
String? id,
String? type,
String? cid,
ChannelConfig? config,
User? createdBy,
bool? frozen,
DateTime? lastMessageAt,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? deletedAt,
int? memberCount,
Map<String, dynamic>? extraData,
String? team,
}) =>
ChannelModel(
id: id ?? this.id,
@@ -141,7 +150,7 @@ class ChannelModel {
/// Returns a new [ChannelModel] that is a combination of this channelModel
/// and the given [other] channelModel.
ChannelModel merge(ChannelModel other) {
ChannelModel merge(ChannelModel? other) {
if (other == null) return this;
return copyWith(
id: other.id,
@@ -6,22 +6,18 @@ part of 'channel_model.dart';
// JsonSerializableGenerator
// **************************************************************************
ChannelModel _$ChannelModelFromJson(Map json) {
ChannelModel _$ChannelModelFromJson(Map<String, dynamic> json) {
return ChannelModel(
id: json['id'] as String,
type: json['type'] as String,
cid: json['cid'] as String,
id: json['id'] as String?,
type: json['type'] as String?,
cid: json['cid'] as String?,
config: json['config'] == null
? null
: ChannelConfig.fromJson((json['config'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: ChannelConfig.fromJson(json['config'] as Map<String, dynamic>),
createdBy: json['created_by'] == null
? null
: User.fromJson((json['created_by'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
frozen: json['frozen'] as bool,
: User.fromJson(json['created_by'] as Map<String, dynamic>),
frozen: json['frozen'] as bool? ?? false,
lastMessageAt: json['last_message_at'] == null
? null
: DateTime.parse(json['last_message_at'] as String),
@@ -34,11 +30,9 @@ ChannelModel _$ChannelModelFromJson(Map json) {
deletedAt: json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
memberCount: json['member_count'] as int,
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
),
team: json['team'] as String,
memberCount: json['member_count'] as int? ?? 0,
extraData: json['extra_data'] as Map<String, dynamic>?,
team: json['team'] as String?,
);
}
@@ -57,7 +51,7 @@ Map<String, dynamic> _$ChannelModelToJson(ChannelModel instance) {
writeNotNull('cid', readonly(instance.cid));
writeNotNull('config', readonly(instance.config));
writeNotNull('created_by', readonly(instance.createdBy));
writeNotNull('frozen', instance.frozen);
val['frozen'] = instance.frozen;
writeNotNull('last_message_at', readonly(instance.lastMessageAt));
writeNotNull('created_at', readonly(instance.createdAt));
writeNotNull('updated_at', readonly(instance.updatedAt));
@@ -22,24 +22,29 @@ class ChannelState {
});
/// The channel to which this state belongs
final ChannelModel channel;
final ChannelModel? channel;
/// A paginated list of channel messages
@JsonKey(defaultValue: <Message>[])
final List<Message> messages;
/// A paginated list of channel members
@JsonKey(defaultValue: <Member>[])
final List<Member> members;
/// A paginated list of pinned messages
@JsonKey(defaultValue: <Message>[])
final List<Message> pinnedMessages;
/// The count of users watching the channel
final int watcherCount;
final int? watcherCount;
/// A paginated list of users watching the channel
@JsonKey(defaultValue: <User>[])
final List<User> watchers;
/// The list of channel reads
@JsonKey(defaultValue: <Read>[])
final List<Read> read;
/// Create a new instance from a json
@@ -51,13 +56,13 @@ class ChannelState {
/// Creates a copy of [ChannelState] with specified attributes overridden.
ChannelState copyWith({
ChannelModel channel,
List<Message> messages,
List<Member> members,
List<Message> pinnedMessages,
int watcherCount,
List<User> watchers,
List<Read> read,
ChannelModel? channel,
List<Message>? messages,
List<Member>? members,
List<Message>? pinnedMessages,
int? watcherCount,
List<User>? watchers,
List<Read>? read,
}) =>
ChannelState(
channel: channel ?? this.channel,
@@ -6,60 +6,43 @@ part of 'channel_state.dart';
// JsonSerializableGenerator
// **************************************************************************
ChannelState _$ChannelStateFromJson(Map json) {
ChannelState _$ChannelStateFromJson(Map<String, dynamic> json) {
return ChannelState(
channel: json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
messages: (json['messages'] as List)
?.map((e) => e == null
? null
: Message.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
members: (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
pinnedMessages: (json['pinned_messages'] as List)
?.map((e) => e == null
? null
: Message.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
watcherCount: json['watcher_count'] as int,
watchers: (json['watchers'] as List)
?.map((e) => e == null
? null
: User.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
read: (json['read'] as List)
?.map((e) => e == null
? null
: Read.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
: ChannelModel.fromJson(json['channel'] as Map<String, dynamic>),
messages: (json['messages'] as List<dynamic>?)
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
members: (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
pinnedMessages: (json['pinned_messages'] as List<dynamic>?)
?.map((e) => Message.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
watcherCount: json['watcher_count'] as int?,
watchers: (json['watchers'] as List<dynamic>?)
?.map((e) => User.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
read: (json['read'] as List<dynamic>?)
?.map((e) => Read.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
);
}
Map<String, dynamic> _$ChannelStateToJson(ChannelState instance) =>
<String, dynamic>{
'channel': instance.channel?.toJson(),
'messages': instance.messages?.map((e) => e?.toJson())?.toList(),
'members': instance.members?.map((e) => e?.toJson())?.toList(),
'messages': instance.messages.map((e) => e.toJson()).toList(),
'members': instance.members.map((e) => e.toJson()).toList(),
'pinned_messages':
instance.pinnedMessages?.map((e) => e?.toJson())?.toList(),
instance.pinnedMessages.map((e) => e.toJson()).toList(),
'watcher_count': instance.watcherCount,
'watchers': instance.watchers?.map((e) => e?.toJson())?.toList(),
'read': instance.read?.map((e) => e?.toJson())?.toList(),
'watchers': instance.watchers.map((e) => e.toJson()).toList(),
'read': instance.read.map((e) => e.toJson()).toList(),
};
@@ -7,9 +7,9 @@ part 'command.g.dart';
class Command {
/// Constructor used for json serialization
Command({
this.name,
this.description,
this.args,
required this.name,
required this.description,
required this.args,
});
/// Create a new instance from a json
@@ -6,7 +6,7 @@ part of 'command.dart';
// JsonSerializableGenerator
// **************************************************************************
Command _$CommandFromJson(Map json) {
Command _$CommandFromJson(Map<String, dynamic> json) {
return Command(
name: json['name'] as String,
description: json['description'] as String,
@@ -7,8 +7,8 @@ part 'device.g.dart';
class Device {
/// Constructor used for json serialization
Device({
this.id,
this.pushProvider,
required this.id,
required this.pushProvider,
});
/// Create a new instance from a json
@@ -6,7 +6,7 @@ part of 'device.dart';
// JsonSerializableGenerator
// **************************************************************************
Device _$DeviceFromJson(Map json) {
Device _$DeviceFromJson(Map<String, dynamic> json) {
return Device(
id: json['id'] as String,
pushProvider: json['push_provider'] as String,
+54 -55
View File
@@ -10,7 +10,7 @@ part 'event.g.dart';
@JsonSerializable()
class Event {
/// Constructor used for json serialization
Event({
const Event({
this.type,
this.cid,
this.connectionId,
@@ -27,71 +27,72 @@ class Event {
this.channelId,
this.channelType,
this.parentId,
this.extraData,
}) : isLocal = true;
this.extraData = const {},
this.isLocal = true,
});
/// Create a new instance from a json
factory Event.fromJson(Map<String, dynamic> json) =>
_$EventFromJson(Serialization.moveToExtraDataFromRoot(
json,
topLevelFields,
))
..isLocal = false;
));
/// The type of the event
/// [EventType] contains some predefined constant types
final String type;
final String? type;
/// The channel cid to which the event belongs
final String cid;
final String? cid;
/// The channel id to which the event belongs
final String channelId;
final String? channelId;
/// The channel type to which the event belongs
final String channelType;
final String? channelType;
/// The connection id in which the event has been sent
final String connectionId;
final String? connectionId;
/// The date of creation of the event
final DateTime createdAt;
final DateTime? createdAt;
/// User object of the health check user
final OwnUser me;
final OwnUser? me;
/// User object of the current user
final User user;
final User? user;
/// The message sent with the event
final Message message;
final Message? message;
/// The channel sent with the event
final EventChannel channel;
final EventChannel? channel;
/// The member sent with the event
final Member member;
final Member? member;
/// The reaction sent with the event
final Reaction reaction;
final Reaction? reaction;
/// The number of unread messages for current user
final int totalUnreadCount;
final int? totalUnreadCount;
/// User total unread channels
final int unreadChannels;
final int? unreadChannels;
/// Online status
final bool online;
final bool? online;
/// The id of the parent message of a thread
final String parentId;
final String? parentId;
/// True if the event is generated by this client
bool isLocal;
@JsonKey(defaultValue: false)
final bool isLocal;
/// Map of custom channel extraData
@JsonKey(includeIfNull: false)
@JsonKey(defaultValue: {})
final Map<String, dynamic> extraData;
/// Known top level fields.
@@ -119,28 +120,27 @@ class Event {
/// Serialize to json
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$EventToJson(this),
topLevelFields,
);
/// Creates a copy of [Event] with specified attributes overridden.
Event copyWith({
String type,
String cid,
String channelId,
String channelType,
String connectionId,
DateTime createdAt,
OwnUser me,
User user,
Message message,
EventChannel channel,
Member member,
Reaction reaction,
int totalUnreadCount,
int unreadChannels,
bool online,
String parentId,
Map<String, dynamic> extraData,
String? type,
String? cid,
String? channelId,
String? channelType,
String? connectionId,
DateTime? createdAt,
OwnUser? me,
User? user,
Message? message,
EventChannel? channel,
Member? member,
Reaction? reaction,
int? totalUnreadCount,
int? unreadChannels,
bool? online,
String? parentId,
Map<String, dynamic>? extraData,
}) =>
Event(
type: type ?? this.type,
@@ -169,18 +169,18 @@ class EventChannel extends ChannelModel {
/// Constructor used for json serialization
EventChannel({
this.members,
String id,
String type,
String cid,
ChannelConfig config,
User createdBy,
bool frozen,
DateTime lastMessageAt,
DateTime createdAt,
DateTime updatedAt,
DateTime deletedAt,
int memberCount,
Map<String, dynamic> extraData,
String? id,
String? type,
required String cid,
required ChannelConfig config,
User? createdBy,
bool frozen = false,
DateTime? lastMessageAt,
required DateTime createdAt,
required DateTime updatedAt,
DateTime? deletedAt,
required int memberCount,
Map<String, dynamic>? extraData,
}) : super(
id: id,
type: type,
@@ -204,7 +204,7 @@ class EventChannel extends ChannelModel {
));
/// A paginated list of channel members
final List<Member> members;
final List<Member>? members;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -217,6 +217,5 @@ class EventChannel extends ChannelModel {
@override
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$EventChannelToJson(this),
topLevelFields,
);
}
@@ -6,126 +6,87 @@ part of 'event.dart';
// JsonSerializableGenerator
// **************************************************************************
Event _$EventFromJson(Map json) {
Event _$EventFromJson(Map<String, dynamic> json) {
return Event(
type: json['type'] as String,
cid: json['cid'] as String,
connectionId: json['connection_id'] as String,
type: json['type'] as String?,
cid: json['cid'] as String?,
connectionId: json['connection_id'] as String?,
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
me: json['me'] == null
? null
: OwnUser.fromJson((json['me'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: OwnUser.fromJson(json['me'] as Map<String, dynamic>),
user: json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: User.fromJson(json['user'] as Map<String, dynamic>),
message: json['message'] == null
? null
: Message.fromJson((json['message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
totalUnreadCount: json['total_unread_count'] as int,
unreadChannels: json['unread_channels'] as int,
: Message.fromJson(json['message'] as Map<String, dynamic>),
totalUnreadCount: json['total_unread_count'] as int?,
unreadChannels: json['unread_channels'] as int?,
reaction: json['reaction'] == null
? null
: Reaction.fromJson((json['reaction'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
online: json['online'] as bool,
: Reaction.fromJson(json['reaction'] as Map<String, dynamic>),
online: json['online'] as bool?,
channel: json['channel'] == null
? null
: EventChannel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: EventChannel.fromJson(json['channel'] as Map<String, dynamic>),
member: json['member'] == null
? null
: Member.fromJson((json['member'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
channelId: json['channel_id'] as String,
channelType: json['channel_type'] as String,
parentId: json['parent_id'] as String,
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
),
)..isLocal = json['is_local'] as bool;
: Member.fromJson(json['member'] as Map<String, dynamic>),
channelId: json['channel_id'] as String?,
channelType: json['channel_type'] as String?,
parentId: json['parent_id'] as String?,
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
isLocal: json['is_local'] as bool? ?? false,
);
}
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,
};
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,
};
void writeNotNull(String key, dynamic value) {
if (value != null) {
val[key] = value;
}
}
writeNotNull('extra_data', instance.extraData);
return val;
}
EventChannel _$EventChannelFromJson(Map json) {
EventChannel _$EventChannelFromJson(Map<String, dynamic> json) {
return EventChannel(
members: (json['members'] as List)
?.map((e) => e == null
? null
: Member.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
id: json['id'] as String,
type: json['type'] as String,
members: (json['members'] as List<dynamic>?)
?.map((e) => Member.fromJson(e as Map<String, dynamic>))
.toList(),
id: json['id'] as String?,
type: json['type'] as String?,
cid: json['cid'] as String,
config: json['config'] == null
? null
: ChannelConfig.fromJson((json['config'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
config: ChannelConfig.fromJson(json['config'] as Map<String, dynamic>),
createdBy: json['created_by'] == null
? null
: User.fromJson((json['created_by'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
frozen: json['frozen'] as bool,
: User.fromJson(json['created_by'] as Map<String, dynamic>),
frozen: json['frozen'] as bool? ?? false,
lastMessageAt: json['last_message_at'] == null
? null
: DateTime.parse(json['last_message_at'] as String),
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
updatedAt: json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String),
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt: json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
memberCount: json['member_count'] as int,
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
),
memberCount: json['member_count'] as int? ?? 0,
extraData: json['extra_data'] as Map<String, dynamic>?,
);
}
@@ -144,13 +105,13 @@ Map<String, dynamic> _$EventChannelToJson(EventChannel instance) {
writeNotNull('cid', readonly(instance.cid));
writeNotNull('config', readonly(instance.config));
writeNotNull('created_by', readonly(instance.createdBy));
writeNotNull('frozen', instance.frozen);
val['frozen'] = instance.frozen;
writeNotNull('last_message_at', readonly(instance.lastMessageAt));
writeNotNull('created_at', readonly(instance.createdAt));
writeNotNull('updated_at', readonly(instance.updatedAt));
writeNotNull('deleted_at', readonly(instance.deletedAt));
writeNotNull('member_count', readonly(instance.memberCount));
writeNotNull('extra_data', instance.extraData);
val['members'] = instance.members?.map((e) => e?.toJson())?.toList();
val['members'] = instance.members?.map((e) => e.toJson()).toList();
return val;
}
+28 -23
View File
@@ -12,15 +12,16 @@ class Member {
this.user,
this.inviteAcceptedAt,
this.inviteRejectedAt,
this.invited,
this.invited = false,
this.role,
this.userId,
this.isModerator,
this.createdAt,
this.updatedAt,
this.banned,
this.shadowBanned,
});
this.isModerator = false,
DateTime? createdAt,
DateTime? updatedAt,
this.banned = false,
this.shadowBanned = false,
}) : createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
/// Create a new instance from a json
factory Member.fromJson(Map<String, dynamic> json) {
@@ -31,30 +32,34 @@ class Member {
}
/// The interested user
final User user;
final User? user;
/// The date in which the user accepted the invite to the channel
final DateTime inviteAcceptedAt;
final DateTime? inviteAcceptedAt;
/// The date in which the user rejected the invite to the channel
final DateTime inviteRejectedAt;
final DateTime? inviteRejectedAt;
/// True if the user has been invited to the channel
@JsonKey(defaultValue: false)
final bool invited;
/// The role of the user in the channel
final String role;
final String? role;
/// The id of the interested user
final String userId;
final String? userId;
/// True if the user is a moderator of the channel
@JsonKey(defaultValue: false)
final bool isModerator;
/// True if the member is banned from the channel
@JsonKey(defaultValue: false)
final bool banned;
/// True if the member is shadow banned from the channel
@JsonKey(defaultValue: false)
final bool shadowBanned;
/// The date of creation
@@ -65,17 +70,17 @@ class Member {
/// Creates a copy of [Member] with specified attributes overridden.
Member copyWith({
User user,
DateTime inviteAcceptedAt,
DateTime inviteRejectedAt,
bool invited,
String role,
String userId,
bool isModerator,
DateTime createdAt,
DateTime updatedAt,
bool banned,
bool shadowBanned,
User? user,
DateTime? inviteAcceptedAt,
DateTime? inviteRejectedAt,
bool? invited,
String? role,
String? userId,
bool? isModerator,
DateTime? createdAt,
DateTime? updatedAt,
bool? banned,
bool? shadowBanned,
}) =>
Member(
user: user ?? this.user,
@@ -6,31 +6,29 @@ part of 'member.dart';
// JsonSerializableGenerator
// **************************************************************************
Member _$MemberFromJson(Map json) {
Member _$MemberFromJson(Map<String, dynamic> json) {
return Member(
user: json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
: User.fromJson(json['user'] as Map<String, dynamic>),
inviteAcceptedAt: json['invite_accepted_at'] == null
? null
: DateTime.parse(json['invite_accepted_at'] as String),
inviteRejectedAt: json['invite_rejected_at'] == null
? null
: DateTime.parse(json['invite_rejected_at'] as String),
invited: json['invited'] as bool,
role: json['role'] as String,
userId: json['user_id'] as String,
isModerator: json['is_moderator'] as bool,
invited: json['invited'] as bool? ?? false,
role: json['role'] as String?,
userId: json['user_id'] as String?,
isModerator: json['is_moderator'] as bool? ?? false,
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
updatedAt: json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String),
banned: json['banned'] as bool,
shadowBanned: json['shadow_banned'] as bool,
banned: json['banned'] as bool? ?? false,
shadowBanned: json['shadow_banned'] as bool? ?? false,
);
}
@@ -44,6 +42,6 @@ Map<String, dynamic> _$MemberToJson(Member instance) => <String, dynamic>{
'is_moderator': instance.isModerator,
'banned': instance.banned,
'shadow_banned': instance.shadowBanned,
'created_at': instance.createdAt?.toIso8601String(),
'updated_at': instance.updatedAt?.toIso8601String(),
'created_at': instance.createdAt.toIso8601String(),
'updated_at': instance.updatedAt.toIso8601String(),
};
+122 -102
View File
@@ -45,13 +45,13 @@ enum MessageSendingStatus {
class Message extends Equatable {
/// Constructor used for json serialization
Message({
String id,
String? id,
this.text,
this.type,
this.attachments,
this.mentionedUsers,
this.silent,
this.shadowed,
this.type = 'regular',
this.attachments = const [],
this.mentionedUsers = const [],
this.silent = false,
this.shadowed = false,
this.reactionCounts,
this.reactionScores,
this.latestReactions,
@@ -63,19 +63,21 @@ class Message extends Equatable {
this.threadParticipants,
this.showInChannel,
this.command,
this.createdAt,
this.updatedAt,
DateTime? createdAt,
DateTime? updatedAt,
this.user,
this.pinned = false,
this.pinnedAt,
DateTime pinExpires,
DateTime? pinExpires,
this.pinnedBy,
this.extraData,
this.extraData = const {},
this.deletedAt,
this.status = MessageSendingStatus.sent,
this.skipPush,
this.skipPush = false,
}) : id = id ?? const Uuid().v4(),
pinExpires = pinExpires?.toUtc();
pinExpires = pinExpires?.toUtc(),
createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
/// Create a new instance from a json
factory Message.fromJson(Map<String, dynamic> json) => _$MessageFromJson(
@@ -86,75 +88,91 @@ class Message extends Equatable {
final String id;
/// The text of this message
final String text;
final String? text;
/// The status of a sending message
@JsonKey(ignore: true)
final MessageSendingStatus status;
/// The message type
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: 'regular',
)
final String type;
/// The list of attachments, either provided by the user or generated from a
/// command or as a result of URL scraping.
@JsonKey(includeIfNull: false)
@JsonKey(
includeIfNull: false,
defaultValue: [],
)
final List<Attachment> attachments;
/// The list of user mentioned in the message
@JsonKey(toJson: Serialization.userIds)
@JsonKey(
toJson: Serialization.userIds,
defaultValue: [],
)
final List<User> mentionedUsers;
/// A map describing the count of number of every reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final Map<String, int> reactionCounts;
final Map<String, int>? reactionCounts;
/// A map describing the count of score of every reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final Map<String, int> reactionScores;
final Map<String, int>? reactionScores;
/// The latest reactions to the message created by any user.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<Reaction> latestReactions;
final List<Reaction>? latestReactions;
/// The reactions added to the message by the current user.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<Reaction> ownReactions;
final List<Reaction>? ownReactions;
/// The ID of the parent message, if the message is a thread reply.
final String parentId;
final String? parentId;
/// A quoted reply message
@JsonKey(toJson: Serialization.readOnly)
final Message quotedMessage;
final Message? quotedMessage;
/// The ID of the quoted message, if the message is a quoted reply.
final String quotedMessageId;
final String? quotedMessageId;
/// Reserved field indicating the number of replies for this message.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final int replyCount;
final int? replyCount;
/// Reserved field indicating the thread participants for this message.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<User> threadParticipants;
final List<User>? threadParticipants;
/// Check if this message needs to show in the channel.
final bool showInChannel;
final bool? showInChannel;
/// If true the message is silent
@JsonKey(defaultValue: false)
final bool silent;
/// If true the message will not send a push notification
@JsonKey(defaultValue: false)
final bool skipPush;
/// If true the message is shadowed
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: false,
)
final bool shadowed;
/// A used command name.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String command;
final String? command;
/// Reserved field indicating when the message was created.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@@ -166,26 +184,30 @@ class Message extends Equatable {
/// User who sent the message
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final User user;
final User? user;
/// If true the message is pinned
@JsonKey(defaultValue: false)
final bool pinned;
/// Reserved field indicating when the message was pinned
@JsonKey(toJson: Serialization.readOnly)
final DateTime pinnedAt;
final DateTime? pinnedAt;
/// Reserved field indicating when the message will expire
///
/// if `null` message has no expiry
final DateTime pinExpires;
final DateTime? pinExpires;
/// Reserved field indicating who pinned the message
@JsonKey(toJson: Serialization.readOnly)
final User pinnedBy;
final User? pinnedBy;
/// Message custom extraData
@JsonKey(includeIfNull: false)
@JsonKey(
includeIfNull: false,
defaultValue: {},
)
final Map<String, dynamic> extraData;
/// True if the message is a system info
@@ -199,7 +221,7 @@ class Message extends Equatable {
/// Reserved field indicating when the message was deleted.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime deletedAt;
final DateTime? deletedAt;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -236,39 +258,40 @@ class Message extends Equatable {
/// Serialize to json
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$MessageToJson(this), topLevelFields);
_$MessageToJson(this),
);
/// Creates a copy of [Message] with specified attributes overridden.
Message copyWith({
String id,
String text,
String type,
List<Attachment> attachments,
List<User> mentionedUsers,
Map<String, int> reactionCounts,
Map<String, int> reactionScores,
List<Reaction> latestReactions,
List<Reaction> ownReactions,
String parentId,
Message quotedMessage,
String quotedMessageId,
int replyCount,
List<User> threadParticipants,
bool showInChannel,
bool shadowed,
bool silent,
String command,
DateTime createdAt,
DateTime updatedAt,
DateTime deletedAt,
User user,
bool pinned,
DateTime pinnedAt,
Object pinExpires = _pinExpires,
User pinnedBy,
Map<String, dynamic> extraData,
MessageSendingStatus status,
bool skipPush,
String? id,
String? text,
String? type,
List<Attachment>? attachments,
List<User>? mentionedUsers,
Map<String, int>? reactionCounts,
Map<String, int>? reactionScores,
List<Reaction>? latestReactions,
List<Reaction>? ownReactions,
String? parentId,
Message? quotedMessage,
String? quotedMessageId,
int? replyCount,
List<User>? threadParticipants,
bool? showInChannel,
bool? shadowed,
bool? silent,
String? command,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? deletedAt,
User? user,
bool? pinned,
DateTime? pinnedAt,
Object? pinExpires = _pinExpires,
User? pinnedBy,
Map<String, dynamic>? extraData,
MessageSendingStatus? status,
bool? skipPush,
}) {
assert(() {
if (pinExpires is! DateTime &&
@@ -306,49 +329,47 @@ class Message extends Equatable {
pinned: pinned ?? this.pinned,
pinnedAt: pinnedAt ?? this.pinnedAt,
pinnedBy: pinnedBy ?? this.pinnedBy,
pinExpires: pinExpires == _pinExpires ? this.pinExpires : pinExpires,
pinExpires:
pinExpires == _pinExpires ? this.pinExpires : pinExpires as DateTime?,
skipPush: skipPush ?? this.skipPush,
);
}
/// Returns a new [Message] that is a combination of this message and the
/// given [other] message.
Message merge(Message other) {
if (other == null) return this;
return copyWith(
id: other.id,
text: other.text,
type: other.type,
attachments: other.attachments,
mentionedUsers: other.mentionedUsers,
reactionCounts: other.reactionCounts,
reactionScores: other.reactionScores,
latestReactions: other.latestReactions,
ownReactions: other.ownReactions,
parentId: other.parentId,
quotedMessage: other.quotedMessage,
quotedMessageId: other.quotedMessageId,
replyCount: other.replyCount,
threadParticipants: other.threadParticipants,
showInChannel: other.showInChannel,
command: other.command,
createdAt: other.createdAt,
silent: other.silent,
extraData: other.extraData,
user: other.user,
shadowed: other.shadowed,
updatedAt: other.updatedAt,
deletedAt: other.deletedAt,
status: other.status,
pinned: other.pinned,
pinnedAt: other.pinnedAt,
pinExpires: other.pinExpires,
pinnedBy: other.pinnedBy,
);
}
Message merge(Message other) => copyWith(
id: other.id,
text: other.text,
type: other.type,
attachments: other.attachments,
mentionedUsers: other.mentionedUsers,
reactionCounts: other.reactionCounts,
reactionScores: other.reactionScores,
latestReactions: other.latestReactions,
ownReactions: other.ownReactions,
parentId: other.parentId,
quotedMessage: other.quotedMessage,
quotedMessageId: other.quotedMessageId,
replyCount: other.replyCount,
threadParticipants: other.threadParticipants,
showInChannel: other.showInChannel,
command: other.command,
createdAt: other.createdAt,
silent: other.silent,
extraData: other.extraData,
user: other.user,
shadowed: other.shadowed,
updatedAt: other.updatedAt,
deletedAt: other.deletedAt,
status: other.status,
pinned: other.pinned,
pinnedAt: other.pinnedAt,
pinExpires: other.pinExpires,
pinnedBy: other.pinnedBy,
);
@override
List<Object> get props => [
List<Object?> get props => [
id,
text,
type,
@@ -386,7 +407,7 @@ class Message extends Equatable {
@JsonSerializable()
class TranslatedMessage extends Message {
/// Constructor used for json serialization
TranslatedMessage(this.i18n);
TranslatedMessage(this.i18n) : super();
/// Create a new instance from a json
factory TranslatedMessage.fromJson(Map<String, dynamic> json) =>
@@ -395,7 +416,7 @@ class TranslatedMessage extends Message {
);
/// A Map of
final Map<String, String> i18n;
final Map<String, String>? i18n;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -408,6 +429,5 @@ class TranslatedMessage extends Message {
@override
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$TranslatedMessageToJson(this),
topLevelFields,
);
}
@@ -6,64 +6,44 @@ part of 'message.dart';
// JsonSerializableGenerator
// **************************************************************************
Message _$MessageFromJson(Map json) {
Message _$MessageFromJson(Map<String, dynamic> json) {
return Message(
id: json['id'] as String,
text: json['text'] as String,
type: json['type'] as String,
attachments: (json['attachments'] as List)
?.map((e) => e == null
? null
: Attachment.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
mentionedUsers: (json['mentioned_users'] as List)
?.map((e) => e == null
? null
: User.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
silent: json['silent'] as bool,
shadowed: json['shadowed'] as bool,
reactionCounts: (json['reaction_counts'] as Map)?.map(
(k, e) => MapEntry(k as String, e as int),
id: json['id'] as String?,
text: json['text'] as String?,
type: json['type'] as String? ?? 'regular',
attachments: (json['attachments'] as List<dynamic>?)
?.map((e) => Attachment.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
mentionedUsers: (json['mentioned_users'] as List<dynamic>?)
?.map((e) => User.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
silent: json['silent'] as bool? ?? false,
shadowed: json['shadowed'] as bool? ?? false,
reactionCounts: (json['reaction_counts'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as int),
),
reactionScores: (json['reaction_scores'] as Map)?.map(
(k, e) => MapEntry(k as String, e as int),
reactionScores: (json['reaction_scores'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as int),
),
latestReactions: (json['latest_reactions'] as List)
?.map((e) => e == null
? null
: Reaction.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
ownReactions: (json['own_reactions'] as List)
?.map((e) => e == null
? null
: Reaction.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
parentId: json['parent_id'] as String,
latestReactions: (json['latest_reactions'] as List<dynamic>?)
?.map((e) => Reaction.fromJson(e as Map<String, dynamic>))
.toList(),
ownReactions: (json['own_reactions'] as List<dynamic>?)
?.map((e) => Reaction.fromJson(e as Map<String, dynamic>))
.toList(),
parentId: json['parent_id'] as String?,
quotedMessage: json['quoted_message'] == null
? null
: Message.fromJson((json['quoted_message'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
quotedMessageId: json['quoted_message_id'] as String,
replyCount: json['reply_count'] as int,
threadParticipants: (json['thread_participants'] as List)
?.map((e) => e == null
? null
: User.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
showInChannel: json['show_in_channel'] as bool,
command: json['command'] as String,
: Message.fromJson(json['quoted_message'] as Map<String, dynamic>),
quotedMessageId: json['quoted_message_id'] as String?,
replyCount: json['reply_count'] as int?,
threadParticipants: (json['thread_participants'] as List<dynamic>?)
?.map((e) => User.fromJson(e as Map<String, dynamic>))
.toList(),
showInChannel: json['show_in_channel'] as bool?,
command: json['command'] as String?,
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
@@ -72,10 +52,8 @@ Message _$MessageFromJson(Map json) {
: DateTime.parse(json['updated_at'] as String),
user: json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
pinned: json['pinned'] as bool,
: User.fromJson(json['user'] as Map<String, dynamic>),
pinned: json['pinned'] as bool? ?? false,
pinnedAt: json['pinned_at'] == null
? null
: DateTime.parse(json['pinned_at'] as String),
@@ -84,16 +62,12 @@ Message _$MessageFromJson(Map json) {
: DateTime.parse(json['pin_expires'] as String),
pinnedBy: json['pinned_by'] == null
? null
: User.fromJson((json['pinned_by'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
),
: User.fromJson(json['pinned_by'] as Map<String, dynamic>),
extraData: json['extra_data'] as Map<String, dynamic>? ?? {},
deletedAt: json['deleted_at'] == null
? null
: DateTime.parse(json['deleted_at'] as String),
skipPush: json['skip_push'] as bool,
skipPush: json['skip_push'] as bool? ?? false,
);
}
@@ -110,8 +84,7 @@ Map<String, dynamic> _$MessageToJson(Message instance) {
}
writeNotNull('type', readonly(instance.type));
writeNotNull(
'attachments', instance.attachments?.map((e) => e?.toJson())?.toList());
val['attachments'] = instance.attachments.map((e) => e.toJson()).toList();
val['mentioned_users'] = Serialization.userIds(instance.mentionedUsers);
writeNotNull('reaction_counts', readonly(instance.reactionCounts));
writeNotNull('reaction_scores', readonly(instance.reactionScores));
@@ -134,15 +107,15 @@ Map<String, dynamic> _$MessageToJson(Message instance) {
val['pinned_at'] = readonly(instance.pinnedAt);
val['pin_expires'] = instance.pinExpires?.toIso8601String();
val['pinned_by'] = readonly(instance.pinnedBy);
writeNotNull('extra_data', instance.extraData);
val['extra_data'] = instance.extraData;
writeNotNull('deleted_at', readonly(instance.deletedAt));
return val;
}
TranslatedMessage _$TranslatedMessageFromJson(Map json) {
TranslatedMessage _$TranslatedMessageFromJson(Map<String, dynamic> json) {
return TranslatedMessage(
(json['i18n'] as Map)?.map(
(k, e) => MapEntry(k as String, e as String),
(json['i18n'] as Map<String, dynamic>?)?.map(
(k, e) => MapEntry(k, e as String),
),
);
}
@@ -9,7 +9,12 @@ part 'mute.g.dart';
@JsonSerializable()
class Mute {
/// Constructor used for json serialization
Mute({this.user, this.channel, this.createdAt, this.updatedAt});
Mute({
required this.user,
required this.channel,
required this.createdAt,
required this.updatedAt,
});
/// Create a new instance from a json
factory Mute.fromJson(Map<String, dynamic> json) => _$MuteFromJson(json);
@@ -6,24 +6,12 @@ part of 'mute.dart';
// JsonSerializableGenerator
// **************************************************************************
Mute _$MuteFromJson(Map json) {
Mute _$MuteFromJson(Map<String, dynamic> json) {
return Mute(
user: json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
channel: json['channel'] == null
? null
: ChannelModel.fromJson((json['channel'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
updatedAt: json['updated_at'] == null
? null
: DateTime.parse(json['updated_at'] as String),
user: User.fromJson(json['user'] as Map<String, dynamic>),
channel: ChannelModel.fromJson(json['channel'] as Map<String, dynamic>),
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
);
}
@@ -12,19 +12,19 @@ part 'own_user.g.dart';
class OwnUser extends User {
/// Constructor used for json serialization
OwnUser({
this.devices,
this.mutes,
this.totalUnreadCount,
this.devices = const [],
this.mutes = const [],
this.totalUnreadCount = 0,
this.unreadChannels,
this.channelMutes,
String id,
String role,
DateTime createdAt,
DateTime updatedAt,
DateTime lastActive,
bool online,
Map<String, dynamic> extraData,
bool banned,
this.channelMutes = const [],
String id = '',
String role = '',
DateTime? createdAt,
DateTime? updatedAt,
DateTime? lastActive,
bool online = false,
Map<String, dynamic> extraData = const {},
bool banned = false,
}) : super(
id: id,
role: role,
@@ -41,24 +41,34 @@ class OwnUser extends User {
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
/// List of user devices
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: <Device>[])
final List<Device> devices;
/// List of users muted by the user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: <Mute>[])
final List<Mute> mutes;
/// List of users muted by the user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: <Mute>[])
final List<Mute> channelMutes;
/// Total unread messages by the user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false, toJson: Serialization.readOnly, defaultValue: 0)
final int totalUnreadCount;
/// Total unread channels by the user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final int unreadChannels;
final int? unreadChannels;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -74,5 +84,6 @@ class OwnUser extends User {
/// Serialize to json
@override
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$OwnUserToJson(this), topLevelFields);
_$OwnUserToJson(this),
);
}
@@ -6,33 +6,24 @@ part of 'own_user.dart';
// JsonSerializableGenerator
// **************************************************************************
OwnUser _$OwnUserFromJson(Map json) {
OwnUser _$OwnUserFromJson(Map<String, dynamic> json) {
return OwnUser(
devices: (json['devices'] as List)
?.map((e) => e == null
? null
: Device.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
mutes: (json['mutes'] as List)
?.map((e) => e == null
? null
: Mute.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
totalUnreadCount: json['total_unread_count'] as int,
unreadChannels: json['unread_channels'] as int,
channelMutes: (json['channel_mutes'] as List)
?.map((e) => e == null
? null
: Mute.fromJson((e as Map)?.map(
(k, e) => MapEntry(k as String, e),
)))
?.toList(),
devices: (json['devices'] as List<dynamic>?)
?.map((e) => Device.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
mutes: (json['mutes'] as List<dynamic>?)
?.map((e) => Mute.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
totalUnreadCount: json['total_unread_count'] as int? ?? 0,
unreadChannels: json['unread_channels'] as int?,
channelMutes: (json['channel_mutes'] as List<dynamic>?)
?.map((e) => Mute.fromJson(e as Map<String, dynamic>))
.toList() ??
[],
id: json['id'] as String,
role: json['role'] as String,
role: json['role'] as String? ?? '',
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
@@ -42,11 +33,9 @@ OwnUser _$OwnUserFromJson(Map json) {
lastActive: json['last_active'] == null
? null
: DateTime.parse(json['last_active'] as String),
online: json['online'] as bool,
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
),
banned: json['banned'] as bool,
online: json['online'] as bool? ?? false,
extraData: json['extra_data'] as Map<String, dynamic>,
banned: json['banned'] as bool? ?? false,
);
}
@@ -67,7 +56,7 @@ Map<String, dynamic> _$OwnUserToJson(OwnUser instance) {
writeNotNull('last_active', readonly(instance.lastActive));
writeNotNull('online', readonly(instance.online));
writeNotNull('banned', readonly(instance.banned));
writeNotNull('extra_data', instance.extraData);
val['extra_data'] = instance.extraData;
writeNotNull('devices', readonly(instance.devices));
writeNotNull('mutes', readonly(instance.mutes));
writeNotNull('channel_mutes', readonly(instance.channelMutes));
@@ -10,20 +10,24 @@ class Reaction {
/// Constructor used for json serialization
Reaction({
this.messageId,
this.createdAt,
this.type,
DateTime? createdAt,
required this.type,
this.user,
String userId,
this.score,
String? userId,
this.score = 0,
this.extraData,
}) : userId = userId ?? user?.id;
}) : userId = userId ?? user?.id,
createdAt = createdAt ?? DateTime.now();
/// Create a new instance from a json
factory Reaction.fromJson(Map<String, dynamic> json) => _$ReactionFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
factory Reaction.fromJson(Map<String, dynamic> json) =>
_$ReactionFromJson(Serialization.moveToExtraDataFromRoot(
json,
topLevelFields,
));
/// The messageId to which the reaction belongs
final String messageId;
final String? messageId;
/// The type of the reaction
final String type;
@@ -34,18 +38,19 @@ class Reaction {
/// The user that sent the reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final User user;
final User? user;
/// The score of the reaction (ie. number of reactions sent)
@JsonKey(defaultValue: 0)
final int score;
/// The userId that sent the reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String userId;
final String? userId;
/// Reaction custom extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
final Map<String, dynamic>? extraData;
/// Map of custom user extraData
static const topLevelFields = [
@@ -59,17 +64,18 @@ class Reaction {
/// Serialize to json
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$ReactionToJson(this), topLevelFields);
_$ReactionToJson(this),
);
/// Creates a copy of [Reaction] with specified attributes overridden.
Reaction copyWith({
String messageId,
DateTime createdAt,
String type,
User user,
String userId,
int score,
Map<String, dynamic> extraData,
String? messageId,
DateTime? createdAt,
String? type,
User? user,
String? userId,
int? score,
Map<String, dynamic>? extraData,
}) =>
Reaction(
messageId: messageId ?? this.messageId,
@@ -83,16 +89,13 @@ class Reaction {
/// Returns a new [Reaction] that is a combination of this reaction and the
/// given [other] reaction.
Reaction merge(Reaction other) {
if (other == null) return this;
return copyWith(
messageId: other.messageId,
createdAt: other.createdAt,
type: other.type,
user: other.user,
userId: other.userId,
score: other.score,
extraData: other.extraData,
);
}
Reaction merge(Reaction other) => copyWith(
messageId: other.messageId,
createdAt: other.createdAt,
type: other.type,
user: other.user,
userId: other.userId,
score: other.score,
extraData: other.extraData,
);
}
@@ -6,23 +6,19 @@ part of 'reaction.dart';
// JsonSerializableGenerator
// **************************************************************************
Reaction _$ReactionFromJson(Map json) {
Reaction _$ReactionFromJson(Map<String, dynamic> json) {
return Reaction(
messageId: json['message_id'] as String,
messageId: json['message_id'] as String?,
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
type: json['type'] as String,
user: json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
userId: json['user_id'] as String,
score: json['score'] as int,
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
),
: User.fromJson(json['user'] as Map<String, dynamic>),
userId: json['user_id'] as String?,
score: json['score'] as int? ?? 0,
extraData: json['extra_data'] as Map<String, dynamic>?,
);
}
@@ -8,9 +8,9 @@ part 'read.g.dart';
class Read {
/// Constructor used for json serialization
Read({
this.lastRead,
this.user,
this.unreadMessages,
required this.lastRead,
required this.user,
this.unreadMessages = 0,
});
/// Create a new instance from a json
@@ -23,6 +23,7 @@ class Read {
final User user;
/// Number of unread messages
@JsonKey(defaultValue: 0)
final int unreadMessages;
/// Serialize to json
@@ -30,9 +31,9 @@ class Read {
/// Creates a copy of [Read] with specified attributes overridden.
Read copyWith({
DateTime lastRead,
User user,
int unreadMessages,
DateTime? lastRead,
User? user,
int? unreadMessages,
}) =>
Read(
lastRead: lastRead ?? this.lastRead,
@@ -6,22 +6,16 @@ part of 'read.dart';
// JsonSerializableGenerator
// **************************************************************************
Read _$ReadFromJson(Map json) {
Read _$ReadFromJson(Map<String, dynamic> json) {
return Read(
lastRead: json['last_read'] == null
? null
: DateTime.parse(json['last_read'] as String),
user: json['user'] == null
? null
: User.fromJson((json['user'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
)),
unreadMessages: json['unread_messages'] as int,
lastRead: DateTime.parse(json['last_read'] as String),
user: User.fromJson(json['user'] as Map<String, dynamic>),
unreadMessages: json['unread_messages'] as int? ?? 0,
);
}
Map<String, dynamic> _$ReadToJson(Read instance) => <String, dynamic>{
'last_read': instance.lastRead?.toIso8601String(),
'user': instance.user?.toJson(),
'last_read': instance.lastRead.toIso8601String(),
'user': instance.user.toJson(),
'unread_messages': instance.unreadMessages,
};
@@ -10,16 +10,14 @@ class Serialization {
static const Function readOnly = readonly;
/// List of users to list of userIds
static List<String> userIds(List<User> users) =>
users?.map((u) => u.id)?.toList();
static List<String>? userIds(List<User>? users) =>
users?.map((u) => u.id).toList();
/// Takes unknown json keys and puts them in the `extra_data` key
static Map<String, dynamic> moveToExtraDataFromRoot(
Map<String, dynamic> json,
List<String> topLevelFields,
) {
if (json == null) return null;
final jsonClone = Map<String, dynamic>.from(json);
final extraDataMap = Map<String, dynamic>.from(json)
@@ -38,7 +36,6 @@ class Serialization {
/// the json map
static Map<String, dynamic> moveFromExtraDataToRoot(
Map<String, dynamic> json,
List<String> topLevelFields,
) {
final jsonClone = Map<String, dynamic>.from(json);
return jsonClone
+43 -35
View File
@@ -8,16 +8,17 @@ part 'user.g.dart';
class User {
/// Constructor used for json serialization
User({
this.id,
this.role,
this.createdAt,
this.updatedAt,
this.id = '',
this.role = '',
DateTime? createdAt,
DateTime? updatedAt,
this.lastActive,
this.online,
this.extraData,
this.banned,
this.teams,
});
this.online = false,
this.extraData = const {},
this.banned = false,
this.teams = const [],
}) : createdAt = createdAt ?? DateTime.now(),
updatedAt = updatedAt ?? DateTime.now();
/// Create a new instance from a json
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(
@@ -26,14 +27,14 @@ class User {
/// Use this named constructor to create a new user instance
User.init(
this.id, {
this.online,
this.extraData,
}) : createdAt = null,
updatedAt = null,
lastActive = null,
banned = null,
teams = null,
role = null;
this.online = false,
this.extraData = const {},
required this.createdAt,
required this.updatedAt,
this.teams = const [],
required this.role,
}) : lastActive = null,
banned = false;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -52,11 +53,15 @@ class User {
final String id;
/// User role
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false, toJson: Serialization.readOnly, defaultValue: '')
final String role;
/// User role
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false,
toJson: Serialization.readOnly,
defaultValue: <String>[])
final List<String> teams;
/// Date of user creation
@@ -69,14 +74,16 @@ class User {
/// Date of last user connection
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime lastActive;
final DateTime? lastActive;
/// True if user is online
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false, toJson: Serialization.readOnly, defaultValue: false)
final bool online;
/// True if user is banned from the chat
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
@JsonKey(
includeIfNull: false, toJson: Serialization.readOnly, defaultValue: false)
final bool banned;
/// Map of custom user extraData
@@ -87,8 +94,8 @@ class User {
int get hashCode => id.hashCode;
/// Shortcut for user name
String get name =>
(extraData?.containsKey('name') == true && extraData['name'] != '')
String? get name =>
(extraData.containsKey('name') == true && extraData['name'] != '')
? extraData['name']
: id;
@@ -98,20 +105,21 @@ class User {
other is User && runtimeType == other.runtimeType && id == other.id;
/// Serialize to json
Map<String, dynamic> toJson() =>
Serialization.moveFromExtraDataToRoot(_$UserToJson(this), topLevelFields);
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
_$UserToJson(this),
);
/// Creates a copy of [User] with specified attributes overridden.
User copyWith({
String id,
String role,
DateTime createdAt,
DateTime updatedAt,
DateTime lastActive,
bool online,
Map<String, dynamic> extraData,
bool banned,
List<String> teams,
String? id,
String? role,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? lastActive,
bool? online,
Map<String, dynamic>? extraData,
bool? banned,
List<String>? teams,
}) =>
User(
id: id ?? this.id,
@@ -6,10 +6,10 @@ part of 'user.dart';
// JsonSerializableGenerator
// **************************************************************************
User _$UserFromJson(Map json) {
User _$UserFromJson(Map<String, dynamic> json) {
return User(
id: json['id'] as String,
role: json['role'] as String,
role: json['role'] as String? ?? '',
createdAt: json['created_at'] == null
? null
: DateTime.parse(json['created_at'] as String),
@@ -19,12 +19,12 @@ User _$UserFromJson(Map json) {
lastActive: json['last_active'] == null
? null
: DateTime.parse(json['last_active'] as String),
online: json['online'] as bool,
extraData: (json['extra_data'] as Map)?.map(
(k, e) => MapEntry(k as String, e),
),
banned: json['banned'] as bool,
teams: (json['teams'] as List)?.map((e) => e as String)?.toList(),
online: json['online'] as bool? ?? false,
extraData: json['extra_data'] as Map<String, dynamic>,
banned: json['banned'] as bool? ?? false,
teams:
(json['teams'] as List<dynamic>?)?.map((e) => e as String).toList() ??
[],
);
}
@@ -46,6 +46,6 @@ Map<String, dynamic> _$UserToJson(User instance) {
writeNotNull('last_active', readonly(instance.lastActive));
writeNotNull('online', readonly(instance.online));
writeNotNull('banned', readonly(instance.banned));
writeNotNull('extra_data', instance.extraData);
val['extra_data'] = instance.extraData;
return val;
}
+10 -10
View File
@@ -6,26 +6,26 @@ repository: https://github.com/GetStream/stream-chat-flutter
issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues
environment:
sdk: ">=2.7.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
async: ^2.5.0
collection: ^1.15.0
dio: ">=4.0.0-prev3 <4.0.0"
dio: ^4.0.0
equatable: ^2.0.0
freezed_annotation: ^0.14.0
http_parser: ^4.0.0
json_annotation: ^4.0.0
logging: ^1.0.0
json_annotation: ^4.0.1
logging: ^1.0.1
meta: ^1.3.0
mime: ^1.0.0
rxdart: ^0.26.0
uuid: ^3.0.0
uuid: ^3.0.4
web_socket_channel: ^2.0.0
dev_dependencies:
build_runner: ^1.10.0
freezed: ^0.14.0
json_serializable: ^4.0.0
mocktail: ^0.1.0
test: ^1.16.0
build_runner: ^1.12.2
freezed: ^0.14.1+2
json_serializable: ^4.1.0
mocktail: ^0.1.1
test: ^1.16.8
@@ -1,3 +1,5 @@
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:dio/native_imp.dart';
import 'package:mocktail/mocktail.dart';
@@ -6,11 +8,10 @@ import 'package:stream_chat/src/client.dart';
import 'package:stream_chat/src/event_type.dart';
import 'package:stream_chat/src/models/event.dart';
import 'package:stream_chat/src/models/message.dart';
import 'package:stream_chat/src/models/reaction.dart';
import 'package:stream_chat/src/models/own_user.dart';
import 'package:test/test.dart';
import 'package:stream_chat/src/models/reaction.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart';
class MockDio extends Mock implements DioForNative {}
@@ -37,6 +38,17 @@ void main() {
final channelClient = client.channel('messaging', id: 'testid');
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(
() => mockDio.post<String>(
'/channels/messaging/testid/message',
@@ -44,7 +56,7 @@ void main() {
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({'message': message}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
@@ -76,7 +88,7 @@ void main() {
any(),
data: any(named: 'data'),
)).thenAnswer((_) async => Response(
data: '{}',
data: jsonEncode(ChannelState()),
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
@@ -226,6 +238,17 @@ void main() {
);
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))
.thenAnswer((_) async => SendFileResponse());
@@ -254,6 +277,17 @@ void main() {
);
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))
.thenAnswer((_) async => SendImageResponse());
@@ -277,6 +311,17 @@ void main() {
final channelClient = client.channel('messaging', id: 'testid');
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(
() => mockDio.delete<String>(
'/channels/messaging/testid/file',
@@ -310,6 +355,17 @@ void main() {
final channelClient = client.channel('messaging', id: 'testid');
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(
() => mockDio.delete<String>(
'/channels/messaging/testid/image',
@@ -356,6 +412,17 @@ void main() {
final channelClient = client.channel('messaging', id: 'testid');
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(
() => mockDio.post<String>(
'/messages/${message.id}',
@@ -363,7 +430,7 @@ void main() {
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({'message': message}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
@@ -390,6 +457,17 @@ void main() {
final channelClient = client.channel('messaging', id: 'testid');
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(
() => mockDio.post<String>(
'/messages/${message.id}',
@@ -397,7 +475,7 @@ void main() {
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({'message': message}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
@@ -564,14 +642,32 @@ void main() {
'api-key',
httpClient: mockDio,
tokenProvider: (_) async => '',
)..state.user = OwnUser(id: 'test-id');
);
final channelClient = client.channel('messaging', id: 'testid');
final user = OwnUser(id: 'test-id');
client.state.user = user;
final message = Message(id: 'messageid');
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(
() => mockDio.post<String>(
'/messages/messageid/reaction',
'/messages/${message.id}/reaction',
data: {
'reaction': {
'type': reactionType,
@@ -581,20 +677,17 @@ void main() {
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({
'message': message,
'reaction': reaction,
}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
);
await channelClient.sendReaction(
Message(
id: 'messageid',
reactionCounts: const <String, int>{},
reactionScores: const <String, int>{},
latestReactions: const <Reaction>[],
ownReactions: const <Reaction>[],
),
message,
reactionType,
);
@@ -617,7 +710,9 @@ void main() {
'api-key',
httpClient: mockDio,
tokenProvider: (_) async => '',
)..state.user = OwnUser(id: 'test-id');
);
client.state.user = OwnUser(id: 'test-id');
final channelClient = client.channel('messaging', id: 'testid');
@@ -634,12 +729,14 @@ void main() {
await channelClient.deleteReaction(
Message(
id: 'messageid',
reactionCounts: const <String, int>{},
reactionScores: const <String, int>{},
latestReactions: const <Reaction>[],
ownReactions: const <Reaction>[],
),
Reaction(type: 'test'),
Reaction(
type: 'test',
createdAt: DateTime.now(),
user: User(
id: client.state.user?.id ?? '',
),
),
);
verify(() =>
@@ -694,26 +791,44 @@ void main() {
tokenProvider: (_) async => '',
);
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');
when(
() => mockDio.post<String>(
'/channels/messaging/testid',
data: {'add_members': members, 'message': message.toJson()},
data: {'add_members': memberIds, 'message': message.toJson()},
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({
'members': members,
'message': message,
'channel': channelModel,
}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
);
await channelClient.addMembers(members, message);
await channelClient.addMembers(memberIds, message);
verify(() => mockDio.post<String>('/channels/messaging/testid',
data: {'add_members': members, 'message': message.toJson()}))
data: {'add_members': memberIds, 'message': message.toJson()}))
.called(1);
});
@@ -729,6 +844,19 @@ void main() {
tokenProvider: (_) async => '',
);
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');
when(
@@ -738,7 +866,10 @@ void main() {
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({
'message': message,
'channel': channelModel,
}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
@@ -1069,8 +1200,8 @@ void main() {
verify(() => mockDio.post<String>('/channels/messaging/query',
data: options)).called(1);
expect(channelClient.id, response.channel.id);
expect(channelClient.cid, response.channel.cid);
expect(channelClient.id, response.channel?.id);
expect(channelClient.cid, response.channel?.cid);
});
test('with id', () async {
@@ -1706,8 +1837,8 @@ void main() {
verify(() => mockDio.post<String>('/channels/messaging/query',
data: options)).called(1);
expect(channelClient.id, response.channel.id);
expect(channelClient.cid, response.channel.cid);
expect(channelClient.id, response.channel?.id);
expect(channelClient.cid, response.channel?.cid);
});
test('watch', () async {
@@ -2027,8 +2158,8 @@ void main() {
verify(() => mockDio.post<String>('/channels/messaging/query',
data: options)).called(1);
expect(channelClient.id, response.channel.id);
expect(channelClient.cid, response.channel.cid);
expect(channelClient.id, response.channel?.id);
expect(channelClient.cid, response.channel?.cid);
});
test('stopWatching', () async {
@@ -2077,6 +2208,19 @@ void main() {
tokenProvider: (_) async => '',
);
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');
when(
@@ -2089,7 +2233,10 @@ void main() {
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({
'channel': channelModel,
'message': message,
}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
@@ -2176,6 +2323,19 @@ void main() {
tokenProvider: (_) async => '',
);
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');
when(
@@ -2185,7 +2345,10 @@ void main() {
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({
'message': message,
'channel': channelModel,
}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
@@ -2210,26 +2373,45 @@ void main() {
tokenProvider: (_) async => '',
);
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');
when(
() => mockDio.post<String>(
'/channels/messaging/testid',
data: {'invites': members, 'message': message.toJson()},
data: {'invites': memberIds, 'message': message.toJson()},
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({
'members': members,
'message': message,
'channel': channelModel,
}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
);
await channelClient.inviteMembers(members, message);
await channelClient.inviteMembers(memberIds, message);
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 {
@@ -2244,27 +2426,46 @@ void main() {
tokenProvider: (_) async => '',
);
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');
when(
() => mockDio.post<String>(
'/channels/messaging/testid',
data: {'remove_members': members, 'message': message.toJson()},
data: {'remove_members': memberIds, 'message': message.toJson()},
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({
'members': members,
'message': message,
'channel': channelModel,
}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
);
await channelClient.removeMembers(members, message);
await channelClient.removeMembers(memberIds, message);
verify(() => mockDio.post<String>('/channels/messaging/testid',
data: {'remove_members': members, 'message': message.toJson()}))
.called(1);
verify(() => mockDio.post<String>('/channels/messaging/testid', data: {
'remove_members': memberIds,
'message': message.toJson()
})).called(1);
});
test('hide', () async {
@@ -1,5 +1,5 @@
import 'package:test/test.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart';
void main() {
group('src/api/requests', () {
@@ -12,7 +12,8 @@ void main() {
test('PaginationParams', () {
const option = PaginationParams();
final j = option.toJson();
expect(j, {'limit': 10, 'offset': 0});
expect(j, containsPair('limit', 10));
expect(j, containsPair('offset', 0));
});
});
}
@@ -12,7 +12,8 @@ import 'package:stream_chat/stream_chat.dart';
void main() {
group('src/api/responses', () {
test('QueryChannelsResponse', () {
const jsonExample = r'''{
const jsonExample = r'''
{
"channels": [
{
"channel": {
@@ -3432,7 +3433,8 @@ void main() {
});
test('SendReactionResponse', () {
const jsonExample = r'''{"message": {
const jsonExample = r'''
{"message": {
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3481,7 +3483,8 @@ void main() {
});
test('UpdateUsersResponse', () {
const jsonExample = '''{"users": {"bbb19d9a-ee50-45bc-84e5-0584e79d0c9e":{
const jsonExample = '''
{"users": {"bbb19d9a-ee50-45bc-84e5-0584e79d0c9e":{
"id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e",
"role": "user",
"created_at": "2020-01-28T22:17:30.826259Z",
@@ -3505,7 +3508,8 @@ void main() {
});
test('GetMessagesByIdResponse', () {
const jsonExample = r'''{"messages":[{
const jsonExample = r'''
{"messages":[{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3536,7 +3540,8 @@ void main() {
});
test('SendActionResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3566,7 +3571,8 @@ void main() {
});
test('UpdateMessageResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3596,7 +3602,8 @@ void main() {
});
test('SendMessageResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3626,7 +3633,8 @@ void main() {
});
test('GetMessageResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3656,7 +3664,8 @@ void main() {
});
test('UpdateChannelResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3769,7 +3778,8 @@ void main() {
});
test('InviteMembersResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3882,7 +3892,8 @@ void main() {
});
test('RemoveMembersResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -3995,7 +4006,8 @@ void main() {
});
test('AddMembersResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -4108,7 +4120,8 @@ void main() {
});
test('AcceptInviteResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -4221,7 +4234,8 @@ void main() {
});
test('RejectInviteResponse', () {
const jsonExample = r'''{"message":{
const jsonExample = r'''
{"message":{
"id": "c6076f11-7768-4a04-bdf2-c43dddd6d666",
"text": "What we don't know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.",
"html": "\u003cp\u003eWhat we dont know for sure is whether or not a step-daughter of the bear is assumed to be a farci hourglass.\u003c/p\u003e\n",
@@ -12,12 +12,10 @@ import 'package:web_socket_channel/web_socket_channel.dart';
class Functions {
WebSocketChannel connectFunc(
String url, {
Iterable<String> protocols,
Map<String, dynamic> headers,
Duration pingInterval,
String? url, {
Iterable<String>? protocols,
}) =>
null;
WebSocketChannel.connect(Uri());
void handleFunc(Event event) {}
}
@@ -37,7 +35,7 @@ void main() {
});
test('should connect with correct parameters', () async {
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
user: User(id: 'testid'),
@@ -75,7 +73,7 @@ void main() {
test('should connect with correct parameters and handle events', () async {
final handleFunc = MockFunctions().handleFunc;
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
user: User(id: 'testid'),
@@ -111,7 +109,7 @@ void main() {
test('should close correctly the controller', () async {
final handleFunc = MockFunctions().handleFunc;
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
user: User(id: 'testid'),
@@ -126,8 +124,10 @@ void main() {
const computedUrl =
'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D';
final mockWSSink = MockWSSink();
when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink);
when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null);
when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink());
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
final connect = ws.connect().then((_) {
@@ -148,7 +148,7 @@ void main() {
test('should close correctly the controller while connecting', () async {
final handleFunc = MockFunctions().handleFunc;
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
@@ -167,8 +167,10 @@ void main() {
const computedUrl =
'wss://baseurl/connect?test=true&json=%7B%22payload%22%3A%22test%22%2C%22user_details%22%3A%7B%22id%22%3A%22testid%22%7D%7D';
final mockWSSink = MockWSSink();
when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink);
when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null);
when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink());
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
ws.connect();
@@ -183,7 +185,7 @@ void main() {
test('should run correctly health check', () async {
final handleFunc = MockFunctions().handleFunc;
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
user: User(id: 'testid'),
@@ -201,7 +203,8 @@ void main() {
when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
when(() => mockWSChannel.sink).thenReturn(mockWSSink);
when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink);
when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null);
final timer = Timer.periodic(
const Duration(milliseconds: 1000),
@@ -227,7 +230,7 @@ void main() {
test('should run correctly reconnection check', () async {
final handleFunc = MockFunctions().handleFunc;
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
Logger.root.level = Level.ALL;
final ws = WebSocket(
baseUrl: 'baseurl',
@@ -247,7 +250,8 @@ void main() {
when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
when(() => mockWSChannel.sink).thenReturn(mockWSSink);
when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink);
when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null);
final connect = ws.connect().then((_) {
streamController.sink.add('{}');
@@ -272,7 +276,7 @@ void main() {
test('should close correctly the controller', () async {
final handleFunc = MockFunctions().handleFunc;
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
user: User(id: 'testid'),
@@ -290,7 +294,8 @@ void main() {
when(() => connectFunc(computedUrl)).thenAnswer((_) => mockWSChannel);
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
when(() => mockWSChannel.sink).thenReturn(mockWSSink);
when(() => mockWSChannel.sink).thenAnswer((_) => mockWSSink);
when(() => mockWSSink.close(any(), any())).thenAnswer((_) async => null);
final connect = ws.connect().then((_) {
streamController.sink.add('{}');
@@ -309,7 +314,7 @@ void main() {
});
test('should throw an error', () async {
final ConnectWebSocket connectFunc = MockFunctions().connectFunc;
final connectFunc = MockFunctions().connectFunc;
final ws = WebSocket(
baseUrl: 'baseurl',
user: User(id: 'testid'),
+13 -9
View File
@@ -9,9 +9,9 @@ import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/src/api/requests.dart';
import 'package:stream_chat/src/client.dart';
import 'package:stream_chat/src/exceptions.dart';
import 'package:stream_chat/src/models/channel_model.dart';
import 'package:stream_chat/src/models/message.dart';
import 'package:stream_chat/src/models/user.dart';
import 'package:stream_chat/src/models/channel_model.dart';
import 'package:test/test.dart';
class MockDio extends Mock implements DioForNative {}
@@ -21,7 +21,7 @@ class FakeRequestOptions extends Fake implements RequestOptions {}
class MockHttpClientAdapter extends Mock implements HttpClientAdapter {}
class Functions {
Future<String> tokenProvider(String userId) => null;
Future<String> tokenProvider(String userId) async => '';
}
class MockFunctions extends Mock implements Functions {}
@@ -155,7 +155,9 @@ void main() {
'sort': sortOptions,
}
..addAll(options)
..addAll(paginationParams.toJson())),
..addAll(paginationParams
.toJson()
.map((key, value) => MapEntry(key, value as Object)))),
};
when(
@@ -735,7 +737,9 @@ void main() {
when(() => mockDio.interceptors).thenReturn(Interceptors());
final client = StreamChatClient('api-key', httpClient: mockDio);
final message = Message(id: 'test', updatedAt: DateTime.now());
final message = Message(
id: 'test',
);
when(
() => mockDio.post<String>(
@@ -744,7 +748,7 @@ void main() {
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({'message': message}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
@@ -789,7 +793,7 @@ void main() {
when(() => mockDio.get<String>('/messages/$messageId')).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({'message': Message(id: messageId)}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
@@ -1111,7 +1115,7 @@ void main() {
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({'message': message}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
@@ -1133,7 +1137,7 @@ void main() {
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({'message': message}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
@@ -1173,7 +1177,7 @@ void main() {
),
).thenAnswer(
(_) async => Response(
data: '{}',
data: jsonEncode({'channel': ChannelModel(cid: 'messaging:test')}),
statusCode: 200,
requestOptions: FakeRequestOptions(),
),
@@ -5,7 +5,8 @@ import 'package:stream_chat/src/models/action.dart';
void main() {
group('src/models/action', () {
const jsonExample = '''{
const jsonExample = '''
{
"name": "name",
"style": "style",
"text": "text",
@@ -1,12 +1,13 @@
import 'dart:convert';
import 'package:stream_chat/src/models/attachment.dart';
import 'package:stream_chat/src/models/action.dart';
import 'package:stream_chat/src/models/action.dart';
import 'package:stream_chat/src/models/attachment.dart';
import 'package:test/test.dart';
void main() {
group('src/models/attachment', () {
const jsonExample = '''{
const jsonExample = '''
{
"type": "giphy",
"title": "awesome",
"title_link": "https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti",
@@ -66,7 +67,8 @@ void main() {
'type': 'image',
'title': 'soo',
'title_link':
'https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti'
'https://giphy.com/gifs/nrkp3-dance-happy-3o7TKnCdBx5cMg0qti',
'actions': [],
},
);
});
@@ -1,16 +1,17 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:stream_chat/src/models/channel_config.dart';
import 'package:stream_chat/src/models/channel_state.dart';
import 'package:stream_chat/src/models/command.dart';
import 'package:stream_chat/src/models/message.dart';
import 'package:stream_chat/src/models/user.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart';
void main() {
group('src/models/channel_state', () {
const jsonExample = '''{
const jsonExample = '''
{
"channel": {
"id": "dev",
"type": "team",
@@ -844,26 +845,26 @@ void main() {
test('should parse json correctly', () {
final channelState = ChannelState.fromJson(json.decode(jsonExample));
expect(channelState.channel.cid, 'team:dev');
expect(channelState.channel.id, 'dev');
expect(channelState.channel.team, 'test');
expect(channelState.channel.type, 'team');
expect(channelState.channel.config, isA<ChannelConfig>());
expect(channelState.channel.config, isNotNull);
expect(channelState.channel.config.commands, hasLength(1));
expect(channelState.channel.config.commands[0], isA<Command>());
expect(channelState.channel.lastMessageAt,
expect(channelState.channel?.cid, 'team:dev');
expect(channelState.channel?.id, 'dev');
expect(channelState.channel?.team, 'test');
expect(channelState.channel?.type, 'team');
expect(channelState.channel?.config, isA<ChannelConfig>());
expect(channelState.channel?.config, isNotNull);
expect(channelState.channel?.config.commands, hasLength(1));
expect(channelState.channel?.config.commands[0], isA<Command>());
expect(channelState.channel?.lastMessageAt,
DateTime.parse('2020-01-30T13:43:41.062362Z'));
expect(channelState.channel.createdAt,
expect(channelState.channel?.createdAt,
DateTime.parse('2019-04-03T18:43:33.213373Z'));
expect(channelState.channel.updatedAt,
expect(channelState.channel?.updatedAt,
DateTime.parse('2019-04-03T18:43:33.213374Z'));
expect(channelState.channel.createdBy, isA<User>());
expect(channelState.channel.frozen, true);
expect(channelState.channel.extraData['example'], 1);
expect(channelState.channel.extraData['name'], '#dev');
expect(channelState.channel?.createdBy, isA<User>());
expect(channelState.channel?.frozen, true);
expect(channelState.channel?.extraData!['example'], 1);
expect(channelState.channel?.extraData!['name'], '#dev');
expect(
channelState.channel.extraData['image'],
channelState.channel?.extraData!['image'],
'https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png',
);
expect(channelState.messages, hasLength(25));
@@ -888,8 +889,8 @@ void main() {
"image": "https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png",
"example": 1
},
"watchers": null,
"read": null,
"watchers": [],
"read": [],
"messages": [
{
"id": "dry-meadow-0-2b73cc8b-cd86-4a01-8d40-bd82ad07a030",
@@ -901,7 +902,7 @@ void main() {
"show_in_channel": null,
"mentioned_users": [],
"status": "SENT",
"skip_push": null,
"skip_push": false,
"silent": false,
"pinned": false,
"pinned_at": null,
@@ -918,7 +919,7 @@ void main() {
"show_in_channel": null,
"mentioned_users": [],
"status": "SENT",
"skip_push": null,
"skip_push": false,
"silent": false,
"pinned": false,
"pinned_at": null,
@@ -928,7 +929,7 @@ void main() {
{
"id": "dry-meadow-0-53e6299f-9b97-4a9c-a27e-7e2dde49b7e0",
"text": "test message",
"skip_push": null,
"skip_push": false,
"attachments": [],
"parent_id": null,
"quoted_message": null,
@@ -951,7 +952,7 @@ void main() {
"quoted_message_id": null,
"show_in_channel": null,
"mentioned_users": [],
"skip_push": null,
"skip_push": false,
"status": "SENT",
"silent": false,
"pinned": false,
@@ -963,7 +964,7 @@ void main() {
"id": "dry-meadow-0-64d7970f-ede8-4b31-9738-1bc1756d2bfe",
"text": "test",
"attachments": [],
"skip_push": null,
"skip_push": false,
"parent_id": null,
"quoted_message": null,
"quoted_message_id": null,
@@ -981,7 +982,7 @@ void main() {
"text": "hi",
"attachments": [],
"parent_id": null,
"skip_push": null,
"skip_push": false,
"quoted_message": null,
"quoted_message_id": null,
"show_in_channel": null,
@@ -999,7 +1000,7 @@ void main() {
"attachments": [],
"parent_id": null,
"quoted_message": null,
"skip_push": null,
"skip_push": false,
"quoted_message_id": null,
"show_in_channel": null,
"mentioned_users": [],
@@ -1022,7 +1023,7 @@ void main() {
"status": "SENT",
"silent": false,
"pinned": false,
"skip_push": null,
"skip_push": false,
"pinned_at": null,
"pin_expires": null,
"pinned_by": null
@@ -1040,7 +1041,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1052,7 +1053,7 @@ void main() {
"quoted_message": null,
"quoted_message_id": null,
"show_in_channel": null,
"skip_push": null,
"skip_push": false,
"mentioned_users": [],
"status": "SENT",
"silent": false,
@@ -1070,7 +1071,7 @@ void main() {
"quoted_message_id": null,
"show_in_channel": null,
"mentioned_users": [],
"skip_push": null,
"skip_push": false,
"status": "SENT",
"silent": false,
"pinned": false,
@@ -1089,7 +1090,7 @@ void main() {
"mentioned_users": [],
"status": "SENT",
"silent": false,
"skip_push": null,
"skip_push": false,
"pinned": false,
"pinned_at": null,
"pin_expires": null,
@@ -1099,7 +1100,7 @@ void main() {
"id": "icy-recipe-7-935c396e-ddf8-4a9a-951c-0a12fa5bf055",
"text": "what are you doing?",
"attachments": [],
"skip_push": null,
"skip_push": false,
"parent_id": null,
"quoted_message": null,
"quoted_message_id": null,
@@ -1117,7 +1118,7 @@ void main() {
"text": "👍",
"attachments": [],
"parent_id": null,
"skip_push": null,
"skip_push": false,
"quoted_message": null,
"quoted_message_id": null,
"show_in_channel": null,
@@ -1133,7 +1134,7 @@ void main() {
"id": "snowy-credit-3-3e0c1a0d-d22f-42ee-b2a1-f9f49477bf21",
"text": "sdasas",
"attachments": [],
"skip_push": null,
"skip_push": false,
"parent_id": null,
"quoted_message": null,
"quoted_message_id": null,
@@ -1154,7 +1155,7 @@ void main() {
"quoted_message": null,
"quoted_message_id": null,
"show_in_channel": null,
"skip_push": null,
"skip_push": false,
"mentioned_users": [],
"status": "SENT",
"silent": false,
@@ -1167,7 +1168,7 @@ void main() {
"id": "snowy-credit-3-cfaf0b46-1daa-49c5-947c-b16d6697487d",
"text": "nhisagdhsadz",
"attachments": [],
"skip_push": null,
"skip_push": false,
"parent_id": null,
"quoted_message": null,
"quoted_message_id": null,
@@ -1186,7 +1187,7 @@ void main() {
"attachments": [],
"parent_id": null,
"quoted_message": null,
"skip_push": null,
"skip_push": false,
"quoted_message_id": null,
"show_in_channel": null,
"mentioned_users": [],
@@ -1203,7 +1204,7 @@ void main() {
"attachments": [],
"parent_id": null,
"quoted_message": null,
"skip_push": null,
"skip_push": false,
"quoted_message_id": null,
"show_in_channel": null,
"mentioned_users": [],
@@ -1211,7 +1212,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1228,7 +1229,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1245,7 +1246,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1262,7 +1263,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1279,7 +1280,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1296,7 +1297,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
},
@@ -1313,7 +1314,7 @@ void main() {
"silent": false,
"pinned": false,
"pinned_at": null,
"skip_push": null,
"skip_push": false,
"pin_expires": null,
"pinned_by": null
}
@@ -1329,10 +1330,10 @@ void main() {
members: [],
messages:
(j['messages'] as List).map((m) => Message.fromJson(m)).toList(),
read: null,
read: [],
watcherCount: 5,
pinnedMessages: [],
watchers: null,
watchers: [],
);
expect(
@@ -1,7 +1,7 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:stream_chat/src/models/channel_model.dart';
import 'package:test/test.dart';
void main() {
group('src/models/channel', () {
@@ -9,7 +9,7 @@ void main() {
{
"id": "test",
"type": "livestream",
"cid": "test:livestream",
"cid": "livestream:test",
"cats": true,
"fruit": ["bananas", "apples"]
}
@@ -19,9 +19,9 @@ void main() {
final channel = ChannelModel.fromJson(json.decode(jsonExample));
expect(channel.id, equals('test'));
expect(channel.type, equals('livestream'));
expect(channel.cid, equals('test:livestream'));
expect(channel.extraData['cats'], equals(true));
expect(channel.extraData['fruit'], equals(['bananas', 'apples']));
expect(channel.cid, equals('livestream:test'));
expect(channel.extraData!['cats'], equals(true));
expect(channel.extraData!['fruit'], equals(['bananas', 'apples']));
});
test('should serialize to json correctly', () {
@@ -34,7 +34,7 @@ void main() {
expect(
channel.toJson(),
{'id': 'id', 'type': 'type', 'name': 'cool'},
{'id': 'id', 'type': 'type', 'frozen': false, 'name': 'cool'},
);
});
@@ -44,7 +44,6 @@ void main() {
id: 'id',
cid: 'a:a',
extraData: {'name': 'cool'},
frozen: false,
);
expect(
@@ -5,7 +5,8 @@ import 'package:stream_chat/src/models/device.dart';
void main() {
group('src/models/device', () {
const jsonExample = '''{
const jsonExample = '''
{
"id": "device-id",
"push_provider": "push-provider"
}''';
@@ -1,9 +1,9 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:stream_chat/src/models/event.dart';
import 'package:stream_chat/src/models/own_user.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart';
void main() {
group('src/models/event', () {
@@ -47,6 +47,7 @@ void main() {
expect(event.createdAt, isA<DateTime>());
expect(event.me, isA<OwnUser>());
expect(event.user, isA<User>());
expect(event.isLocal, false);
});
test('should serialize to json correctly', () {
@@ -77,11 +78,11 @@ void main() {
'total_unread_count': 1,
'unread_channels': 1,
'online': true,
'is_local': true,
'member': null,
'channel_id': null,
'channel_type': null,
'parent_id': null,
'is_local': true,
},
);
});
@@ -1,14 +1,15 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:stream_chat/src/models/attachment.dart';
import 'package:stream_chat/src/models/message.dart';
import 'package:stream_chat/src/models/reaction.dart';
import 'package:stream_chat/src/models/user.dart';
import 'package:test/test.dart';
void main() {
group('src/models/message', () {
const jsonExample = r'''{
const jsonExample = r'''
{
"id": "4637f7e4-a06b-42db-ba5a-8d8270dd926f",
"text": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA",
"type": "regular",
@@ -101,9 +102,8 @@ void main() {
id: '4637f7e4-a06b-42db-ba5a-8d8270dd926f',
text:
'https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA',
silent: false,
attachments: [
Attachment.fromJson({
Attachment.fromJson(const {
'type': 'video',
'author_name': 'GIPHY',
'title': 'The Lion King Disney GIF - Find \u0026 Share on GIPHY',
@@ -123,7 +123,7 @@ void main() {
],
showInChannel: true,
parentId: 'parentId',
extraData: {'hey': 'test'},
extraData: const {'hey': 'test'},
);
expect(
@@ -133,7 +133,7 @@ void main() {
"id": "4637f7e4-a06b-42db-ba5a-8d8270dd926f",
"text": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA",
"silent": false,
"skip_push": null,
"skip_push": false,
"attachments": [
{
"type": "video",
@@ -144,10 +144,11 @@ void main() {
"og_scrape_url": "https://giphy.com/gifs/the-lion-king-live-action-5zvN79uTGfLMOVfQaA",
"image_url": "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.gif",
"author_name": "GIPHY",
"asset_url": "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.mp4"
"asset_url": "https://media.giphy.com/media/5zvN79uTGfLMOVfQaA/giphy.mp4",
"actions": []
}
],
"mentioned_users": null,
"mentioned_users": [],
"parent_id": "parentId",
"quoted_message": null,
"quoted_message_id": null,
@@ -1,8 +1,8 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:stream_chat/src/models/reaction.dart';
import 'package:stream_chat/src/models/user.dart';
import 'package:test/test.dart';
void main() {
group('src/models/reaction', () {
@@ -33,8 +33,8 @@ void main() {
expect(reaction.createdAt, DateTime.parse('2020-01-28T22:17:31.108742Z'));
expect(reaction.type, 'wow');
expect(
reaction.user.toJson(),
User.init('2de0297c-f3f2-489d-b930-ef77342edccf', extraData: {
reaction.user?.toJson(),
User(id: '2de0297c-f3f2-489d-b930-ef77342edccf', extraData: {
'image': 'https://randomuser.me/api/portraits/women/45.jpg',
'name': 'Daisy Morgan'
}).toJson(),
@@ -49,7 +49,7 @@ void main() {
messageId: '76cd8c82-b557-4e48-9d12-87995d3a0e04',
createdAt: DateTime.parse('2020-01-28T22:17:31.108742Z'),
type: 'wow',
user: User.init('2de0297c-f3f2-489d-b930-ef77342edccf', extraData: {
user: User(id: '2de0297c-f3f2-489d-b930-ef77342edccf', extraData: {
'image': 'https://randomuser.me/api/portraits/women/45.jpg',
'name': 'Daisy Morgan'
}),
@@ -26,7 +26,7 @@ void main() {
test('should serialize to json correctly', () {
final read = Read(
lastRead: DateTime.parse('2020-01-28T22:17:30.966485504Z'),
user: User.init('bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'),
user: User(id: 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'),
unreadMessages: 10,
);
@@ -49,12 +49,12 @@ void main() {
});
test('should return null', () {
final result = Serialization.moveToExtraDataFromRoot(null, [
final result = Serialization.moveToExtraDataFromRoot({}, [
'prop1',
'prop2',
]);
expect(result, null);
expect(result, {'extra_data': {}});
});
});
}
@@ -1,13 +1,14 @@
import 'dart:convert';
import 'package:test/test.dart';
import 'package:stream_chat/src/models/user.dart';
import 'package:test/test.dart';
void main() {
group('src/models/user', () {
const jsonExample = '''
{
"id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e"
"id": "bbb19d9a-ee50-45bc-84e5-0584e79d0c9e",
"role": "test-role"
}
''';
+2 -2
View File
@@ -17,8 +17,8 @@ void main() {
final String pubspecPath = '${Directory.current.path}/pubspec.yaml';
final String pubspec = File(pubspecPath).readAsStringSync();
final RegExp regex = RegExp('version:\s*(.*)');
final RegExpMatch match = regex.firstMatch(pubspec);
final RegExpMatch? match = regex.firstMatch(pubspec);
expect(match, isNotNull);
expect(PACKAGE_VERSION, match.group(1).trim());
expect(PACKAGE_VERSION, match?.group(1)?.trim());
});
}
+1 -1
View File
@@ -43,7 +43,7 @@ dependencies:
ezanimation: ^0.4.1
synchronized: ^3.0.0
characters: ^1.0.0
dio: ">=4.0.0-prev3 <4.0.0"
dio: ^4.0.0
path_provider: ^2.0.0
video_thumbnail: ^0.2.5+1
@@ -90,6 +90,7 @@ void main() {
Reaction(
messageId: 'test',
user: User(id: 'testid'),
type: 'test',
),
],
);