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