added null safety for llc

This commit is contained in:
Deven Joshi
2021-04-08 15:03:16 +05:30
parent 3a5308a9c0
commit c80fc0a6b4
41 changed files with 1222 additions and 1202 deletions
File diff suppressed because it is too large Load Diff
+11 -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,
+102 -102
View File
@@ -13,192 +13,192 @@ 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; 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) =>
_$SyncResponseFromJson(json); _$SyncResponseFromJson(json!);
} }
/// Model response for [StreamChatClient.queryChannels] api call /// Model response for [StreamChatClient.queryChannels] api call
@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; 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) =>
_$QueryChannelsResponseFromJson(json); _$QueryChannelsResponseFromJson(json!);
} }
/// 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 /// List of channels state returned by the query
TranslatedMessage message; 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) =>
_$TranslateMessageResponseFromJson(json); _$TranslateMessageResponseFromJson(json!);
} }
/// Model response for [StreamChatClient.queryChannels] api call /// Model response for [StreamChatClient.queryChannels] api call
@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; 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) =>
_$QueryMembersResponseFromJson(json); _$QueryMembersResponseFromJson(json!);
} }
/// Model response for [StreamChatClient.queryUsers] api call /// Model response for [StreamChatClient.queryUsers] api call
@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; 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) =>
_$QueryUsersResponseFromJson(json); _$QueryUsersResponseFromJson(json!);
} }
/// Model response for [channel.getReactions] api call /// Model response for [channel.getReactions] api call
@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; 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) =>
_$QueryReactionsResponseFromJson(json); _$QueryReactionsResponseFromJson(json!);
} }
/// Model response for [Channel.getReplies] api call /// Model response for [Channel.getReplies] api call
@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; 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) =>
_$QueryRepliesResponseFromJson(json); _$QueryRepliesResponseFromJson(json!);
} }
/// Model response for [StreamChatClient.getDevices] api call /// Model response for [StreamChatClient.getDevices] api call
@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; 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) =>
_$ListDevicesResponseFromJson(json); _$ListDevicesResponseFromJson(json!);
} }
/// Model response for [Channel.sendFile] api call /// Model response for [Channel.sendFile] api call
@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; 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) =>
_$SendFileResponseFromJson(json); _$SendFileResponseFromJson(json!);
} }
/// Model response for [Channel.sendImage] api call /// Model response for [Channel.sendImage] api call
@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; 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) =>
_$SendImageResponseFromJson(json); _$SendImageResponseFromJson(json!);
} }
/// Model response for [Channel.sendReaction] api call /// Model response for [Channel.sendReaction] api call
@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; Message? message;
/// The reaction created by the api call /// The reaction created by the api call
Reaction reaction; 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) =>
_$SendReactionResponseFromJson(json); _$SendReactionResponseFromJson(json!);
} }
/// Model response for [StreamChatClient.connectGuestUser] api call /// Model response for [StreamChatClient.connectGuestUser] api call
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class ConnectGuestUserResponse extends _BaseResponse { class ConnectGuestUserResponse extends _BaseResponse {
/// Guest user access token /// Guest user access token
String accessToken; String? accessToken;
/// Guest user /// Guest user
User user; 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) =>
_$ConnectGuestUserResponseFromJson(json); _$ConnectGuestUserResponseFromJson(json!);
} }
/// Model response for [StreamChatClient.updateUser] api call /// Model response for [StreamChatClient.updateUser] api call
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class UpdateUsersResponse extends _BaseResponse { class UpdateUsersResponse extends _BaseResponse {
/// Updated users /// Updated users
Map<String, User> users; 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) =>
_$UpdateUsersResponseFromJson(json); _$UpdateUsersResponseFromJson(json!);
} }
/// Model response for [StreamChatClient.updateMessage] api call /// Model response for [StreamChatClient.updateMessage] api call
@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; 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) =>
_$UpdateMessageResponseFromJson(json); _$UpdateMessageResponseFromJson(json!);
} }
/// Model response for [Channel.sendMessage] api call /// Model response for [Channel.sendMessage] api call
@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; 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) =>
_$SendMessageResponseFromJson(json); _$SendMessageResponseFromJson(json!);
} }
/// Model response for [StreamChatClient.getMessage] api call /// Model response for [StreamChatClient.getMessage] api call
@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; 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,176 +211,176 @@ 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; 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) =>
_$SearchMessagesResponseFromJson(json); _$SearchMessagesResponseFromJson(json!);
} }
/// Model response for [Channel.getMessagesById] api call /// Model response for [Channel.getMessagesById] api call
@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; 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) =>
_$GetMessagesByIdResponseFromJson(json); _$GetMessagesByIdResponseFromJson(json!);
} }
/// Model response for [Channel.update] api call /// Model response for [Channel.update] api call
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class UpdateChannelResponse extends _BaseResponse { class UpdateChannelResponse extends _BaseResponse {
/// Updated channel /// Updated channel
ChannelModel channel; 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) =>
_$UpdateChannelResponseFromJson(json); _$UpdateChannelResponseFromJson(json!);
} }
/// Model response for [Channel.updatePartial] api call /// Model response for [Channel.updatePartial] api call
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class PartialUpdateChannelResponse extends _BaseResponse { class PartialUpdateChannelResponse extends _BaseResponse {
/// Updated channel /// Updated channel
ChannelModel channel; 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) =>
_$PartialUpdateChannelResponseFromJson(json); _$PartialUpdateChannelResponseFromJson(json!);
} }
/// Model response for [Channel.inviteMembers] api call /// Model response for [Channel.inviteMembers] api call
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class InviteMembersResponse extends _BaseResponse { class InviteMembersResponse extends _BaseResponse {
/// Updated channel /// Updated channel
ChannelModel channel; 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 InviteMembersResponse fromJson(Map<String, dynamic> json) => static InviteMembersResponse fromJson(Map<String, dynamic>? json) =>
_$InviteMembersResponseFromJson(json); _$InviteMembersResponseFromJson(json!);
} }
/// Model response for [Channel.removeMembers] api call /// Model response for [Channel.removeMembers] api call
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class RemoveMembersResponse extends _BaseResponse { class RemoveMembersResponse extends _BaseResponse {
/// Updated channel /// Updated channel
ChannelModel channel; 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 RemoveMembersResponse fromJson(Map<String, dynamic> json) => static RemoveMembersResponse fromJson(Map<String, dynamic>? json) =>
_$RemoveMembersResponseFromJson(json); _$RemoveMembersResponseFromJson(json!);
} }
/// Model response for [Channel.sendAction] api call /// Model response for [Channel.sendAction] api call
@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) =>
_$SendActionResponseFromJson(json); _$SendActionResponseFromJson(json!);
} }
/// Model response for [Channel.addMembers] api call /// Model response for [Channel.addMembers] api call
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class AddMembersResponse extends _BaseResponse { class AddMembersResponse extends _BaseResponse {
/// Updated channel /// Updated channel
ChannelModel channel; 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 AddMembersResponse fromJson(Map<String, dynamic> json) => static AddMembersResponse fromJson(Map<String, dynamic>? json) =>
_$AddMembersResponseFromJson(json); _$AddMembersResponseFromJson(json!);
} }
/// Model response for [Channel.acceptInvite] api call /// Model response for [Channel.acceptInvite] api call
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class AcceptInviteResponse extends _BaseResponse { class AcceptInviteResponse extends _BaseResponse {
/// Updated channel /// Updated channel
ChannelModel channel; 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 AcceptInviteResponse fromJson(Map<String, dynamic> json) => static AcceptInviteResponse fromJson(Map<String, dynamic>? json) =>
_$AcceptInviteResponseFromJson(json); _$AcceptInviteResponseFromJson(json!);
} }
/// Model response for [Channel.rejectInvite] api call /// Model response for [Channel.rejectInvite] api call
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class RejectInviteResponse extends _BaseResponse { class RejectInviteResponse extends _BaseResponse {
/// Updated channel /// Updated channel
ChannelModel channel; 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 RejectInviteResponse fromJson(Map<String, dynamic> json) => static RejectInviteResponse fromJson(Map<String, dynamic>? json) =>
_$RejectInviteResponseFromJson(json); _$RejectInviteResponseFromJson(json!);
} }
/// Model response for empty responses /// Model response for empty responses
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class EmptyResponse extends _BaseResponse { class EmptyResponse extends _BaseResponse {
/// Create a new instance from a json /// Create a new instance from a json
static EmptyResponse fromJson(Map<String, dynamic> json) => static EmptyResponse fromJson(Map<String, dynamic>? json) =>
_$EmptyResponseFromJson(json); _$EmptyResponseFromJson(json!);
} }
/// Model response for [Channel.query] api call /// Model response for [Channel.query] api call
@JsonSerializable(createToJson: false) @JsonSerializable(createToJson: false)
class ChannelStateResponse extends _BaseResponse { class ChannelStateResponse extends _BaseResponse {
/// Updated channel /// Updated channel
ChannelModel channel; ChannelModel? channel;
/// List of messages returned by the api call /// List of messages returned by the api call
List<Message> messages; List<Message>? messages;
/// Channel members /// Channel members
List<Member> members; List<Member>? members;
/// Number of users watching the channel /// Number of users watching the channel
int watcherCount; int? watcherCount;
/// List of read states /// List of read states
List<Read> read; 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) =>
@@ -6,30 +6,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,
@@ -14,7 +14,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,29 +28,29 @@ 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();
} }
})); }));
} }
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)));
if (_messageQueue.isNotEmpty && !_isRetrying) { if (_messageQueue.isNotEmpty && !_isRetrying) {
_startRetrying(); _startRetrying();
@@ -60,10 +60,10 @@ 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!;
try { try {
logger?.info('retry attempt ${retryPolicy.attempt}'); logger?.info('retry attempt ${retryPolicy.attempt}');
await _sendMessage(message); await _sendMessage(message);
@@ -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,20 @@ 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]); add([event.message]);
} 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]);
} }
} }
@@ -167,14 +168,14 @@ class RetryQueue {
_subscriptions.forEach((s) => s.cancel()); _subscriptions.forEach((s) => s.cancel());
} }
static int _byDate(Message m1, Message m2) { static int _byDate(Message? m1, Message? m2) {
final date1 = _getMessageDate(m1); final date1 = _getMessageDate(m1!)!;
final date2 = _getMessageDate(m2); final date2 = _getMessageDate(m2!)!;
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();
+40 -40
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,7 +27,7 @@ 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, this.user,
this.connectParams, this.connectParams,
this.connectPayload, this.connectPayload,
@@ -38,22 +38,22 @@ class WebSocket {
this.healthCheckInterval = 20, this.healthCheckInterval = 20,
this.reconnectionMonitorTimeout = 40, this.reconnectionMonitorTimeout = 40,
}) { }) {
final qs = Map<String, String>.from(connectParams); final qs = Map<String, String>.from(connectParams!);
final data = Map<String, dynamic>.from(connectPayload); final data = Map<String, dynamic>.from(connectPayload!);
data['user_details'] = user.toJson(); data['user_details'] = user!.toJson();
qs['json'] = json.encode(data); qs['json'] = json.encode(data);
if (baseUrl.startsWith('https')) { if (baseUrl.startsWith('https')) {
_path = baseUrl.replaceFirst('https://', ''); _path = baseUrl.replaceFirst('https://', '');
_path = Uri.https(_path, 'connect', qs) _path = Uri.https(_path!, 'connect', qs)
.toString() .toString()
.replaceFirst('https', 'wss'); .replaceFirst('https', 'wss');
} else if (baseUrl.startsWith('http')) { } else if (baseUrl.startsWith('http')) {
_path = baseUrl.replaceFirst('http://', ''); _path = baseUrl.replaceFirst('http://', '');
_path = _path =
Uri.http(_path, 'connect', qs).toString().replaceFirst('http', 'ws'); Uri.http(_path!, 'connect', qs).toString().replaceFirst('http', 'ws');
} else { } else {
_path = Uri.https(baseUrl, 'connect', qs) _path = Uri.https(baseUrl, 'connect', qs)
.toString() .toString()
@@ -65,25 +65,25 @@ class WebSocket {
final String baseUrl; final String baseUrl;
/// User performing the WS connection /// User performing the WS connection
final User user; final User? user;
/// Querystring connection parameters /// Querystring connection parameters
final Map<String, String> connectParams; final Map<String, String?>? connectParams;
/// WS connection payload /// WS connection payload
final Map<String, dynamic> connectPayload; final Map<String, dynamic>? connectPayload;
/// Functions that will be called every time a new event is received from the /// Functions that will be called every time a new event is received from the
/// connection /// connection
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,17 +107,17 @@ 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; 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;
@@ -127,23 +127,23 @@ class WebSocket {
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() {
_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']);
@@ -166,7 +166,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 +180,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();
@@ -199,14 +199,14 @@ class WebSocket {
_startHealthCheck(); _startHealthCheck();
} }
handler(event); handler!(event);
_lastEventAt = DateTime.now(); _lastEventAt = DateTime.now();
} }
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 +225,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 +244,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 +267,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 +279,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,7 +311,7 @@ class WebSocket {
if (_manuallyDisconnected) { if (_manuallyDisconnected) {
return; return;
} }
logger.info('disconnecting'); logger!.info('disconnecting');
_connectionCompleter = Completer(); _connectionCompleter = Completer();
_cancelTimers(); _cancelTimers();
_reconnecting = false; _reconnecting = false;
@@ -11,12 +11,12 @@ abstract class AttachmentFileUploader {
/// ///
/// Optionally, access upload progress using [onSendProgress] /// Optionally, access upload progress using [onSendProgress]
/// and cancel the request using [cancelToken] /// and cancel the request using [cancelToken]
Future<SendImageResponse> sendImage( Future<SendImageResponse?> sendImage(
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.
@@ -24,34 +24,34 @@ abstract class AttachmentFileUploader {
/// ///
/// Optionally, access upload progress using [onSendProgress] /// Optionally, access upload progress using [onSendProgress]
/// and cancel the request using [cancelToken] /// and cancel the request using [cancelToken]
Future<SendFileResponse> sendFile( Future<SendFileResponse?> sendFile(
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.
/// Returns [EmptyResponse] once deleted successfully. /// Returns [EmptyResponse] once deleted successfully.
/// ///
/// Optionally, cancel the request using [cancelToken] /// Optionally, cancel the request using [cancelToken]
Future<EmptyResponse> deleteImage( Future<EmptyResponse?> deleteImage(
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.
/// Returns [EmptyResponse] once deleted successfully. /// Returns [EmptyResponse] once deleted successfully.
/// ///
/// Optionally, cancel the request using [cancelToken] /// Optionally, cancel the request using [cancelToken]
Future<EmptyResponse> deleteFile( Future<EmptyResponse?> deleteFile(
String url, String url,
String channelId, String? channelId,
String channelType, { String? channelType, {
CancelToken cancelToken, CancelToken? cancelToken,
}); });
} }
@@ -63,26 +63,26 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
final StreamChatClient _client; final StreamChatClient _client;
@override @override
Future<SendImageResponse> sendImage( Future<SendImageResponse?> sendImage(
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,
); );
@@ -100,26 +100,26 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
} }
@override @override
Future<SendFileResponse> sendFile( Future<SendFileResponse?> sendFile(
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,
); );
@@ -137,11 +137,11 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
} }
@override @override
Future<EmptyResponse> deleteImage( Future<EmptyResponse?> deleteImage(
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',
@@ -152,11 +152,11 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
} }
@override @override
Future<EmptyResponse> deleteFile( Future<EmptyResponse?> deleteFile(
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',
File diff suppressed because it is too large Load Diff
@@ -11,7 +11,7 @@ import 'package:stream_chat/src/models/user.dart';
/// A simple client used for persisting chat data locally. /// A simple client used for persisting chat data locally.
abstract class ChatPersistenceClient { abstract class ChatPersistenceClient {
/// Creates a new connection to the client /// Creates a new connection to the client
Future<void> connect(String userId); Future<void> connect(String? userId);
/// Closes the client connection /// Closes the client connection
/// If [flush] is true, the data will also be deleted /// If [flush] is true, the data will also be deleted
@@ -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
@@ -33,40 +33,40 @@ abstract class ChatPersistenceClient {
Future<void> updateConnectionInfo(Event event); Future<void> updateConnectionInfo(Event event);
/// Update stored lastSyncAt /// Update stored lastSyncAt
Future<void> updateLastSyncAt(DateTime lastSyncAt); Future<void> updateLastSyncAt(DateTime? lastSyncAt);
/// Get the channel cids saved in the offline storage /// Get the channel cids saved in the offline storage
Future<List<String>> getChannelCids(); Future<List<String>> getChannelCids();
/// Get stored [ChannelModel]s by providing channel [cid] /// Get stored [ChannelModel]s by providing channel [cid]
Future<ChannelModel> getChannelByCid(String cid); Future<ChannelModel> getChannelByCid(String? cid);
/// Get stored channel [Member]s by providing channel [cid] /// Get stored channel [Member]s by providing channel [cid]
Future<List<Member>> getMembersByCid(String cid); Future<List<Member>> getMembersByCid(String? cid);
/// Get stored channel [Read]s by providing channel [cid] /// Get stored channel [Read]s by providing channel [cid]
Future<List<Read>> getReadsByCid(String cid); Future<List<Read>> getReadsByCid(String? cid);
/// Get stored [Message]s by providing channel [cid] /// Get stored [Message]s by providing channel [cid]
/// ///
/// Optionally, you can [messagePagination] /// Optionally, you can [messagePagination]
/// 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.
@@ -99,8 +99,8 @@ abstract class ChatPersistenceClient {
/// If [clearQueryCache] is true before the insert /// If [clearQueryCache] is true before the insert
/// the list of matching rows will be deleted /// the list of matching rows will be deleted
Future<void> updateChannelQueries( Future<void> updateChannelQueries(
Map<String, dynamic> filter, Map<String, dynamic>? filter,
List<String> cids, { List<String?> cids, {
bool clearQueryCache = false, bool clearQueryCache = false,
}); });
@@ -119,46 +119,46 @@ abstract class ChatPersistenceClient {
Future<void> deletePinnedMessageByIds(List<String> messageIds); Future<void> deletePinnedMessageByIds(List<String> messageIds);
/// Remove a message by channel [cid] /// Remove a message by channel [cid]
Future<void> deleteMessageByCid(String cid) => deleteMessageByCids([cid]); Future<void> deleteMessageByCid(String? cid) => deleteMessageByCids([cid]);
/// Remove a pinned message by channel [cid] /// Remove a pinned message by channel [cid]
Future<void> deletePinnedMessageByCid(String cid) async => Future<void> deletePinnedMessageByCid(String cid) async =>
deletePinnedMessageByCids([cid]); deletePinnedMessageByCids([cid]);
/// Remove a message by message [cids] /// Remove a message by message [cids]
Future<void> deleteMessageByCids(List<String> cids); Future<void> deleteMessageByCids(List<String?> cids);
/// Remove a pinned message by message [cids] /// Remove a pinned message by message [cids]
Future<void> deletePinnedMessageByCids(List<String> cids); Future<void> deletePinnedMessageByCids(List<String> cids);
/// Remove a channel by [cid] /// Remove a channel by [cid]
Future<void> deleteChannels(List<String> cids); Future<void> deleteChannels(List<String?> cids);
/// Updates the message data of a particular channel [cid] with /// Updates the message data of a particular channel [cid] with
/// the new [messages] data /// the new [messages] data
Future<void> updateMessages(String cid, List<Message> messages); Future<void> updateMessages(String? cid, List<Message> messages);
/// Updates the pinned message data of a particular channel [cid] with /// Updates the pinned message data of a particular channel [cid] with
/// the new [messages] data /// the new [messages] data
Future<void> updatePinnedMessages(String cid, List<Message> messages); Future<void> updatePinnedMessages(String? cid, List<Message> messages);
/// Returns all the threads by parent message of a particular channel by /// Returns all the threads by parent message of a particular channel by
/// providing channel [cid] /// providing channel [cid]
Future<Map<String, List<Message>>> getChannelThreads(String cid); Future<Map<String, List<Message>>> getChannelThreads(String? cid);
/// Updates all the channels using the new [channels] data. /// Updates all the channels using the new [channels] data.
Future<void> updateChannels(List<ChannelModel> channels); Future<void> updateChannels(List<ChannelModel?> channels);
/// Updates all the members of a particular channle [cid] /// Updates all the members of a particular channle [cid]
/// with the new [members] data /// with the new [members] data
Future<void> updateMembers(String cid, List<Member> members); Future<void> updateMembers(String? cid, List<Member?> members);
/// Updates the read data of a particular channel [cid] with /// Updates the read data of a particular channel [cid] with
/// the new [reads] data /// the new [reads] data
Future<void> updateReads(String cid, List<Read> reads); Future<void> updateReads(String? cid, List<Read> reads);
/// Updates the users data with the new [users] data /// Updates the users data with the new [users] data
Future<void> updateUsers(List<User> users); Future<void> updateUsers(List<User?> users);
/// Updates the reactions data with the new [reactions] data /// Updates the reactions data with the new [reactions] data
Future<void> updateReactions(List<Reaction> reactions); Future<void> updateReactions(List<Reaction> reactions);
@@ -167,7 +167,7 @@ abstract class ChatPersistenceClient {
Future<void> deleteReactionsByMessageId(List<String> messageIds); Future<void> deleteReactionsByMessageId(List<String> messageIds);
/// Deletes all the members by channel [cids] /// Deletes all the members by channel [cids]
Future<void> deleteMembersByCids(List<String> cids); Future<void> deleteMembersByCids(List<String?> cids);
/// Update the channel state data using [channelState] /// Update the channel state data using [channelState]
Future<void> updateChannelState(ChannelState channelState) => Future<void> updateChannelState(ChannelState channelState) =>
@@ -176,12 +176,12 @@ abstract class ChatPersistenceClient {
/// Update list of channel states /// Update list of channel states
Future<void> updateChannelStates(List<ChannelState> channelStates) async { Future<void> updateChannelStates(List<ChannelState> channelStates) async {
final deleteReactions = deleteReactionsByMessageId(channelStates final deleteReactions = deleteReactionsByMessageId(channelStates
.expand((it) => it.messages) .expand((it) => it.messages!)
.map((m) => m.id) .map((m) => m.id)
.toList(growable: false)); .toList(growable: false));
final deleteMembers = deleteMembersByCids( final deleteMembers = deleteMembersByCids(
channelStates.map((it) => it.channel.cid).toList(growable: false), channelStates.map((it) => it.channel!.cid).toList(growable: false),
); );
await Future.wait([ await Future.wait([
@@ -193,54 +193,54 @@ abstract class ChatPersistenceClient {
channelStates.map((it) => it.channel).where((it) => it != null); channelStates.map((it) => it.channel).where((it) => it != null);
final reactions = channelStates final reactions = channelStates
.expand((it) => it.messages) .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); .where((it) => it != null);
final users = channelStates final users = channelStates
.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), if (cs.read != null) ...cs.read!.map((r) => r.user),
if (cs.members != null) ...cs.members.map((m) => m.user), if (cs.members != null) ...cs.members!.map((m) => m!.user),
]) ])
.expand((it) => it) .expand((it) => it)
.where((it) => it != null); .where((it) => it != null);
final updateMessagesFuture = channelStates.map((it) { final updateMessagesFuture = channelStates.map((it) {
final cid = it.channel.cid; final cid = it.channel!.cid;
final messages = it.messages.where((it) => it != null); final messages = it.messages!.where((it) => it != null);
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 = channelStates.map((it) {
final cid = it.channel.cid; final cid = it.channel!.cid;
final messages = it.pinnedMessages.where((it) => it != null); final messages = it.pinnedMessages!.where((it) => it != null);
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 = channelStates.map((it) {
final cid = it.channel.cid; final cid = it.channel!.cid;
final reads = it.read?.where((it) => it != null) ?? []; final reads = it.read?.where((it) => it != null) ?? [];
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 = channelStates.map((it) {
final cid = it.channel.cid; final cid = it.channel!.cid;
final members = it.members.where((it) => it != null); final members = it.members!.where((it) => it != null);
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 on Map {
/// 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<String, dynamic> get nullProtected => {...this as Map<String, dynamic>}
{...this}..removeWhere((key, value) => key == null || value == null); ..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 ?? 0,
_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);
} }
@@ -2,14 +2,14 @@ import 'package:http_parser/http_parser.dart' as http_parser;
import 'package:mime/mime.dart'; 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 (this == null) return null;
if (toLowerCase().endsWith('heic')) { if (this!.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)); return http_parser.MediaType.parse(lookupMimeType(this!)!);
} }
} }
} }
@@ -12,19 +12,19 @@ class Action {
factory Action.fromJson(Map<String, dynamic> json) => _$ActionFromJson(json); factory Action.fromJson(Map<String, dynamic> json) => _$ActionFromJson(json);
/// The name of the action /// The name of the action
final String name; final String? name;
/// The style of the action /// The style of the action
final String style; final String? style;
/// The test of the action /// The test of the action
final String text; final String? text;
/// The type of the action /// The type of the 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);
@@ -13,10 +13,10 @@ part 'attachment.g.dart';
class Attachment { class Attachment {
/// 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,
@@ -34,10 +34,10 @@ class Attachment {
this.actions, this.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 {
this.uploadState = uploadState ?? this.uploadState = uploadState ??
((assetUrl != null || imageUrl != null) ((assetUrl != null || imageUrl != null)
? const UploadState.success() ? const UploadState.success()
@@ -47,68 +47,68 @@ class Attachment {
/// 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) =>
_$AttachmentFromJson( _$AttachmentFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields)); Serialization.moveToExtraDataFromRoot(json, topLevelFields)!);
/// Create a new instance from a db data /// Create a new instance from a db data
factory Attachment.fromData(Map<String, dynamic> json) => factory Attachment.fromData(Map<String, dynamic> json) =>
_$AttachmentFromJson(Serialization.moveToExtraDataFromRoot( _$AttachmentFromJson(Serialization.moveToExtraDataFromRoot(
json, topLevelFields + dbSpecificTopLevelFields)); json, topLevelFields + dbSpecificTopLevelFields)!);
///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
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
UploadState uploadState; 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.
/// ///
@@ -156,28 +156,28 @@ class Attachment {
_$AttachmentToJson(this), topLevelFields + dbSpecificTopLevelFields); _$AttachmentToJson(this), topLevelFields + dbSpecificTopLevelFields);
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,
@@ -19,7 +19,7 @@ abstract class UploadState with _$UploadState {
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 +27,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;
@@ -65,21 +65,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);
@@ -30,52 +30,52 @@ class ChannelConfig {
_$ChannelConfigFromJson(json); _$ChannelConfigFromJson(json);
/// Moderation configuration /// Moderation configuration
final String automod; final String? automod;
/// List of available commands /// List of available commands
final List<Command> commands; final List<Command>? commands;
/// True if the channel should send connect events /// True if the channel should send connect events
final bool connectEvents; final bool? connectEvents;
/// Date of channel creation /// Date of channel creation
final DateTime createdAt; final DateTime? createdAt;
/// Date of last channel update /// Date of last channel update
final DateTime updatedAt; final DateTime? updatedAt;
/// Max channel message length /// Max channel message length
final int maxMessageLength; final int? maxMessageLength;
/// Duration of message retention /// Duration of message retention
final String messageRetention; final String? messageRetention;
/// True if users can be muted /// True if users can be muted
final bool mutes; final bool? mutes;
/// Name of the channel /// Name of the channel
final String name; final String? name;
/// True if reaction are active for this channel /// True if reaction are active for this channel
final bool reactions; final bool? reactions;
/// True if readEvents are active for this channel /// True if readEvents are active for this channel
final bool readEvents; final bool? readEvents;
/// True if reply message are active for this channel /// True if reply message are active for this channel
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
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
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
final bool uploads; final bool? uploads;
/// True if urls appears as attachments /// True if urls appears as attachments
final bool urlEnrichment; final bool? urlEnrichment;
/// Serialize to json /// Serialize to json
Map<String, dynamic> toJson() => _$ChannelConfigToJson(this); Map<String, dynamic> toJson() => _$ChannelConfigToJson(this);
@@ -26,59 +26,59 @@ class ChannelModel {
}); });
/// 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) =>
_$ChannelModelFromJson( _$ChannelModelFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields)); Serialization.moveToExtraDataFromRoot(json, topLevelFields)!);
/// The id of this channel /// The id of this channel
final String id; final String? id;
/// The type of this channel /// The type of this channel
final String type; final String? type;
/// The cid of this channel /// The cid of this channel
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String cid; final String? cid;
/// The channel configuration data /// The channel configuration data
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final ChannelConfig config; final ChannelConfig? config;
/// 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)
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)
final DateTime createdAt; final DateTime? createdAt;
/// The date of the last channel update /// The date of the last channel update
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime updatedAt; final DateTime? updatedAt;
/// 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)
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,8 +98,8 @@ 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(
@@ -109,19 +109,19 @@ class ChannelModel {
/// 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 +141,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,
@@ -22,42 +22,42 @@ 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
final List<Message> messages; final List<Message>? messages;
/// A paginated list of channel members /// A paginated list of channel members
final List<Member> members; final List<Member?>? members;
/// A paginated list of pinned messages /// A paginated list of pinned messages
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
final List<User> watchers; final List<User>? watchers;
/// The list of channel reads /// The list of channel reads
final List<Read> read; final List<Read>? read;
/// Create a new instance from a json /// Create a new instance from a json
static ChannelState fromJson(Map<String, dynamic> json) => static ChannelState fromJson(Map<String, dynamic>? json) =>
_$ChannelStateFromJson(json); _$ChannelStateFromJson(json!);
/// Serialize to json /// Serialize to json
Map<String, dynamic> toJson() => _$ChannelStateToJson(this); Map<String, dynamic> toJson() => _$ChannelStateToJson(this);
/// 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,
@@ -17,13 +17,13 @@ class Command {
_$CommandFromJson(json); _$CommandFromJson(json);
/// The name of the command /// The name of the command
final String name; final String? name;
/// The description explaining the command /// The description explaining the command
final String description; final String? description;
/// The arguments of the command /// The arguments of the command
final String args; final String? args;
/// Serialize to json /// Serialize to json
Map<String, dynamic> toJson() => _$CommandToJson(this); Map<String, dynamic> toJson() => _$CommandToJson(this);
@@ -15,10 +15,10 @@ class Device {
factory Device.fromJson(Map<String, dynamic> json) => _$DeviceFromJson(json); factory Device.fromJson(Map<String, dynamic> json) => _$DeviceFromJson(json);
/// The id of the device /// The id of the device
final String id; final String? id;
/// The notification push provider /// The notification push provider
final String pushProvider; final String? pushProvider;
/// Serialize to json /// Serialize to json
Map<String, dynamic> toJson() => _$DeviceToJson(this); Map<String, dynamic> toJson() => _$DeviceToJson(this);
+52 -52
View File
@@ -31,68 +31,68 @@ class Event {
}) : isLocal = true; }) : 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; ..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; bool? isLocal;
/// 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;
/// Known top level fields. /// Known top level fields.
/// Useful for [Serialization] methods. /// Useful for [Serialization] methods.
@@ -124,23 +124,23 @@ class Event {
/// 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, 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,
}) : super( }) : super(
id: id, id: id,
type: type, type: type,
@@ -197,14 +197,14 @@ class EventChannel extends ChannelModel {
); );
/// Create a new instance from a json /// Create a new instance from a json
factory EventChannel.fromJson(Map<String, dynamic> json) => factory EventChannel.fromJson(Map<String, dynamic>? json) =>
_$EventChannelFromJson(Serialization.moveToExtraDataFromRoot( _$EventChannelFromJson(Serialization.moveToExtraDataFromRoot(
json, json,
topLevelFields, topLevelFields,
)); )!);
/// 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.
+22 -22
View File
@@ -31,51 +31,51 @@ 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
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
final bool isModerator; final bool? isModerator;
/// True if the member is banned from the channel /// True if the member is banned from the channel
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
final bool shadowBanned; final bool? shadowBanned;
/// The date of creation /// The date of creation
final DateTime createdAt; final DateTime? createdAt;
/// The last date of update /// The last date of update
final DateTime updatedAt; final DateTime? updatedAt;
/// 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,
@@ -44,7 +44,7 @@ enum MessageSendingStatus {
class Message { class Message {
/// Constructor used for json serialization /// Constructor used for json serialization
Message({ Message({
String id, String? id,
this.text, this.text,
this.type, this.type,
this.attachments, this.attachments,
@@ -67,7 +67,7 @@ class Message {
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,
this.deletedAt, this.deletedAt,
@@ -77,15 +77,15 @@ class Message {
pinExpires = pinExpires?.toUtc(); pinExpires = pinExpires?.toUtc();
/// 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(
Serialization.moveToExtraDataFromRoot(json, topLevelFields)); Serialization.moveToExtraDataFromRoot(json, topLevelFields)!);
/// The message ID. This is either created by Stream or set client side when /// The message ID. This is either created by Stream or set client side when
/// the message is added. /// the message is added.
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)
@@ -93,99 +93,99 @@ class Message {
/// The message type /// The message type
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
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)
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)
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
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
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)
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)
final DateTime createdAt; final DateTime? createdAt;
/// Reserved field indicating when the message was updated last time. /// Reserved field indicating when the message was updated last time.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime updatedAt; final DateTime? updatedAt;
/// 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
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)
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
bool get isSystem => type == 'system'; bool get isSystem => type == 'system';
@@ -198,7 +198,7 @@ class Message {
/// 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.
@@ -239,35 +239,35 @@ class Message {
/// 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 &&
@@ -305,7 +305,8 @@ class Message {
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,
); );
} }
@@ -355,13 +356,13 @@ class TranslatedMessage extends Message {
TranslatedMessage(this.i18n); TranslatedMessage(this.i18n);
/// 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) =>
_$TranslatedMessageFromJson( _$TranslatedMessageFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields), Serialization.moveToExtraDataFromRoot(json, topLevelFields)!,
); );
/// 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.
@@ -16,19 +16,19 @@ class Mute {
/// The user that performed the muting action /// The user that performed the muting action
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final User user; final User? user;
/// The target user /// The target user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final ChannelModel channel; final ChannelModel? channel;
/// The date in which the use was muted /// The date in which the use was muted
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime createdAt; final DateTime? createdAt;
/// The date of the last update /// The date of the last update
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime updatedAt; final DateTime? updatedAt;
/// Serialize to json /// Serialize to json
Map<String, dynamic> toJson() => _$MuteToJson(this); Map<String, dynamic> toJson() => _$MuteToJson(this);
@@ -17,14 +17,14 @@ class OwnUser extends User {
this.totalUnreadCount, this.totalUnreadCount,
this.unreadChannels, this.unreadChannels,
this.channelMutes, this.channelMutes,
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,
}) : super( }) : super(
id: id, id: id,
role: role, role: role,
@@ -37,28 +37,28 @@ class OwnUser extends User {
); );
/// Create a new instance from a json /// Create a new instance from a json
factory OwnUser.fromJson(Map<String, dynamic> json) => _$OwnUserFromJson( factory OwnUser.fromJson(Map<String, dynamic>? json) => _$OwnUserFromJson(
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)
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)
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)
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)
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.
@@ -13,39 +13,39 @@ class Reaction {
this.createdAt, this.createdAt,
this.type, this.type,
this.user, this.user,
String userId, String? userId,
this.score, this.score,
this.extraData, this.extraData,
}) : userId = userId ?? user?.id; }) : userId = userId ?? user?.id;
/// 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) => _$ReactionFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields)); 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;
/// The date of the reaction /// The date of the reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime createdAt; final DateTime? createdAt;
/// 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)
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 = [
@@ -63,13 +63,13 @@ class Reaction {
/// 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,
@@ -17,22 +17,22 @@ class Read {
factory Read.fromJson(Map<String, dynamic> json) => _$ReadFromJson(json); factory Read.fromJson(Map<String, dynamic> json) => _$ReadFromJson(json);
/// Date of the read event /// Date of the read event
final DateTime lastRead; final DateTime? lastRead;
/// User who sent the event /// User who sent the event
final User user; final User? user;
/// Number of unread messages /// Number of unread messages
final int unreadMessages; final int? unreadMessages;
/// Serialize to json /// Serialize to json
Map<String, dynamic> toJson() => _$ReadToJson(this); Map<String, dynamic> toJson() => _$ReadToJson(this);
/// 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,
@@ -10,12 +10,12 @@ 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; if (json == null) return null;
+23 -23
View File
@@ -20,8 +20,8 @@ class User {
}); });
/// 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(
Serialization.moveToExtraDataFromRoot(json, topLevelFields)); Serialization.moveToExtraDataFromRoot(json, topLevelFields)!);
/// Use this named constructor to create a new user instance /// Use this named constructor to create a new user instance
User.init( User.init(
@@ -49,47 +49,47 @@ class User {
]; ];
/// User id /// User id
final String id; final String? id;
/// User role /// User role
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String role; final String? role;
/// User role /// User role
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<String> teams; final List<String>? teams;
/// Date of user creation /// Date of user creation
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime createdAt; final DateTime? createdAt;
/// Date of last user update /// Date of last user update
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly) @JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime updatedAt; final DateTime? updatedAt;
/// 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)
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)
final bool banned; final bool? banned;
/// Map of custom user extraData /// Map of custom user extraData
@JsonKey(includeIfNull: false) @JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData; final Map<String, dynamic>? extraData;
@override @override
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;
@override @override
@@ -103,15 +103,15 @@ class User {
/// 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,
+10 -10
View File
@@ -6,25 +6,25 @@ 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
freezed_annotation: ^0.14.0 freezed_annotation: ^0.14.1
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.10.0
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
@@ -50,7 +50,7 @@ void main() {
), ),
); );
await channelClient.sendMessage(message); await channelClient?.sendMessage(message);
verify(() => verify(() =>
mockDio.post<String>('/channels/messaging/testid/message', data: { mockDio.post<String>('/channels/messaging/testid/message', data: {
@@ -80,7 +80,7 @@ void main() {
statusCode: 200, statusCode: 200,
requestOptions: FakeRequestOptions(), requestOptions: FakeRequestOptions(),
)); ));
await channelClient.watch(); await channelClient?.watch();
when( when(
() => mockDio.post<String>( () => mockDio.post<String>(
@@ -95,7 +95,7 @@ void main() {
), ),
); );
await channelClient.markRead(); await channelClient?.markRead();
verify(() => mockDio.post<String>('/channels/messaging/testid/read', verify(() => mockDio.post<String>('/channels/messaging/testid/read',
data: {})).called(1); data: {})).called(1);
@@ -124,7 +124,7 @@ void main() {
), ),
); );
await channelClient.getReplies('messageid', pagination); await channelClient?.getReplies('messageid', pagination);
verify(() => mockDio.get<String>('/messages/messageid/replies', verify(() => mockDio.get<String>('/messages/messageid/replies',
queryParameters: pagination.toJson())).called(1); queryParameters: pagination.toJson())).called(1);
@@ -141,7 +141,7 @@ void main() {
httpClient: mockDio, httpClient: mockDio,
tokenProvider: (_) async => '', tokenProvider: (_) async => '',
); );
final channelClient = client.channel('messaging', id: 'testid'); Channel channelClient = client.channel('messaging', id: 'testid');
when(() => mockDio.post<String>( when(() => mockDio.post<String>(
any(), any(),
@@ -564,7 +564,11 @@ void main() {
'api-key', 'api-key',
httpClient: mockDio, httpClient: mockDio,
tokenProvider: (_) async => '', tokenProvider: (_) async => '',
)..state.user = OwnUser(id: 'test-id'); );
if (client != null) {
client.state?.user = OwnUser(id: 'test-id');
}
final channelClient = client.channel('messaging', id: 'testid'); final channelClient = client.channel('messaging', id: 'testid');
const reactionType = 'test'; const reactionType = 'test';
@@ -617,7 +621,11 @@ void main() {
'api-key', 'api-key',
httpClient: mockDio, httpClient: mockDio,
tokenProvider: (_) async => '', tokenProvider: (_) async => '',
)..state.user = OwnUser(id: 'test-id'); );
if (client != null) {
client.state?.user = OwnUser(id: 'test-id');
}
final channelClient = client.channel('messaging', id: 'testid'); final channelClient = client.channel('messaging', id: 'testid');
@@ -1069,8 +1077,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 +1714,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 +2035,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 {
@@ -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) {}
} }
@@ -94,7 +92,7 @@ void main() {
when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink()); 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((_) {
streamController.sink.add('{}'); streamController.sink.add('{}');
return Future.delayed(const Duration(milliseconds: 200)); return Future.delayed(const Duration(milliseconds: 200));
}).then((value) { }).then((value) {
@@ -130,7 +128,7 @@ void main() {
when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink()); 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((_) {
streamController.sink.add('{}'); streamController.sink.add('{}');
return Future.delayed(const Duration(milliseconds: 200)); return Future.delayed(const Duration(milliseconds: 200));
}).then((value) { }).then((value) {
@@ -208,7 +206,7 @@ void main() {
(_) => streamController.sink.add('{}'), (_) => streamController.sink.add('{}'),
); );
final connect = ws.connect().then((_) { final connect = ws.connect()?.then((_) {
streamController.sink.add('{}'); streamController.sink.add('{}');
return Future.delayed(const Duration(milliseconds: 200)); return Future.delayed(const Duration(milliseconds: 200));
}).then((value) async { }).then((value) async {
@@ -249,7 +247,7 @@ void main() {
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
when(() => mockWSChannel.sink).thenReturn(mockWSSink); when(() => mockWSChannel.sink).thenReturn(mockWSSink);
final connect = ws.connect().then((_) { final connect = ws.connect()?.then((_) {
streamController.sink.add('{}'); streamController.sink.add('{}');
streamController.close(); streamController.close();
streamController = StreamController<String>.broadcast(); streamController = StreamController<String>.broadcast();
@@ -292,7 +290,7 @@ void main() {
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream); when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
when(() => mockWSChannel.sink).thenReturn(mockWSSink); when(() => mockWSChannel.sink).thenReturn(mockWSSink);
final connect = ws.connect().then((_) { final connect = ws.connect()?.then((_) {
streamController.sink.add('{}'); streamController.sink.add('{}');
return Future.delayed(const Duration(milliseconds: 200)); return Future.delayed(const Duration(milliseconds: 200));
}).then((value) async { }).then((value) async {
@@ -49,7 +49,7 @@ void main() {
'https://media0.giphy.com/media/3o7TKnCdBx5cMg0qti/giphy.gif', 'https://media0.giphy.com/media/3o7TKnCdBx5cMg0qti/giphy.gif',
); );
expect(attachment.actions, hasLength(3)); expect(attachment.actions, hasLength(3));
expect(attachment.actions[0], isA<Action>()); expect(attachment.actions![0], isA<Action>());
}); });
test('should serialize to json correctly', () { test('should serialize to json correctly', () {
@@ -10,7 +10,8 @@ import 'package:stream_chat/stream_chat.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,36 +845,36 @@ 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));
expect(channelState.messages[0], isA<Message>()); expect(channelState.messages![0], isA<Message>());
expect(channelState.messages[0], isNotNull); expect(channelState.messages![0], isNotNull);
expect( expect(
channelState.messages[0].createdAt, channelState.messages![0].createdAt,
DateTime.parse('2020-01-29T03:23:02.843948Z'), DateTime.parse('2020-01-29T03:23:02.843948Z'),
); );
expect(channelState.messages[0].user, isA<User>()); expect(channelState.messages![0].user, isA<User>());
expect(channelState.watcherCount, 5); expect(channelState.watcherCount, 5);
}); });
@@ -20,8 +20,8 @@ void main() {
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('test:livestream'));
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', () {
@@ -33,7 +33,7 @@ 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.init('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'
@@ -19,7 +19,7 @@ void main() {
test('should parse json correctly', () { test('should parse json correctly', () {
final read = Read.fromJson(json.decode(jsonExample)); final read = Read.fromJson(json.decode(jsonExample));
expect(read.lastRead, DateTime.parse('2020-01-28T22:17:30.966485504Z')); expect(read.lastRead, DateTime.parse('2020-01-28T22:17:30.966485504Z'));
expect(read.user.id, 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e'); expect(read.user?.id, 'bbb19d9a-ee50-45bc-84e5-0584e79d0c9e');
expect(read.unreadMessages, 10); expect(read.unreadMessages, 10);
}); });