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
@JsonKey(ignore: true)
final Comparator<T> comparator;
final Comparator<T>? comparator;
/// Serialize model to json
Map<String, dynamic> toJson() => _$SortOptionToJson(this);
@@ -70,31 +70,31 @@ class PaginationParams {
/// Filter on ids greater than the given value.
@JsonKey(name: 'id_gt')
final String greaterThan;
final String? greaterThan;
/// Filter on ids greater than or equal to the given value.
@JsonKey(name: 'id_gte')
final String greaterThanOrEqual;
final String? greaterThanOrEqual;
/// Filter on ids smaller than the given value.
@JsonKey(name: 'id_lt')
final String lessThan;
final String? lessThan;
/// Filter on ids smaller than or equal to the given value.
@JsonKey(name: 'id_lte')
final String lessThanOrEqual;
final String? lessThanOrEqual;
/// Serialize model to json
Map<String, dynamic> toJson() => _$PaginationParamsToJson(this);
/// Creates a copy of [PaginationParams] with specified attributes overridden.
PaginationParams copyWith({
int limit,
int offset,
String greaterThan,
String greaterThanOrEqual,
String lessThan,
String lessThanOrEqual,
int? limit,
int? offset,
String? greaterThan,
String? greaterThanOrEqual,
String? lessThan,
String? lessThanOrEqual,
}) =>
PaginationParams(
limit: limit ?? this.limit,
+102 -102
View File
@@ -13,192 +13,192 @@ import 'package:stream_chat/src/models/user.dart';
part 'responses.g.dart';
class _BaseResponse {
String duration;
String? duration;
}
/// Model response for [StreamChatClient.resync] api call
@JsonSerializable(createToJson: false)
class SyncResponse extends _BaseResponse {
/// The list of events
List<Event> events;
List<Event>? events;
/// Create a new instance from a json
static SyncResponse fromJson(Map<String, dynamic> json) =>
_$SyncResponseFromJson(json);
static SyncResponse fromJson(Map<String, dynamic>? json) =>
_$SyncResponseFromJson(json!);
}
/// Model response for [StreamChatClient.queryChannels] api call
@JsonSerializable(createToJson: false)
class QueryChannelsResponse extends _BaseResponse {
/// List of channels state returned by the query
List<ChannelState> channels;
List<ChannelState>? channels;
/// Create a new instance from a json
static QueryChannelsResponse fromJson(Map<String, dynamic> json) =>
_$QueryChannelsResponseFromJson(json);
static QueryChannelsResponse fromJson(Map<String, dynamic>? json) =>
_$QueryChannelsResponseFromJson(json!);
}
/// Model response for [StreamChatClient.queryChannels] api call
@JsonSerializable(createToJson: false)
class TranslateMessageResponse extends _BaseResponse {
/// List of channels state returned by the query
TranslatedMessage message;
TranslatedMessage? message;
/// Create a new instance from a json
static TranslateMessageResponse fromJson(Map<String, dynamic> json) =>
_$TranslateMessageResponseFromJson(json);
static TranslateMessageResponse fromJson(Map<String, dynamic>? json) =>
_$TranslateMessageResponseFromJson(json!);
}
/// Model response for [StreamChatClient.queryChannels] api call
@JsonSerializable(createToJson: false)
class QueryMembersResponse extends _BaseResponse {
/// List of channels state returned by the query
List<Member> members;
List<Member>? members;
/// Create a new instance from a json
static QueryMembersResponse fromJson(Map<String, dynamic> json) =>
_$QueryMembersResponseFromJson(json);
static QueryMembersResponse fromJson(Map<String, dynamic>? json) =>
_$QueryMembersResponseFromJson(json!);
}
/// Model response for [StreamChatClient.queryUsers] api call
@JsonSerializable(createToJson: false)
class QueryUsersResponse extends _BaseResponse {
/// List of users returned by the query
List<User> users;
List<User>? users;
/// Create a new instance from a json
static QueryUsersResponse fromJson(Map<String, dynamic> json) =>
_$QueryUsersResponseFromJson(json);
static QueryUsersResponse fromJson(Map<String, dynamic>? json) =>
_$QueryUsersResponseFromJson(json!);
}
/// Model response for [channel.getReactions] api call
@JsonSerializable(createToJson: false)
class QueryReactionsResponse extends _BaseResponse {
/// List of reactions returned by the query
List<Reaction> reactions;
List<Reaction>? reactions;
/// Create a new instance from a json
static QueryReactionsResponse fromJson(Map<String, dynamic> json) =>
_$QueryReactionsResponseFromJson(json);
static QueryReactionsResponse fromJson(Map<String, dynamic>? json) =>
_$QueryReactionsResponseFromJson(json!);
}
/// Model response for [Channel.getReplies] api call
@JsonSerializable(createToJson: false)
class QueryRepliesResponse extends _BaseResponse {
/// List of messages returned by the api call
List<Message> messages;
List<Message>? messages;
/// Create a new instance from a json
static QueryRepliesResponse fromJson(Map<String, dynamic> json) =>
_$QueryRepliesResponseFromJson(json);
static QueryRepliesResponse fromJson(Map<String, dynamic>? json) =>
_$QueryRepliesResponseFromJson(json!);
}
/// Model response for [StreamChatClient.getDevices] api call
@JsonSerializable(createToJson: false)
class ListDevicesResponse extends _BaseResponse {
/// List of user devices
List<Device> devices;
List<Device>? devices;
/// Create a new instance from a json
static ListDevicesResponse fromJson(Map<String, dynamic> json) =>
_$ListDevicesResponseFromJson(json);
static ListDevicesResponse fromJson(Map<String, dynamic>? json) =>
_$ListDevicesResponseFromJson(json!);
}
/// Model response for [Channel.sendFile] api call
@JsonSerializable(createToJson: false)
class SendFileResponse extends _BaseResponse {
/// The url of the uploaded file
String file;
String? file;
/// Create a new instance from a json
static SendFileResponse fromJson(Map<String, dynamic> json) =>
_$SendFileResponseFromJson(json);
static SendFileResponse fromJson(Map<String, dynamic>? json) =>
_$SendFileResponseFromJson(json!);
}
/// Model response for [Channel.sendImage] api call
@JsonSerializable(createToJson: false)
class SendImageResponse extends _BaseResponse {
/// The url of the uploaded file
String file;
String? file;
/// Create a new instance from a json
static SendImageResponse fromJson(Map<String, dynamic> json) =>
_$SendImageResponseFromJson(json);
static SendImageResponse fromJson(Map<String, dynamic>? json) =>
_$SendImageResponseFromJson(json!);
}
/// Model response for [Channel.sendReaction] api call
@JsonSerializable(createToJson: false)
class SendReactionResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
Message? message;
/// The reaction created by the api call
Reaction reaction;
Reaction? reaction;
/// Create a new instance from a json
static SendReactionResponse fromJson(Map<String, dynamic> json) =>
_$SendReactionResponseFromJson(json);
static SendReactionResponse fromJson(Map<String, dynamic>? json) =>
_$SendReactionResponseFromJson(json!);
}
/// Model response for [StreamChatClient.connectGuestUser] api call
@JsonSerializable(createToJson: false)
class ConnectGuestUserResponse extends _BaseResponse {
/// Guest user access token
String accessToken;
String? accessToken;
/// Guest user
User user;
User? user;
/// Create a new instance from a json
static ConnectGuestUserResponse fromJson(Map<String, dynamic> json) =>
_$ConnectGuestUserResponseFromJson(json);
static ConnectGuestUserResponse fromJson(Map<String, dynamic>? json) =>
_$ConnectGuestUserResponseFromJson(json!);
}
/// Model response for [StreamChatClient.updateUser] api call
@JsonSerializable(createToJson: false)
class UpdateUsersResponse extends _BaseResponse {
/// Updated users
Map<String, User> users;
Map<String, User>? users;
/// Create a new instance from a json
static UpdateUsersResponse fromJson(Map<String, dynamic> json) =>
_$UpdateUsersResponseFromJson(json);
static UpdateUsersResponse fromJson(Map<String, dynamic>? json) =>
_$UpdateUsersResponseFromJson(json!);
}
/// Model response for [StreamChatClient.updateMessage] api call
@JsonSerializable(createToJson: false)
class UpdateMessageResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static UpdateMessageResponse fromJson(Map<String, dynamic> json) =>
_$UpdateMessageResponseFromJson(json);
static UpdateMessageResponse fromJson(Map<String, dynamic>? json) =>
_$UpdateMessageResponseFromJson(json!);
}
/// Model response for [Channel.sendMessage] api call
@JsonSerializable(createToJson: false)
class SendMessageResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static SendMessageResponse fromJson(Map<String, dynamic> json) =>
_$SendMessageResponseFromJson(json);
static SendMessageResponse fromJson(Map<String, dynamic>? json) =>
_$SendMessageResponseFromJson(json!);
}
/// Model response for [StreamChatClient.getMessage] api call
@JsonSerializable(createToJson: false)
class GetMessageResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
Message? message;
/// Channel of the message
ChannelModel channel;
ChannelModel? channel;
/// Create a new instance from a json
static GetMessageResponse fromJson(Map<String, dynamic> json) {
final res = _$GetMessageResponseFromJson(json);
static GetMessageResponse fromJson(Map<String, dynamic>? json) {
final res = _$GetMessageResponseFromJson(json!);
final jsonChannel = res.message?.extraData?.remove('channel');
if (jsonChannel != null) {
res.channel = ChannelModel.fromJson(jsonChannel);
@@ -211,176 +211,176 @@ class GetMessageResponse extends _BaseResponse {
@JsonSerializable(createToJson: false)
class SearchMessagesResponse extends _BaseResponse {
/// List of messages returned by the api call
List<GetMessageResponse> results;
List<GetMessageResponse>? results;
/// Create a new instance from a json
static SearchMessagesResponse fromJson(Map<String, dynamic> json) =>
_$SearchMessagesResponseFromJson(json);
static SearchMessagesResponse fromJson(Map<String, dynamic>? json) =>
_$SearchMessagesResponseFromJson(json!);
}
/// Model response for [Channel.getMessagesById] api call
@JsonSerializable(createToJson: false)
class GetMessagesByIdResponse extends _BaseResponse {
/// Message returned by the api call
List<Message> messages;
List<Message>? messages;
/// Create a new instance from a json
static GetMessagesByIdResponse fromJson(Map<String, dynamic> json) =>
_$GetMessagesByIdResponseFromJson(json);
static GetMessagesByIdResponse fromJson(Map<String, dynamic>? json) =>
_$GetMessagesByIdResponseFromJson(json!);
}
/// Model response for [Channel.update] api call
@JsonSerializable(createToJson: false)
class UpdateChannelResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
ChannelModel? channel;
/// Channel members
List<Member> members;
List<Member>? members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static UpdateChannelResponse fromJson(Map<String, dynamic> json) =>
_$UpdateChannelResponseFromJson(json);
static UpdateChannelResponse fromJson(Map<String, dynamic>? json) =>
_$UpdateChannelResponseFromJson(json!);
}
/// Model response for [Channel.updatePartial] api call
@JsonSerializable(createToJson: false)
class PartialUpdateChannelResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
ChannelModel? channel;
/// Channel members
List<Member> members;
List<Member>? members;
/// Create a new instance from a json
static PartialUpdateChannelResponse fromJson(Map<String, dynamic> json) =>
_$PartialUpdateChannelResponseFromJson(json);
static PartialUpdateChannelResponse fromJson(Map<String, dynamic>? json) =>
_$PartialUpdateChannelResponseFromJson(json!);
}
/// Model response for [Channel.inviteMembers] api call
@JsonSerializable(createToJson: false)
class InviteMembersResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
ChannelModel? channel;
/// Channel members
List<Member> members;
List<Member>? members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static InviteMembersResponse fromJson(Map<String, dynamic> json) =>
_$InviteMembersResponseFromJson(json);
static InviteMembersResponse fromJson(Map<String, dynamic>? json) =>
_$InviteMembersResponseFromJson(json!);
}
/// Model response for [Channel.removeMembers] api call
@JsonSerializable(createToJson: false)
class RemoveMembersResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
ChannelModel? channel;
/// Channel members
List<Member> members;
List<Member>? members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static RemoveMembersResponse fromJson(Map<String, dynamic> json) =>
_$RemoveMembersResponseFromJson(json);
static RemoveMembersResponse fromJson(Map<String, dynamic>? json) =>
_$RemoveMembersResponseFromJson(json!);
}
/// Model response for [Channel.sendAction] api call
@JsonSerializable(createToJson: false)
class SendActionResponse extends _BaseResponse {
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static SendActionResponse fromJson(Map<String, dynamic> json) =>
_$SendActionResponseFromJson(json);
static SendActionResponse fromJson(Map<String, dynamic>? json) =>
_$SendActionResponseFromJson(json!);
}
/// Model response for [Channel.addMembers] api call
@JsonSerializable(createToJson: false)
class AddMembersResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
ChannelModel? channel;
/// Channel members
List<Member> members;
List<Member>? members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static AddMembersResponse fromJson(Map<String, dynamic> json) =>
_$AddMembersResponseFromJson(json);
static AddMembersResponse fromJson(Map<String, dynamic>? json) =>
_$AddMembersResponseFromJson(json!);
}
/// Model response for [Channel.acceptInvite] api call
@JsonSerializable(createToJson: false)
class AcceptInviteResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
ChannelModel? channel;
/// Channel members
List<Member> members;
List<Member>? members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static AcceptInviteResponse fromJson(Map<String, dynamic> json) =>
_$AcceptInviteResponseFromJson(json);
static AcceptInviteResponse fromJson(Map<String, dynamic>? json) =>
_$AcceptInviteResponseFromJson(json!);
}
/// Model response for [Channel.rejectInvite] api call
@JsonSerializable(createToJson: false)
class RejectInviteResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
ChannelModel? channel;
/// Channel members
List<Member> members;
List<Member>? members;
/// Message returned by the api call
Message message;
Message? message;
/// Create a new instance from a json
static RejectInviteResponse fromJson(Map<String, dynamic> json) =>
_$RejectInviteResponseFromJson(json);
static RejectInviteResponse fromJson(Map<String, dynamic>? json) =>
_$RejectInviteResponseFromJson(json!);
}
/// Model response for empty responses
@JsonSerializable(createToJson: false)
class EmptyResponse extends _BaseResponse {
/// Create a new instance from a json
static EmptyResponse fromJson(Map<String, dynamic> json) =>
_$EmptyResponseFromJson(json);
static EmptyResponse fromJson(Map<String, dynamic>? json) =>
_$EmptyResponseFromJson(json!);
}
/// Model response for [Channel.query] api call
@JsonSerializable(createToJson: false)
class ChannelStateResponse extends _BaseResponse {
/// Updated channel
ChannelModel channel;
ChannelModel? channel;
/// List of messages returned by the api call
List<Message> messages;
List<Message>? messages;
/// Channel members
List<Member> members;
List<Member>? members;
/// Number of users watching the channel
int watcherCount;
int? watcherCount;
/// List of read states
List<Read> read;
List<Read>? read;
/// Create a new instance from a json
static ChannelStateResponse fromJson(Map<String, dynamic> json) =>
@@ -6,30 +6,30 @@ import 'package:stream_chat/src/exceptions.dart';
class RetryPolicy {
/// Instantiate a new RetryPolicy
RetryPolicy({
@required this.shouldRetry,
@required this.retryTimeout,
this.attempt,
required this.shouldRetry,
required this.retryTimeout,
this.attempt = 0,
});
/// The number of attempts tried so far
int attempt = 0;
/// This function evaluates if we should retry the failure
final bool Function(StreamChatClient client, int attempt, ApiError apiError)
final bool Function(StreamChatClient client, int attempt, ApiError? apiError)
shouldRetry;
/// In the case that we want to retry a failed request the retryTimeout
/// method is called to determine the timeout
final Duration Function(
StreamChatClient client, int attempt, ApiError apiError) retryTimeout;
StreamChatClient client, int attempt, ApiError? apiError) retryTimeout;
/// Creates a copy of [RetryPolicy] with specified attributes overridden.
RetryPolicy copyWith({
bool Function(StreamChatClient client, int attempt, ApiError apiError)
bool Function(StreamChatClient client, int attempt, ApiError? apiError)?
shouldRetry,
Duration Function(StreamChatClient client, int attempt, ApiError apiError)
Duration Function(StreamChatClient client, int attempt, ApiError? apiError)?
retryTimeout,
int attempt,
int? attempt,
}) =>
RetryPolicy(
retryTimeout: retryTimeout ?? this.retryTimeout,
@@ -14,7 +14,7 @@ import 'package:stream_chat/stream_chat.dart';
class RetryQueue {
/// Instantiate a new RetryQueue object
RetryQueue({
@required this.channel,
required this.channel,
this.logger,
}) {
_retryPolicy = channel.client.retryPolicy;
@@ -28,29 +28,29 @@ class RetryQueue {
final Channel channel;
/// The logger associated to this queue
final Logger logger;
final Logger? logger;
final _subscriptions = <StreamSubscription>[];
void _listenConnectionRecovered() {
_subscriptions
.add(channel.client.on(EventType.connectionRecovered).listen((event) {
if (!_isRetrying && event.online) {
if (!_isRetrying && event.online!) {
_startRetrying();
}
}));
}
final HeapPriorityQueue<Message> _messageQueue = HeapPriorityQueue(_byDate);
final HeapPriorityQueue<Message?> _messageQueue = HeapPriorityQueue(_byDate);
bool _isRetrying = false;
RetryPolicy _retryPolicy;
RetryPolicy? _retryPolicy;
/// Add a list of messages
void add(List<Message> messages) {
void add(List<Message?> messages) {
logger?.info('added ${messages.length} messages');
final messageList = _messageQueue.toList();
_messageQueue.addAll(messages
.where((element) => !messageList.any((m) => m.id == element.id)));
.where((element) => !messageList.any((m) => m!.id == element!.id)));
if (_messageQueue.isNotEmpty && !_isRetrying) {
_startRetrying();
@@ -60,10 +60,10 @@ class RetryQueue {
Future<void> _startRetrying() async {
logger?.info('start retrying');
_isRetrying = true;
final retryPolicy = _retryPolicy.copyWith(attempt: 0);
final retryPolicy = _retryPolicy!.copyWith(attempt: 0);
while (_messageQueue.isNotEmpty) {
final message = _messageQueue.first;
final message = _messageQueue.first!;
try {
logger?.info('retry attempt ${retryPolicy.attempt}');
await _sendMessage(message);
@@ -72,7 +72,7 @@ class RetryQueue {
logger?.info('now ${_messageQueue.length} messages in the queue');
retryPolicy.attempt = 0;
} catch (error) {
ApiError apiError;
ApiError? apiError;
if (error is DioError) {
if (error.type == DioErrorType.response) {
_messageQueue.remove(message);
@@ -84,7 +84,7 @@ class RetryQueue {
);
} else if (error is ApiError) {
apiError = error;
if (apiError.status?.toString()?.startsWith('4') == true) {
if (apiError.status?.toString().startsWith('4') == true) {
_messageQueue.remove(message);
return;
}
@@ -101,6 +101,7 @@ class RetryQueue {
}
retryPolicy.attempt++;
final timeout = retryPolicy.retryTimeout(
channel.client,
retryPolicy.attempt,
@@ -112,13 +113,13 @@ class RetryQueue {
_isRetrying = false;
}
void _sendFailedEvent(Message message) {
final newStatus = message.status == MessageSendingStatus.sending
void _sendFailedEvent(Message? message) {
final newStatus = message!.status == MessageSendingStatus.sending
? MessageSendingStatus.failed
: (message.status == MessageSendingStatus.updating
? MessageSendingStatus.failed_update
: MessageSendingStatus.failed_delete);
channel.state.addMessage(message.copyWith(
channel.state!.addMessage(message.copyWith(
status: newStatus,
));
}
@@ -141,20 +142,20 @@ class RetryQueue {
final messageList = _messageQueue.toList();
if (event.message != null) {
final messageIndex =
messageList.indexWhere((m) => m.id == event.message.id);
messageList.indexWhere((m) => m!.id == event.message!.id);
if (messageIndex == -1 &&
[
MessageSendingStatus.failed_update,
MessageSendingStatus.failed,
MessageSendingStatus.failed_delete,
].contains(event.message.status)) {
].contains(event.message!.status)) {
logger?.info('add message from events');
add([event.message]);
} else if (messageIndex != -1 &&
[
MessageSendingStatus.sent,
null,
].contains(event.message.status)) {
].contains(event.message!.status)) {
_messageQueue.remove(messageList[messageIndex]);
}
}
@@ -167,14 +168,14 @@ class RetryQueue {
_subscriptions.forEach((s) => s.cancel());
}
static int _byDate(Message m1, Message m2) {
final date1 = _getMessageDate(m1);
final date2 = _getMessageDate(m2);
static int _byDate(Message? m1, Message? m2) {
final date1 = _getMessageDate(m1!)!;
final date2 = _getMessageDate(m2!)!;
return date1.compareTo(date2);
}
static DateTime _getMessageDate(Message m1) {
static DateTime? _getMessageDate(Message m1) {
switch (m1.status) {
case MessageSendingStatus.failed_delete:
case MessageSendingStatus.deleting:
@@ -3,5 +3,5 @@ import 'package:web_socket_channel/web_socket_channel.dart';
/// Html version of websocket implementation
/// Used in Flutter web version
WebSocketChannel connectWebSocket(String url, {Iterable<String> protocols}) =>
WebSocketChannel connectWebSocket(String url, {Iterable<String>? protocols}) =>
HtmlWebSocketChannel.connect(url, protocols: protocols);
@@ -3,5 +3,5 @@ import 'package:web_socket_channel/web_socket_channel.dart';
/// IO version of websocket implementation
/// Used in Flutter mobile version
WebSocketChannel connectWebSocket(String url, {Iterable<String> protocols}) =>
WebSocketChannel connectWebSocket(String url, {Iterable<String>? protocols}) =>
IOWebSocketChannel.connect(url, protocols: protocols);
@@ -3,7 +3,7 @@ import 'package:web_socket_channel/web_socket_channel.dart';
/// Stub version of websocket implementation
/// Used just for conditional library import
WebSocketChannel connectWebSocket(String url,
{Iterable<String> protocols,
Map<String, dynamic> headers,
Duration pingInterval}) =>
{Iterable<String>? protocols,
Map<String, dynamic>? headers,
Duration? pingInterval}) =>
throw UnimplementedError();
+40 -40
View File
@@ -16,8 +16,8 @@ typedef EventHandler = void Function(Event);
/// Typedef used for connecting to a websocket. Method returns a
/// [WebSocketChannel] and accepts a connection [url] and an optional
/// [Iterable] of `protocols`.
typedef ConnectWebSocket = WebSocketChannel Function(String url,
{Iterable<String> protocols});
typedef ConnectWebSocket = WebSocketChannel Function(String? url,
{Iterable<String>? protocols});
// TODO: parse error even
// TODO: if parsing an error into an event fails we should not hide the
@@ -27,7 +27,7 @@ class WebSocket {
/// Creates a new websocket
/// To connect the WS call [connect]
WebSocket({
@required this.baseUrl,
required this.baseUrl,
this.user,
this.connectParams,
this.connectPayload,
@@ -38,22 +38,22 @@ class WebSocket {
this.healthCheckInterval = 20,
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);
if (baseUrl.startsWith('https')) {
_path = baseUrl.replaceFirst('https://', '');
_path = Uri.https(_path, 'connect', qs)
_path = Uri.https(_path!, 'connect', qs)
.toString()
.replaceFirst('https', 'wss');
} else if (baseUrl.startsWith('http')) {
_path = baseUrl.replaceFirst('http://', '');
_path =
Uri.http(_path, 'connect', qs).toString().replaceFirst('http', 'ws');
Uri.http(_path!, 'connect', qs).toString().replaceFirst('http', 'ws');
} else {
_path = Uri.https(baseUrl, 'connect', qs)
.toString()
@@ -65,25 +65,25 @@ class WebSocket {
final String baseUrl;
/// User performing the WS connection
final User user;
final User? user;
/// Querystring connection parameters
final Map<String, String> connectParams;
final Map<String, String?>? connectParams;
/// 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
/// connection
final EventHandler handler;
final EventHandler? handler;
/// A WS specific logger instance
final Logger logger;
final Logger? logger;
/// Connection function
/// Used only for testing purpose
@visibleForTesting
final ConnectWebSocket connectFunc;
final ConnectWebSocket? connectFunc;
/// Interval of the reconnection monitor timer
/// This checks that it received a new event in the last
@@ -107,17 +107,17 @@ class WebSocket {
_connectionStatusController.add(status);
/// The current connection status value
ConnectionStatus get connectionStatus => _connectionStatusController.value;
ConnectionStatus? get connectionStatus => _connectionStatusController.value;
/// This notifies of connection status changes
Stream<ConnectionStatus> get connectionStatusStream =>
_connectionStatusController.stream;
String _path;
String? _path;
int _retryAttempt = 1;
WebSocketChannel _channel;
Timer _healthCheck, _reconnectionMonitor;
DateTime _lastEventAt;
late WebSocketChannel _channel;
Timer? _healthCheck, _reconnectionMonitor;
DateTime? _lastEventAt;
bool _manuallyDisconnected = false;
bool _connecting = false;
bool _reconnecting = false;
@@ -127,23 +127,23 @@ class WebSocket {
Completer<Event> _connectionCompleter = Completer<Event>();
/// Connect the WS using the parameters passed in the constructor
Future<Event> connect() {
Future<Event>? connect() {
_manuallyDisconnected = false;
if (_connecting) {
logger.severe('already connecting');
logger!.severe('already connecting');
return null;
}
_connecting = true;
_connectionStatus = ConnectionStatus.connecting;
logger.info('connecting to $_path');
logger!.info('connecting to $_path');
_channel =
connectFunc?.call(_path) ?? WebSocketChannel.connect(Uri.parse(_path));
connectFunc?.call(_path) ?? WebSocketChannel.connect(Uri.parse(_path!));
_channel.stream.listen(
(data) {
(data) async {
final jsonData = json.decode(data);
if (jsonData['error'] != null) {
return _onConnectionError(jsonData['error']);
@@ -166,7 +166,7 @@ class WebSocket {
return;
}
logger.info('connection closed | closeCode: ${_channel.closeCode} | '
logger!.info('connection closed | closeCode: ${_channel.closeCode} | '
'closedReason: ${_channel.closeReason}');
if (!_reconnecting) {
@@ -180,10 +180,10 @@ class WebSocket {
}
final event = _decodeEvent(data);
logger.info('received new event: $data');
logger!.info('received new event: $data');
if (_lastEventAt == null) {
logger.info('connection estabilished');
logger!.info('connection estabilished');
_connecting = false;
_reconnecting = false;
_lastEventAt = DateTime.now();
@@ -199,14 +199,14 @@ class WebSocket {
_startHealthCheck();
}
handler(event);
handler!(event);
_lastEventAt = DateTime.now();
}
Future<void> _onConnectionError(error, [stacktrace]) async {
logger..severe('error connecting')..severe(error);
logger!..severe('error connecting')..severe(error);
if (stacktrace != null) {
logger.severe(stacktrace);
logger!.severe(stacktrace);
}
_connecting = false;
@@ -225,7 +225,7 @@ class WebSocket {
void _reconnectionTimer(_) {
final now = DateTime.now();
if (_lastEventAt != null &&
now.difference(_lastEventAt).inSeconds > reconnectionMonitorTimeout) {
now.difference(_lastEventAt!).inSeconds > reconnectionMonitorTimeout) {
_channel.sink.close();
}
}
@@ -244,18 +244,18 @@ class WebSocket {
return;
}
if (_connecting) {
logger.info('already connecting');
logger!.info('already connecting');
return;
}
logger.info('reconnecting..');
logger!.info('reconnecting..');
_cancelTimers();
try {
await connect();
} catch (e) {
logger.log(Level.SEVERE, e.toString());
logger!.log(Level.SEVERE, e.toString());
}
await Future.delayed(
Duration(seconds: min(_retryAttempt * 5, 25)),
@@ -267,7 +267,7 @@ class WebSocket {
}
Future<void> _reconnect() async {
logger.info('reconnect');
logger!.info('reconnect');
if (!_reconnecting) {
_reconnecting = true;
_connectionStatus = ConnectionStatus.connecting;
@@ -279,20 +279,20 @@ class WebSocket {
void _cancelTimers() {
_lastEventAt = null;
if (_healthCheck != null) {
_healthCheck.cancel();
_healthCheck!.cancel();
}
if (_reconnectionMonitor != null) {
_reconnectionMonitor.cancel();
_reconnectionMonitor!.cancel();
}
}
void _healthCheckTimer(_) {
logger.info('sending health.check');
logger!.info('sending health.check');
_channel.sink.add("{'type': 'health.check'}");
}
void _startHealthCheck() {
logger.info('start health check monitor');
logger!.info('start health check monitor');
_healthCheck = Timer.periodic(
Duration(seconds: healthCheckInterval),
@@ -311,7 +311,7 @@ class WebSocket {
if (_manuallyDisconnected) {
return;
}
logger.info('disconnecting');
logger!.info('disconnecting');
_connectionCompleter = Completer();
_cancelTimers();
_reconnecting = false;
@@ -11,12 +11,12 @@ abstract class AttachmentFileUploader {
///
/// Optionally, access upload progress using [onSendProgress]
/// and cancel the request using [cancelToken]
Future<SendImageResponse> sendImage(
AttachmentFile image,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
Future<SendImageResponse?> sendImage(
AttachmentFile? image,
String? channelId,
String? channelType, {
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
});
/// Uploads a [file] to the given channel.
@@ -24,34 +24,34 @@ abstract class AttachmentFileUploader {
///
/// Optionally, access upload progress using [onSendProgress]
/// and cancel the request using [cancelToken]
Future<SendFileResponse> sendFile(
AttachmentFile file,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
Future<SendFileResponse?> sendFile(
AttachmentFile? file,
String? channelId,
String? channelType, {
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
});
/// Deletes a image using its [url] from the given channel.
/// Returns [EmptyResponse] once deleted successfully.
///
/// Optionally, cancel the request using [cancelToken]
Future<EmptyResponse> deleteImage(
Future<EmptyResponse?> deleteImage(
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
String? channelId,
String? channelType, {
CancelToken? cancelToken,
});
/// Deletes a file using its [url] from the given channel.
/// Returns [EmptyResponse] once deleted successfully.
///
/// Optionally, cancel the request using [cancelToken]
Future<EmptyResponse> deleteFile(
Future<EmptyResponse?> deleteFile(
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
String? channelId,
String? channelType, {
CancelToken? cancelToken,
});
}
@@ -63,26 +63,26 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
final StreamChatClient _client;
@override
Future<SendImageResponse> sendImage(
AttachmentFile file,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
Future<SendImageResponse?> sendImage(
AttachmentFile? file,
String? channelId,
String? channelType, {
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
}) async {
final filename = file.path?.split('/')?.last ?? file.name;
final filename = file!.path?.split('/')?.last ?? file.name;
final mimeType = filename.mimeType;
MultipartFile multiPartFile;
MultipartFile? multiPartFile;
if (file.path != null) {
multiPartFile = await MultipartFile.fromFile(
file.path,
file.path!,
filename: filename,
contentType: mimeType,
);
} else if (file.bytes != null) {
multiPartFile = MultipartFile.fromBytes(
file.bytes,
file.bytes!,
filename: filename,
contentType: mimeType,
);
@@ -100,26 +100,26 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
}
@override
Future<SendFileResponse> sendFile(
AttachmentFile file,
String channelId,
String channelType, {
ProgressCallback onSendProgress,
CancelToken cancelToken,
Future<SendFileResponse?> sendFile(
AttachmentFile? file,
String? channelId,
String? channelType, {
ProgressCallback? onSendProgress,
CancelToken? cancelToken,
}) async {
final filename = file.path?.split('/')?.last ?? file.name;
final filename = file!.path?.split('/')?.last ?? file.name;
final mimeType = filename.mimeType;
MultipartFile multiPartFile;
MultipartFile? multiPartFile;
if (file.path != null) {
multiPartFile = await MultipartFile.fromFile(
file.path,
file.path!,
filename: filename,
contentType: mimeType,
);
} else if (file.bytes != null) {
multiPartFile = MultipartFile.fromBytes(
file.bytes,
file.bytes!,
filename: filename,
contentType: mimeType,
);
@@ -137,11 +137,11 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
}
@override
Future<EmptyResponse> deleteImage(
Future<EmptyResponse?> deleteImage(
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
String? channelId,
String? channelType, {
CancelToken? cancelToken,
}) async {
final response = await _client.delete(
'/channels/$channelType/$channelId/image',
@@ -152,11 +152,11 @@ class StreamAttachmentFileUploader implements AttachmentFileUploader {
}
@override
Future<EmptyResponse> deleteFile(
Future<EmptyResponse?> deleteFile(
String url,
String channelId,
String channelType, {
CancelToken cancelToken,
String? channelId,
String? channelType, {
CancelToken? cancelToken,
}) async {
final response = await _client.delete(
'/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.
abstract class ChatPersistenceClient {
/// Creates a new connection to the client
Future<void> connect(String userId);
Future<void> connect(String? userId);
/// Closes the client connection
/// If [flush] is true, the data will also be deleted
@@ -20,7 +20,7 @@ abstract class ChatPersistenceClient {
/// Get stored replies by messageId
Future<List<Message>> getReplies(
String parentId, {
PaginationParams options,
PaginationParams? options,
});
/// Get stored connection event
@@ -33,40 +33,40 @@ abstract class ChatPersistenceClient {
Future<void> updateConnectionInfo(Event event);
/// Update stored lastSyncAt
Future<void> updateLastSyncAt(DateTime lastSyncAt);
Future<void> updateLastSyncAt(DateTime? lastSyncAt);
/// Get the channel cids saved in the offline storage
Future<List<String>> getChannelCids();
/// 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]
Future<List<Member>> getMembersByCid(String cid);
Future<List<Member>> getMembersByCid(String? 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]
///
/// Optionally, you can [messagePagination]
/// for filtering out messages
Future<List<Message>> getMessagesByCid(
String cid, {
PaginationParams messagePagination,
String? cid, {
PaginationParams? messagePagination,
});
/// Get stored pinned [Message]s by providing channel [cid]
Future<List<Message>> getPinnedMessagesByCid(
String cid, {
PaginationParams messagePagination,
String? cid, {
PaginationParams? messagePagination,
});
/// Get [ChannelState] data by providing channel [cid]
Future<ChannelState> getChannelStateByCid(
String cid, {
PaginationParams messagePagination,
PaginationParams pinnedMessagePagination,
String? cid, {
PaginationParams? messagePagination,
PaginationParams? pinnedMessagePagination,
}) async {
final data = await Future.wait([
getMembersByCid(cid),
@@ -76,11 +76,11 @@ abstract class ChatPersistenceClient {
getPinnedMessagesByCid(cid, messagePagination: pinnedMessagePagination),
]);
return ChannelState(
members: data[0],
read: data[1],
channel: data[2],
messages: data[3],
pinnedMessages: data[4],
members: data[0] as List<Member?>?,
read: data[1] as List<Read>?,
channel: data[2] as ChannelModel?,
messages: data[3] as List<Message>?,
pinnedMessages: data[4] as List<Message>?,
);
}
@@ -89,9 +89,9 @@ abstract class ChatPersistenceClient {
/// Optionally, pass [filter], [sort], [paginationParams]
/// for filtering out states.
Future<List<ChannelState>> getChannelStates({
Map<String, dynamic> filter,
List<SortOption<ChannelModel>> sort = const [],
PaginationParams paginationParams,
Map<String, dynamic>? filter,
List<SortOption<ChannelModel>>? sort = const [],
PaginationParams? paginationParams,
});
/// Update list of channel queries.
@@ -99,8 +99,8 @@ abstract class ChatPersistenceClient {
/// If [clearQueryCache] is true before the insert
/// the list of matching rows will be deleted
Future<void> updateChannelQueries(
Map<String, dynamic> filter,
List<String> cids, {
Map<String, dynamic>? filter,
List<String?> cids, {
bool clearQueryCache = false,
});
@@ -119,46 +119,46 @@ abstract class ChatPersistenceClient {
Future<void> deletePinnedMessageByIds(List<String> messageIds);
/// 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]
Future<void> deletePinnedMessageByCid(String cid) async =>
deletePinnedMessageByCids([cid]);
/// 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]
Future<void> deletePinnedMessageByCids(List<String> cids);
/// 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
/// 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
/// 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
/// 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.
Future<void> updateChannels(List<ChannelModel> channels);
Future<void> updateChannels(List<ChannelModel?> channels);
/// Updates all the members of a particular channle [cid]
/// 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
/// 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
Future<void> updateUsers(List<User> users);
Future<void> updateUsers(List<User?> users);
/// Updates the reactions data with the new [reactions] data
Future<void> updateReactions(List<Reaction> reactions);
@@ -167,7 +167,7 @@ abstract class ChatPersistenceClient {
Future<void> deleteReactionsByMessageId(List<String> messageIds);
/// 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]
Future<void> updateChannelState(ChannelState channelState) =>
@@ -176,12 +176,12 @@ abstract class ChatPersistenceClient {
/// Update list of channel states
Future<void> updateChannelStates(List<ChannelState> channelStates) async {
final deleteReactions = deleteReactionsByMessageId(channelStates
.expand((it) => it.messages)
.expand((it) => it.messages!)
.map((m) => m.id)
.toList(growable: false));
final deleteMembers = deleteMembersByCids(
channelStates.map((it) => it.channel.cid).toList(growable: false),
channelStates.map((it) => it.channel!.cid).toList(growable: false),
);
await Future.wait([
@@ -193,54 +193,54 @@ abstract class ChatPersistenceClient {
channelStates.map((it) => it.channel).where((it) => it != null);
final reactions = channelStates
.expand((it) => it.messages)
.expand((it) => it.messages!)
.expand((it) => [
if (it.ownReactions != null)
...it.ownReactions.where((r) => r.userId != null),
...it.ownReactions!.where((r) => r.userId != null),
if (it.latestReactions != null)
...it.latestReactions.where((r) => r.userId != null)
...it.latestReactions!.where((r) => r.userId != null)
])
.where((it) => it != null);
final users = channelStates
.map((cs) => [
cs.channel?.createdBy,
...cs.messages
...?cs.messages
?.map((m) => [
m.user,
if (m.latestReactions != null)
...m.latestReactions.map((r) => r.user),
...m.latestReactions!.map((r) => r.user),
if (m.ownReactions != null)
...m.ownReactions.map((r) => r.user),
...m.ownReactions!.map((r) => r.user),
])
?.expand((v) => v),
if (cs.read != null) ...cs.read.map((r) => r.user),
if (cs.members != null) ...cs.members.map((m) => m.user),
if (cs.read != null) ...cs.read!.map((r) => r.user),
if (cs.members != null) ...cs.members!.map((m) => m!.user),
])
.expand((it) => it)
.where((it) => it != null);
final updateMessagesFuture = channelStates.map((it) {
final cid = it.channel.cid;
final messages = it.messages.where((it) => it != null);
final cid = it.channel!.cid;
final messages = it.messages!.where((it) => it != null);
return updateMessages(cid, messages.toList(growable: false));
}).toList(growable: false);
final updatePinnedMessagesFuture = channelStates.map((it) {
final cid = it.channel.cid;
final messages = it.pinnedMessages.where((it) => it != null);
final cid = it.channel!.cid;
final messages = it.pinnedMessages!.where((it) => it != null);
return updatePinnedMessages(cid, messages.toList(growable: false));
}).toList(growable: false);
final updateReadsFuture = channelStates.map((it) {
final cid = it.channel.cid;
final cid = it.channel!.cid;
final reads = it.read?.where((it) => it != null) ?? [];
return updateReads(cid, reads.toList(growable: false));
}).toList(growable: false);
final updateMembersFuture = channelStates.map((it) {
final cid = it.channel.cid;
final members = it.members.where((it) => it != null);
final cid = it.channel!.cid;
final members = it.members!.where((it) => it != null);
return updateMembers(cid, members.toList(growable: false));
}).toList(growable: false);
+8 -8
View File
@@ -4,25 +4,25 @@ import 'dart:convert';
class ApiError extends Error {
/// Creates a new ApiError instance using the response body and status code
ApiError(this.body, this.status) : jsonData = _decode(body) {
if (jsonData != null && jsonData.containsKey('code')) {
_code = jsonData['code'];
if (jsonData != null && jsonData!.containsKey('code')) {
_code = jsonData!['code'];
}
}
/// Raw body of the response
final String body;
final String? body;
/// Json parsed body
final Map<String, dynamic> jsonData;
final Map<String, dynamic>? jsonData;
/// Http status code of the response
final int status;
final int? status;
/// Stream specific error code
int get code => _code;
int _code;
int? get code => _code;
int? _code;
static Map<String, dynamic> _decode(String body) {
static Map<String, dynamic>? _decode(String? body) {
try {
if (body == null) {
return null;
@@ -1,6 +1,6 @@
/// Useful extension functions for [Map]
extension MapX on Map {
/// Returns a new map with null keys or values removed
Map<String, dynamic> get nullProtected =>
{...this}..removeWhere((key, value) => key == null || value == null);
Map<String, dynamic> get nullProtected => {...this as Map<String, dynamic>}
..removeWhere((key, value) => key == null || value == null);
}
@@ -10,7 +10,7 @@ extension RateLimit on Function {
Duration wait, {
bool leading = false,
bool trailing = true,
Duration maxWait,
Duration? maxWait,
}) =>
Debounce(
this,
@@ -40,7 +40,7 @@ Debounce debounce(
Duration wait, {
bool leading = false,
bool trailing = true,
Duration maxWait,
Duration? maxWait,
}) =>
Debounce(
func,
@@ -121,13 +121,13 @@ class Debounce {
Duration wait, {
bool leading = false,
bool trailing = true,
Duration maxWait,
Duration? maxWait,
}) : _leading = leading,
_trailing = trailing,
_wait = wait?.inMilliseconds ?? 0,
_maxing = maxWait != null {
if (_maxing) {
_maxWait = math.max(maxWait.inMilliseconds, _wait);
_maxWait = math.max(maxWait!.inMilliseconds, _wait);
}
}
@@ -137,15 +137,15 @@ class Debounce {
final int _wait;
final bool _maxing;
int _maxWait;
List<Object> _lastArgs;
Map<Symbol, Object> _lastNamedArgs;
Timer _timer;
int _lastCallTime;
Object _result;
int _lastInvokeTime = 0;
late int _maxWait;
List<Object?>? _lastArgs;
Map<Symbol, Object>? _lastNamedArgs;
Timer? _timer;
int? _lastCallTime;
Object? _result;
int? _lastInvokeTime = 0;
Object _invokeFunc(int time) {
Object? _invokeFunc(int? time) {
final args = _lastArgs;
final namedArgs = _lastNamedArgs;
_lastArgs = _lastNamedArgs = null;
@@ -154,11 +154,11 @@ class Debounce {
}
Timer _startTimer(Function pendingFunc, int wait) =>
Timer(Duration(milliseconds: wait), pendingFunc);
Timer(Duration(milliseconds: wait), pendingFunc as void Function());
bool _shouldInvoke(int time) {
final timeSinceLastCall = time - (_lastCallTime ?? double.nan);
final timeSinceLastInvoke = time - _lastInvokeTime;
final timeSinceLastInvoke = time - _lastInvokeTime!;
// Either this is the first call, activity has stopped and we're at the
// trailing edge, the system time has gone backwards and we're treating
@@ -169,7 +169,7 @@ class Debounce {
(_maxing && timeSinceLastInvoke >= _maxWait);
}
Object _trailingEdge(int time) {
Object? _trailingEdge(int time) {
_timer = null;
// Only invoke if we have `lastArgs` which means `func` has been
@@ -182,8 +182,8 @@ class Debounce {
}
int _remainingWait(int time) {
final timeSinceLastCall = time - _lastCallTime;
final timeSinceLastInvoke = time - _lastInvokeTime;
final timeSinceLastCall = time - _lastCallTime!;
final timeSinceLastInvoke = time - _lastInvokeTime!;
final timeWaiting = _wait - timeSinceLastCall;
return _maxing
@@ -201,7 +201,7 @@ class Debounce {
}
}
Object _leadingEdge(int time) {
Object? _leadingEdge(int? time) {
// Reset any `maxWait` timer.
_lastInvokeTime = time;
// Start the timer for the trailing edge.
@@ -218,7 +218,7 @@ class Debounce {
}
/// Immediately invokes all the remaining delayed functions.
Object flush() {
Object? flush() {
final now = DateTime.now().millisecondsSinceEpoch;
return _timer == null ? _result : _trailingEdge(now);
}
@@ -228,15 +228,15 @@ class Debounce {
/// Calls/invokes this class like a function.
/// Pass [args] and [namedArgs] to be used while invoking [_func].
Object call(
Object? call(
List<dynamic> args, {
Map<Symbol, dynamic> namedArgs,
Map<Symbol, dynamic>? namedArgs,
}) {
final time = DateTime.now().millisecondsSinceEpoch;
final isInvoking = _shouldInvoke(time);
_lastArgs = args;
_lastNamedArgs = namedArgs;
_lastNamedArgs = namedArgs as Map<Symbol, Object>?;
_lastCallTime = time;
if (isInvoking) {
@@ -323,13 +323,13 @@ class Throttle {
void cancel() => _debounce.cancel();
/// Immediately invokes all the remaining delayed functions.
Object flush() => _debounce.flush();
Object? flush() => _debounce.flush();
/// True if there are functions remaining to get invoked.
bool get isPending => _debounce.isPending;
/// Calls/invokes this class like a function.
/// Pass [args] and [namedArgs] to be used while invoking `func`.
Object call(List<dynamic> args, {Map<Symbol, dynamic> namedArgs}) =>
Object? call(List<dynamic> args, {Map<Symbol, dynamic>? namedArgs}) =>
_debounce.call(args, namedArgs: namedArgs);
}
@@ -2,14 +2,14 @@ import 'package:http_parser/http_parser.dart' as http_parser;
import 'package:mime/mime.dart';
/// Useful extension functions for [String]
extension StringX on String {
extension StringX on String? {
/// Returns the mime type from the passed file name.
http_parser.MediaType get mimeType {
http_parser.MediaType? get mimeType {
if (this == null) return null;
if (toLowerCase().endsWith('heic')) {
if (this!.toLowerCase().endsWith('heic')) {
return http_parser.MediaType.parse('image/heic');
} 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);
/// The name of the action
final String name;
final String? name;
/// The style of the action
final String style;
final String? style;
/// The test of the action
final String text;
final String? text;
/// The type of the action
final String type;
final String? type;
/// The value of the action
final String value;
final String? value;
/// Serialize to json
Map<String, dynamic> toJson() => _$ActionToJson(this);
@@ -13,10 +13,10 @@ part 'attachment.g.dart';
class Attachment {
/// Constructor used for json serialization
Attachment({
String id,
String? id,
this.type,
this.titleLink,
String title,
String? title,
this.thumbUrl,
this.text,
this.pretext,
@@ -34,10 +34,10 @@ class Attachment {
this.actions,
this.extraData,
this.file,
UploadState uploadState,
UploadState? uploadState,
}) : id = id ?? const Uuid().v4(),
title = title ?? file?.name,
localUri = file?.path != null ? Uri.parse(file.path) : null {
localUri = file?.path != null ? Uri.parse(file!.path!) : null {
this.uploadState = uploadState ??
((assetUrl != null || imageUrl != null)
? const UploadState.success()
@@ -47,68 +47,68 @@ class Attachment {
/// Create a new instance from a json
factory Attachment.fromJson(Map<String, dynamic> json) =>
_$AttachmentFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
Serialization.moveToExtraDataFromRoot(json, topLevelFields)!);
/// Create a new instance from a db data
factory Attachment.fromData(Map<String, dynamic> json) =>
_$AttachmentFromJson(Serialization.moveToExtraDataFromRoot(
json, topLevelFields + dbSpecificTopLevelFields));
json, topLevelFields + dbSpecificTopLevelFields)!);
///The attachment type based on the URL resource. This can be: audio,
///image or video
final String type;
final String? type;
///The link to which the attachment message points to.
final String titleLink;
final String? titleLink;
/// The attachment title
final String title;
final String? title;
/// The URL to the attached file thumbnail. You can use this to represent the
/// attached link.
final String thumbUrl;
final String? thumbUrl;
/// The attachment text. It will be displayed in the channel next to the
/// original message.
final String text;
final String? text;
/// Optional text that appears above the attachment block
final String pretext;
final String? pretext;
/// The original URL that was used to scrape this attachment.
final String ogScrapeUrl;
final String? ogScrapeUrl;
/// The URL to the attached image. This is present for URL pointing to an
/// image article (eg. Unsplash)
final String imageUrl;
final String footerIcon;
final String footer;
final String? imageUrl;
final String? footerIcon;
final String? footer;
final dynamic fields;
final String fallback;
final String color;
final String? fallback;
final String? color;
/// The name of the author.
final String authorName;
final String authorLink;
final String authorIcon;
final String? authorName;
final String? authorLink;
final String? authorIcon;
/// The URL to the audio, video or image related to the URL.
final String assetUrl;
final String? assetUrl;
/// Actions from a command
final List<Action> actions;
final List<Action>? actions;
final Uri localUri;
final Uri? localUri;
/// The file present inside this attachment.
final AttachmentFile file;
final AttachmentFile? file;
/// The current upload state of the attachment
UploadState uploadState;
UploadState? uploadState;
/// Map of custom channel extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
final Map<String, dynamic>? extraData;
/// The attachment ID.
///
@@ -156,28 +156,28 @@ class Attachment {
_$AttachmentToJson(this), topLevelFields + dbSpecificTopLevelFields);
Attachment copyWith({
String id,
String type,
String titleLink,
String title,
String thumbUrl,
String text,
String pretext,
String ogScrapeUrl,
String imageUrl,
String footerIcon,
String footer,
String? id,
String? type,
String? titleLink,
String? title,
String? thumbUrl,
String? text,
String? pretext,
String? ogScrapeUrl,
String? imageUrl,
String? footerIcon,
String? footer,
dynamic fields,
String fallback,
String color,
String authorName,
String authorLink,
String authorIcon,
String assetUrl,
List<Action> actions,
AttachmentFile file,
UploadState uploadState,
Map<String, dynamic> extraData,
String? fallback,
String? color,
String? authorName,
String? authorLink,
String? authorIcon,
String? assetUrl,
List<Action>? actions,
AttachmentFile? file,
UploadState? uploadState,
Map<String, dynamic>? extraData,
}) =>
Attachment(
id: id ?? this.id,
@@ -19,7 +19,7 @@ abstract class UploadState with _$UploadState {
const factory UploadState.success() = Success;
/// Failed state of the union
const factory UploadState.failed({@required String error}) = Failed;
const factory UploadState.failed({required String error}) = Failed;
/// Creates a new instance from a json
factory UploadState.fromJson(Map<String, dynamic> json) =>
@@ -27,7 +27,7 @@ abstract class UploadState with _$UploadState {
}
/// Helper extension for UploadState
extension UploadStateX on UploadState {
extension UploadStateX on UploadState? {
/// Returns true if state is [Preparing]
bool get isPreparing => this is Preparing;
@@ -65,21 +65,21 @@ class AttachmentFile {
/// ```
/// final File myFile = File(platformFile.path);
/// ```
final String path;
final String? path;
/// File name including its extension.
final String name;
final String? name;
/// Byte data for this file. Particularly useful if you want to manipulate
/// its data or easily upload to somewhere else.
@JsonKey(toJson: _toString, fromJson: _fromString)
final Uint8List bytes;
final Uint8List? bytes;
/// The file size in bytes.
final int size;
final int? size;
/// File extension for this file.
String get extension => name?.split('.')?.last;
String? get extension => name?.split('.')?.last;
/// Serialize to json
Map<String, dynamic> toJson() => _$AttachmentFileToJson(this);
@@ -30,52 +30,52 @@ class ChannelConfig {
_$ChannelConfigFromJson(json);
/// Moderation configuration
final String automod;
final String? automod;
/// List of available commands
final List<Command> commands;
final List<Command>? commands;
/// True if the channel should send connect events
final bool connectEvents;
final bool? connectEvents;
/// Date of channel creation
final DateTime createdAt;
final DateTime? createdAt;
/// Date of last channel update
final DateTime updatedAt;
final DateTime? updatedAt;
/// Max channel message length
final int maxMessageLength;
final int? maxMessageLength;
/// Duration of message retention
final String messageRetention;
final String? messageRetention;
/// True if users can be muted
final bool mutes;
final bool? mutes;
/// Name of the channel
final String name;
final String? name;
/// True if reaction are active for this channel
final bool reactions;
final bool? reactions;
/// True if readEvents are active for this channel
final bool readEvents;
final bool? readEvents;
/// 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
final bool search;
final bool? search;
/// 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
final bool uploads;
final bool? uploads;
/// True if urls appears as attachments
final bool urlEnrichment;
final bool? urlEnrichment;
/// Serialize to json
Map<String, dynamic> toJson() => _$ChannelConfigToJson(this);
@@ -26,59 +26,59 @@ class ChannelModel {
});
/// Create a new instance from a json
factory ChannelModel.fromJson(Map<String, dynamic> json) =>
factory ChannelModel.fromJson(Map<String, dynamic>? json) =>
_$ChannelModelFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
Serialization.moveToExtraDataFromRoot(json, topLevelFields)!);
/// The id of this channel
final String id;
final String? id;
/// The type of this channel
final String type;
final String? type;
/// The cid of this channel
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String cid;
final String? cid;
/// The channel configuration data
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final ChannelConfig config;
final ChannelConfig? config;
/// The user that created this channel
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final User createdBy;
final User? createdBy;
/// True if this channel is frozen
@JsonKey(includeIfNull: false)
final bool frozen;
final bool? frozen;
/// The date of the last message
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime lastMessageAt;
final DateTime? lastMessageAt;
/// The date of channel creation
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime createdAt;
final DateTime? createdAt;
/// The date of the last channel update
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime updatedAt;
final DateTime? updatedAt;
/// The date of channel deletion
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime deletedAt;
final DateTime? deletedAt;
/// The count of this channel members
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final int memberCount;
final int? memberCount;
/// Map of custom channel extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
final Map<String, dynamic>? extraData;
/// The team the channel belongs to
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String team;
final String? team;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -98,8 +98,8 @@ class ChannelModel {
];
/// Shortcut for channel name
String get name =>
extraData?.containsKey('name') == true ? extraData['name'] : cid;
String? get name =>
extraData?.containsKey('name') == true ? extraData!['name'] : cid;
/// Serialize to json
Map<String, dynamic> toJson() => Serialization.moveFromExtraDataToRoot(
@@ -109,19 +109,19 @@ class ChannelModel {
/// Creates a copy of [ChannelModel] with specified attributes overridden.
ChannelModel copyWith({
String id,
String type,
String cid,
ChannelConfig config,
User createdBy,
bool frozen,
DateTime lastMessageAt,
DateTime createdAt,
DateTime updatedAt,
DateTime deletedAt,
int memberCount,
Map<String, dynamic> extraData,
String team,
String? id,
String? type,
String? cid,
ChannelConfig? config,
User? createdBy,
bool? frozen,
DateTime? lastMessageAt,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? deletedAt,
int? memberCount,
Map<String, dynamic>? extraData,
String? team,
}) =>
ChannelModel(
id: id ?? this.id,
@@ -141,7 +141,7 @@ class ChannelModel {
/// Returns a new [ChannelModel] that is a combination of this channelModel
/// and the given [other] channelModel.
ChannelModel merge(ChannelModel other) {
ChannelModel merge(ChannelModel? other) {
if (other == null) return this;
return copyWith(
id: other.id,
@@ -22,42 +22,42 @@ class ChannelState {
});
/// The channel to which this state belongs
final ChannelModel channel;
final ChannelModel? channel;
/// A paginated list of channel messages
final List<Message> messages;
final List<Message>? messages;
/// A paginated list of channel members
final List<Member> members;
final List<Member?>? members;
/// A paginated list of pinned messages
final List<Message> pinnedMessages;
final List<Message>? pinnedMessages;
/// The count of users watching the channel
final int watcherCount;
final int? watcherCount;
/// A paginated list of users watching the channel
final List<User> watchers;
final List<User>? watchers;
/// The list of channel reads
final List<Read> read;
final List<Read>? read;
/// Create a new instance from a json
static ChannelState fromJson(Map<String, dynamic> json) =>
_$ChannelStateFromJson(json);
static ChannelState fromJson(Map<String, dynamic>? json) =>
_$ChannelStateFromJson(json!);
/// Serialize to json
Map<String, dynamic> toJson() => _$ChannelStateToJson(this);
/// Creates a copy of [ChannelState] with specified attributes overridden.
ChannelState copyWith({
ChannelModel channel,
List<Message> messages,
List<Member> members,
List<Message> pinnedMessages,
int watcherCount,
List<User> watchers,
List<Read> read,
ChannelModel? channel,
List<Message>? messages,
List<Member?>? members,
List<Message>? pinnedMessages,
int? watcherCount,
List<User>? watchers,
List<Read>? read,
}) =>
ChannelState(
channel: channel ?? this.channel,
@@ -17,13 +17,13 @@ class Command {
_$CommandFromJson(json);
/// The name of the command
final String name;
final String? name;
/// The description explaining the command
final String description;
final String? description;
/// The arguments of the command
final String args;
final String? args;
/// Serialize to json
Map<String, dynamic> toJson() => _$CommandToJson(this);
@@ -15,10 +15,10 @@ class Device {
factory Device.fromJson(Map<String, dynamic> json) => _$DeviceFromJson(json);
/// The id of the device
final String id;
final String? id;
/// The notification push provider
final String pushProvider;
final String? pushProvider;
/// Serialize to json
Map<String, dynamic> toJson() => _$DeviceToJson(this);
+52 -52
View File
@@ -31,68 +31,68 @@ class Event {
}) : isLocal = true;
/// Create a new instance from a json
factory Event.fromJson(Map<String, dynamic> json) =>
factory Event.fromJson(Map<String, dynamic>? json) =>
_$EventFromJson(Serialization.moveToExtraDataFromRoot(
json,
topLevelFields,
))
)!)
..isLocal = false;
/// The type of the event
/// [EventType] contains some predefined constant types
final String type;
final String? type;
/// The channel cid to which the event belongs
final String cid;
final String? cid;
/// The channel id to which the event belongs
final String channelId;
final String? channelId;
/// The channel type to which the event belongs
final String channelType;
final String? channelType;
/// The connection id in which the event has been sent
final String connectionId;
final String? connectionId;
/// The date of creation of the event
final DateTime createdAt;
final DateTime? createdAt;
/// User object of the health check user
final OwnUser me;
final OwnUser? me;
/// User object of the current user
final User user;
final User? user;
/// The message sent with the event
final Message message;
final Message? message;
/// The channel sent with the event
final EventChannel channel;
final EventChannel? channel;
/// The member sent with the event
final Member member;
final Member? member;
/// The reaction sent with the event
final Reaction reaction;
final Reaction? reaction;
/// The number of unread messages for current user
final int totalUnreadCount;
final int? totalUnreadCount;
/// User total unread channels
final int unreadChannels;
final int? unreadChannels;
/// Online status
final bool online;
final bool? online;
/// The id of the parent message of a thread
final String parentId;
final String? parentId;
/// True if the event is generated by this client
bool isLocal;
bool? isLocal;
/// Map of custom channel extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
final Map<String, dynamic>? extraData;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -124,23 +124,23 @@ class Event {
/// Creates a copy of [Event] with specified attributes overridden.
Event copyWith({
String type,
String cid,
String channelId,
String channelType,
String connectionId,
DateTime createdAt,
OwnUser me,
User user,
Message message,
EventChannel channel,
Member member,
Reaction reaction,
int totalUnreadCount,
int unreadChannels,
bool online,
String parentId,
Map<String, dynamic> extraData,
String? type,
String? cid,
String? channelId,
String? channelType,
String? connectionId,
DateTime? createdAt,
OwnUser? me,
User? user,
Message? message,
EventChannel? channel,
Member? member,
Reaction? reaction,
int? totalUnreadCount,
int? unreadChannels,
bool? online,
String? parentId,
Map<String, dynamic>? extraData,
}) =>
Event(
type: type ?? this.type,
@@ -169,18 +169,18 @@ class EventChannel extends ChannelModel {
/// Constructor used for json serialization
EventChannel({
this.members,
String id,
String type,
String cid,
ChannelConfig config,
User createdBy,
bool frozen,
DateTime lastMessageAt,
DateTime createdAt,
DateTime updatedAt,
DateTime deletedAt,
int memberCount,
Map<String, dynamic> extraData,
String? id,
String? type,
String? cid,
ChannelConfig? config,
User? createdBy,
bool? frozen,
DateTime? lastMessageAt,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? deletedAt,
int? memberCount,
Map<String, dynamic>? extraData,
}) : super(
id: id,
type: type,
@@ -197,14 +197,14 @@ class EventChannel extends ChannelModel {
);
/// Create a new instance from a json
factory EventChannel.fromJson(Map<String, dynamic> json) =>
factory EventChannel.fromJson(Map<String, dynamic>? json) =>
_$EventChannelFromJson(Serialization.moveToExtraDataFromRoot(
json,
topLevelFields,
));
)!);
/// A paginated list of channel members
final List<Member> members;
final List<Member>? members;
/// Known top level fields.
/// Useful for [Serialization] methods.
+22 -22
View File
@@ -31,51 +31,51 @@ class Member {
}
/// The interested user
final User user;
final User? user;
/// The date in which the user accepted the invite to the channel
final DateTime inviteAcceptedAt;
final DateTime? inviteAcceptedAt;
/// The date in which the user rejected the invite to the channel
final DateTime inviteRejectedAt;
final DateTime? inviteRejectedAt;
/// True if the user has been invited to the channel
final bool invited;
final bool? invited;
/// The role of the user in the channel
final String role;
final String? role;
/// The id of the interested user
final String userId;
final String? userId;
/// True if the user is a moderator of the channel
final bool isModerator;
final bool? isModerator;
/// 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
final bool shadowBanned;
final bool? shadowBanned;
/// The date of creation
final DateTime createdAt;
final DateTime? createdAt;
/// The last date of update
final DateTime updatedAt;
final DateTime? updatedAt;
/// Creates a copy of [Member] with specified attributes overridden.
Member copyWith({
User user,
DateTime inviteAcceptedAt,
DateTime inviteRejectedAt,
bool invited,
String role,
String userId,
bool isModerator,
DateTime createdAt,
DateTime updatedAt,
bool banned,
bool shadowBanned,
User? user,
DateTime? inviteAcceptedAt,
DateTime? inviteRejectedAt,
bool? invited,
String? role,
String? userId,
bool? isModerator,
DateTime? createdAt,
DateTime? updatedAt,
bool? banned,
bool? shadowBanned,
}) =>
Member(
user: user ?? this.user,
@@ -44,7 +44,7 @@ enum MessageSendingStatus {
class Message {
/// Constructor used for json serialization
Message({
String id,
String? id,
this.text,
this.type,
this.attachments,
@@ -67,7 +67,7 @@ class Message {
this.user,
this.pinned = false,
this.pinnedAt,
DateTime pinExpires,
DateTime? pinExpires,
this.pinnedBy,
this.extraData,
this.deletedAt,
@@ -77,15 +77,15 @@ class Message {
pinExpires = pinExpires?.toUtc();
/// Create a new instance from a json
factory Message.fromJson(Map<String, dynamic> json) => _$MessageFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
factory Message.fromJson(Map<String, dynamic>? json) => _$MessageFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields)!);
/// The message ID. This is either created by Stream or set client side when
/// the message is added.
final String id;
/// The text of this message
final String text;
final String? text;
/// The status of a sending message
@JsonKey(ignore: true)
@@ -93,99 +93,99 @@ class Message {
/// The message type
@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
/// command or as a result of URL scraping.
@JsonKey(includeIfNull: false)
final List<Attachment> attachments;
final List<Attachment>? attachments;
/// The list of user mentioned in the message
@JsonKey(toJson: Serialization.userIds)
final List<User> mentionedUsers;
final List<User>? mentionedUsers;
/// A map describing the count of number of every reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final Map<String, int> reactionCounts;
final Map<String?, int>? reactionCounts;
/// A map describing the count of score of every reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final Map<String, int> reactionScores;
final Map<String?, int>? reactionScores;
/// The latest reactions to the message created by any user.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<Reaction> latestReactions;
final List<Reaction>? latestReactions;
/// The reactions added to the message by the current user.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<Reaction> ownReactions;
final List<Reaction>? ownReactions;
/// The ID of the parent message, if the message is a thread reply.
final String parentId;
final String? parentId;
/// A quoted reply message
@JsonKey(toJson: Serialization.readOnly)
final Message quotedMessage;
final Message? quotedMessage;
/// The ID of the quoted message, if the message is a quoted reply.
final String quotedMessageId;
final String? quotedMessageId;
/// Reserved field indicating the number of replies for this message.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final int replyCount;
final int? replyCount;
/// Reserved field indicating the thread participants for this message.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<User> threadParticipants;
final List<User>? threadParticipants;
/// Check if this message needs to show in the channel.
final bool showInChannel;
final bool? showInChannel;
/// If true the message is silent
final bool silent;
final bool? silent;
/// If true the message will not send a push notification
final bool skipPush;
final bool? skipPush;
/// If true the message is shadowed
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final bool shadowed;
final bool? shadowed;
/// A used command name.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String command;
final String? command;
/// Reserved field indicating when the message was created.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime createdAt;
final DateTime? createdAt;
/// Reserved field indicating when the message was updated last time.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime updatedAt;
final DateTime? updatedAt;
/// User who sent the message
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final User user;
final User? user;
/// If true the message is pinned
final bool pinned;
final bool? pinned;
/// Reserved field indicating when the message was pinned
@JsonKey(toJson: Serialization.readOnly)
final DateTime pinnedAt;
final DateTime? pinnedAt;
/// Reserved field indicating when the message will expire
///
/// if `null` message has no expiry
final DateTime pinExpires;
final DateTime? pinExpires;
/// Reserved field indicating who pinned the message
@JsonKey(toJson: Serialization.readOnly)
final User pinnedBy;
final User? pinnedBy;
/// Message custom extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
final Map<String, dynamic>? extraData;
/// True if the message is a system info
bool get isSystem => type == 'system';
@@ -198,7 +198,7 @@ class Message {
/// Reserved field indicating when the message was deleted.
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime deletedAt;
final DateTime? deletedAt;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -239,35 +239,35 @@ class Message {
/// Creates a copy of [Message] with specified attributes overridden.
Message copyWith({
String id,
String text,
String type,
List<Attachment> attachments,
List<User> mentionedUsers,
Map<String, int> reactionCounts,
Map<String, int> reactionScores,
List<Reaction> latestReactions,
List<Reaction> ownReactions,
String parentId,
Message quotedMessage,
String quotedMessageId,
int replyCount,
List<User> threadParticipants,
bool showInChannel,
bool shadowed,
bool silent,
String command,
DateTime createdAt,
DateTime updatedAt,
DateTime deletedAt,
User user,
bool pinned,
DateTime pinnedAt,
Object pinExpires = _pinExpires,
User pinnedBy,
Map<String, dynamic> extraData,
MessageSendingStatus status,
bool skipPush,
String? id,
String? text,
String? type,
List<Attachment>? attachments,
List<User>? mentionedUsers,
Map<String?, int>? reactionCounts,
Map<String?, int>? reactionScores,
List<Reaction>? latestReactions,
List<Reaction>? ownReactions,
String? parentId,
Message? quotedMessage,
String? quotedMessageId,
int? replyCount,
List<User>? threadParticipants,
bool? showInChannel,
bool? shadowed,
bool? silent,
String? command,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? deletedAt,
User? user,
bool? pinned,
DateTime? pinnedAt,
Object? pinExpires = _pinExpires,
User? pinnedBy,
Map<String, dynamic>? extraData,
MessageSendingStatus? status,
bool? skipPush,
}) {
assert(() {
if (pinExpires is! DateTime &&
@@ -305,7 +305,8 @@ class Message {
pinned: pinned ?? this.pinned,
pinnedAt: pinnedAt ?? this.pinnedAt,
pinnedBy: pinnedBy ?? this.pinnedBy,
pinExpires: pinExpires == _pinExpires ? this.pinExpires : pinExpires,
pinExpires:
pinExpires == _pinExpires ? this.pinExpires : pinExpires as DateTime?,
skipPush: skipPush ?? this.skipPush,
);
}
@@ -355,13 +356,13 @@ class TranslatedMessage extends Message {
TranslatedMessage(this.i18n);
/// Create a new instance from a json
factory TranslatedMessage.fromJson(Map<String, dynamic> json) =>
factory TranslatedMessage.fromJson(Map<String, dynamic>? json) =>
_$TranslatedMessageFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields),
Serialization.moveToExtraDataFromRoot(json, topLevelFields)!,
);
/// A Map of
final Map<String, String> i18n;
final Map<String, String>? i18n;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -16,19 +16,19 @@ class Mute {
/// The user that performed the muting action
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final User user;
final User? user;
/// The target user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final ChannelModel channel;
final ChannelModel? channel;
/// The date in which the use was muted
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime createdAt;
final DateTime? createdAt;
/// The date of the last update
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime updatedAt;
final DateTime? updatedAt;
/// Serialize to json
Map<String, dynamic> toJson() => _$MuteToJson(this);
@@ -17,14 +17,14 @@ class OwnUser extends User {
this.totalUnreadCount,
this.unreadChannels,
this.channelMutes,
String id,
String role,
DateTime createdAt,
DateTime updatedAt,
DateTime lastActive,
bool online,
Map<String, dynamic> extraData,
bool banned,
String? id,
String? role,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? lastActive,
bool? online,
Map<String, dynamic>? extraData,
bool? banned,
}) : super(
id: id,
role: role,
@@ -37,28 +37,28 @@ class OwnUser extends User {
);
/// Create a new instance from a json
factory OwnUser.fromJson(Map<String, dynamic> json) => _$OwnUserFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
factory OwnUser.fromJson(Map<String, dynamic>? json) => _$OwnUserFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields)!);
/// List of user devices
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<Device> devices;
final List<Device>? devices;
/// List of users muted by the user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<Mute> mutes;
final List<Mute>? mutes;
/// List of users muted by the user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<Mute> channelMutes;
final List<Mute>? channelMutes;
/// Total unread messages by the user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final int totalUnreadCount;
final int? totalUnreadCount;
/// Total unread channels by the user
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final int unreadChannels;
final int? unreadChannels;
/// Known top level fields.
/// Useful for [Serialization] methods.
@@ -13,39 +13,39 @@ class Reaction {
this.createdAt,
this.type,
this.user,
String userId,
String? userId,
this.score,
this.extraData,
}) : userId = userId ?? user?.id;
/// Create a new instance from a json
factory Reaction.fromJson(Map<String, dynamic> json) => _$ReactionFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
factory Reaction.fromJson(Map<String, dynamic>? json) => _$ReactionFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields)!);
/// The messageId to which the reaction belongs
final String messageId;
final String? messageId;
/// The type of the reaction
final String type;
final String? type;
/// The date of the reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime createdAt;
final DateTime? createdAt;
/// The user that sent the reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final User user;
final User? user;
/// The score of the reaction (ie. number of reactions sent)
final int score;
final int? score;
/// The userId that sent the reaction
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String userId;
final String? userId;
/// Reaction custom extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
final Map<String, dynamic>? extraData;
/// Map of custom user extraData
static const topLevelFields = [
@@ -63,13 +63,13 @@ class Reaction {
/// Creates a copy of [Reaction] with specified attributes overridden.
Reaction copyWith({
String messageId,
DateTime createdAt,
String type,
User user,
String userId,
int score,
Map<String, dynamic> extraData,
String? messageId,
DateTime? createdAt,
String? type,
User? user,
String? userId,
int? score,
Map<String, dynamic>? extraData,
}) =>
Reaction(
messageId: messageId ?? this.messageId,
@@ -17,22 +17,22 @@ class Read {
factory Read.fromJson(Map<String, dynamic> json) => _$ReadFromJson(json);
/// Date of the read event
final DateTime lastRead;
final DateTime? lastRead;
/// User who sent the event
final User user;
final User? user;
/// Number of unread messages
final int unreadMessages;
final int? unreadMessages;
/// Serialize to json
Map<String, dynamic> toJson() => _$ReadToJson(this);
/// Creates a copy of [Read] with specified attributes overridden.
Read copyWith({
DateTime lastRead,
User user,
int unreadMessages,
DateTime? lastRead,
User? user,
int? unreadMessages,
}) =>
Read(
lastRead: lastRead ?? this.lastRead,
@@ -10,12 +10,12 @@ class Serialization {
static const Function readOnly = readonly;
/// 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();
/// Takes unknown json keys and puts them in the `extra_data` key
static Map<String, dynamic> moveToExtraDataFromRoot(
Map<String, dynamic> json,
static Map<String, dynamic>? moveToExtraDataFromRoot(
Map<String, dynamic>? json,
List<String> topLevelFields,
) {
if (json == null) return null;
+23 -23
View File
@@ -20,8 +20,8 @@ class User {
});
/// Create a new instance from a json
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields));
factory User.fromJson(Map<String, dynamic>? json) => _$UserFromJson(
Serialization.moveToExtraDataFromRoot(json, topLevelFields)!);
/// Use this named constructor to create a new user instance
User.init(
@@ -49,47 +49,47 @@ class User {
];
/// User id
final String id;
final String? id;
/// User role
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final String role;
final String? role;
/// User role
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final List<String> teams;
final List<String>? teams;
/// Date of user creation
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime createdAt;
final DateTime? createdAt;
/// Date of last user update
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime updatedAt;
final DateTime? updatedAt;
/// Date of last user connection
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final DateTime lastActive;
final DateTime? lastActive;
/// True if user is online
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final bool online;
final bool? online;
/// True if user is banned from the chat
@JsonKey(includeIfNull: false, toJson: Serialization.readOnly)
final bool banned;
final bool? banned;
/// Map of custom user extraData
@JsonKey(includeIfNull: false)
final Map<String, dynamic> extraData;
final Map<String, dynamic>? extraData;
@override
int get hashCode => id.hashCode;
/// Shortcut for user name
String get name =>
(extraData?.containsKey('name') == true && extraData['name'] != '')
? extraData['name']
String? get name =>
(extraData?.containsKey('name') == true && extraData!['name'] != '')
? extraData!['name']
: id;
@override
@@ -103,15 +103,15 @@ class User {
/// Creates a copy of [User] with specified attributes overridden.
User copyWith({
String id,
String role,
DateTime createdAt,
DateTime updatedAt,
DateTime lastActive,
bool online,
Map<String, dynamic> extraData,
bool banned,
List<String> teams,
String? id,
String? role,
DateTime? createdAt,
DateTime? updatedAt,
DateTime? lastActive,
bool? online,
Map<String, dynamic>? extraData,
bool? banned,
List<String>? teams,
}) =>
User(
id: id ?? this.id,
+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
environment:
sdk: ">=2.7.0 <3.0.0"
sdk: '>=2.12.0 <3.0.0'
dependencies:
async: ^2.5.0
collection: ^1.15.0
dio: ">=4.0.0-prev3 <4.0.0"
freezed_annotation: ^0.14.0
dio: ^4.0.0
freezed_annotation: ^0.14.1
http_parser: ^4.0.0
json_annotation: ^4.0.0
logging: ^1.0.0
json_annotation: ^4.0.1
logging: ^1.0.1
meta: ^1.3.0
mime: ^1.0.0
rxdart: ^0.26.0
uuid: ^3.0.0
uuid: ^3.0.4
web_socket_channel: ^2.0.0
dev_dependencies:
build_runner: ^1.10.0
freezed: ^0.14.0
json_serializable: ^4.0.0
mocktail: ^0.1.0
test: ^1.16.0
freezed: ^0.14.1+2
json_serializable: ^4.1.0
mocktail: ^0.1.1
test: ^1.16.8
@@ -50,7 +50,7 @@ void main() {
),
);
await channelClient.sendMessage(message);
await channelClient?.sendMessage(message);
verify(() =>
mockDio.post<String>('/channels/messaging/testid/message', data: {
@@ -80,7 +80,7 @@ void main() {
statusCode: 200,
requestOptions: FakeRequestOptions(),
));
await channelClient.watch();
await channelClient?.watch();
when(
() => mockDio.post<String>(
@@ -95,7 +95,7 @@ void main() {
),
);
await channelClient.markRead();
await channelClient?.markRead();
verify(() => mockDio.post<String>('/channels/messaging/testid/read',
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',
queryParameters: pagination.toJson())).called(1);
@@ -141,7 +141,7 @@ void main() {
httpClient: mockDio,
tokenProvider: (_) async => '',
);
final channelClient = client.channel('messaging', id: 'testid');
Channel channelClient = client.channel('messaging', id: 'testid');
when(() => mockDio.post<String>(
any(),
@@ -564,7 +564,11 @@ void main() {
'api-key',
httpClient: mockDio,
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');
const reactionType = 'test';
@@ -617,7 +621,11 @@ void main() {
'api-key',
httpClient: mockDio,
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');
@@ -1069,8 +1077,8 @@ void main() {
verify(() => mockDio.post<String>('/channels/messaging/query',
data: options)).called(1);
expect(channelClient.id, response.channel.id);
expect(channelClient.cid, response.channel.cid);
expect(channelClient.id, response?.channel?.id);
expect(channelClient.cid, response?.channel?.cid);
});
test('with id', () async {
@@ -1706,8 +1714,8 @@ void main() {
verify(() => mockDio.post<String>('/channels/messaging/query',
data: options)).called(1);
expect(channelClient.id, response.channel.id);
expect(channelClient.cid, response.channel.cid);
expect(channelClient.id, response?.channel?.id);
expect(channelClient.cid, response?.channel?.cid);
});
test('watch', () async {
@@ -2027,8 +2035,8 @@ void main() {
verify(() => mockDio.post<String>('/channels/messaging/query',
data: options)).called(1);
expect(channelClient.id, response.channel.id);
expect(channelClient.cid, response.channel.cid);
expect(channelClient.id, response.channel?.id);
expect(channelClient.cid, response.channel?.cid);
});
test('stopWatching', () async {
@@ -12,12 +12,10 @@ import 'package:web_socket_channel/web_socket_channel.dart';
class Functions {
WebSocketChannel connectFunc(
String url, {
Iterable<String> protocols,
Map<String, dynamic> headers,
Duration pingInterval,
String? url, {
Iterable<String>? protocols,
}) =>
null;
WebSocketChannel.connect(Uri());
void handleFunc(Event event) {}
}
@@ -94,7 +92,7 @@ void main() {
when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink());
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
final connect = ws.connect().then((_) {
final connect = ws.connect()?.then((_) {
streamController.sink.add('{}');
return Future.delayed(const Duration(milliseconds: 200));
}).then((value) {
@@ -130,7 +128,7 @@ void main() {
when(() => mockWSChannel.sink).thenAnswer((_) => MockWSSink());
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
final connect = ws.connect().then((_) {
final connect = ws.connect()?.then((_) {
streamController.sink.add('{}');
return Future.delayed(const Duration(milliseconds: 200));
}).then((value) {
@@ -208,7 +206,7 @@ void main() {
(_) => streamController.sink.add('{}'),
);
final connect = ws.connect().then((_) {
final connect = ws.connect()?.then((_) {
streamController.sink.add('{}');
return Future.delayed(const Duration(milliseconds: 200));
}).then((value) async {
@@ -249,7 +247,7 @@ void main() {
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
when(() => mockWSChannel.sink).thenReturn(mockWSSink);
final connect = ws.connect().then((_) {
final connect = ws.connect()?.then((_) {
streamController.sink.add('{}');
streamController.close();
streamController = StreamController<String>.broadcast();
@@ -292,7 +290,7 @@ void main() {
when(() => mockWSChannel.stream).thenAnswer((_) => streamController.stream);
when(() => mockWSChannel.sink).thenReturn(mockWSSink);
final connect = ws.connect().then((_) {
final connect = ws.connect()?.then((_) {
streamController.sink.add('{}');
return Future.delayed(const Duration(milliseconds: 200));
}).then((value) async {
@@ -49,7 +49,7 @@ void main() {
'https://media0.giphy.com/media/3o7TKnCdBx5cMg0qti/giphy.gif',
);
expect(attachment.actions, hasLength(3));
expect(attachment.actions[0], isA<Action>());
expect(attachment.actions![0], isA<Action>());
});
test('should serialize to json correctly', () {
@@ -10,7 +10,8 @@ import 'package:stream_chat/stream_chat.dart';
void main() {
group('src/models/channel_state', () {
const jsonExample = '''{
const jsonExample = '''
{
"channel": {
"id": "dev",
"type": "team",
@@ -844,36 +845,36 @@ void main() {
test('should parse json correctly', () {
final channelState = ChannelState.fromJson(json.decode(jsonExample));
expect(channelState.channel.cid, 'team:dev');
expect(channelState.channel.id, 'dev');
expect(channelState.channel.team, 'test');
expect(channelState.channel.type, 'team');
expect(channelState.channel.config, isA<ChannelConfig>());
expect(channelState.channel.config, isNotNull);
expect(channelState.channel.config.commands, hasLength(1));
expect(channelState.channel.config.commands[0], isA<Command>());
expect(channelState.channel.lastMessageAt,
expect(channelState.channel?.cid, 'team:dev');
expect(channelState.channel?.id, 'dev');
expect(channelState.channel?.team, 'test');
expect(channelState.channel?.type, 'team');
expect(channelState.channel?.config, isA<ChannelConfig>());
expect(channelState.channel?.config, isNotNull);
expect(channelState.channel?.config?.commands, hasLength(1));
expect(channelState.channel?.config?.commands![0], isA<Command>());
expect(channelState.channel?.lastMessageAt,
DateTime.parse('2020-01-30T13:43:41.062362Z'));
expect(channelState.channel.createdAt,
expect(channelState.channel?.createdAt,
DateTime.parse('2019-04-03T18:43:33.213373Z'));
expect(channelState.channel.updatedAt,
expect(channelState.channel?.updatedAt,
DateTime.parse('2019-04-03T18:43:33.213374Z'));
expect(channelState.channel.createdBy, isA<User>());
expect(channelState.channel.frozen, true);
expect(channelState.channel.extraData['example'], 1);
expect(channelState.channel.extraData['name'], '#dev');
expect(channelState.channel?.createdBy, isA<User>());
expect(channelState.channel?.frozen, true);
expect(channelState.channel?.extraData!['example'], 1);
expect(channelState.channel?.extraData!['name'], '#dev');
expect(
channelState.channel.extraData['image'],
channelState.channel?.extraData!['image'],
'https://cdn.chrisshort.net/testing-certificate-chains-in-go/GOPHER_MIC_DROP.png',
);
expect(channelState.messages, hasLength(25));
expect(channelState.messages[0], isA<Message>());
expect(channelState.messages[0], isNotNull);
expect(channelState.messages![0], isA<Message>());
expect(channelState.messages![0], isNotNull);
expect(
channelState.messages[0].createdAt,
channelState.messages![0].createdAt,
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);
});
@@ -20,8 +20,8 @@ void main() {
expect(channel.id, equals('test'));
expect(channel.type, equals('livestream'));
expect(channel.cid, equals('test:livestream'));
expect(channel.extraData['cats'], equals(true));
expect(channel.extraData['fruit'], equals(['bananas', 'apples']));
expect(channel.extraData!['cats'], equals(true));
expect(channel.extraData!['fruit'], equals(['bananas', 'apples']));
});
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.type, 'wow');
expect(
reaction.user.toJson(),
reaction.user?.toJson(),
User.init('2de0297c-f3f2-489d-b930-ef77342edccf', extraData: {
'image': 'https://randomuser.me/api/portraits/women/45.jpg',
'name': 'Daisy Morgan'
@@ -19,7 +19,7 @@ void main() {
test('should parse json correctly', () {
final read = Read.fromJson(json.decode(jsonExample));
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);
});