From 74fb9d8dd83441d27a715f1c7f3529b3090f3824 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Thu, 8 Apr 2021 13:22:21 +0530 Subject: [PATCH 01/15] added non-nullability for core --- .../lib/src/channel_list_core.dart | 36 +++++++-------- .../lib/src/channels_bloc.dart | 39 ++++++++-------- .../lib/src/lazy_load_scroll_view.dart | 24 +++++----- .../lib/src/message_list_core.dart | 34 +++++++------- .../lib/src/message_search_bloc.dart | 33 +++++++------- .../lib/src/message_search_list_core.dart | 36 +++++++-------- .../lib/src/stream_channel.dart | 40 ++++++++--------- .../lib/src/stream_chat_core.dart | 28 ++++++------ .../lib/src/typedef.dart | 2 +- .../lib/src/user_list_core.dart | 45 ++++++++++--------- .../lib/src/users_bloc.dart | 26 +++++------ .../stream_chat_flutter_core/pubspec.yaml | 7 +-- .../test/matchers/channel_matcher.dart | 4 +- .../get_message_response_matcher.dart | 4 +- .../test/matchers/message_matcher.dart | 4 +- .../test/matchers/users_matcher.dart | 4 +- 16 files changed, 185 insertions(+), 181 deletions(-) diff --git a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart index 3adea312..70d97df7 100644 --- a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart @@ -57,11 +57,11 @@ import 'package:stream_chat_flutter_core/src/typedef.dart'; class ChannelListCore extends StatefulWidget { /// Instantiate a new ChannelListView const ChannelListCore({ - Key key, - @required this.errorBuilder, - @required this.emptyBuilder, - @required this.loadingBuilder, - @required this.listBuilder, + Key? key, + required this.errorBuilder, + required this.emptyBuilder, + required this.loadingBuilder, + required this.listBuilder, this.filter, this.options, this.sort, @@ -91,7 +91,7 @@ class ChannelListCore extends StatefulWidget { /// Use [ChannelListController.loadData] and /// [ChannelListController.paginateData] respectively for reloading and /// pagination. - final ChannelListController channelListController; + final ChannelListController? channelListController; /// The builder that will be used in case of error final ErrorBuilder errorBuilder; @@ -100,7 +100,7 @@ class ChannelListCore extends StatefulWidget { final WidgetBuilder loadingBuilder; /// The builder which is used when list of channels loads - final Function(BuildContext, List) listBuilder; + final Function(BuildContext, List) listBuilder; /// The builder used when the channel list is empty. final WidgetBuilder emptyBuilder; @@ -108,20 +108,20 @@ class ChannelListCore extends StatefulWidget { /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - final Map filter; + final Map? filter; /// Query channels options. /// /// state: if true returns the Channel state /// watch: if true listen to changes to this Channel in real time. - final Map options; + final Map? options; /// The sorting used for the channels matching the filters. /// Sorting is based on field and direction, multiple sorting options can be /// provided. /// You can sort based on last_updated, last_message_at, updated_at, created /// _at or member_count. Direction can be ascending or descending. - final List> sort; + final List>? sort; /// Pagination parameters /// limit: the number of channels to return (max is 30) @@ -142,10 +142,10 @@ class ChannelListCoreState extends State { return _buildListView(channelsBloc); } - StreamBuilder> _buildListView( + StreamBuilder> _buildListView( ChannelsBlocState channelsBlocState, ) => - StreamBuilder>( + StreamBuilder>( stream: channelsBlocState.channelsStream, builder: (context, snapshot) { if (snapshot.hasError) { @@ -154,7 +154,7 @@ class ChannelListCoreState extends State { if (!snapshot.hasData) { return widget.loadingBuilder(context); } - final channels = snapshot.data; + final channels = snapshot.data!; if (channels.isEmpty) { return widget.emptyBuilder(context); } @@ -186,7 +186,7 @@ class ChannelListCoreState extends State { ); } - StreamSubscription _subscription; + late StreamSubscription _subscription; @override void initState() { @@ -203,8 +203,8 @@ class ChannelListCoreState extends State { .listen((event) => loadData()); if (widget.channelListController != null) { - widget.channelListController.loadData = loadData; - widget.channelListController.paginateData = paginateData; + widget.channelListController!.loadData = loadData; + widget.channelListController!.paginateData = paginateData; } } @@ -233,10 +233,10 @@ class ChannelListCoreState extends State { class ChannelListController { /// This function calls Stream's servers to load a list of channels. /// If there is existing data, calling this function causes a reload. - AsyncCallback loadData; + AsyncCallback? loadData; /// This function is used to load another page of data. Note, [loadData] /// should be used to populate the initial page of data. Calling /// [paginateData] performs a query to load subsequent pages. - AsyncCallback paginateData; + AsyncCallback? paginateData; } diff --git a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart index 2b4968eb..2e42c0af 100644 --- a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart @@ -20,8 +20,8 @@ class ChannelsBloc extends StatefulWidget { /// Creates a new [ChannelsBloc]. The parameter [child] must be supplied and /// not null. const ChannelsBloc({ - Key key, - @required this.child, + Key? key, + required this.child, this.lockChannelsOrder = false, this.channelsComparator, this.shouldAddChannel, @@ -36,18 +36,18 @@ class ChannelsBloc extends StatefulWidget { final bool lockChannelsOrder; /// Comparator used to sort the channels when a message.new event is received - final Comparator channelsComparator; + final Comparator? channelsComparator; /// Function used to evaluate if a channel should be added to the list when a /// message.new event is received - final bool Function(Event) shouldAddChannel; + final bool Function(Event)? shouldAddChannel; @override ChannelsBlocState createState() => ChannelsBlocState(); /// Use this method to get the current [ChannelsBlocState] instance static ChannelsBlocState of(BuildContext context) { - ChannelsBlocState streamChatState; + ChannelsBlocState? streamChatState; streamChatState = context.findAncestorStateOfType(); @@ -69,14 +69,15 @@ class ChannelsBlocState extends State } /// The current channel list - List get channels => _channelsController.value; + List? get channels => _channelsController.value as List?; /// The current channel list as a stream - Stream> get channelsStream => _channelsController.stream; + Stream> get channelsStream => _channelsController.stream; final _queryChannelsLoadingController = BehaviorSubject.seeded(false); - final _channelsController = BehaviorSubject>(); + final BehaviorSubject> _channelsController = + BehaviorSubject>(); /// The stream notifying the state of queryChannel call Stream get queryChannelsLoading => @@ -88,10 +89,10 @@ class ChannelsBlocState extends State /// Calls [client.queryChannels] updating [queryChannelsLoading] stream Future queryChannels({ - Map filter, - List> sortOptions, - PaginationParams paginationParams, - Map options, + Map? filter, + List>? sortOptions, + PaginationParams? paginationParams, + Map? options, }) async { final client = StreamChatCore.of(context).client; @@ -110,10 +111,10 @@ class ChannelsBlocState extends State final oldChannels = List.from(channels ?? []); var newChannels = []; await for (final channels in client.queryChannels( - filter: filter, - sort: sortOptions, - options: options, - paginationParams: paginationParams, + filter: filter!, + sort: sortOptions!, + options: options!, + paginationParams: paginationParams!, )) { newChannels = channels; if (clear) { @@ -123,7 +124,7 @@ class ChannelsBlocState extends State _channelsController.add(temp); } if (_channelsController.hasValue && - _queryChannelsLoadingController.value) { + _queryChannelsLoadingController.value!) { _queryChannelsLoadingController.sink.add(false); } } @@ -149,8 +150,8 @@ class ChannelsBlocState extends State if (!widget.lockChannelsOrder) { _subscriptions.add(client.on(EventType.messageNew).listen((e) { - final newChannels = List.from(channels ?? []); - final index = newChannels.indexWhere((c) => c.cid == e.cid); + final newChannels = List.from(channels ?? []); + final index = newChannels.indexWhere((c) => c!.cid == e.cid); if (index != -1) { if (index > 0) { final channel = newChannels.removeAt(index); diff --git a/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart b/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart index 808c1dae..1f07c8ce 100644 --- a/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart +++ b/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart @@ -9,8 +9,8 @@ class LazyLoadScrollView extends StatefulWidget { /// Creates a new instance of [LazyLoadScrollView]. The parameter [child] /// must be supplied and not null. const LazyLoadScrollView({ - Key key, - @required this.child, + Key? key, + required this.child, this.onStartOfPage, this.onEndOfPage, this.onPageScrollStart, @@ -24,19 +24,19 @@ class LazyLoadScrollView extends StatefulWidget { final Widget child; /// Called when the [child] reaches the start of the list - final AsyncCallback onStartOfPage; + final AsyncCallback? onStartOfPage; /// Called when the [child] reaches the end of the list - final AsyncCallback onEndOfPage; + final AsyncCallback? onEndOfPage; /// Called when the list scrolling starts - final VoidCallback onPageScrollStart; + final VoidCallback? onPageScrollStart; /// Called when the list scrolling ends - final VoidCallback onPageScrollEnd; + final VoidCallback? onPageScrollEnd; /// Called every time the [child] is in-between the list - final VoidCallback onInBetweenOfPage; + final VoidCallback? onInBetweenOfPage; /// The offset to take into account when triggering [onEndOfPage]/[onStartOfPage] in pixels final double scrollOffset; @@ -59,13 +59,13 @@ class _LazyLoadScrollViewState extends State { bool _onNotification(ScrollNotification notification) { if (notification is ScrollStartNotification) { if (widget.onPageScrollStart != null) { - widget.onPageScrollStart(); + widget.onPageScrollStart!(); return true; } } if (notification is ScrollEndNotification) { if (widget.onPageScrollEnd != null) { - widget.onPageScrollEnd(); + widget.onPageScrollEnd!(); return true; } } @@ -78,7 +78,7 @@ class _LazyLoadScrollViewState extends State { if (pixels > (minScrollExtent + scrollOffset) && pixels < (maxScrollExtent - scrollOffset)) { if (widget.onInBetweenOfPage != null) { - widget.onInBetweenOfPage(); + widget.onInBetweenOfPage!(); return true; } } @@ -117,7 +117,7 @@ class _LazyLoadScrollViewState extends State { if (_loadMoreStatus != null && _loadMoreStatus == _LoadingStatus.stable) { if (widget.onEndOfPage != null) { _loadMoreStatus = _LoadingStatus.loading; - widget.onEndOfPage().whenComplete(() { + widget.onEndOfPage!().whenComplete(() { _loadMoreStatus = _LoadingStatus.stable; }); } @@ -128,7 +128,7 @@ class _LazyLoadScrollViewState extends State { if (_loadMoreStatus != null && _loadMoreStatus == _LoadingStatus.stable) { if (widget.onStartOfPage != null) { _loadMoreStatus = _LoadingStatus.loading; - widget.onStartOfPage().whenComplete(() { + widget.onStartOfPage!().whenComplete(() { _loadMoreStatus = _LoadingStatus.stable; }); } diff --git a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_list_core.dart index 53844447..65209297 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_list_core.dart @@ -61,11 +61,11 @@ import 'package:stream_chat_flutter_core/src/typedef.dart'; class MessageListCore extends StatefulWidget { /// Instantiate a new [MessageListView]. const MessageListCore({ - Key key, - @required this.loadingBuilder, - @required this.emptyBuilder, - @required this.messageListBuilder, - @required this.errorWidgetBuilder, + Key? key, + required this.loadingBuilder, + required this.emptyBuilder, + required this.messageListBuilder, + required this.errorWidgetBuilder, this.showScrollToBottom = true, this.parentMessage, this.messageListController, @@ -84,7 +84,7 @@ class MessageListCore extends StatefulWidget { /// A [MessageListController] allows pagination. /// Use [ChannelListController.paginateData] pagination. - final MessageListController messageListController; + final MessageListController? messageListController; /// Function called when messages are fetched final Widget Function(BuildContext, List) messageListBuilder; @@ -108,10 +108,10 @@ class MessageListCore extends StatefulWidget { /// If the current message belongs to a `thread`, this property represents the /// first message or the parent of the conversation. - final Message parentMessage; + final Message? parentMessage; /// Predicate used to filter messages - final bool Function(Message) messageFilter; + final bool Function(Message)? messageFilter; @override MessageListCoreState createState() => MessageListCoreState(); @@ -119,7 +119,7 @@ class MessageListCore extends StatefulWidget { /// The current state of the [MessageListCore]. class MessageListCoreState extends State { - StreamChannelState _streamChannel; + late StreamChannelState _streamChannel; bool get _upToDate => _streamChannel.channel.state.isUpToDate; @@ -133,8 +133,8 @@ class MessageListCoreState extends State { Widget build(BuildContext context) { final messagesStream = _isThreadConversation ? _streamChannel.channel.state.threadsStream - .where((threads) => threads.containsKey(widget.parentMessage.id)) - .map((threads) => threads[widget.parentMessage.id]) + .where((threads) => threads.containsKey(widget.parentMessage!.id)) + .map((threads) => threads[widget.parentMessage!.id]) : _streamChannel.channel.state?.messagesStream; bool defaultFilter(Message m) { @@ -144,7 +144,7 @@ class MessageListCoreState extends State { return true; } - return StreamBuilder>( + return StreamBuilder?>( stream: messagesStream?.map((messages) => messages?.where(widget.messageFilter ?? defaultFilter)?.toList()), builder: (context, snapshot) { @@ -171,11 +171,11 @@ class MessageListCoreState extends State { /// /// Optionally pass the fetch direction, defaults to [QueryDirection.bottom] Future paginateData( - {QueryDirection direction = QueryDirection.bottom}) { + {QueryDirection? direction = QueryDirection.bottom}) { if (!_isThreadConversation) { return _streamChannel.queryMessages(direction: direction); } else { - return _streamChannel.getReplies(widget.parentMessage.id); + return _streamChannel.getReplies(widget.parentMessage!.id); } } @@ -184,11 +184,11 @@ class MessageListCoreState extends State { _streamChannel = StreamChannel.of(context); if (_isThreadConversation) { - _streamChannel.getReplies(widget.parentMessage.id); + _streamChannel.getReplies(widget.parentMessage!.id); } if (widget.messageListController != null) { - widget.messageListController.paginateData = paginateData; + widget.messageListController!.paginateData = paginateData; } super.initState(); @@ -206,5 +206,5 @@ class MessageListCoreState extends State { /// Controller used for paginating data in [ChannelListView] class MessageListController { /// Call this function to load further data - Future Function({QueryDirection direction}) paginateData; + Future Function({QueryDirection? direction})? paginateData; } diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart index 1c5c78de..d41086cd 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart @@ -13,9 +13,9 @@ import 'package:stream_chat_flutter_core/src/stream_chat_core.dart'; class MessageSearchBloc extends StatefulWidget { /// Instantiate a new MessageSearchBloc const MessageSearchBloc({ - Key key, - @required this.child, - }) : assert(child != null, 'Parameter child should not be null.'), + Key? key, + required this.child, + }) : assert(child != null, 'Parameter child should not be null.'), super(key: key); /// The widget child @@ -26,7 +26,7 @@ class MessageSearchBloc extends StatefulWidget { /// Use this method to get the current [MessageSearchBlocState] instance static MessageSearchBlocState of(BuildContext context) { - MessageSearchBlocState state; + MessageSearchBlocState? state; state = context.findAncestorStateOfType(); @@ -42,7 +42,7 @@ class MessageSearchBloc extends StatefulWidget { class MessageSearchBlocState extends State with AutomaticKeepAliveClientMixin { /// The current messages list - List get messageResponses => _messageResponses.value; + List? get messageResponses => _messageResponses.value; /// The current messages list as a stream Stream> get messagesStream => @@ -59,11 +59,11 @@ class MessageSearchBlocState extends State /// Calls [StreamChatClient.search] updating /// [messagesStream] and [queryMessagesLoading] stream Future search({ - Map filter, - Map messageFilter, - List sort, - String query, - PaginationParams pagination, + Map? filter, + Map? messageFilter, + List? sort, + String? query, + PaginationParams? pagination, }) async { final client = StreamChatCore.of(context).client; @@ -80,11 +80,11 @@ class MessageSearchBlocState extends State final oldMessages = List.from(messageResponses ?? []); final messages = await client.search( - filter, - sort: sort, - query: query, - paginationParams: pagination, - messageFilters: messageFilter, + filter!, + sort: sort!, + query: query!, + paginationParams: pagination!, + messageFilters: messageFilter!, ); if (clear) { @@ -93,7 +93,8 @@ class MessageSearchBlocState extends State final temp = oldMessages + messages.results; _messageResponses.add(temp); } - if (_messageResponses.hasValue && _queryMessagesLoadingController.value) { + if (_messageResponses.hasValue && + _queryMessagesLoadingController.value!) { _queryMessagesLoadingController.add(false); } } catch (e, stk) { diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart index 72c5ea86..67019677 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart @@ -42,11 +42,11 @@ class MessageSearchListCore extends StatefulWidget { /// * [loadingBuilder] /// * [childBuilder] const MessageSearchListCore({ - Key key, - @required this.emptyBuilder, - @required this.errorBuilder, - @required this.loadingBuilder, - @required this.childBuilder, + Key? key, + required this.emptyBuilder, + required this.errorBuilder, + required this.loadingBuilder, + required this.childBuilder, this.messageQuery, this.filters, this.sortOptions, @@ -63,36 +63,36 @@ class MessageSearchListCore extends StatefulWidget { /// Use [MessageSearchListController.loadData] and /// [MessageSearchListController.paginateData] respectively for reloading and /// pagination. - final MessageSearchListController messageSearchListController; + final MessageSearchListController? messageSearchListController; /// Message String to search on - final String messageQuery; + final String? messageQuery; /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - final Map filters; + final Map? filters; /// The sorting used for the channels matching the filters. /// Sorting is based on field and direction, multiple sorting options can be /// provided. /// You can sort based on last_updated, last_message_at, updated_at, created_ /// at or member_count. Direction can be ascending or descending. - final List sortOptions; + final List? sortOptions; /// Pagination parameters /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams paginationParams; + final PaginationParams? paginationParams; /// The message query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - final Map messageFilters; + final Map? messageFilters; /// The builder that is used when the search messages are fetched - final Widget Function(List) childBuilder; + final Widget Function(List?) childBuilder; /// The builder used when the channel list is empty. final WidgetBuilder emptyBuilder; @@ -114,8 +114,8 @@ class MessageSearchListCoreState extends State { super.didChangeDependencies(); loadData(); if (widget.messageSearchListController != null) { - widget.messageSearchListController.loadData = loadData; - widget.messageSearchListController.paginateData = paginateData; + widget.messageSearchListController!.loadData = loadData; + widget.messageSearchListController!.paginateData = paginateData; } } @@ -135,7 +135,7 @@ class MessageSearchListCoreState extends State { if (!snapshot.hasData) { return widget.loadingBuilder(context); } - final items = snapshot.data; + final items = snapshot.data!; if (items.isEmpty) { return widget.emptyBuilder(context); } @@ -161,7 +161,7 @@ class MessageSearchListCoreState extends State { return messageSearchBloc.search( filter: widget.filters, sort: widget.sortOptions, - pagination: widget.paginationParams.copyWith( + pagination: widget.paginationParams!.copyWith( offset: messageSearchBloc.messageResponses?.length ?? 0, ), query: widget.messageQuery, @@ -187,8 +187,8 @@ class MessageSearchListCoreState extends State { /// Controller used for paginating data in [ChannelListView] class MessageSearchListController { /// Call this function to reload data - AsyncCallback loadData; + AsyncCallback? loadData; /// Call this function to load further data - AsyncCallback paginateData; + AsyncCallback? paginateData; } diff --git a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart index 58a47d0a..f4b6e6c7 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:rxdart/rxdart.dart'; @@ -21,9 +22,9 @@ class StreamChannel extends StatefulWidget { /// Creates a new instance of [StreamChannel]. Both [child] and [client] must /// be supplied and not null. const StreamChannel({ - Key key, - @required this.child, - @required this.channel, + Key? key, + required this.child, + required this.channel, this.showLoading = true, this.initialMessageId, }) : assert(child != null, 'Child should not be null'), @@ -40,11 +41,11 @@ class StreamChannel extends StatefulWidget { final bool showLoading; /// If passed the channel will load from this particular message. - final String initialMessageId; + final String? initialMessageId; /// Use this method to get the current [StreamChannelState] instance static StreamChannelState of(BuildContext context) { - StreamChannelState streamChannelState; + StreamChannelState? streamChannelState; streamChannelState = context.findAncestorStateOfType(); @@ -67,7 +68,7 @@ class StreamChannelState extends State { Channel get channel => widget.channel; /// InitialMessageId - String get initialMessageId => widget.initialMessageId; + String? get initialMessageId => widget.initialMessageId; /// Current channel state stream Stream get channelStateStream => @@ -146,7 +147,7 @@ class StreamChannelState extends State { } /// Calls [channel.query] updating [queryMessage] stream - Future queryMessages({QueryDirection direction = QueryDirection.top}) { + Future queryMessages({QueryDirection? direction = QueryDirection.top}) { if (direction == QueryDirection.top) return _queryTopMessages(); return _queryBottomMessages(); } @@ -157,12 +158,12 @@ class StreamChannelState extends State { int limit = 50, bool preferOffline = false, }) async { - if (_topPaginationEnded || _queryTopMessagesController.value) return; + if (_topPaginationEnded || _queryTopMessagesController.value!) return; _queryTopMessagesController.add(true); - Message message; + late Message message; if (channel.state.threads.containsKey(parentId)) { - final thread = channel.state.threads[parentId]; + final thread = channel.state.threads[parentId]!; if (thread.isNotEmpty) { message = thread.first; } @@ -202,7 +203,7 @@ class StreamChannelState extends State { /// Loads channel at specific message Future loadChannelAtMessage( - String messageId, { + String? messageId, { int before = 20, int after = 20, bool preferOffline = false, @@ -214,13 +215,13 @@ class StreamChannelState extends State { preferOffline: preferOffline, ); - Future _queryAtMessage({ - String messageId, + Future> _queryAtMessage({ + String? messageId, int before = 20, int after = 20, bool preferOffline = false, }) async { - if (channel.state == null) return; + if (channel.state == null) return []; channel.state.isUpToDate = false; channel.state.truncate(); @@ -232,7 +233,7 @@ class StreamChannelState extends State { preferOffline: preferOffline, ); channel.state.isUpToDate = true; - return; + return []; } return Future.wait([ @@ -284,9 +285,8 @@ class StreamChannelState extends State { /// Future getMessage(String messageId) async { - var message = channel.state.messages.firstWhere( + var message = channel.state.messages.firstWhereOrNull( (it) => it.id == messageId, - orElse: () => null, ); if (message == null) { final response = await channel.getMessagesById([messageId]); @@ -298,7 +298,7 @@ class StreamChannelState extends State { /// Reloads the channel with latest message Future reloadChannel() => _queryAtMessage(before: 30); - List> _futures; + late List> _futures; Future get _loadChannelAtMessage async { try { @@ -358,9 +358,9 @@ class StreamChannelState extends State { } return Center(child: Text(message)); } - final initialized = snapshot.data[0]; + final initialized = snapshot.data![0]; // ignore: avoid_bool_literals_in_conditional_expressions - final dataLoaded = initialMessageId == null ? true : snapshot.data[1]; + final dataLoaded = initialMessageId == null ? true : snapshot.data![1]; if (widget.showLoading && (!initialized || !dataLoaded)) { return const Center( child: CircularProgressIndicator(), diff --git a/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart b/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart index 15c2ddf3..42ec8a21 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart @@ -38,9 +38,9 @@ class StreamChatCore extends StatefulWidget { /// [StreamChatCore] is a stateful widget which reacts to system events and /// updates Stream's connection status accordingly. const StreamChatCore({ - Key key, - @required this.client, - @required this.child, + Key? key, + required this.client, + required this.child, this.onBackgroundEventReceived, this.backgroundKeepAlive = const Duration(minutes: 1), }) : assert(client != null, 'Stream Chat Client should not be null'), @@ -61,14 +61,14 @@ class StreamChatCore extends StatefulWidget { /// Handler called whenever the [client] receives a new [Event] while the app /// is in background. Can be used to display various notifications depending /// upon the [Event.type] - final EventHandler onBackgroundEventReceived; + final EventHandler? onBackgroundEventReceived; @override StreamChatCoreState createState() => StreamChatCoreState(); /// Use this method to get the current [StreamChatCoreState] instance static StreamChatCoreState of(BuildContext context) { - StreamChatCoreState streamChatState; + StreamChatCoreState? streamChatState; streamChatState = context.findAncestorStateOfType(); @@ -87,24 +87,24 @@ class StreamChatCoreState extends State /// Initialized client used throughout the application. StreamChatClient get client => widget.client; - Timer _disconnectTimer; + Timer? _disconnectTimer; @override Widget build(BuildContext context) => widget.child; /// The current user - User get user => client.state?.user; + User? get user => client.state?.user; /// The current user as a stream - Stream get userStream => client.state?.userStream; + Stream? get userStream => client.state?.userStream; @override void initState() { super.initState(); - WidgetsBinding.instance.addObserver(this); + WidgetsBinding.instance!.addObserver(this); } - StreamSubscription _eventSubscription; + StreamSubscription? _eventSubscription; @override void didChangeAppLifecycleState(AppLifecycleState state) { @@ -119,15 +119,15 @@ class StreamChatCoreState extends State ); void onTimerComplete() { - _eventSubscription.cancel(); + _eventSubscription!.cancel(); client.disconnect(); } _disconnectTimer = Timer(widget.backgroundKeepAlive, onTimerComplete); } else if (state == AppLifecycleState.resumed) { if (_disconnectTimer?.isActive == true) { - _eventSubscription.cancel(); - _disconnectTimer.cancel(); + _eventSubscription!.cancel(); + _disconnectTimer!.cancel(); } else { if (client.wsConnectionStatus == ConnectionStatus.disconnected) { client.connect(); @@ -139,7 +139,7 @@ class StreamChatCoreState extends State @override void dispose() { - WidgetsBinding.instance.removeObserver(this); + WidgetsBinding.instance!.removeObserver(this); _eventSubscription?.cancel(); _disconnectTimer?.cancel(); super.dispose(); diff --git a/packages/stream_chat_flutter_core/lib/src/typedef.dart b/packages/stream_chat_flutter_core/lib/src/typedef.dart index ace8d7e6..d5e7af2c 100644 --- a/packages/stream_chat_flutter_core/lib/src/typedef.dart +++ b/packages/stream_chat_flutter_core/lib/src/typedef.dart @@ -4,7 +4,7 @@ import 'package:stream_chat/stream_chat.dart'; /// A signature for a callback which exposes an error and returns a function. /// This Callback can be used in cases where an API failure occurs and the /// widget is unable to render data. -typedef ErrorBuilder = Widget Function(BuildContext context, Object error); +typedef ErrorBuilder = Widget Function(BuildContext context, Object? error); /// A Signature for a handler function which will expose a [event]. typedef EventHandler = void Function(Event event); diff --git a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart index f1bd68ee..d2412b97 100644 --- a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart @@ -57,11 +57,11 @@ import 'package:stream_chat_flutter_core/src/users_bloc.dart'; class UserListCore extends StatefulWidget { /// Instantiate a new [UserListCore] const UserListCore({ - @required this.errorBuilder, - @required this.emptyBuilder, - @required this.loadingBuilder, - @required this.listBuilder, - Key key, + required this.errorBuilder, + required this.emptyBuilder, + required this.loadingBuilder, + required this.listBuilder, + Key? key, this.filter, this.options, this.sort, @@ -77,10 +77,10 @@ class UserListCore extends StatefulWidget { /// A [UserListController] allows reloading and pagination. /// Use [UserListController.loadData] and [UserListController.paginateData] /// respectively for reloading and pagination. - final UserListController userListController; + final UserListController? userListController; /// The builder that will be used in case of error - final Widget Function(Object error) errorBuilder; + final Widget Function(Object? error) errorBuilder; /// The builder that will be used to build the list final Widget Function(BuildContext context, List users) listBuilder; @@ -94,25 +94,25 @@ class UserListCore extends StatefulWidget { /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - final Map filter; + final Map? filter; /// Query channels options. /// /// state: if true returns the Channel state /// watch: if true listen to changes to this Channel in real time. - final Map options; + final Map? options; /// The sorting used for the channels matching the filters. /// Sorting is based on field and direction, multiple sorting options can be /// provided. You can sort based on last_updated, last_message_at, updated_at, /// created_at or member_count. Direction can be ascending or descending. - final List sort; + final List? sort; /// Pagination parameters /// limit: the number of users to return (max is 30) /// offset: the offset (max is 1000) /// message_limit: how many messages should be included to each channel - final PaginationParams pagination; + final PaginationParams? pagination; /// Set it to true to group users by their first character /// @@ -131,8 +131,8 @@ class UserListCoreState extends State super.didChangeDependencies(); loadData(); if (widget.userListController != null) { - widget.userListController.loadData = loadData; - widget.userListController.paginateData = paginateData; + widget.userListController!.loadData = loadData; + widget.userListController!.paginateData = paginateData; } } @@ -158,14 +158,14 @@ class UserListCoreState extends State } final groupedUsers = >{}; for (final e in temp) { - final alphabet = e.name[0]?.toUpperCase(); + final alphabet = e.name[0].toUpperCase(); groupedUsers[alphabet] = [...groupedUsers[alphabet] ?? [], e]; } final items = []; for (final key in groupedUsers.keys) { items ..add(ListHeaderItem(key)) - ..addAll(groupedUsers[key].map((e) => ListUserItem(e))); + ..addAll(groupedUsers[key]!.map((e) => ListUserItem(e))); } return items; } @@ -185,7 +185,7 @@ class UserListCoreState extends State if (!snapshot.hasData) { return widget.loadingBuilder(context); } - final items = snapshot.data; + final items = snapshot.data!; if (items.isEmpty) { return widget.emptyBuilder(context); } @@ -210,7 +210,7 @@ class UserListCoreState extends State return _usersBloc.queryUsers( filter: widget.filter, sort: widget.sort, - pagination: widget.pagination.copyWith( + pagination: widget.pagination!.copyWith( offset: _usersBloc.users?.length ?? 0, ), options: widget.options, @@ -235,7 +235,7 @@ class UserListCoreState extends State /// with `USER`. abstract class ListItem { /// Unique key per list item - String get key { + String? get key { if (this is ListHeaderItem) { final header = (this as ListHeaderItem).heading; return 'HEADER-${header.toLowerCase()}'; @@ -250,8 +250,8 @@ abstract class ListItem { /// Helper function to build widget based on ListItem type // ignore: missing_return Widget when({ - @required Widget Function(String heading) headerItem, - @required Widget Function(User user) userItem, + required Widget Function(String heading) headerItem, + required Widget Function(User user) userItem, }) { if (this is ListHeaderItem) { return headerItem((this as ListHeaderItem).heading); @@ -259,6 +259,7 @@ abstract class ListItem { if (this is ListUserItem) { return userItem((this as ListUserItem).user); } + return Container(); } } @@ -283,8 +284,8 @@ class ListUserItem extends ListItem { /// Controller used for paginating data in [ChannelListView] class UserListController { /// Call this function to reload data - AsyncCallback loadData; + AsyncCallback? loadData; /// Call this function to load further data - AsyncCallback paginateData; + AsyncCallback? paginateData; } diff --git a/packages/stream_chat_flutter_core/lib/src/users_bloc.dart b/packages/stream_chat_flutter_core/lib/src/users_bloc.dart index 92881619..2470fba7 100644 --- a/packages/stream_chat_flutter_core/lib/src/users_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/users_bloc.dart @@ -14,8 +14,8 @@ class UsersBloc extends StatefulWidget { /// Instantiate a new [UsersBloc]. The parameter [child] must be supplied and /// not null. const UsersBloc({ - @required this.child, - Key key, + required this.child, + Key? key, }) : assert( child != null, 'When constructing a UsersBloc, the parameter ' @@ -30,7 +30,7 @@ class UsersBloc extends StatefulWidget { /// Use this method to get the current [UsersBlocState] instance static UsersBlocState of(BuildContext context) { - UsersBlocState state; + UsersBlocState? state; state = context.findAncestorStateOfType(); @@ -46,7 +46,7 @@ class UsersBloc extends StatefulWidget { class UsersBlocState extends State with AutomaticKeepAliveClientMixin { /// The current users list - List get users => _usersController.value; + List? get users => _usersController.value; /// The current users list as a stream Stream> get usersStream => _usersController.stream; @@ -62,10 +62,10 @@ class UsersBlocState extends State /// online/offline. /// [API Reference](https://getstream.io/chat/docs/flutter-dart/query_users/?language=dart) Future queryUsers({ - Map filter, - List sort, - Map options, - PaginationParams pagination, + Map? filter, + List? sort, + Map? options, + PaginationParams? pagination, }) async { final client = StreamChatCore.of(context).client; @@ -83,10 +83,10 @@ class UsersBlocState extends State final oldUsers = List.from(users ?? []); final usersResponse = await client.queryUsers( - filter: filter, - sort: sort, - options: options, - pagination: pagination, + filter: filter!, + sort: sort!, + options: options!, + pagination: pagination!, ); if (clear) { @@ -95,7 +95,7 @@ class UsersBlocState extends State final temp = oldUsers + usersResponse.users; _usersController.add(temp); } - if (_usersController.hasValue && _queryUsersLoadingController.value) { + if (_usersController.hasValue && _queryUsersLoadingController.value!) { _queryUsersLoadingController.add(false); } } catch (e, stk) { diff --git a/packages/stream_chat_flutter_core/pubspec.yaml b/packages/stream_chat_flutter_core/pubspec.yaml index 4b3d81de..195c4ee3 100644 --- a/packages/stream_chat_flutter_core/pubspec.yaml +++ b/packages/stream_chat_flutter_core/pubspec.yaml @@ -8,7 +8,7 @@ issue_tracker: https://github.com/GetStream/stream-chat-flutter/issues publish_to: none environment: - sdk: ">=2.7.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' flutter: ">=1.17.0" dependencies: @@ -17,14 +17,15 @@ dependencies: meta: ^1.2.4 rxdart: ^0.26.0 stream_chat: ^1.5.0 + collection: ^1.15.0-nullsafety.4 dependency_overrides: stream_chat: path: ../stream_chat dev_dependencies: - fake_async: ^1.1.0 + fake_async: ^1.2.0 flutter_test: sdk: flutter - mockito: ^4.1.3 + mockito: ^5.0.3 diff --git a/packages/stream_chat_flutter_core/test/matchers/channel_matcher.dart b/packages/stream_chat_flutter_core/test/matchers/channel_matcher.dart index f371acff..f6d7fb50 100644 --- a/packages/stream_chat_flutter_core/test/matchers/channel_matcher.dart +++ b/packages/stream_chat_flutter_core/test/matchers/channel_matcher.dart @@ -7,7 +7,7 @@ Matcher isSameChannelAs(Channel targetChannel) => class _IsSameChannelAs extends Matcher { const _IsSameChannelAs({ - @required this.targetChannel, + required this.targetChannel, }) : assert(targetChannel != null, ''); final Channel targetChannel; @@ -26,7 +26,7 @@ Matcher isSameChannelListAs(List targetChannelList) => class _IsSameChannelListAs extends Matcher { const _IsSameChannelListAs({ - @required this.targetChannelList, + required this.targetChannelList, }) : assert(targetChannelList != null, ''); final List targetChannelList; diff --git a/packages/stream_chat_flutter_core/test/matchers/get_message_response_matcher.dart b/packages/stream_chat_flutter_core/test/matchers/get_message_response_matcher.dart index bd89420b..42ea0d34 100644 --- a/packages/stream_chat_flutter_core/test/matchers/get_message_response_matcher.dart +++ b/packages/stream_chat_flutter_core/test/matchers/get_message_response_matcher.dart @@ -7,7 +7,7 @@ Matcher isSameMessageResponseAs(GetMessageResponse targetResponse) => class _IsSameMessageResponseAs extends Matcher { const _IsSameMessageResponseAs({ - @required this.targetResponse, + required this.targetResponse, }) : assert(targetResponse != null, ''); final GetMessageResponse targetResponse; @@ -28,7 +28,7 @@ Matcher isSameMessageResponseListAs( class _IsSameMessageResponseListAs extends Matcher { const _IsSameMessageResponseListAs({ - @required this.targetResponseList, + required this.targetResponseList, }) : assert(targetResponseList != null, ''); final List targetResponseList; diff --git a/packages/stream_chat_flutter_core/test/matchers/message_matcher.dart b/packages/stream_chat_flutter_core/test/matchers/message_matcher.dart index 12a3e19d..69a24f49 100644 --- a/packages/stream_chat_flutter_core/test/matchers/message_matcher.dart +++ b/packages/stream_chat_flutter_core/test/matchers/message_matcher.dart @@ -7,7 +7,7 @@ Matcher isSameMessageAs(Message targetMessage) => class _IsSameMessageAs extends Matcher { const _IsSameMessageAs({ - @required this.targetMessage, + required this.targetMessage, }) : assert(targetMessage != null, ''); final Message targetMessage; @@ -26,7 +26,7 @@ Matcher isSameMessageListAs(List targetMessageList) => class _IsSameMessageListAs extends Matcher { const _IsSameMessageListAs({ - @required this.targetMessageList, + required this.targetMessageList, }) : assert(targetMessageList != null, ''); final List targetMessageList; diff --git a/packages/stream_chat_flutter_core/test/matchers/users_matcher.dart b/packages/stream_chat_flutter_core/test/matchers/users_matcher.dart index 3816fa24..38e25a64 100644 --- a/packages/stream_chat_flutter_core/test/matchers/users_matcher.dart +++ b/packages/stream_chat_flutter_core/test/matchers/users_matcher.dart @@ -6,7 +6,7 @@ Matcher isSameUserAs(User targetUser) => _IsSameUserAs(targetUser: targetUser); class _IsSameUserAs extends Matcher { const _IsSameUserAs({ - @required this.targetUser, + required this.targetUser, }) : assert(targetUser != null, ''); final User targetUser; @@ -24,7 +24,7 @@ Matcher isSameUserListAs(List targetUserList) => class _IsSameUserListAs extends Matcher { const _IsSameUserListAs({ - @required this.targetUserList, + required this.targetUserList, }) : assert(targetUserList != null, ''); final List targetUserList; From e2fed839462170fd13eb348c37b92292c3e9e3a8 Mon Sep 17 00:00:00 2001 From: Neevash Ramdial Date: Fri, 16 Apr 2021 09:30:16 -0400 Subject: [PATCH 02/15] remove assert for nullable constructor params --- .../lib/src/channels_bloc.dart | 3 +-- .../lib/src/message_list_core.dart | 12 +----------- .../lib/src/message_search_bloc.dart | 3 +-- 3 files changed, 3 insertions(+), 15 deletions(-) diff --git a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart index 2e42c0af..b7ff3d85 100644 --- a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart @@ -25,8 +25,7 @@ class ChannelsBloc extends StatefulWidget { this.lockChannelsOrder = false, this.channelsComparator, this.shouldAddChannel, - }) : assert(child != null, 'Parameter child should not be null.'), - super(key: key); + }) : super(key: key); /// The widget child final Widget child; diff --git a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_list_core.dart index 65209297..219b4ab1 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_list_core.dart @@ -70,17 +70,7 @@ class MessageListCore extends StatefulWidget { this.parentMessage, this.messageListController, this.messageFilter, - }) : assert(loadingBuilder != null, 'loadingBuilder should not be null'), - assert(emptyBuilder != null, 'emptyBuilder should not be null'), - assert( - messageListBuilder != null, - 'messageListBuilder should not be null', - ), - assert( - errorWidgetBuilder != null, - 'errorWidgetBuilder should not be null', - ), - super(key: key); + }) : super(key: key); /// A [MessageListController] allows pagination. /// Use [ChannelListController.paginateData] pagination. diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart index d41086cd..9f55b7e8 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart @@ -15,8 +15,7 @@ class MessageSearchBloc extends StatefulWidget { const MessageSearchBloc({ Key? key, required this.child, - }) : assert(child != null, 'Parameter child should not be null.'), - super(key: key); + }) : super(key: key); /// The widget child final Widget child; From 2f4595c379beb0b061bf07ee67609d4cff24a588 Mon Sep 17 00:00:00 2001 From: Neevash Ramdial Date: Fri, 16 Apr 2021 09:30:54 -0400 Subject: [PATCH 03/15] migrate message_search_bloc.dart --- .../lib/src/message_search_bloc.dart | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart index 9f55b7e8..fc858e5b 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart @@ -72,9 +72,7 @@ class MessageSearchBlocState extends State _queryMessagesLoadingController.add(true); } try { - final clear = pagination == null || - pagination.offset == null || - pagination.offset == 0; + final clear = pagination == null || pagination.offset == 0; final oldMessages = List.from(messageResponses ?? []); @@ -86,11 +84,13 @@ class MessageSearchBlocState extends State messageFilters: messageFilter!, ); - if (clear) { - _messageResponses.add(messages.results); - } else { - final temp = oldMessages + messages.results; - _messageResponses.add(temp); + if (messages.results != null) { + if (clear) { + _messageResponses.add(messages.results!); + } else { + final temp = oldMessages + messages.results!; + _messageResponses.add(temp); + } } if (_messageResponses.hasValue && _queryMessagesLoadingController.value!) { From e52e79bafa6d84afa0e66d8de4affa7f5397dd86 Mon Sep 17 00:00:00 2001 From: Neevash Ramdial Date: Fri, 16 Apr 2021 09:31:50 -0400 Subject: [PATCH 04/15] wip message_list_core --- .../lib/src/message_list_core.dart | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_list_core.dart index 219b4ab1..c376dddc 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_list_core.dart @@ -128,7 +128,7 @@ class MessageListCoreState extends State { : _streamChannel.channel.state?.messagesStream; bool defaultFilter(Message m) { - final isMyMessage = m.user.id == _currentUser.id; + final isMyMessage = m.user?.id == _currentUser?.id; final isDeletedOrShadowed = m.isDeleted == true || m.shadowed == true; if (isDeletedOrShadowed && !isMyMessage) return false; return true; @@ -136,14 +136,17 @@ class MessageListCoreState extends State { return StreamBuilder?>( stream: messagesStream?.map((messages) => - messages?.where(widget.messageFilter ?? defaultFilter)?.toList()), + messages?.where(widget.messageFilter ?? defaultFilter).toList( + growable: false, + )), builder: (context, snapshot) { if (snapshot.hasError) { return widget.errorWidgetBuilder(context, snapshot.error); } else if (!snapshot.hasData) { return widget.loadingBuilder(context); } else { - final messageList = snapshot.data?.reversed?.toList() ?? []; + final messageList = + snapshot.data?.reversed.toList(growable: false) ?? []; if (messageList.isEmpty && !_isThreadConversation) { if (_upToDate) { return widget.emptyBuilder(context); From 8f7471fec23d1e49f5b0640da788fa1473906a79 Mon Sep 17 00:00:00 2001 From: Neevash Ramdial Date: Fri, 16 Apr 2021 09:32:28 -0400 Subject: [PATCH 05/15] migrate channels_bloc --- .../lib/src/channels_bloc.dart | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart index b7ff3d85..ad39809f 100644 --- a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart @@ -104,9 +104,7 @@ class ChannelsBlocState extends State } try { - final clear = paginationParams == null || - paginationParams.offset == null || - paginationParams.offset == 0; + final clear = paginationParams == null || paginationParams.offset == 0; final oldChannels = List.from(channels ?? []); var newChannels = []; await for (final channels in client.queryChannels( @@ -162,9 +160,9 @@ class ChannelsBlocState extends State newChannels.insert(0, _hiddenChannels[hiddenIndex]); _hiddenChannels.removeAt(hiddenIndex); } else { - if (client.state?.channels != null && - client.state?.channels[e.cid] != null) { - newChannels.insert(0, client.state.channels[e.cid]); + if (client.state.channels != null && + client.state.channels?[e.cid] != null) { + newChannels.insert(0, client.state.channels?[e.cid]); } } } @@ -195,7 +193,7 @@ class ChannelsBlocState extends State // ignore: cascade_invocations final channel = e.channel; _channelsController.add(List.from( - (channels ?? [])..removeWhere((c) => c.cid == channel.cid))); + (channels ?? [])..removeWhere((c) => c.cid == channel?.cid))); })); } From 34f2539595559c2773294b66ab9039e61bd8b676 Mon Sep 17 00:00:00 2001 From: Neevash Ramdial Date: Fri, 16 Apr 2021 09:33:24 -0400 Subject: [PATCH 06/15] wip stream_chat.dart --- .../lib/src/stream_channel.dart | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart index f4b6e6c7..c119f207 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart @@ -1,6 +1,5 @@ import 'dart:async'; -import 'package:collection/collection.dart' show IterableExtension; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:rxdart/rxdart.dart'; @@ -27,9 +26,7 @@ class StreamChannel extends StatefulWidget { required this.channel, this.showLoading = true, this.initialMessageId, - }) : assert(child != null, 'Child should not be null'), - assert(channel != null, 'Channel should not be null'), - super(key: key); + }) : super(key: key); // ignore: public_member_api_docs final Widget child; @@ -189,16 +186,21 @@ class StreamChannelState extends State { /// Query the channel members and watchers Future queryMembersAndWatchers() async { - await widget.channel.query( - membersPagination: PaginationParams( - offset: channel.state.members?.length, - limit: 100, - ), - watchersPagination: PaginationParams( - offset: channel.state.watchers?.length, - limit: 100, - ), - ); + final _members = channel.state?.members; + if (_members != null) { + await widget.channel.query( + membersPagination: PaginationParams( + offset: _members.length, + limit: 100, + ), + watchersPagination: PaginationParams( + offset: _members.length, + limit: 100, + ), + ); + } else { + return; + } } /// Loads channel at specific message From 987da10cba09a68c68deac146cb6bf393734f15b Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 20 Apr 2021 12:44:11 +0200 Subject: [PATCH 07/15] migrate code --- .../analysis_options.yaml | 1 - .../lib/src/channel_list_core.dart | 30 +- .../lib/src/channels_bloc.dart | 26 +- .../lib/src/lazy_load_scroll_view.dart | 11 +- .../lib/src/message_list_core.dart | 15 +- .../lib/src/message_search_bloc.dart | 24 +- .../lib/src/message_search_list_core.dart | 22 +- .../lib/src/stream_channel.dart | 44 +- .../lib/src/stream_chat_core.dart | 18 +- .../lib/src/typedef.dart | 2 +- .../lib/src/user_list_core.dart | 14 +- .../lib/src/users_bloc.dart | 18 +- .../stream_chat_flutter_core/pubspec.yaml | 8 +- .../test/channel_list_core_test.dart | 251 +++++----- .../test/channels_bloc_test.dart | 437 +++++++++--------- .../test/lazy_load_scroll_view_test.dart | 12 - .../get_message_response_matcher.dart | 9 +- .../test/message_list_core_test.dart | 106 ++--- .../test/message_search_bloc_test.dart | 198 ++++---- .../test/message_search_list_core_test.dart | 319 ++++++------- .../stream_chat_flutter_core/test/mocks.dart | 11 +- .../test/stream_channel_test.dart | 195 ++++---- .../test/stream_chat_core_test.dart | 60 +-- .../test/user_list_core_test.dart | 259 +++++------ .../test/users_bloc_test.dart | 160 +++---- 25 files changed, 986 insertions(+), 1264 deletions(-) diff --git a/packages/stream_chat_flutter_core/analysis_options.yaml b/packages/stream_chat_flutter_core/analysis_options.yaml index 545d5492..039d0230 100644 --- a/packages/stream_chat_flutter_core/analysis_options.yaml +++ b/packages/stream_chat_flutter_core/analysis_options.yaml @@ -3,7 +3,6 @@ analyzer: - lib/**/*.g.dart - lib/**/*.freezed.dart - example/* - - test/* linter: rules: - always_use_package_imports diff --git a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart index 70d97df7..322a2780 100644 --- a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart @@ -69,23 +69,7 @@ class ChannelListCore extends StatefulWidget { limit: 25, ), this.channelListController, - }) : assert( - errorBuilder != null, - 'Parameter errorBuilder should not be null', - ), - assert( - emptyBuilder != null, - 'Parameter emptyBuilder should not be null', - ), - assert( - loadingBuilder != null, - 'Parameter loadingBuilder should not be null', - ), - assert( - listBuilder != null, - 'Parameter listBuilder should not be null', - ), - super(key: key); + }) : super(key: key); /// A [ChannelListController] allows reloading and pagination. /// Use [ChannelListController.loadData] and @@ -100,7 +84,7 @@ class ChannelListCore extends StatefulWidget { final WidgetBuilder loadingBuilder; /// The builder which is used when list of channels loads - final Function(BuildContext, List) listBuilder; + final Function(BuildContext, List) listBuilder; /// The builder used when the channel list is empty. final WidgetBuilder emptyBuilder; @@ -142,14 +126,14 @@ class ChannelListCoreState extends State { return _buildListView(channelsBloc); } - StreamBuilder> _buildListView( + StreamBuilder> _buildListView( ChannelsBlocState channelsBlocState, ) => - StreamBuilder>( + StreamBuilder>( stream: channelsBlocState.channelsStream, builder: (context, snapshot) { if (snapshot.hasError) { - return widget.errorBuilder(context, snapshot.error); + return widget.errorBuilder(context, snapshot.error!); } if (!snapshot.hasData) { return widget.loadingBuilder(context); @@ -215,8 +199,8 @@ class ChannelListCoreState extends State { if (widget.filter?.toString() != oldWidget.filter?.toString() || jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) || widget.options?.toString() != oldWidget.options?.toString() || - widget.pagination?.toJson()?.toString() != - oldWidget.pagination?.toJson()?.toString()) { + widget.pagination.toJson().toString() != + oldWidget.pagination.toJson().toString()) { loadData(); } } diff --git a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart index ad39809f..7910ab7e 100644 --- a/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/channels_bloc.dart @@ -35,7 +35,7 @@ class ChannelsBloc extends StatefulWidget { final bool lockChannelsOrder; /// Comparator used to sort the channels when a message.new event is received - final Comparator? channelsComparator; + final Comparator? channelsComparator; /// Function used to evaluate if a channel should be added to the list when a /// message.new event is received @@ -68,14 +68,14 @@ class ChannelsBlocState extends State } /// The current channel list - List? get channels => _channelsController.value as List?; + List? get channels => _channelsController.value; /// The current channel list as a stream - Stream> get channelsStream => _channelsController.stream; + Stream> get channelsStream => _channelsController.stream; final _queryChannelsLoadingController = BehaviorSubject.seeded(false); - final BehaviorSubject> _channelsController = + final BehaviorSubject> _channelsController = BehaviorSubject>(); /// The stream notifying the state of queryChannel call @@ -90,7 +90,7 @@ class ChannelsBlocState extends State Future queryChannels({ Map? filter, List>? sortOptions, - PaginationParams? paginationParams, + PaginationParams paginationParams = const PaginationParams(limit: 30), Map? options, }) async { final client = StreamChatCore.of(context).client; @@ -104,14 +104,14 @@ class ChannelsBlocState extends State } try { - final clear = paginationParams == null || paginationParams.offset == 0; + final clear = paginationParams.offset == 0; final oldChannels = List.from(channels ?? []); var newChannels = []; await for (final channels in client.queryChannels( - filter: filter!, - sort: sortOptions!, - options: options!, - paginationParams: paginationParams!, + filter: filter, + sort: sortOptions, + options: options, + paginationParams: paginationParams, )) { newChannels = channels; if (clear) { @@ -147,8 +147,8 @@ class ChannelsBlocState extends State if (!widget.lockChannelsOrder) { _subscriptions.add(client.on(EventType.messageNew).listen((e) { - final newChannels = List.from(channels ?? []); - final index = newChannels.indexWhere((c) => c!.cid == e.cid); + final newChannels = List.from(channels ?? []); + final index = newChannels.indexWhere((c) => c.cid == e.cid); if (index != -1) { if (index > 0) { final channel = newChannels.removeAt(index); @@ -162,7 +162,7 @@ class ChannelsBlocState extends State } else { if (client.state.channels != null && client.state.channels?[e.cid] != null) { - newChannels.insert(0, client.state.channels?[e.cid]); + newChannels.insert(0, client.state.channels![e.cid]!); } } } diff --git a/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart b/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart index 1f07c8ce..834d00b0 100644 --- a/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart +++ b/packages/stream_chat_flutter_core/lib/src/lazy_load_scroll_view.dart @@ -17,8 +17,7 @@ class LazyLoadScrollView extends StatefulWidget { this.onPageScrollEnd, this.onInBetweenOfPage, this.scrollOffset = 100, - }) : assert(child != null, 'Parameter child should not be null'), - super(key: key); + }) : super(key: key); /// The [Widget] that this widget watches for changes on final Widget child; @@ -46,7 +45,7 @@ class LazyLoadScrollView extends StatefulWidget { } class _LazyLoadScrollViewState extends State { - _LoadingStatus _loadMoreStatus = _LoadingStatus.stable; + var _loadMoreStatus = _LoadingStatus.stable; double _scrollPosition = 0; @override @@ -73,7 +72,7 @@ class _LazyLoadScrollViewState extends State { final pixels = notification.metrics.pixels; final maxScrollExtent = notification.metrics.maxScrollExtent; final minScrollExtent = notification.metrics.minScrollExtent; - final scrollOffset = widget.scrollOffset ?? 0; + final scrollOffset = widget.scrollOffset; if (pixels > (minScrollExtent + scrollOffset) && pixels < (maxScrollExtent - scrollOffset)) { @@ -114,7 +113,7 @@ class _LazyLoadScrollViewState extends State { } void _onEndOfPage() { - if (_loadMoreStatus != null && _loadMoreStatus == _LoadingStatus.stable) { + if (_loadMoreStatus == _LoadingStatus.stable) { if (widget.onEndOfPage != null) { _loadMoreStatus = _LoadingStatus.loading; widget.onEndOfPage!().whenComplete(() { @@ -125,7 +124,7 @@ class _LazyLoadScrollViewState extends State { } void _onStartOfPage() { - if (_loadMoreStatus != null && _loadMoreStatus == _LoadingStatus.stable) { + if (_loadMoreStatus == _LoadingStatus.stable) { if (widget.onStartOfPage != null) { _loadMoreStatus = _LoadingStatus.loading; widget.onStartOfPage!().whenComplete(() { diff --git a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_list_core.dart index c376dddc..f7bce6cd 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_list_core.dart @@ -111,18 +111,18 @@ class MessageListCore extends StatefulWidget { class MessageListCoreState extends State { late StreamChannelState _streamChannel; - bool get _upToDate => _streamChannel.channel.state.isUpToDate; + bool get _upToDate => _streamChannel.channel.state?.isUpToDate ?? true; bool get _isThreadConversation => widget.parentMessage != null; - OwnUser get _currentUser => _streamChannel.channel.client.state.user; + OwnUser? get _currentUser => _streamChannel.channel.client.state.user; var _messages = []; @override Widget build(BuildContext context) { final messagesStream = _isThreadConversation - ? _streamChannel.channel.state.threadsStream + ? _streamChannel.channel.state?.threadsStream .where((threads) => threads.containsKey(widget.parentMessage!.id)) .map((threads) => threads[widget.parentMessage!.id]) : _streamChannel.channel.state?.messagesStream; @@ -141,7 +141,7 @@ class MessageListCoreState extends State { )), builder: (context, snapshot) { if (snapshot.hasError) { - return widget.errorWidgetBuilder(context, snapshot.error); + return widget.errorWidgetBuilder(context, snapshot.error!); } else if (!snapshot.hasData) { return widget.loadingBuilder(context); } else { @@ -163,8 +163,9 @@ class MessageListCoreState extends State { /// Fetches more messages with updated pagination and updates the widget. /// /// Optionally pass the fetch direction, defaults to [QueryDirection.bottom] - Future paginateData( - {QueryDirection? direction = QueryDirection.bottom}) { + Future paginateData({ + QueryDirection direction = QueryDirection.bottom, + }) { if (!_isThreadConversation) { return _streamChannel.queryMessages(direction: direction); } else { @@ -199,5 +200,5 @@ class MessageListCoreState extends State { /// Controller used for paginating data in [ChannelListView] class MessageListController { /// Call this function to load further data - Future Function({QueryDirection? direction})? paginateData; + Future Function({QueryDirection direction})? paginateData; } diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart index fc858e5b..8586e1ff 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_bloc.dart @@ -58,7 +58,7 @@ class MessageSearchBlocState extends State /// Calls [StreamChatClient.search] updating /// [messagesStream] and [queryMessagesLoading] stream Future search({ - Map? filter, + required Map filter, Map? messageFilter, List? sort, String? query, @@ -77,20 +77,18 @@ class MessageSearchBlocState extends State final oldMessages = List.from(messageResponses ?? []); final messages = await client.search( - filter!, - sort: sort!, - query: query!, - paginationParams: pagination!, - messageFilters: messageFilter!, + filter, + sort: sort, + query: query, + paginationParams: pagination, + messageFilters: messageFilter, ); - if (messages.results != null) { - if (clear) { - _messageResponses.add(messages.results!); - } else { - final temp = oldMessages + messages.results!; - _messageResponses.add(temp); - } + if (clear) { + _messageResponses.add(messages.results); + } else { + final temp = oldMessages + messages.results; + _messageResponses.add(temp); } if (_messageResponses.hasValue && _queryMessagesLoadingController.value!) { diff --git a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart index 67019677..ca661dc6 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_search_list_core.dart @@ -47,17 +47,13 @@ class MessageSearchListCore extends StatefulWidget { required this.errorBuilder, required this.loadingBuilder, required this.childBuilder, + required this.filters, this.messageQuery, - this.filters, this.sortOptions, this.paginationParams, this.messageFilters, this.messageSearchListController, - }) : assert(emptyBuilder != null, 'emptyBuilder should not be null'), - assert(errorBuilder != null, 'errorBuilder should not be null'), - assert(loadingBuilder != null, 'loadingBuilder should not be null'), - assert(childBuilder != null, 'childBuilder should not be null'), - super(key: key); + }) : super(key: key); /// A [MessageSearchListController] allows reloading and pagination. /// Use [MessageSearchListController.loadData] and @@ -71,7 +67,7 @@ class MessageSearchListCore extends StatefulWidget { /// The query filters to use. /// You can query on any of the custom fields you've defined on the [Channel]. /// You can also filter other built-in channel fields. - final Map? filters; + final Map filters; /// The sorting used for the channels matching the filters. /// Sorting is based on field and direction, multiple sorting options can be @@ -92,7 +88,7 @@ class MessageSearchListCore extends StatefulWidget { final Map? messageFilters; /// The builder that is used when the search messages are fetched - final Widget Function(List?) childBuilder; + final Widget Function(List) childBuilder; /// The builder used when the channel list is empty. final WidgetBuilder emptyBuilder; @@ -130,7 +126,7 @@ class MessageSearchListCoreState extends State { stream: messageSearchBloc.messagesStream, builder: (context, snapshot) { if (snapshot.hasError) { - return widget.errorBuilder(context, snapshot.error); + return widget.errorBuilder(context, snapshot.error!); } if (!snapshot.hasData) { return widget.loadingBuilder(context); @@ -139,7 +135,7 @@ class MessageSearchListCoreState extends State { if (items.isEmpty) { return widget.emptyBuilder(context); } - return widget.childBuilder(snapshot.data); + return widget.childBuilder(items); }, ); @@ -172,13 +168,13 @@ class MessageSearchListCoreState extends State { @override void didUpdateWidget(MessageSearchListCore oldWidget) { super.didUpdateWidget(oldWidget); - if (widget.filters?.toString() != oldWidget.filters?.toString() || + if (widget.filters.toString() != oldWidget.filters.toString() || jsonEncode(widget.sortOptions) != jsonEncode(oldWidget.sortOptions) || widget.messageQuery?.toString() != oldWidget.messageQuery?.toString() || widget.messageFilters?.toString() != oldWidget.messageFilters?.toString() || - widget.paginationParams?.toJson()?.toString() != - oldWidget.paginationParams?.toJson()?.toString()) { + widget.paginationParams?.toJson().toString() != + oldWidget.paginationParams?.toJson().toString()) { loadData(); } } diff --git a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart index c119f207..34f58faa 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_channel.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_channel.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:rxdart/rxdart.dart'; @@ -28,7 +29,7 @@ class StreamChannel extends StatefulWidget { this.initialMessageId, }) : super(key: key); - // ignore: public_member_api_docs + /// The child of the widget final Widget child; /// [channel] specifies the channel with which child should be wrapped @@ -68,8 +69,8 @@ class StreamChannelState extends State { String? get initialMessageId => widget.initialMessageId; /// Current channel state stream - Stream get channelStateStream => - widget.channel.state.channelStateStream; + Stream? get channelStateStream => + widget.channel.state?.channelStateStream; final _queryTopMessagesController = BehaviorSubject.seeded(false); final _queryBottomMessagesController = BehaviorSubject.seeded(false); @@ -87,16 +88,18 @@ class StreamChannelState extends State { int limit = 20, bool preferOffline = false, }) async { - if (_topPaginationEnded || _queryTopMessagesController?.value == true) { + if (_topPaginationEnded || + _queryTopMessagesController.value == true || + channel.state == null) { return; } _queryTopMessagesController.add(true); - if (channel.state.messages.isEmpty) { + if (channel.state!.messages.isEmpty) { return _queryTopMessagesController.add(false); } - final oldestMessage = channel.state.messages.first; + final oldestMessage = channel.state!.messages.first; try { final state = await queryBeforeMessage( @@ -118,15 +121,16 @@ class StreamChannelState extends State { bool preferOffline = false, }) async { if (_bottomPaginationEnded || - _queryBottomMessagesController?.value == true || - channel?.state?.isUpToDate == true) return; + _queryBottomMessagesController.value == true || + channel.state == null || + channel.state!.isUpToDate == true) return; _queryBottomMessagesController.add(true); - if (channel.state.messages.isEmpty) { + if (channel.state!.messages.isEmpty) { return _queryBottomMessagesController.add(false); } - final recentMessage = channel.state.messages.last; + final recentMessage = channel.state!.messages.last; try { final state = await queryAfterMessage( @@ -155,12 +159,14 @@ class StreamChannelState extends State { int limit = 50, bool preferOffline = false, }) async { - if (_topPaginationEnded || _queryTopMessagesController.value!) return; + if (_topPaginationEnded || + _queryTopMessagesController.value! || + channel.state == null) return; _queryTopMessagesController.add(true); late Message message; - if (channel.state.threads.containsKey(parentId)) { - final thread = channel.state.threads[parentId]!; + if (channel.state!.threads.containsKey(parentId)) { + final thread = channel.state!.threads[parentId]!; if (thread.isNotEmpty) { message = thread.first; } @@ -170,7 +176,7 @@ class StreamChannelState extends State { final response = await channel.getReplies( parentId, PaginationParams( - lessThan: message?.id, + lessThan: message.id, limit: limit, ), preferOffline: preferOffline, @@ -224,8 +230,8 @@ class StreamChannelState extends State { bool preferOffline = false, }) async { if (channel.state == null) return []; - channel.state.isUpToDate = false; - channel.state.truncate(); + channel.state!.isUpToDate = false; + channel.state!.truncate(); if (messageId == null) { await channel.query( @@ -234,7 +240,7 @@ class StreamChannelState extends State { ), preferOffline: preferOffline, ); - channel.state.isUpToDate = true; + channel.state!.isUpToDate = true; return []; } @@ -280,14 +286,14 @@ class StreamChannelState extends State { preferOffline: preferOffline, ); if (state.messages.isEmpty || state.messages.length < limit) { - channel.state.isUpToDate = true; + channel.state?.isUpToDate = true; } return state; } /// Future getMessage(String messageId) async { - var message = channel.state.messages.firstWhereOrNull( + var message = channel.state?.messages.firstWhereOrNull( (it) => it.id == messageId, ); if (message == null) { diff --git a/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart b/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart index 42ec8a21..a72425ed 100644 --- a/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/stream_chat_core.dart @@ -43,9 +43,7 @@ class StreamChatCore extends StatefulWidget { required this.child, this.onBackgroundEventReceived, this.backgroundKeepAlive = const Duration(minutes: 1), - }) : assert(client != null, 'Stream Chat Client should not be null'), - assert(child != null, 'Child should not be null'), - super(key: key); + }) : super(key: key); /// Instance of Stream Chat Client containing information about the current /// application. @@ -93,15 +91,15 @@ class StreamChatCoreState extends State Widget build(BuildContext context) => widget.child; /// The current user - User? get user => client.state?.user; + User? get user => client.state.user; /// The current user as a stream - Stream? get userStream => client.state?.userStream; + Stream get userStream => client.state.userStream; @override void initState() { super.initState(); - WidgetsBinding.instance!.addObserver(this); + WidgetsBinding.instance?.addObserver(this); } StreamSubscription? _eventSubscription; @@ -119,15 +117,15 @@ class StreamChatCoreState extends State ); void onTimerComplete() { - _eventSubscription!.cancel(); + _eventSubscription?.cancel(); client.disconnect(); } _disconnectTimer = Timer(widget.backgroundKeepAlive, onTimerComplete); } else if (state == AppLifecycleState.resumed) { if (_disconnectTimer?.isActive == true) { - _eventSubscription!.cancel(); - _disconnectTimer!.cancel(); + _eventSubscription?.cancel(); + _disconnectTimer?.cancel(); } else { if (client.wsConnectionStatus == ConnectionStatus.disconnected) { client.connect(); @@ -139,7 +137,7 @@ class StreamChatCoreState extends State @override void dispose() { - WidgetsBinding.instance!.removeObserver(this); + WidgetsBinding.instance?.removeObserver(this); _eventSubscription?.cancel(); _disconnectTimer?.cancel(); super.dispose(); diff --git a/packages/stream_chat_flutter_core/lib/src/typedef.dart b/packages/stream_chat_flutter_core/lib/src/typedef.dart index d5e7af2c..ace8d7e6 100644 --- a/packages/stream_chat_flutter_core/lib/src/typedef.dart +++ b/packages/stream_chat_flutter_core/lib/src/typedef.dart @@ -4,7 +4,7 @@ import 'package:stream_chat/stream_chat.dart'; /// A signature for a callback which exposes an error and returns a function. /// This Callback can be used in cases where an API failure occurs and the /// widget is unable to render data. -typedef ErrorBuilder = Widget Function(BuildContext context, Object? error); +typedef ErrorBuilder = Widget Function(BuildContext context, Object error); /// A Signature for a handler function which will expose a [event]. typedef EventHandler = void Function(Event event); diff --git a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart index d2412b97..1443ef17 100644 --- a/packages/stream_chat_flutter_core/lib/src/user_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/user_list_core.dart @@ -68,11 +68,7 @@ class UserListCore extends StatefulWidget { this.pagination, this.groupAlphabetically = false, this.userListController, - }) : assert(errorBuilder != null, ''), - assert(emptyBuilder != null, ''), - assert(loadingBuilder != null, ''), - assert(listBuilder != null, ''), - super(key: key); + }) : super(key: key); /// A [UserListController] allows reloading and pagination. /// Use [UserListController.loadData] and [UserListController.paginateData] @@ -80,7 +76,7 @@ class UserListCore extends StatefulWidget { final UserListController? userListController; /// The builder that will be used in case of error - final Widget Function(Object? error) errorBuilder; + final Widget Function(Object error) errorBuilder; /// The builder that will be used to build the list final Widget Function(BuildContext context, List users) listBuilder; @@ -180,7 +176,7 @@ class UserListCoreState extends State stream: _buildUserStream(usersBlocState), builder: (context, snapshot) { if (snapshot.hasError) { - return widget.errorBuilder(snapshot.error); + return widget.errorBuilder(snapshot.error!); } if (!snapshot.hasData) { return widget.loadingBuilder(context); @@ -223,8 +219,8 @@ class UserListCoreState extends State if (widget.filter?.toString() != oldWidget.filter?.toString() || jsonEncode(widget.sort) != jsonEncode(oldWidget.sort) || widget.options?.toString() != oldWidget.options?.toString() || - widget.pagination?.toJson()?.toString() != - oldWidget.pagination?.toJson()?.toString()) { + widget.pagination?.toJson().toString() != + oldWidget.pagination?.toJson().toString()) { loadData(); } } diff --git a/packages/stream_chat_flutter_core/lib/src/users_bloc.dart b/packages/stream_chat_flutter_core/lib/src/users_bloc.dart index 2470fba7..dcc3252f 100644 --- a/packages/stream_chat_flutter_core/lib/src/users_bloc.dart +++ b/packages/stream_chat_flutter_core/lib/src/users_bloc.dart @@ -16,11 +16,7 @@ class UsersBloc extends StatefulWidget { const UsersBloc({ required this.child, Key? key, - }) : assert( - child != null, - 'When constructing a UsersBloc, the parameter ' - 'child should not be null.'), - super(key: key); + }) : super(key: key); /// The widget child final Widget child; @@ -76,17 +72,15 @@ class UsersBlocState extends State } try { - final clear = pagination == null || - pagination.offset == null || - pagination.offset == 0; + final clear = pagination == null || pagination.offset == 0; final oldUsers = List.from(users ?? []); final usersResponse = await client.queryUsers( - filter: filter!, - sort: sort!, - options: options!, - pagination: pagination!, + filter: filter, + sort: sort, + options: options, + pagination: pagination, ); if (clear) { diff --git a/packages/stream_chat_flutter_core/pubspec.yaml b/packages/stream_chat_flutter_core/pubspec.yaml index 195c4ee3..750aef95 100644 --- a/packages/stream_chat_flutter_core/pubspec.yaml +++ b/packages/stream_chat_flutter_core/pubspec.yaml @@ -12,12 +12,12 @@ environment: flutter: ">=1.17.0" dependencies: + collection: ^1.15.0-nullsafety.4 flutter: sdk: flutter - meta: ^1.2.4 + meta: ^1.3.0 rxdart: ^0.26.0 - stream_chat: ^1.5.0 - collection: ^1.15.0-nullsafety.4 + stream_chat: ^1.5.2 dependency_overrides: stream_chat: @@ -27,5 +27,5 @@ dev_dependencies: fake_async: ^1.2.0 flutter_test: sdk: flutter - mockito: ^5.0.3 + mocktail: ^0.1.1 diff --git a/packages/stream_chat_flutter_core/test/channel_list_core_test.dart b/packages/stream_chat_flutter_core/test/channel_list_core_test.dart index b0fb9ded..5bbdaf39 100644 --- a/packages/stream_chat_flutter_core/test/channel_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/channel_list_core_test.dart @@ -1,8 +1,8 @@ import 'dart:async'; -import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/widgets.dart'; -import 'package:mockito/mockito.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/src/channel_list_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; @@ -30,58 +30,6 @@ void main() { ); } - test( - 'should throw assertion error in case listBuilder is null', - () { - final channelListCore = () => ChannelListCore( - listBuilder: null, - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (BuildContext context, Object error) => Offstage(), - ); - expect(channelListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case loadingBuilder is null', - () { - final channelListCore = () => ChannelListCore( - listBuilder: (_, __) => Offstage(), - loadingBuilder: null, - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (BuildContext context, Object error) => Offstage(), - ); - expect(channelListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case emptyBuilder is null', - () { - final channelListCore = () => ChannelListCore( - listBuilder: (_, __) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: null, - errorBuilder: (BuildContext context, Object error) => Offstage(), - ); - expect(channelListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case errorBuilder is null', - () { - final channelListCore = () => ChannelListCore( - listBuilder: (_, __) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: null, - ); - expect(channelListCore, throwsA(isA())); - }, - ); - testWidgets( 'should throw if ChannelListCore is used where ChannelsBloc is not present ' 'in the widget tree', @@ -116,7 +64,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -151,7 +100,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -185,15 +135,16 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); const error = 'Error! Error! Error!'; - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).thenThrow(error); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).thenThrow(error); await tester.pumpWidget( StreamChatCore( @@ -208,12 +159,12 @@ void main() { expect(find.byKey(errorWidgetKey), findsOneWidget); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).called(1); }, ); @@ -233,15 +184,16 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); const channels = []; - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).thenAnswer((_) => Stream.value(channels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).thenAnswer((_) => Stream.value(channels)); await tester.pumpWidget( StreamChatCore( @@ -256,12 +208,12 @@ void main() { expect(find.byKey(emptyWidgetKey), findsOneWidget); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).called(1); }, ); @@ -281,15 +233,16 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).thenAnswer((_) => Stream.value(channels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).thenAnswer((_) => Stream.value(channels)); await tester.pumpWidget( StreamChatCore( @@ -304,12 +257,12 @@ void main() { expect(find.byKey(listWidgetKey), findsOneWidget); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).called(1); }, ); @@ -337,15 +290,16 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).thenAnswer((_) => Stream.value(channels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).thenAnswer((_) => Stream.value(channels)); await tester.pumpWidget( Directionality( @@ -364,12 +318,12 @@ void main() { expect(find.byKey(listWidgetKey), findsOneWidget); expect(find.text(channels.map((e) => e.cid).join(',')), findsOneWidget); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).called(1); final channelListCoreState = tester.state( find.byKey(channelListCoreKey), @@ -378,12 +332,12 @@ void main() { final offset = channels.length; final paginatedChannels = _generateChannels(mockClient, offset: offset); final updatedPagination = pagination.copyWith(offset: offset); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: updatedPagination, - )).thenAnswer((_) => Stream.value(paginatedChannels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: updatedPagination, + )).thenAnswer((_) => Stream.value(paginatedChannels)); await channelListCoreState.paginateData(); @@ -398,12 +352,12 @@ void main() { findsOneWidget, ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: updatedPagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: updatedPagination, + )).called(1); }, ); @@ -411,8 +365,8 @@ void main() { 'should rebuild ChannelListCore with updated widget data ' 'on calling setState()', (tester) async { - StateSetter _stateSetter; - int limit = pagination.limit; + StateSetter? _stateSetter; + var limit = pagination.limit; const channelListCoreKey = Key('channelListCore'); const listWidgetKey = Key('listWidget'); @@ -435,15 +389,16 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).thenAnswer((_) => Stream.value(channels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).thenAnswer((_) => Stream.value(channels)); await tester.pumpWidget( Directionality( @@ -466,24 +421,24 @@ void main() { expect(find.byKey(listWidgetKey), findsOneWidget); expect(find.text(channels.map((e) => e.cid).join(',')), findsOneWidget); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: pagination, + )).called(1); // Rebuilding ChannelListCore with new pagination limit - _stateSetter(() => limit = 6); + _stateSetter?.call(() => limit = 6); final updatedChannels = _generateChannels(mockClient, count: limit); final updatedPagination = pagination.copyWith(limit: limit); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: updatedPagination, - )).thenAnswer((_) => Stream.value(updatedChannels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: updatedPagination, + )).thenAnswer((_) => Stream.value(updatedChannels)); await tester.pumpAndSettle(); @@ -493,12 +448,12 @@ void main() { findsOneWidget, ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: updatedPagination, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: updatedPagination, + )).called(1); }, ); } diff --git a/packages/stream_chat_flutter_core/test/channels_bloc_test.dart b/packages/stream_chat_flutter_core/test/channels_bloc_test.dart index e021e9b7..de8b0af1 100644 --- a/packages/stream_chat_flutter_core/test/channels_bloc_test.dart +++ b/packages/stream_chat_flutter_core/test/channels_bloc_test.dart @@ -2,13 +2,17 @@ import 'dart:async'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'matchers/channel_matcher.dart'; import 'mocks.dart'; void main() { + setUpAll(() { + registerFallbackValue(const PaginationParams()); + }); + List _generateChannels( StreamChatClient client, { int count = 3, @@ -28,18 +32,6 @@ void main() { ); } - test( - 'should throw assertion error if child is null', - () async { - const channelsBlocKey = Key('channelsBloc'); - final channelsBloc = () => ChannelsBloc( - key: channelsBlocKey, - child: null, - ); - expect(channelsBloc, throwsA(isA())); - }, - ); - testWidgets( 'should throw if ChannelsBloc is used where StreamChat is not present in the widget tree', (tester) async { @@ -70,7 +62,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -101,7 +94,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -117,12 +111,12 @@ void main() { final offlineChannels = _generateChannels(mockClient); final onlineChannels = _generateChannels(mockClient, offset: 3); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.fromIterable([offlineChannels, onlineChannels]), ); @@ -136,12 +130,12 @@ void main() { ]), ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -163,7 +157,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -176,14 +171,14 @@ void main() { find.byKey(channelsBlocKey), ); - final error = 'Error! Error! Error!'; + const error = 'Error! Error! Error!'; - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenThrow(error); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenThrow(error); channelsBlocState.queryChannels(); @@ -192,12 +187,12 @@ void main() { emitsError(error), ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -214,7 +209,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -229,38 +225,41 @@ void main() { final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer((_) => Stream.value(channels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer((_) => Stream.value(channels)); - channelsBlocState.queryChannels(); + const pagination = PaginationParams(limit: 3); + channelsBlocState.queryChannels( + paginationParams: pagination, + ); await expectLater( channelsBlocState.channelsStream, emits(isSameChannelListAs(channels)), ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final offset = channels.length; - final paginationParams = PaginationParams(offset: offset); + final paginationParams = pagination.copyWith(offset: offset); final newChannels = _generateChannels(mockClient, offset: offset); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: paginationParams, - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: paginationParams, + )).thenAnswer( (_) => Stream.value(newChannels), ); @@ -277,12 +276,12 @@ void main() { ), ]); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: paginationParams, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: paginationParams, + )).called(1); }, ); @@ -299,7 +298,8 @@ void main() { final mockClient = MockClient(); - when(mockClient.on(any, any, any, any)).thenAnswer((_) => Stream.empty()); + when(() => mockClient.on(any(), any(), any(), any())) + .thenAnswer((_) => Stream.empty()); await tester.pumpWidget( StreamChatCore( @@ -313,39 +313,41 @@ void main() { ); final channels = _generateChannels(mockClient); + final paginationParams = const PaginationParams( + limit: 3, + ); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer((_) => Stream.value(channels)); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: paginationParams, + )).thenAnswer((_) => Stream.value(channels)); - channelsBlocState.queryChannels(); + channelsBlocState.queryChannels( + paginationParams: paginationParams, + ); await expectLater( channelsBlocState.channelsStream, emits(isSameChannelListAs(channels)), ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); - - final offset = channels.length; - final paginationParams = PaginationParams(offset: offset); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: paginationParams, + )).called(1); final error = 'Error! Error! Error!'; - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: paginationParams, - )).thenThrow(error); + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: paginationParams, + )).thenThrow(error); channelsBlocState.queryChannels(paginationParams: paginationParams); @@ -354,17 +356,17 @@ void main() { emitsError(error), ); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: paginationParams, - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: paginationParams, + )).called(1); }, ); group('event controller test', () { - StreamController eventController; + late StreamController eventController; setUp(() { eventController = StreamController.broadcast(); }); @@ -379,12 +381,12 @@ void main() { child: Offstage(), ); - when(mockClient.on(any, any, any, any)) + when(() => mockClient.on(any(), any(), any(), any())) .thenAnswer((_) => Stream.empty()); - when(mockClient.on( - EventType.channelHidden, - )).thenAnswer((_) => eventController.stream); + when(() => mockClient.on( + EventType.channelHidden, + )).thenAnswer((_) => eventController.stream); await tester.pumpWidget( StreamChatCore( @@ -399,23 +401,23 @@ void main() { final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.value(channels), ); await channelsBlocState.queryChannels(); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final channelHiddenEvent = Event( type: EventType.channelHidden, @@ -435,7 +437,7 @@ void main() { ]), ); - verify(mockClient.on(EventType.channelHidden)).called(1); + verify(() => mockClient.on(EventType.channelHidden)).called(1); }, ); @@ -450,13 +452,13 @@ void main() { child: Offstage(), ); - when(mockClient.on(any, any, any, any)) + when(() => mockClient.on(any(), any(), any(), any())) .thenAnswer((_) => Stream.empty()); - when(mockClient.on( - EventType.channelDeleted, - EventType.notificationRemovedFromChannel, - )).thenAnswer((_) => eventController.stream); + when(() => mockClient.on( + EventType.channelDeleted, + EventType.notificationRemovedFromChannel, + )).thenAnswer((_) => eventController.stream); await tester.pumpWidget( StreamChatCore( @@ -471,31 +473,38 @@ void main() { final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.value(channels), ); await channelsBlocState.queryChannels(); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final channelDeletedOrNotificationRemovedEvent = Event( - channel: EventChannel(cid: channels.first.cid), + channel: EventChannel( + cid: channels.first.cid!, + updatedAt: DateTime.now(), + config: ChannelConfig(), + createdAt: DateTime.now(), + memberCount: 1, + ), ); eventController.add(channelDeletedOrNotificationRemovedEvent); - final channelCid = channelDeletedOrNotificationRemovedEvent.channel.cid; + final channelCid = + channelDeletedOrNotificationRemovedEvent.channel?.cid; final newChannels = [...channels] ..removeWhere((it) => it.cid == channelCid); @@ -507,10 +516,10 @@ void main() { ]), ); - verify(mockClient.on( - EventType.channelDeleted, - EventType.notificationRemovedFromChannel, - )).called(1); + verify(() => mockClient.on( + EventType.channelDeleted, + EventType.notificationRemovedFromChannel, + )).called(1); }, ); @@ -525,12 +534,12 @@ void main() { child: Offstage(), ); - when(mockClient.on(any, any, any, any)) + when(() => mockClient.on(any(), any(), any(), any())) .thenAnswer((_) => Stream.empty()); - when(mockClient.on( - EventType.messageNew, - )).thenAnswer((_) => eventController.stream); + when(() => mockClient.on( + EventType.messageNew, + )).thenAnswer((_) => eventController.stream); await tester.pumpWidget( StreamChatCore( @@ -545,23 +554,23 @@ void main() { final channels = _generateChannels(mockClient); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.value(channels), ); await channelsBlocState.queryChannels(); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final messageNewEvent = Event( type: EventType.messageNew, @@ -585,7 +594,7 @@ void main() { ]), ); - verify(mockClient.on(EventType.messageNew)).called(1); + verify(() => mockClient.on(EventType.messageNew)).called(1); }, ); @@ -609,16 +618,16 @@ void main() { shouldAddChannel: (e) => channels.map((it) => it.cid).contains(e.cid), ); - when(mockClient.on(any, any, any, any)) + when(() => mockClient.on(any(), any(), any(), any())) .thenAnswer((_) => Stream.empty()); - when(mockClient.on( - EventType.channelHidden, - )).thenAnswer((_) => hiddenChannelEventController.stream); + when(() => mockClient.on( + EventType.channelHidden, + )).thenAnswer((_) => hiddenChannelEventController.stream); - when(mockClient.on( - EventType.messageNew, - )).thenAnswer((_) => eventController.stream); + when(() => mockClient.on( + EventType.messageNew, + )).thenAnswer((_) => eventController.stream); final messageNewEvent = Event( type: EventType.messageNew, @@ -636,23 +645,23 @@ void main() { find.byKey(channelsBlocKey), ); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.value(channels), ); await channelsBlocState.queryChannels(); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final channelHiddenEvent = Event( type: EventType.channelHidden, @@ -681,8 +690,8 @@ void main() { ]), ); - verify(mockClient.on(EventType.channelHidden)).called(1); - verify(mockClient.on(EventType.messageNew)).called(1); + verify(() => mockClient.on(EventType.channelHidden)).called(1); + verify(() => mockClient.on(EventType.messageNew)).called(1); }, ); @@ -703,14 +712,14 @@ void main() { shouldAddChannel: (_) => true, ); - when(mockClient.state.channels).thenReturn(stateChannels); + when(() => mockClient.state.channels).thenReturn(stateChannels); - when(mockClient.on(any, any, any, any)) + when(() => mockClient.on(any(), any(), any(), any())) .thenAnswer((_) => Stream.empty()); - when(mockClient.on( - EventType.messageNew, - )).thenAnswer((_) => eventController.stream); + when(() => mockClient.on( + EventType.messageNew, + )).thenAnswer((_) => eventController.stream); await tester.pumpWidget( StreamChatCore( @@ -723,23 +732,23 @@ void main() { find.byKey(channelsBlocKey), ); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.value(channels), ); await channelsBlocState.queryChannels(); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final messageNewEvent = Event( type: EventType.messageNew, @@ -749,7 +758,7 @@ void main() { eventController.add(messageNewEvent); final newChannels = [...channels] - ..insert(0, stateChannels[stateChannels.keys.first]); + ..insert(0, stateChannels[stateChannels.keys.first]!); await expectLater( channelsBlocState.channelsStream, @@ -759,7 +768,7 @@ void main() { ]), ); - verify(mockClient.on(EventType.messageNew)).called(1); + verify(() => mockClient.on(EventType.messageNew)).called(1); }, ); @@ -770,8 +779,8 @@ void main() { final mockClient = MockClient(); final channels = _generateChannels(mockClient); int channelComparator(Channel a, Channel b) { - final aData = a.extraData['extra_data_key'] as String; - final bData = b.extraData['extra_data_key'] as String; + final aData = a.extraData!['extra_data_key'] as String; + final bData = b.extraData!['extra_data_key'] as String; return bData.compareTo(aData); } @@ -783,12 +792,12 @@ void main() { channelsComparator: channelComparator, ); - when(mockClient.on(any, any, any, any)) + when(() => mockClient.on(any(), any(), any(), any())) .thenAnswer((_) => Stream.empty()); - when(mockClient.on( - EventType.messageNew, - )).thenAnswer((_) => eventController.stream); + when(() => mockClient.on( + EventType.messageNew, + )).thenAnswer((_) => eventController.stream); await tester.pumpWidget( StreamChatCore( @@ -801,23 +810,23 @@ void main() { find.byKey(channelsBlocKey), ); - when(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) => Stream.value(channels), ); await channelsBlocState.queryChannels(); - verify(mockClient.queryChannels( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.queryChannels( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final messageNewEvent = Event( type: EventType.messageNew, @@ -836,7 +845,7 @@ void main() { ]), ); - verify(mockClient.on(EventType.messageNew)).called(1); + verify(() => mockClient.on(EventType.messageNew)).called(1); }, ); diff --git a/packages/stream_chat_flutter_core/test/lazy_load_scroll_view_test.dart b/packages/stream_chat_flutter_core/test/lazy_load_scroll_view_test.dart index 19c1f2a2..af59088f 100644 --- a/packages/stream_chat_flutter_core/test/lazy_load_scroll_view_test.dart +++ b/packages/stream_chat_flutter_core/test/lazy_load_scroll_view_test.dart @@ -3,18 +3,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat_flutter_core/src/lazy_load_scroll_view.dart'; void main() { - test( - 'should throw assertion error if child is null', - () async { - const lazyLoadScrollViewKey = Key('lazyLoadScrollView'); - final lazyLoadScrollView = () => LazyLoadScrollView( - key: lazyLoadScrollViewKey, - child: null, - ); - expect(lazyLoadScrollView, throwsA(isA())); - }, - ); - testWidgets( 'should render LazyLoadScrollView if child is provided', (tester) async { diff --git a/packages/stream_chat_flutter_core/test/matchers/get_message_response_matcher.dart b/packages/stream_chat_flutter_core/test/matchers/get_message_response_matcher.dart index 42ea0d34..c3a6629d 100644 --- a/packages/stream_chat_flutter_core/test/matchers/get_message_response_matcher.dart +++ b/packages/stream_chat_flutter_core/test/matchers/get_message_response_matcher.dart @@ -1,4 +1,3 @@ -import 'package:meta/meta.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; @@ -8,14 +7,14 @@ Matcher isSameMessageResponseAs(GetMessageResponse targetResponse) => class _IsSameMessageResponseAs extends Matcher { const _IsSameMessageResponseAs({ required this.targetResponse, - }) : assert(targetResponse != null, ''); + }); final GetMessageResponse targetResponse; @override bool matches(covariant GetMessageResponse response, Map matchState) => response.message.id == targetResponse.message.id && - response.channel.cid == targetResponse.channel.cid; + response.channel?.cid == targetResponse.channel?.cid; @override Description describe(Description description) => @@ -29,14 +28,14 @@ Matcher isSameMessageResponseListAs( class _IsSameMessageResponseListAs extends Matcher { const _IsSameMessageResponseListAs({ required this.targetResponseList, - }) : assert(targetResponseList != null, ''); + }); final List targetResponseList; @override bool matches( covariant List responseList, Map matchState) { - bool matches = true; + var matches = true; for (var i = 0; i < responseList.length; i++) { final response = responseList[i]; final targetResponse = targetResponseList[i]; diff --git a/packages/stream_chat_flutter_core/test/message_list_core_test.dart b/packages/stream_chat_flutter_core/test/message_list_core_test.dart index 95446f95..9aea9489 100644 --- a/packages/stream_chat_flutter_core/test/message_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/message_list_core_test.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/src/message_list_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; @@ -62,61 +62,6 @@ void main() { return threads ? threadMessages : messages; } - test( - 'should throw assertion error in case messageListBuilder is null', - () { - final messageListCore = () => MessageListCore( - messageListBuilder: null, - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorWidgetBuilder: (BuildContext context, Object error) => - Offstage(), - ); - expect(messageListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case loadingBuilder is null', - () { - final messageListCore = () => MessageListCore( - messageListBuilder: (_, __) => Offstage(), - loadingBuilder: null, - emptyBuilder: (BuildContext context) => Offstage(), - errorWidgetBuilder: (BuildContext context, Object error) => - Offstage(), - ); - expect(messageListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case emptyBuilder is null', - () { - final messageListCore = () => MessageListCore( - messageListBuilder: (_, __) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: null, - errorWidgetBuilder: (BuildContext context, Object error) => - Offstage(), - ); - expect(messageListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case errorWidgetBuilder is null', - () { - final messageListCore = () => MessageListCore( - messageListBuilder: (_, __) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorWidgetBuilder: null, - ); - expect(messageListCore, throwsA(isA())); - }, - ); - testWidgets( 'should throw if MessageListCore is used where StreamChannel is not present ' 'in the widget tree', @@ -150,8 +95,11 @@ void main() { ); final mockChannel = MockChannel(); + when(() => mockChannel.initialized).thenAnswer((_) => Future.value(true)); - when(mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.state.messagesStream) + .thenAnswer((_) => Stream.value([])); await tester.pumpWidget( StreamChannel( @@ -182,7 +130,10 @@ void main() { final mockChannel = MockChannel(); - when(mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.state.messagesStream) + .thenAnswer((_) => Stream.value([])); + when(() => mockChannel.initialized).thenAnswer((_) => Future.value(true)); await tester.pumpWidget( StreamChannel( @@ -213,11 +164,11 @@ void main() { final mockChannel = MockChannel(); - when(mockChannel.state.isUpToDate).thenReturn(true); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.initialized).thenAnswer((_) async => true); const error = 'Error! Error! Error!'; - when(mockChannel.state.messagesStream) + when(() => mockChannel.state.messagesStream) .thenAnswer((_) => Stream.error(error)); await tester.pumpWidget( @@ -252,11 +203,11 @@ void main() { final mockChannel = MockChannel(); - when(mockChannel.state.isUpToDate).thenReturn(true); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.initialized).thenAnswer((_) async => true); const messages = []; - when(mockChannel.state.messagesStream) + when(() => mockChannel.state.messagesStream) .thenAnswer((_) => Stream.value(messages)); await tester.pumpWidget( @@ -291,11 +242,18 @@ void main() { final mockChannel = MockChannel(); - when(mockChannel.state.isUpToDate).thenReturn(false); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.state.isUpToDate).thenReturn(false); + when(() => mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.query( + options: any(named: 'options'), + membersPagination: any(named: 'membersPagination'), + messagesPagination: any(named: 'messagesPagination'), + preferOffline: any(named: 'preferOffline'), + watchersPagination: any(named: 'watchersPagination'), + )).thenAnswer((_) async => ChannelState()); const messages = []; - when(mockChannel.state.messagesStream) + when(() => mockChannel.state.messagesStream) .thenAnswer((_) => Stream.value(messages)); await tester.pumpWidget( @@ -335,11 +293,11 @@ void main() { final mockChannel = MockChannel(); - when(mockChannel.state.isUpToDate).thenReturn(true); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.initialized).thenAnswer((_) async => true); final messages = _generateMessages(); - when(mockChannel.state.messagesStream) + when(() => mockChannel.state.messagesStream) .thenAnswer((_) => Stream.value(messages)); await tester.pumpWidget( @@ -382,13 +340,13 @@ void main() { final mockChannel = MockChannel(); - when(mockChannel.state.isUpToDate).thenReturn(true); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.state.isUpToDate).thenReturn(true); + when(() => mockChannel.initialized).thenAnswer((_) async => true); final threads = {parentMessage.id: messages}; - when(mockChannel.state.threads).thenReturn(threads); - when(mockChannel.state.threadsStream) + when(() => mockChannel.state.threads).thenReturn(threads); + when(() => mockChannel.state.threadsStream) .thenAnswer((_) => Stream.value(threads)); await tester.pumpWidget( diff --git a/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart b/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart index e639c03b..5b427c02 100644 --- a/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart +++ b/packages/stream_chat_flutter_core/test/message_search_bloc_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter_core/src/message_search_bloc.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; @@ -23,24 +23,12 @@ void main() { text: 'testTextData$index', ) ..channel = ChannelModel( - cid: 'testCid', + cid: 'testCid:id', ); }, ); } - test( - 'should throw assertion error if child is null', - () async { - const messageSearchBlocKey = Key('messageSearchBloc'); - final messageSearchBloc = () => MessageSearchBloc( - key: messageSearchBlocKey, - child: null, - ); - expect(messageSearchBloc, throwsA(isA())); - }, - ); - testWidgets( 'messageSearchBlocState.search() should throw if used where ' 'StreamChat is not present in the widget tree', @@ -62,7 +50,7 @@ void main() { ); try { - await usersBlocState.search(); + await usersBlocState.search(filter: {}); } catch (e) { expect(e, isInstanceOf()); } @@ -93,30 +81,30 @@ void main() { final messageResponseList = _generateMessages(); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); - messageSearchBlocState.search(); + messageSearchBlocState.search(filter: {}); await expectLater( messageSearchBlocState.messagesStream, emits(isSameMessageResponseListAs(messageResponseList)), ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -144,28 +132,28 @@ void main() { ); const error = 'Error! Error! Error!'; - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenThrow(error); + when(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenThrow(error); - messageSearchBlocState.search(); + messageSearchBlocState.search(filter: {}); await expectLater( messageSearchBlocState.messagesStream, emitsError(error), ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -196,47 +184,47 @@ void main() { final messageResponseList = _generateMessages(); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); - messageSearchBlocState.search(); + messageSearchBlocState.search(filter: {}); await expectLater( messageSearchBlocState.messagesStream, emits(isSameMessageResponseListAs(messageResponseList)), ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final offset = messageResponseList.length; final paginatedMessageResponseList = _generateMessages(offset: offset); final pagination = PaginationParams(offset: offset); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).thenAnswer( + when(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).thenAnswer( (_) async => SearchMessagesResponse()..results = paginatedMessageResponseList, ); - messageSearchBlocState.search(pagination: pagination); + messageSearchBlocState.search(pagination: pagination, filter: {}); await Future.wait([ expectLater( @@ -251,13 +239,13 @@ void main() { ), ]); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).called(1); }, ); @@ -288,57 +276,57 @@ void main() { final messageResponseList = _generateMessages(); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); - messageSearchBlocState.search(); + messageSearchBlocState.search(filter: {}); await expectLater( messageSearchBlocState.messagesStream, emits(isSameMessageResponseListAs(messageResponseList)), ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); final offset = messageResponseList.length; final pagination = PaginationParams(offset: offset); const error = 'Error! Error! Error!'; - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).thenThrow(error); + when(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).thenThrow(error); - messageSearchBlocState.search(pagination: pagination); + messageSearchBlocState.search(pagination: pagination, filter: {}); await expectLater( messageSearchBlocState.queryMessagesLoading, emitsError(error), ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).called(1); }, ); } diff --git a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart index aa4a47f9..00aee32a 100644 --- a/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/message_search_list_core_test.dart @@ -1,8 +1,8 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; -import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/src/message_search_list_core.dart'; +import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'mocks.dart'; @@ -10,74 +10,21 @@ void main() { List _generateMessages({ int count = 3, int offset = 0, - }) { - return List.generate( - count, - (index) { - index = index + offset; - return GetMessageResponse() - ..message = Message( - id: 'testId$index', - text: 'testTextData$index', - ) - ..channel = ChannelModel( - cid: 'testCid', - ); - }, - ); - } - - test( - 'should throw assertion error in case childBuilder is null', - () { - final messageSearchListCore = () => MessageSearchListCore( - childBuilder: null, - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (BuildContext context, Object error) => Offstage(), - ); - expect(messageSearchListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case loadingBuilder is null', - () { - final messageSearchListCore = () => MessageSearchListCore( - childBuilder: (List messages) => Offstage(), - loadingBuilder: null, - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (BuildContext context, Object error) => Offstage(), - ); - expect(messageSearchListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case emptyBuilder is null', - () { - final messageSearchListCore = () => MessageSearchListCore( - childBuilder: (List messages) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: null, - errorBuilder: (BuildContext context, Object error) => Offstage(), - ); - expect(messageSearchListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case errorBuilder is null', - () { - final messageSearchListCore = () => MessageSearchListCore( - childBuilder: (List messages) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: null, - ); - expect(messageSearchListCore, throwsA(isA())); - }, - ); + }) => + List.generate( + count, + (index) { + index = index + offset; + return GetMessageResponse() + ..message = Message( + id: 'testId$index', + text: 'testTextData$index', + ) + ..channel = ChannelModel( + cid: 'test:Cid', + ); + }, + ); testWidgets( 'should throw if MessageSearchListCore is used where MessageSearchBloc ' @@ -86,10 +33,11 @@ void main() { const messageSearchListCoreKey = Key('messageSearchListCore'); final messageSearchListCore = MessageSearchListCore( key: messageSearchListCoreKey, - childBuilder: (List messages) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (BuildContext context, Object error) => Offstage(), + childBuilder: (List? messages) => const Offstage(), + loadingBuilder: (BuildContext context) => const Offstage(), + emptyBuilder: (BuildContext context) => const Offstage(), + errorBuilder: (BuildContext context, Object? error) => const Offstage(), + filters: const {}, ); await tester.pumpWidget(messageSearchListCore); @@ -109,7 +57,8 @@ void main() { childBuilder: (List messages) => Offstage(), loadingBuilder: (BuildContext context) => Offstage(), emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (BuildContext context, Object error) => Offstage(), + errorBuilder: (BuildContext context, Object? error) => Offstage(), + filters: {}, ); final mockClient = MockClient(); @@ -140,6 +89,7 @@ void main() { emptyBuilder: (BuildContext context) => Offstage(), errorBuilder: (BuildContext context, Object error) => Offstage(), messageSearchListController: controller, + filters: {}, ); expect(controller.loadData, isNull); @@ -175,18 +125,19 @@ void main() { errorBuilder: (BuildContext context, Object error) => Offstage( key: errorWidgetKey, ), + filters: {}, ); final mockClient = MockClient(); const error = 'Error! Error! Error!'; - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenThrow(error); + when(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenThrow(error); await tester.pumpWidget( StreamChatCore( @@ -201,13 +152,13 @@ void main() { expect(find.byKey(errorWidgetKey), findsOneWidget); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -223,18 +174,19 @@ void main() { loadingBuilder: (BuildContext context) => Offstage(), emptyBuilder: (BuildContext context) => Offstage(key: emptyWidgetKey), errorBuilder: (BuildContext context, Object error) => Offstage(), + filters: {}, ); final mockClient = MockClient(); final messageResponseList = []; - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); @@ -251,13 +203,13 @@ void main() { expect(find.byKey(emptyWidgetKey), findsOneWidget); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -274,18 +226,19 @@ void main() { loadingBuilder: (BuildContext context) => Offstage(), emptyBuilder: (BuildContext context) => Offstage(), errorBuilder: (BuildContext context, Object error) => Offstage(), + filters: {}, ); final mockClient = MockClient(); final messageResponseList = _generateMessages(); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).thenAnswer( + when(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); @@ -302,13 +255,13 @@ void main() { expect(find.byKey(childWidgetKey), findsOneWidget); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: anyNamed('paginationParams'), - )).called(1); + verify(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: any(named: 'paginationParams'), + )).called(1); }, ); @@ -324,25 +277,26 @@ void main() { childBuilder: (List messages) => Container( key: childWidgetKey, child: Text( - messages.map((e) => '${e.channel.cid}-${e.message.id}').join(','), + messages.map((e) => '${e.channel?.cid}-${e.message.id}').join(','), ), ), loadingBuilder: (BuildContext context) => Offstage(), emptyBuilder: (BuildContext context) => Offstage(), errorBuilder: (BuildContext context, Object error) => Offstage(), paginationParams: pagination, + filters: {}, ); final mockClient = MockClient(); final messageResponseList = _generateMessages(); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).thenAnswer( + when(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); @@ -364,19 +318,19 @@ void main() { expect( find.text( messageResponseList - .map((e) => '${e.channel.cid}-${e.message.id}') + .map((e) => '${e.channel?.cid}-${e.message.id}') .join(','), ), findsOneWidget, ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).called(1); final messageSearchListCoreState = tester.state( @@ -386,13 +340,13 @@ void main() { final offset = messageResponseList.length; final paginatedMessageResponseList = _generateMessages(offset: offset); final updatedPagination = pagination.copyWith(offset: offset); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: updatedPagination, - )).thenAnswer( + when(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: updatedPagination, + )).thenAnswer( (_) async => SearchMessagesResponse()..results = paginatedMessageResponseList, ); @@ -406,17 +360,17 @@ void main() { find.text([ ...messageResponseList, ...paginatedMessageResponseList, - ].map((e) => '${e.channel.cid}-${e.message.id}').join(',')), + ].map((e) => '${e.channel?.cid}-${e.message.id}').join(',')), findsOneWidget, ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: updatedPagination, - )).called(1); + verify(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: updatedPagination, + )).called(1); }, ); @@ -426,8 +380,8 @@ void main() { (tester) async { const pagination = PaginationParams(); - StateSetter _stateSetter; - int limit = pagination.limit; + StateSetter? _stateSetter; + var limit = pagination.limit; const messageSearchListCoreKey = Key('messageSearchListCore'); const childWidgetKey = Key('childWidget'); @@ -438,7 +392,7 @@ void main() { key: childWidgetKey, child: Text( messages - .map((e) => '${e.channel.cid}-${e.message.id}') + .map((e) => '${e.channel?.cid}-${e.message.id}') .join(','), ), ), @@ -446,18 +400,19 @@ void main() { emptyBuilder: (BuildContext context) => Offstage(), errorBuilder: (BuildContext context, Object error) => Offstage(), paginationParams: pagination.copyWith(limit: limit), + filters: {}, ); final mockClient = MockClient(); final messageResponseList = _generateMessages(); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).thenAnswer( + when(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).thenAnswer( (_) async => SearchMessagesResponse()..results = messageResponseList, ); @@ -483,32 +438,32 @@ void main() { expect( find.text( messageResponseList - .map((e) => '${e.channel.cid}-${e.message.id}') + .map((e) => '${e.channel?.cid}-${e.message.id}') .join(','), ), findsOneWidget, ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: pagination, - )).called(1); + verify(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: pagination, + )).called(1); // Rebuilding MessageSearchListCore with new pagination limit - _stateSetter(() => limit = 6); + _stateSetter?.call(() => limit = 6); final updatedMessageResponseList = _generateMessages(count: limit); final updatedPagination = pagination.copyWith(limit: limit); - when(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: updatedPagination, - )).thenAnswer( + when(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: updatedPagination, + )).thenAnswer( (_) async => SearchMessagesResponse()..results = updatedMessageResponseList, ); @@ -518,18 +473,18 @@ void main() { expect(find.byKey(childWidgetKey), findsOneWidget); expect( find.text(updatedMessageResponseList - .map((e) => '${e.channel.cid}-${e.message.id}') + .map((e) => '${e.channel?.cid}-${e.message.id}') .join(',')), findsOneWidget, ); - verify(mockClient.search( - any, - query: anyNamed('query'), - sort: anyNamed('sort'), - messageFilters: anyNamed('messageFilters'), - paginationParams: updatedPagination, - )).called(1); + verify(() => mockClient.search( + any(), + query: any(named: 'query'), + sort: any(named: 'sort'), + messageFilters: any(named: 'messageFilters'), + paginationParams: updatedPagination, + )).called(1); }, ); } diff --git a/packages/stream_chat_flutter_core/test/mocks.dart b/packages/stream_chat_flutter_core/test/mocks.dart index a46b7b5c..6ca24363 100644 --- a/packages/stream_chat_flutter_core/test/mocks.dart +++ b/packages/stream_chat_flutter_core/test/mocks.dart @@ -1,19 +1,20 @@ -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/stream_chat.dart'; class MockLogger extends Mock implements Logger {} class MockClient extends Mock implements StreamChatClient { + @override final Logger logger = MockLogger(); - ClientState _state; + ClientState? _state; @override ClientState get state => _state ??= MockClientState(); } class MockClientState extends Mock implements ClientState { - OwnUser _user; + OwnUser? _user; @override OwnUser get user => _user ??= OwnUser( @@ -25,12 +26,12 @@ class MockClientState extends Mock implements ClientState { } class MockChannel extends Mock implements Channel { - ChannelClientState _state; + ChannelClientState? _state; @override ChannelClientState get state => _state ??= MockChannelState(); - StreamChatClient _client; + StreamChatClient? _client; @override StreamChatClient get client => _client ??= MockClient(); diff --git a/packages/stream_chat_flutter_core/test/stream_channel_test.dart b/packages/stream_chat_flutter_core/test/stream_channel_test.dart index ef8cb394..b48c02b1 100644 --- a/packages/stream_chat_flutter_core/test/stream_channel_test.dart +++ b/packages/stream_chat_flutter_core/test/stream_channel_test.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'mocks.dart'; @@ -62,39 +62,13 @@ void main() { return threads ? threadMessages : messages; } - test( - 'should throw assertion error if child is null', - () async { - final mockChannel = MockChannel(); - const streamChannelKey = Key('streamChannel'); - final streamChannel = () => StreamChannel( - key: streamChannelKey, - channel: mockChannel, - child: null, - ); - expect(streamChannel, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error if channel is null', - () async { - const streamChannelKey = Key('streamChannel'); - final streamChannel = () => StreamChannel( - key: streamChannelKey, - child: Offstage(), - channel: null, - ); - expect(streamChannel, throwsA(isA())); - }, - ); - testWidgets( 'should render StreamChannel if both channel and child is provided', (tester) async { final mockChannel = MockChannel(); const streamChannelKey = Key('streamChannel'); const childKey = Key('childKey'); + when(() => mockChannel.initialized).thenAnswer((_) => Future.value(true)); final streamChannel = StreamChannel( key: streamChannelKey, channel: mockChannel, @@ -121,8 +95,13 @@ void main() { ); final errorMessage = 'Error! Error! Error!'; - final error = DioError(type: DioErrorType.response, error: errorMessage); - when(mockChannel.initialized).thenAnswer((_) => Future.error(error)); + final error = DioError( + type: DioErrorType.response, + error: errorMessage, + requestOptions: RequestOptions(path: ''), + ); + when(() => mockChannel.initialized) + .thenAnswer((_) => Future.error(error)); await tester.pumpWidget( Directionality( @@ -135,7 +114,7 @@ void main() { expect(find.text(errorMessage), findsOneWidget); - verify(mockChannel.initialized).called(1); + verify(() => mockChannel.initialized).called(1); }, ); @@ -153,7 +132,7 @@ void main() { showLoading: true, ); - when(mockChannel.initialized).thenAnswer((_) async => false); + when(() => mockChannel.initialized).thenAnswer((_) async => false); await tester.pumpWidget( Directionality( @@ -166,7 +145,7 @@ void main() { expect(find.byType(CircularProgressIndicator), findsOneWidget); - verify(mockChannel.initialized).called(1); + verify(() => mockChannel.initialized).called(1); }, ); @@ -183,15 +162,15 @@ void main() { initialMessageId: 'testInitialMessageId', ); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.initialized).thenAnswer((_) async => true); final messages = _generateMessages(); - when(mockChannel.query( - options: anyNamed('options'), - messagesPagination: anyNamed('messagesPagination'), - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).thenAnswer((_) async => ChannelState(messages: messages)); + when(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: any(named: 'messagesPagination'), + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).thenAnswer((_) async => ChannelState(messages: messages)); await tester.pumpWidget( Directionality( @@ -202,14 +181,14 @@ void main() { await tester.pumpAndSettle(); - verify(mockChannel.initialized).called(1); - verify(mockChannel.query( - options: anyNamed('options'), - messagesPagination: anyNamed('messagesPagination'), - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).called( + verify(() => mockChannel.initialized).called(1); + verify(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: any(named: 'messagesPagination'), + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).called( 2, // Fetching After messages + Fetching Before messages, ); }, @@ -219,7 +198,7 @@ void main() { 'should rebuild StreamChannel with updated widget data ' 'on calling setState()', (tester) async { - StateSetter _stateSetter; + StateSetter? _stateSetter; var initialMessageId = 'testInitialMessageId'; @@ -244,25 +223,25 @@ void main() { limit: 20, ); - when(mockChannel.initialized).thenAnswer((_) async => true); + when(() => mockChannel.initialized).thenAnswer((_) async => true); final messages = _generateMessages(); - when(mockChannel.query( - options: anyNamed('options'), - messagesPagination: beforePagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).thenAnswer((_) async => ChannelState(messages: messages)); + when(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: beforePagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).thenAnswer((_) async => ChannelState(messages: messages)); - when(mockChannel.query( - options: anyNamed('options'), - messagesPagination: afterPagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).thenAnswer((_) async => ChannelState(messages: messages)); + when(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: afterPagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).thenAnswer((_) async => ChannelState(messages: messages)); await tester.pumpWidget( Directionality( @@ -279,23 +258,23 @@ void main() { await tester.pumpAndSettle(); - verify(mockChannel.query( - options: anyNamed('options'), - messagesPagination: beforePagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).called(1); + verify(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: beforePagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).called(1); - verify(mockChannel.query( - options: anyNamed('options'), - messagesPagination: afterPagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).called(1); + verify(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: afterPagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).called(1); - _stateSetter(() => initialMessageId = 'testInitialMessageId2'); + _stateSetter?.call(() => initialMessageId = 'testInitialMessageId2'); final updatedBeforePagination = beforePagination.copyWith( lessThan: initialMessageId, @@ -305,39 +284,39 @@ void main() { greaterThanOrEqual: initialMessageId, ); - when(mockChannel.query( - options: anyNamed('options'), - messagesPagination: updatedBeforePagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).thenAnswer((_) async => ChannelState(messages: messages)); + when(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: updatedBeforePagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).thenAnswer((_) async => ChannelState(messages: messages)); - when(mockChannel.query( - options: anyNamed('options'), - messagesPagination: updatedAfterPagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).thenAnswer((_) async => ChannelState(messages: messages)); + when(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: updatedAfterPagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).thenAnswer((_) async => ChannelState(messages: messages)); await tester.pumpAndSettle(); - verify(mockChannel.query( - options: anyNamed('options'), - messagesPagination: updatedBeforePagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).called(1); + verify(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: updatedBeforePagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).called(1); - verify(mockChannel.query( - options: anyNamed('options'), - messagesPagination: updatedAfterPagination, - membersPagination: anyNamed('membersPagination'), - watchersPagination: anyNamed('watchersPagination'), - preferOffline: anyNamed('preferOffline'), - )).called(1); + verify(() => mockChannel.query( + options: any(named: 'options'), + messagesPagination: updatedAfterPagination, + membersPagination: any(named: 'membersPagination'), + watchersPagination: any(named: 'watchersPagination'), + preferOffline: any(named: 'preferOffline'), + )).called(1); }, ); } diff --git a/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart b/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart index 4adc0062..bc5324b8 100644 --- a/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart +++ b/packages/stream_chat_flutter_core/test/stream_chat_core_test.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'mocks.dart'; @@ -12,29 +12,6 @@ class MockOnBackgroundEventReceived extends Mock { } void main() { - test( - 'should throw assertion error in case client is null', - () { - final streamChatCore = () => StreamChatCore( - client: null, - child: Offstage(), - ); - expect(streamChatCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case child is null', - () { - final mockClient = MockClient(); - final streamChatCore = () => StreamChatCore( - client: mockClient, - child: null, - ); - expect(streamChatCore, throwsA(isA())); - }, - ); - testWidgets( 'should render StreamChatCore if both client and child is provided', (tester) async { @@ -92,7 +69,7 @@ void main() { expect(find.byKey(streamChatCoreKey), findsOneWidget); expect(find.byKey(childKey), findsOneWidget); - when(mockClient.disconnect()).thenAnswer((_) async { + when(() => mockClient.disconnect()).thenAnswer((_) async { return; }); @@ -102,7 +79,7 @@ void main() { streamChatCoreState.didChangeAppLifecycleState(AppLifecycleState.paused); - verify(mockClient.disconnect()).called(1); + verify(() => mockClient.disconnect()).called(1); }, ); @@ -131,8 +108,8 @@ void main() { expect(find.byKey(childKey), findsOneWidget); final event = Event(); - when(mockClient.on()).thenAnswer((_) => Stream.value(event)); - when(mockClient.disconnect()).thenAnswer((_) async { + when(() => mockClient.on()).thenAnswer((_) => Stream.value(event)); + when(() => mockClient.disconnect()).thenAnswer((_) async { return; }); @@ -143,14 +120,14 @@ void main() { streamChatCoreState .didChangeAppLifecycleState(AppLifecycleState.paused); - await untilCalled(mockOnBackgroundEventReceived.call(event)); + await untilCalled(() => mockOnBackgroundEventReceived.call(event)); - verify(mockOnBackgroundEventReceived.call(event)).called(1); + verify(() => mockOnBackgroundEventReceived.call(event)).called(1); await Future.delayed(backgroundKeepAlive); - verify(mockClient.disconnect()).called(1); - verifyNever(mockOnBackgroundEventReceived.call(event)); + verify(() => mockClient.disconnect()).called(1); + verifyNever(() => mockOnBackgroundEventReceived.call(event)); }); }, ); @@ -180,7 +157,7 @@ void main() { expect(find.byKey(childKey), findsOneWidget); final event = Event(); - when(mockClient.on()).thenAnswer((_) => Stream.value(event)); + when(() => mockClient.on()).thenAnswer((_) => Stream.value(event)); final streamChatCoreState = tester.state( find.byKey(streamChatCoreKey), @@ -189,14 +166,14 @@ void main() { streamChatCoreState .didChangeAppLifecycleState(AppLifecycleState.paused); - await untilCalled(mockOnBackgroundEventReceived.call(event)); + await untilCalled(() => mockOnBackgroundEventReceived.call(event)); - verify(mockOnBackgroundEventReceived.call(event)).called(1); + verify(() => mockOnBackgroundEventReceived.call(event)).called(1); streamChatCoreState .didChangeAppLifecycleState(AppLifecycleState.resumed); - verifyNever(mockOnBackgroundEventReceived.call(event)); + verifyNever(() => mockOnBackgroundEventReceived.call(event)); }); }, ); @@ -222,9 +199,10 @@ void main() { expect(find.byKey(childKey), findsOneWidget); final event = Event(); - when(mockClient.on()).thenAnswer((_) => Stream.value(event)); - when(mockClient.connect()).thenAnswer((_) async => event); - when(mockClient.wsConnectionStatus) + when(() => mockClient.on()).thenAnswer((_) => Stream.value(event)); + when(() => mockClient.connect()).thenAnswer((_) async => event); + when(mockClient.disconnect).thenAnswer((_) async => null); + when(() => mockClient.wsConnectionStatus) .thenReturn(ConnectionStatus.disconnected); final streamChatCoreState = tester.state( @@ -239,7 +217,7 @@ void main() { streamChatCoreState .didChangeAppLifecycleState(AppLifecycleState.resumed); - verify(mockClient.connect()).called(1); + verify(() => mockClient.connect()).called(1); }); }, ); @@ -265,7 +243,7 @@ void main() { expect(find.byKey(streamChatCoreKey), findsOneWidget); expect(find.byKey(childKey), findsOneWidget); - when(mockClient.state.userStream) + when(() => mockClient.state.userStream) .thenAnswer((_) => userController.stream); final streamChatCoreState = tester.state( diff --git a/packages/stream_chat_flutter_core/test/user_list_core_test.dart b/packages/stream_chat_flutter_core/test/user_list_core_test.dart index d1c7e83a..a0f8bd51 100644 --- a/packages/stream_chat_flutter_core/test/user_list_core_test.dart +++ b/packages/stream_chat_flutter_core/test/user_list_core_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:mockito/mockito.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat_flutter_core/src/user_list_core.dart'; import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; @@ -33,58 +33,6 @@ void main() { ); } - test( - 'should throw assertion error in case listBuilder is null', - () { - final userListCore = () => UserListCore( - listBuilder: null, - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (Object error) => Offstage(), - ); - expect(userListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case loadingBuilder is null', - () { - final userListCore = () => UserListCore( - listBuilder: (_, __) => Offstage(), - loadingBuilder: null, - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: (Object error) => Offstage(), - ); - expect(userListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case emptyBuilder is null', - () { - final userListCore = () => UserListCore( - listBuilder: (_, __) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: null, - errorBuilder: (Object error) => Offstage(), - ); - expect(userListCore, throwsA(isA())); - }, - ); - - test( - 'should throw assertion error in case errorBuilder is null', - () { - final userListCore = () => UserListCore( - listBuilder: (_, __) => Offstage(), - loadingBuilder: (BuildContext context) => Offstage(), - emptyBuilder: (BuildContext context) => Offstage(), - errorBuilder: null, - ); - expect(userListCore, throwsA(isA())); - }, - ); - testWidgets( 'should throw if UserListCore is used where UsersBloc is not present ' 'in the widget tree', @@ -183,12 +131,12 @@ void main() { final mockClient = MockClient(); const error = 'Error! Error! Error!'; - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenThrow(error); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenThrow(error); await tester.pumpWidget( StreamChatCore( @@ -203,12 +151,12 @@ void main() { expect(find.byKey(errorWidgetKey), findsOneWidget); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); }, ); @@ -228,12 +176,12 @@ void main() { final mockClient = MockClient(); const users = []; - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); await tester.pumpWidget( StreamChatCore( @@ -248,12 +196,12 @@ void main() { expect(find.byKey(emptyWidgetKey), findsOneWidget); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); }, ); @@ -273,12 +221,12 @@ void main() { final mockClient = MockClient(); final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); await tester.pumpWidget( StreamChatCore( @@ -293,12 +241,12 @@ void main() { expect(find.byKey(listWidgetKey), findsOneWidget); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); }, ); @@ -314,7 +262,7 @@ void main() { child: ListView( children: items.map((e) { return Container( - key: Key(e.key), + key: Key(e.key ?? ''), child: e.when( headerItem: (heading) => Text(heading), userItem: (user) => Text(user.id), @@ -332,12 +280,12 @@ void main() { final mockClient = MockClient(); final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); await tester.pumpWidget( Directionality( @@ -359,12 +307,12 @@ void main() { expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); } - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); }, ); @@ -382,7 +330,7 @@ void main() { child: ListView( children: items.map((e) { return Container( - key: Key(e.key), + key: Key(e.key ?? ''), child: e.when( headerItem: (heading) => Text(heading), userItem: (user) => Text(user.id), @@ -401,12 +349,12 @@ void main() { final mockClient = MockClient(); final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); await tester.pumpWidget( Directionality( @@ -428,12 +376,12 @@ void main() { expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); } - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); final userListCoreState = tester.state( find.byKey(userListCoreKey), @@ -442,12 +390,14 @@ void main() { final offset = users.length; final paginatedUsers = _generateUsers(offset: offset); final updatedPagination = pagination.copyWith(offset: offset); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: updatedPagination, - )).thenAnswer((_) async => QueryUsersResponse()..users = paginatedUsers); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: updatedPagination, + )) + .thenAnswer( + (_) async => QueryUsersResponse()..users = paginatedUsers); await userListCoreState.paginateData(); @@ -458,12 +408,12 @@ void main() { expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); } - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: updatedPagination, - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: updatedPagination, + )).called(1); }, ); @@ -473,7 +423,7 @@ void main() { (tester) async { const pagination = PaginationParams(); - StateSetter _stateSetter; + StateSetter? _stateSetter; int limit = pagination.limit; const userListCoreKey = Key('userListCore'); @@ -485,7 +435,7 @@ void main() { child: ListView( children: items.map((e) { return Container( - key: Key(e.key), + key: Key(e.key ?? ''), child: e.when( headerItem: (heading) => Text(heading), userItem: (user) => Text(user.id), @@ -504,12 +454,12 @@ void main() { final mockClient = MockClient(); final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); await tester.pumpWidget( Directionality( @@ -535,24 +485,25 @@ void main() { expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); } - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); // Rebuilding UserListCore with new pagination limit - _stateSetter(() => limit = 6); + _stateSetter?.call(() => limit = 6); final updatedUsers = _generateUsers(count: limit); final updatedPagination = pagination.copyWith(limit: limit); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: updatedPagination, - )).thenAnswer((_) async => QueryUsersResponse()..users = updatedUsers); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: updatedPagination, + )) + .thenAnswer((_) async => QueryUsersResponse()..users = updatedUsers); await tester.pumpAndSettle(); @@ -561,12 +512,12 @@ void main() { expect(find.byKey(Key('USER-${user.id}')), findsOneWidget); } - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: updatedPagination, - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: updatedPagination, + )).called(1); }, ); } diff --git a/packages/stream_chat_flutter_core/test/users_bloc_test.dart b/packages/stream_chat_flutter_core/test/users_bloc_test.dart index e9ccd2e7..8c348211 100644 --- a/packages/stream_chat_flutter_core/test/users_bloc_test.dart +++ b/packages/stream_chat_flutter_core/test/users_bloc_test.dart @@ -1,9 +1,9 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter_core/src/stream_chat_core.dart'; import 'package:stream_chat_flutter_core/src/users_bloc.dart'; -import 'package:mockito/mockito.dart'; import 'matchers/users_matcher.dart'; import 'mocks.dart'; @@ -31,18 +31,6 @@ void main() { ); } - test( - 'should throw assertion error if child is null', - () async { - const usersBlocKey = Key('usersBloc'); - final usersBloc = () => UsersBloc( - key: usersBlocKey, - child: null, - ); - expect(usersBloc, throwsA(isA())); - }, - ); - testWidgets( 'usersBlocState.queryUsers() should throw if used where ' 'StreamChat is not present in the widget tree', @@ -96,12 +84,12 @@ void main() { final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); usersBlocState.queryUsers(); @@ -110,12 +98,12 @@ void main() { emits(isSameUserListAs(users)), ); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); }, ); @@ -145,12 +133,12 @@ void main() { final error = 'Error! Error! Error!'; - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenThrow(error); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenThrow(error); usersBlocState.queryUsers(); @@ -159,12 +147,12 @@ void main() { emitsError(error), ); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); }, ); @@ -195,12 +183,12 @@ void main() { final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); usersBlocState.queryUsers(); @@ -209,23 +197,25 @@ void main() { emits(isSameUserListAs(users)), ); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); final offset = users.length; final paginatedUsers = _generateUsers(offset: offset); final pagination = PaginationParams(offset: offset); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: pagination, - )).thenAnswer((_) async => QueryUsersResponse()..users = paginatedUsers); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: pagination, + )) + .thenAnswer( + (_) async => QueryUsersResponse()..users = paginatedUsers); usersBlocState.queryUsers(pagination: pagination); @@ -240,12 +230,12 @@ void main() { ), ]); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: pagination, - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: pagination, + )).called(1); }, ); @@ -276,12 +266,12 @@ void main() { final users = _generateUsers(); - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).thenAnswer((_) async => QueryUsersResponse()..users = users); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).thenAnswer((_) async => QueryUsersResponse()..users = users); usersBlocState.queryUsers(); @@ -290,24 +280,24 @@ void main() { emits(isSameUserListAs(users)), ); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: anyNamed('pagination'), - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: any(named: 'pagination'), + )).called(1); final offset = users.length; final pagination = PaginationParams(offset: offset); final error = 'Error! Error! Error!'; - when(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: pagination, - )).thenThrow(error); + when(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: pagination, + )).thenThrow(error); usersBlocState.queryUsers(pagination: pagination); @@ -316,12 +306,12 @@ void main() { emitsError(error), ); - verify(mockClient.queryUsers( - filter: anyNamed('filter'), - sort: anyNamed('sort'), - options: anyNamed('options'), - pagination: pagination, - )).called(1); + verify(() => mockClient.queryUsers( + filter: any(named: 'filter'), + sort: any(named: 'sort'), + options: any(named: 'options'), + pagination: pagination, + )).called(1); }, ); } From bc9e0d0285133e8fa5c505704e9ad028d2daf040 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 20 Apr 2021 12:44:22 +0200 Subject: [PATCH 08/15] migrate code --- packages/stream_chat/lib/src/api/channel.dart | 88 +++++++++---------- .../lib/src/models/channel_model.dart | 2 +- packages/stream_chat/lib/src/models/user.dart | 16 +--- packages/stream_chat/lib/stream_chat.dart | 1 + 4 files changed, 47 insertions(+), 60 deletions(-) diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index 63fc5595..cc205772 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -81,7 +81,7 @@ class Channel { /// Channel configuration as a stream Stream? get configStream { _checkInitialized(); - return state?.channelStateStream.map((cs) => cs!.channel?.config); + return state?.channelStateStream.map((cs) => cs.channel?.config); } /// Channel user creator @@ -93,7 +93,7 @@ class Channel { /// Channel user creator as a stream Stream? get createdByStream { _checkInitialized(); - return state?.channelStateStream.map((cs) => cs!.channel?.createdBy); + return state?.channelStateStream.map((cs) => cs.channel?.createdBy); } /// Channel frozen status @@ -105,7 +105,7 @@ class Channel { /// Channel frozen status as a stream Stream? get frozenStream { _checkInitialized(); - return state?.channelStateStream.map((cs) => cs!.channel?.frozen); + return state?.channelStateStream.map((cs) => cs.channel?.frozen); } /// Channel creation date @@ -117,7 +117,7 @@ class Channel { /// Channel creation date as a stream Stream? get createdAtStream { _checkInitialized(); - return state?.channelStateStream.map((cs) => cs!.channel?.createdAt); + return state?.channelStateStream.map((cs) => cs.channel?.createdAt); } /// Channel last message date @@ -131,7 +131,7 @@ class Channel { Stream? get lastMessageAtStream { _checkInitialized(); - return state?.channelStateStream.map((cs) => cs!.channel?.lastMessageAt); + return state?.channelStateStream.map((cs) => cs.channel?.lastMessageAt); } /// Channel updated date @@ -145,7 +145,7 @@ class Channel { Stream? get updatedAtStream { _checkInitialized(); - return state?.channelStateStream.map((cs) => cs!.channel?.updatedAt); + return state?.channelStateStream.map((cs) => cs.channel?.updatedAt); } /// Channel deletion date @@ -159,7 +159,7 @@ class Channel { Stream? get deletedAtStream { _checkInitialized(); - return state?.channelStateStream.map((cs) => cs!.channel?.deletedAt); + return state?.channelStateStream.map((cs) => cs.channel?.deletedAt); } /// Channel member count @@ -173,7 +173,7 @@ class Channel { Stream? get memberCountStream { _checkInitialized(); - return state?.channelStateStream.map((cs) => cs!.channel?.memberCount); + return state?.channelStateStream.map((cs) => cs.channel?.memberCount); } /// Channel id @@ -195,7 +195,7 @@ class Channel { /// Channel extra data as a stream Stream?>? get extraDataStream { _checkInitialized(); - return state?.channelStateStream.map((cs) => cs!.channel?.extraData); + return state?.channelStateStream.map((cs) => cs.channel?.extraData); } /// The main Stream chat client @@ -842,7 +842,7 @@ class Channel { messages: state?.messages?..remove(oldMessage), )); } else { - oldMessage = state!.threads!.values + oldMessage = state!.threads.values .expand((messages) => messages) .firstWhereOrNull((m) => m.id == messageId); if (oldMessage?.parentId != null) { @@ -853,8 +853,8 @@ class Channel { state!.addMessage(parentMessage.copyWith( replyCount: parentMessage.replyCount! - 1)); } - state!.updateThreadInfo(oldMessage!.parentId, - state!.threads![oldMessage.parentId!]!..remove(oldMessage)); + state!.updateThreadInfo(oldMessage!.parentId!, + state!.threads[oldMessage.parentId!]!..remove(oldMessage)); } } @@ -1333,7 +1333,7 @@ class ChannelClientState { final _subscriptions = []; void _computeInitialUnread() { - final userRead = channelState?.read.firstWhereOrNull( + final userRead = channelState.read.firstWhereOrNull( (r) => r.user.id == _channel._client.state.user?.id, ); if (userRead != null) { @@ -1372,9 +1372,9 @@ class ChannelClientState { void _listenMemberAdded() { _subscriptions.add(_channel.on(EventType.memberAdded).listen((Event e) { final member = e.member; - updateChannelState(channelState!.copyWith( + updateChannelState(channelState.copyWith( members: [ - ...channelState!.members, + ...channelState.members, member!, ], )); @@ -1384,9 +1384,9 @@ class ChannelClientState { void _listenMemberRemoved() { _subscriptions.add(_channel.on(EventType.memberRemoved).listen((Event e) { final user = e.user; - updateChannelState(channelState!.copyWith( + updateChannelState(channelState.copyWith( members: List.from( - channelState!.members..removeWhere((m) => m.userId == user!.id)), + channelState.members..removeWhere((m) => m.userId == user!.id)), )); })); } @@ -1394,7 +1394,7 @@ class ChannelClientState { void _listenChannelUpdated() { _subscriptions.add(_channel.on(EventType.channelUpdated).listen((Event e) { final channel = e.channel!; - updateChannelState(channelState!.copyWith( + updateChannelState(channelState.copyWith( channel: channel, members: channel.members, )); @@ -1416,14 +1416,14 @@ class ChannelClientState { /// This flag should be managed by UI sdks. /// When false, any new message (received by WebSocket event /// - [EventType.messageNew]) will not be pushed on to message list. - bool get isUpToDate => _isUpToDateController.value!; + bool get isUpToDate => _isUpToDateController.value ?? true; set isUpToDate(bool isUpToDate) => _isUpToDateController.add(isUpToDate); /// [isUpToDate] flag count as a stream - Stream get isUpToDateStream => _isUpToDateController.stream; + Stream get isUpToDateStream => _isUpToDateController.stream; - final BehaviorSubject _isUpToDateController = + final BehaviorSubject _isUpToDateController = BehaviorSubject.seeded(true); /// The retry queue associated to this channel @@ -1432,7 +1432,7 @@ class ChannelClientState { /// Retry failed message Future retryFailedMessages() async { final failedMessages = - [...messages, ...threads!.values.expand((v) => v)] + [...messages, ...threads.values.expand((v) => v)] .where( (message) => message.status != MessageSendingStatus.sent && @@ -1549,7 +1549,7 @@ class ChannelClientState { } if (message.parentId != null) { - updateThreadInfo(message.parentId, [message]); + updateThreadInfo(message.parentId!, [message]); } } @@ -1592,14 +1592,14 @@ class ChannelClientState { /// Channel message list as a stream Stream?> get messagesStream => - channelStateStream.map((cs) => cs!.messages); + channelStateStream.map((cs) => cs.messages); /// Channel pinned message list List? get pinnedMessages => _channelState.pinnedMessages.toList(); /// Channel pinned message list as a stream Stream?> get pinnedMessagesStream => - channelStateStream.map((cs) => cs!.pinnedMessages.toList()); + channelStateStream.map((cs) => cs.pinnedMessages.toList()); /// Get channel last message Message? get lastMessage => _channelState.messages.isNotEmpty == true @@ -1618,7 +1618,7 @@ class ChannelClientState { /// Channel members list as a stream Stream> get membersStream => CombineLatestStream.combine2< List?, Map, List>( - channelStateStream.map((cs) => cs!.members), + channelStateStream.map((cs) => cs.members), _channel.client.state.usersStream, (members, users) => members!.map((e) => e!.copyWith(user: users[e.user!.id])).toList(), @@ -1629,7 +1629,7 @@ class ChannelClientState { /// Channel watcher count as a stream Stream get watcherCountStream => - channelStateStream.map((cs) => cs!.watcherCount); + channelStateStream.map((cs) => cs.watcherCount); /// Channel watchers list List get watchers => _channelState.watchers @@ -1639,7 +1639,7 @@ class ChannelClientState { /// Channel watchers list as a stream Stream> get watchersStream => CombineLatestStream.combine2< List?, Map, List>( - channelStateStream.map((cs) => cs!.watchers), + channelStateStream.map((cs) => cs.watchers), _channel.client.state.usersStream, (watchers, users) => watchers!.map((e) => users[e.id] ?? e).toList(), ); @@ -1648,8 +1648,7 @@ class ChannelClientState { List? get read => _channelState.read; /// Channel read list as a stream - Stream?> get readStream => - channelStateStream.map((cs) => cs!.read); + Stream?> get readStream => channelStateStream.map((cs) => cs.read); final BehaviorSubject _unreadCountController = BehaviorSubject.seeded(0); @@ -1672,17 +1671,17 @@ class ChannelClientState { } /// Update threads with updated information about messages - void updateThreadInfo(String? parentId, List? messages) { - final newThreads = Map?>.from(threads!); + void updateThreadInfo(String parentId, List messages) { + final newThreads = Map>.from(threads); if (newThreads.containsKey(parentId)) { newThreads[parentId] = [ ...newThreads[parentId] - ?.where((newMessage) => - !messages!.any((m) => m.id == newMessage.id)) + ?.where( + (newMessage) => !messages.any((m) => m.id == newMessage.id)) .toList() ?? [], - ...messages!, + ...messages, ]; newThreads[parentId]! @@ -1768,11 +1767,10 @@ class ChannelClientState { ChannelState get _channelState => _channelStateController.value!; /// The channel state related to this client as a stream - Stream get channelStateStream => - _channelStateController.stream; + Stream get channelStateStream => _channelStateController.stream; /// The channel state related to this client - ChannelState? get channelState => _channelStateController.value; + ChannelState get channelState => _channelStateController.value!; late BehaviorSubject _channelStateController; final Debounce _debouncedUpdatePersistenceChannelState; @@ -1783,19 +1781,19 @@ class ChannelClientState { } /// The channel threads related to this channel - Map>? get threads => _threadsController.value - ?.map((key, value) => MapEntry(key ?? '', value ?? [])); + Map> get threads => + _threadsController.value!.map((key, value) => MapEntry(key, value)); /// The channel threads related to this channel as a stream - Stream?>> get threadsStream => + Stream>> get threadsStream => _threadsController.stream; - final BehaviorSubject?>> _threadsController = + final BehaviorSubject>> _threadsController = BehaviorSubject.seeded({}); - set _threads(Map?> v) { + set _threads(Map> v) { _channel._client.chatPersistenceClient?.updateMessages( _channel.cid!, - v.values.expand((v) => v!).toList(), + v.values.expand((v) => v).toList(), ); _threadsController.add(v); } @@ -1888,7 +1886,7 @@ class ChannelClientState { void _startCleaningPinnedMessages() { _pinnedMessagesTimer = Timer.periodic(const Duration(seconds: 30), (_) { final now = DateTime.now(); - var expiredMessages = channelState!.pinnedMessages + var expiredMessages = channelState.pinnedMessages .where((m) => m.pinExpires?.isBefore(now) == true) .toList(); if (expiredMessages.isNotEmpty) { diff --git a/packages/stream_chat/lib/src/models/channel_model.dart b/packages/stream_chat/lib/src/models/channel_model.dart index d68edbde..2bc17761 100644 --- a/packages/stream_chat/lib/src/models/channel_model.dart +++ b/packages/stream_chat/lib/src/models/channel_model.dart @@ -27,7 +27,7 @@ class ChannelModel { createdAt = createdAt ?? DateTime.now(), updatedAt = updatedAt ?? DateTime.now(), assert( - cid != null || (id != null && type != null), + (cid != null && cid.contains(':')) || (id != null && type != null), 'provide either a cid or an id and type', ), id = id ?? cid!.split(':')[1], diff --git a/packages/stream_chat/lib/src/models/user.dart b/packages/stream_chat/lib/src/models/user.dart index 67fc99f0..3f4c37ef 100644 --- a/packages/stream_chat/lib/src/models/user.dart +++ b/packages/stream_chat/lib/src/models/user.dart @@ -8,7 +8,7 @@ part 'user.g.dart'; class User { /// Constructor used for json serialization User({ - this.id = '', + required this.id, this.role = '', DateTime? createdAt, DateTime? updatedAt, @@ -24,18 +24,6 @@ class User { factory User.fromJson(Map json) => _$UserFromJson( Serialization.moveToExtraDataFromRoot(json, topLevelFields)); - /// Use this named constructor to create a new user instance - User.init( - this.id, { - this.online = false, - this.extraData = const {}, - required this.createdAt, - required this.updatedAt, - this.teams = const [], - required this.role, - }) : lastActive = null, - banned = false; - /// Known top level fields. /// Useful for [Serialization] methods. static const topLevelFields = [ @@ -94,7 +82,7 @@ class User { int get hashCode => id.hashCode; /// Shortcut for user name - String? get name => + String get name => (extraData.containsKey('name') == true && extraData['name'] != '') ? extraData['name'] : id; diff --git a/packages/stream_chat/lib/stream_chat.dart b/packages/stream_chat/lib/stream_chat.dart index f491c39a..ef146180 100644 --- a/packages/stream_chat/lib/stream_chat.dart +++ b/packages/stream_chat/lib/stream_chat.dart @@ -3,6 +3,7 @@ library stream_chat; export 'package:async/async.dart'; export 'package:dio/src/dio_error.dart'; export 'package:dio/src/multipart_file.dart'; +export 'package:dio/src/options.dart'; export 'package:dio/src/options.dart' show ProgressCallback; export 'package:logging/logging.dart' show Logger, Level; From 4c4473d4b5d29fd1a518a1571e792533e8420a7a Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 20 Apr 2021 13:14:07 +0200 Subject: [PATCH 09/15] migrate sample app --- packages/stream_chat/lib/src/api/channel.dart | 54 ++++++++---------- packages/stream_chat/lib/src/client.dart | 11 ++-- .../test/src/api/channel_test.dart | 18 +++--- .../analysis_options.yaml | 1 + .../example/lib/main.dart | 57 ++++++++++--------- .../example/pubspec.yaml | 2 +- .../lib/src/channel_list_core.dart | 1 + .../test/channels_bloc_test.dart | 4 +- 8 files changed, 77 insertions(+), 71 deletions(-) diff --git a/packages/stream_chat/lib/src/api/channel.dart b/packages/stream_chat/lib/src/api/channel.dart index cc205772..7389448f 100644 --- a/packages/stream_chat/lib/src/api/channel.dart +++ b/packages/stream_chat/lib/src/api/channel.dart @@ -27,7 +27,7 @@ class Channel { } /// Create a channel client instance from a [ChannelState] object - Channel.fromState(this._client, ChannelState channelState) { + Channel.fromState(this._client, ChannelState channelState) : _extraData = {} { _cid = channelState.channel!.cid; _id = channelState.channel!.id; type = channelState.channel!.type; @@ -45,9 +45,9 @@ class Channel { String? _id; String? _cid; - Map? _extraData; + Map _extraData; - set extraData(Map? extraData) { + set extraData(Map extraData) { if (_initializedCompleter.isCompleted) { throw Exception( 'Once the channel is initialized you should use channel.update ' @@ -189,7 +189,7 @@ class Channel { } /// Channel extra data - Map? get extraData => + Map get extraData => state?._channelState.channel?.extraData ?? _extraData; /// Channel extra data as a stream @@ -1028,7 +1028,7 @@ class Channel { }) ..addAll(options); - if (_extraData != null) { + if (_extraData.isNotEmpty) { payload['data'] = _extraData; } @@ -1684,8 +1684,7 @@ class ChannelClientState { ...messages, ]; - newThreads[parentId]! - .sort(_sortByCreatedAt as int Function(Message, Message)?); + newThreads[parentId]!.sort(_sortByCreatedAt); } else { newThreads[parentId] = messages; } @@ -1712,7 +1711,7 @@ class ChannelClientState { .any((newMessage) => newMessage.id == m.id) != true) .toList(), - ]..sort(_sortByCreatedAt as int Function(Message, Message)?); + ]..sort(_sortByCreatedAt); final newWatchers = [ ...updatedState.watchers, @@ -1751,17 +1750,8 @@ class ChannelClientState { ); } - int? _sortByCreatedAt(a, b) { - if (a.createdAt == null) { - return 1; - } - - if (b.createdAt == null) { - return -1; - } - - return a.createdAt.compareTo(b.createdAt); - } + int _sortByCreatedAt(Message a, Message b) => + a.createdAt.compareTo(b.createdAt); /// The channel state related to this client ChannelState get _channelState => _channelStateController.value!; @@ -1799,15 +1789,15 @@ class ChannelClientState { } /// Channel related typing users last value - List? get typingEvents => _typingEventsController.value as List?; + List get typingEvents => _typingEventsController.value!; /// Channel related typing users stream - Stream> get typingEventsStream => _typingEventsController.stream; - final BehaviorSubject> _typingEventsController = + Stream> get typingEventsStream => _typingEventsController.stream; + final BehaviorSubject> _typingEventsController = BehaviorSubject.seeded([]); final Channel _channel; - final Map _typings = {}; + final Map _typings = {}; void _listenTypingEvents() { if (_channelState.channel?.config.typingEvents == false) { @@ -1818,9 +1808,12 @@ class ChannelClientState { ..add( _channel.on(EventType.typingStart).listen( (event) { - if (event.user!.id != _channel.client.state.user!.id) { - _typings[event.user] = DateTime.now(); - _typingEventsController.add(_typings.keys.toList()); + if (event.user != null) { + final user = event.user!; + if (user.id != _channel.client.state.user?.id) { + _typings[user] = DateTime.now(); + _typingEventsController.add(_typings.keys.toList()); + } } }, ), @@ -1828,9 +1821,12 @@ class ChannelClientState { ..add( _channel.on(EventType.typingStop).listen( (event) { - if (event.user!.id != _channel.client.state.user!.id) { - _typings.remove(event.user); - _typingEventsController.add(_typings.keys.toList()); + if (event.user != null) { + final user = event.user!; + if (user.id != _channel.client.state.user?.id) { + _typings.remove(event.user); + _typingEventsController.add(_typings.keys.toList()); + } } }, ), diff --git a/packages/stream_chat/lib/src/client.dart b/packages/stream_chat/lib/src/client.dart index 403e5a2b..4689e9ec 100644 --- a/packages/stream_chat/lib/src/client.dart +++ b/packages/stream_chat/lib/src/client.dart @@ -780,10 +780,11 @@ class StreamChatClient { PaginationParams paginationParams = const PaginationParams(), }) async { final offlineChannels = (await _chatPersistenceClient?.getChannelStates( - filter: filter, - sort: sort, - paginationParams: paginationParams, - ))!; + filter: filter, + sort: sort, + paginationParams: paginationParams, + )) ?? + []; final updatedData = _mapChannelStateToChannel(offlineChannels); state.channels = updatedData.key; return updatedData.value; @@ -1193,7 +1194,7 @@ class StreamChatClient { Channel channel( String type, { String? id, - Map? extraData, + Map extraData = const {}, }) { if (id != null && state.channels?.containsKey('$type:$id') == true) { if (state.channels!['$type:$id'] != null) { diff --git a/packages/stream_chat/test/src/api/channel_test.dart b/packages/stream_chat/test/src/api/channel_test.dart index 041a3e49..8f18d5e2 100644 --- a/packages/stream_chat/test/src/api/channel_test.dart +++ b/packages/stream_chat/test/src/api/channel_test.dart @@ -516,7 +516,7 @@ void main() { ); await channelClient.watch(); - final event = Event(type: EventType.any); + final event = const Event(type: EventType.any); when( () => mockDio.post( @@ -564,7 +564,7 @@ void main() { ); await channelClient.watch(); - final event = Event(type: EventType.typingStart); + final event = const Event(type: EventType.typingStart); when( () => mockDio.post( @@ -610,7 +610,7 @@ void main() { ); await channelClient.watch(); - final event = Event(type: EventType.typingStop); + final event = const Event(type: EventType.typingStop); when( () => mockDio.post( @@ -901,8 +901,10 @@ void main() { 'presence': true, }; - when(() => mockDio.post('/channels/messaging/query', - data: options)).thenAnswer( + when(() => mockDio.post( + '/channels/messaging/query', + data: options, + )).thenAnswer( (_) async => Response( data: r''' { @@ -1198,8 +1200,10 @@ void main() { final response = await channelClient.query(options: options); - verify(() => mockDio.post('/channels/messaging/query', - data: options)).called(1); + verify(() => mockDio.post( + '/channels/messaging/query', + data: options, + )).called(1); expect(channelClient.id, response.channel?.id); expect(channelClient.cid, response.channel?.cid); }); diff --git a/packages/stream_chat_flutter_core/analysis_options.yaml b/packages/stream_chat_flutter_core/analysis_options.yaml index 039d0230..545d5492 100644 --- a/packages/stream_chat_flutter_core/analysis_options.yaml +++ b/packages/stream_chat_flutter_core/analysis_options.yaml @@ -3,6 +3,7 @@ analyzer: - lib/**/*.g.dart - lib/**/*.freezed.dart - example/* + - test/* linter: rules: - always_use_package_imports diff --git a/packages/stream_chat_flutter_core/example/lib/main.dart b/packages/stream_chat_flutter_core/example/lib/main.dart index aa705db1..a9157e6e 100644 --- a/packages/stream_chat_flutter_core/example/lib/main.dart +++ b/packages/stream_chat_flutter_core/example/lib/main.dart @@ -38,8 +38,8 @@ class StreamExample extends StatelessWidget { /// If you'd prefer using pre-made UI widgets for your app, please see our other /// package, `stream_chat_flutter`. const StreamExample({ - Key key, - @required this.client, + Key? key, + required this.client, }) : super(key: key); /// Instance of Stream Client. @@ -55,7 +55,7 @@ class StreamExample extends StatelessWidget { home: HomeScreen(), builder: (context, child) => StreamChatCore( client: client, - child: child, + child: child!, ), ); } @@ -82,7 +82,7 @@ class HomeScreen extends StatelessWidget { 'type': 'messaging', 'members': { r'$in': [ - StreamChatCore.of(context).user.id, + StreamChatCore.of(context).user!.id, ] } }, @@ -100,10 +100,13 @@ class HomeScreen extends StatelessWidget { ), ); }, - errorBuilder: (BuildContext context, dynamic error) { + errorBuilder: ( + BuildContext context, + dynamic error, + ) { return Center( child: Text( - 'Oh no, something went wrong. Please check your config.'), + 'Oh no, something went wrong. Please check your config. ${error}'), ); }, listBuilder: ( @@ -112,20 +115,20 @@ class HomeScreen extends StatelessWidget { ) => LazyLoadScrollView( onEndOfPage: () async { - channelListController.paginateData(); + channelListController.paginateData!(); }, child: ListView.builder( itemCount: channels.length, itemBuilder: (BuildContext context, int index) { final _item = channels[index]; return ListTile( - title: Text(_item.name), - subtitle: StreamBuilder( - stream: _item.state.lastMessageStream, - initialData: _item.state.lastMessage, + title: Text(_item.name!), + subtitle: StreamBuilder( + stream: _item.state!.lastMessageStream, + initialData: _item.state!.lastMessage, builder: (context, snapshot) { if (snapshot.hasData) { - return Text(snapshot.data.text); + return Text(snapshot.data!.text!); } return SizedBox(); @@ -169,8 +172,8 @@ class MessageScreen extends StatefulWidget { } class _MessageScreenState extends State { - TextEditingController _controller; - ScrollController _scrollController; + late final TextEditingController _controller; + late final ScrollController _scrollController; final messageListController = MessageListController(); @override @@ -203,11 +206,11 @@ class _MessageScreenState extends State { return Scaffold( appBar: AppBar( title: StreamBuilder>( - initialData: channel.state.typingEvents, - stream: channel.state.typingEventsStream, + initialData: channel.state?.typingEvents, + stream: channel.state?.typingEventsStream, builder: (context, snapshot) { - if (snapshot.hasData && snapshot.data.isNotEmpty) { - return Text('${snapshot.data.first.name} is typing...'); + if (snapshot.hasData && snapshot.data!.isNotEmpty) { + return Text('${snapshot.data!.first.name} is typing...'); } return SizedBox(); }, @@ -219,7 +222,7 @@ class _MessageScreenState extends State { Expanded( child: LazyLoadScrollView( onEndOfPage: () async { - messageListController.paginateData(); + messageListController.paginateData!(); }, child: MessageListCore( emptyBuilder: (BuildContext context) { @@ -247,12 +250,12 @@ class _MessageScreenState extends State { itemBuilder: (BuildContext context, int index) { final item = messages[index]; final client = StreamChatCore.of(context).client; - if (item.user.id == client.uid) { + if (item.user!.id == client.uid) { return Align( alignment: Alignment.centerRight, child: Padding( padding: const EdgeInsets.all(8.0), - child: Text(item.text), + child: Text(item.text!), ), ); } else { @@ -260,7 +263,7 @@ class _MessageScreenState extends State { alignment: Alignment.centerLeft, child: Padding( padding: const EdgeInsets.all(8.0), - child: Text(item.text), + child: Text(item.text!), ), ); } @@ -268,7 +271,7 @@ class _MessageScreenState extends State { ); }, errorWidgetBuilder: (BuildContext context, error) { - print(error?.toString()); + print(error.toString()); return Center( child: SizedBox( height: 100.0, @@ -282,7 +285,7 @@ class _MessageScreenState extends State { ), ), Padding( - padding: const EdgeInsets.all(8.0), + padding: const EdgeInsets.all(8), child: Row( children: [ Expanded( @@ -308,7 +311,7 @@ class _MessageScreenState extends State { } }, child: const Padding( - padding: EdgeInsets.all(8.0), + padding: EdgeInsets.all(8), child: Center( child: Icon( Icons.send, @@ -332,12 +335,12 @@ class _MessageScreenState extends State { /// below, we add two simple extensions to the [StreamChatClient] and [Channel]. extension on StreamChatClient { /// Fetches the current user id. - String get uid => state.user.id; + String get uid => state.user!.id; } extension on Channel { /// Fetches the name of the channel by accessing [extraData] or [cid]. - String get name { + String? get name { final _channelName = extraData['name']; if (_channelName != null) { return _channelName; diff --git a/packages/stream_chat_flutter_core/example/pubspec.yaml b/packages/stream_chat_flutter_core/example/pubspec.yaml index 91cf4a01..e03eba6c 100644 --- a/packages/stream_chat_flutter_core/example/pubspec.yaml +++ b/packages/stream_chat_flutter_core/example/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.7.0 <3.0.0" + sdk: '>=2.12.0 <3.0.0' dependencies: flutter: diff --git a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart index 322a2780..992cae5d 100644 --- a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart @@ -133,6 +133,7 @@ class ChannelListCoreState extends State { stream: channelsBlocState.channelsStream, builder: (context, snapshot) { if (snapshot.hasError) { + print('snapshot: ${snapshot.stackTrace}'); return widget.errorBuilder(context, snapshot.error!); } if (!snapshot.hasData) { diff --git a/packages/stream_chat_flutter_core/test/channels_bloc_test.dart b/packages/stream_chat_flutter_core/test/channels_bloc_test.dart index de8b0af1..28136add 100644 --- a/packages/stream_chat_flutter_core/test/channels_bloc_test.dart +++ b/packages/stream_chat_flutter_core/test/channels_bloc_test.dart @@ -779,8 +779,8 @@ void main() { final mockClient = MockClient(); final channels = _generateChannels(mockClient); int channelComparator(Channel a, Channel b) { - final aData = a.extraData!['extra_data_key'] as String; - final bData = b.extraData!['extra_data_key'] as String; + final aData = a.extraData['extra_data_key'] as String; + final bData = b.extraData['extra_data_key'] as String; return bData.compareTo(aData); } From 8f7106020fb0fd89503a8f29033fa67ad0b0e133 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 20 Apr 2021 14:53:19 +0200 Subject: [PATCH 10/15] fix own user model --- packages/stream_chat/lib/src/models/own_user.dart | 4 ++-- packages/stream_chat/lib/src/models/own_user.g.dart | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/stream_chat/lib/src/models/own_user.dart b/packages/stream_chat/lib/src/models/own_user.dart index 3ce951cf..05936cdd 100644 --- a/packages/stream_chat/lib/src/models/own_user.dart +++ b/packages/stream_chat/lib/src/models/own_user.dart @@ -17,8 +17,8 @@ class OwnUser extends User { this.totalUnreadCount = 0, this.unreadChannels, this.channelMutes = const [], - String id = '', - String role = '', + required String id, + String? role, DateTime? createdAt, DateTime? updatedAt, DateTime? lastActive, diff --git a/packages/stream_chat/lib/src/models/own_user.g.dart b/packages/stream_chat/lib/src/models/own_user.g.dart index 785efb04..5683440d 100644 --- a/packages/stream_chat/lib/src/models/own_user.g.dart +++ b/packages/stream_chat/lib/src/models/own_user.g.dart @@ -23,7 +23,7 @@ OwnUser _$OwnUserFromJson(Map json) { .toList() ?? [], id: json['id'] as String, - role: json['role'] as String, + role: json['role'] as String?, createdAt: json['created_at'] == null ? null : DateTime.parse(json['created_at'] as String), From 555ae57782cacf4af485d6432860d1f222f8df57 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 20 Apr 2021 15:01:39 +0200 Subject: [PATCH 11/15] fix core example --- packages/stream_chat_flutter_core/example/lib/main.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/stream_chat_flutter_core/example/lib/main.dart b/packages/stream_chat_flutter_core/example/lib/main.dart index a9157e6e..de10574b 100644 --- a/packages/stream_chat_flutter_core/example/lib/main.dart +++ b/packages/stream_chat_flutter_core/example/lib/main.dart @@ -225,6 +225,7 @@ class _MessageScreenState extends State { messageListController.paginateData!(); }, child: MessageListCore( + messageListController: messageListController, emptyBuilder: (BuildContext context) { return Center( child: Text('Nothing here yet'), From c5647652e2e46e456907c3dd23fc11da8b686415 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 20 Apr 2021 16:23:41 +0200 Subject: [PATCH 12/15] remove log --- packages/stream_chat_flutter_core/lib/src/channel_list_core.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart index 992cae5d..322a2780 100644 --- a/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/channel_list_core.dart @@ -133,7 +133,6 @@ class ChannelListCoreState extends State { stream: channelsBlocState.channelsStream, builder: (context, snapshot) { if (snapshot.hasError) { - print('snapshot: ${snapshot.stackTrace}'); return widget.errorBuilder(context, snapshot.error!); } if (!snapshot.hasData) { From 4f37190fccdb2f28f9c1fe2a59a9582ddbda2cf8 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 20 Apr 2021 16:39:10 +0200 Subject: [PATCH 13/15] change default direction in paginate data --- packages/stream_chat_flutter_core/example/lib/main.dart | 9 +++++---- .../lib/src/message_list_core.dart | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/packages/stream_chat_flutter_core/example/lib/main.dart b/packages/stream_chat_flutter_core/example/lib/main.dart index de10574b..308e63de 100644 --- a/packages/stream_chat_flutter_core/example/lib/main.dart +++ b/packages/stream_chat_flutter_core/example/lib/main.dart @@ -4,7 +4,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; Future main() async { /// Create a new instance of [StreamChatClient] passing the apikey obtained from your /// project dashboard. - final client = StreamChatClient('b67pax5b2wdq'); + final client = StreamChatClient('kv7mcsxr24p8'); /// Set the current user. In a production scenario, this should be done using /// a backend to generate a user token using our server SDK. @@ -12,13 +12,14 @@ Future main() async { /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ await client.connectUser( User( - id: 'cool-shadow-7', + id: 'salvatore', extraData: { + 'name': 'Salvatore Giordano', 'image': - 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', + 'https://avatars.githubusercontent.com/u/20601437?s=460&u=3f66c22a7483980624804054ae7f357cf102c784&v=4', }, ), - 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoic2FsdmF0b3JlIn0.pgiJz7sIc7iP29BHKFwe3nLm5-OaR_1l2P-SlgiC9a8', ); runApp( diff --git a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart b/packages/stream_chat_flutter_core/lib/src/message_list_core.dart index f7bce6cd..e1fed859 100644 --- a/packages/stream_chat_flutter_core/lib/src/message_list_core.dart +++ b/packages/stream_chat_flutter_core/lib/src/message_list_core.dart @@ -162,9 +162,9 @@ class MessageListCoreState extends State { /// Fetches more messages with updated pagination and updates the widget. /// - /// Optionally pass the fetch direction, defaults to [QueryDirection.bottom] + /// Optionally pass the fetch direction, defaults to [QueryDirection.top] Future paginateData({ - QueryDirection direction = QueryDirection.bottom, + QueryDirection direction = QueryDirection.top, }) { if (!_isThreadConversation) { return _streamChannel.queryMessages(direction: direction); From 408bbe14f59e89f3d4c4dc7bf8a0489506453905 Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Tue, 20 Apr 2021 16:40:46 +0200 Subject: [PATCH 14/15] restore example --- packages/stream_chat_flutter_core/example/lib/main.dart | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/packages/stream_chat_flutter_core/example/lib/main.dart b/packages/stream_chat_flutter_core/example/lib/main.dart index 308e63de..de10574b 100644 --- a/packages/stream_chat_flutter_core/example/lib/main.dart +++ b/packages/stream_chat_flutter_core/example/lib/main.dart @@ -4,7 +4,7 @@ import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; Future main() async { /// Create a new instance of [StreamChatClient] passing the apikey obtained from your /// project dashboard. - final client = StreamChatClient('kv7mcsxr24p8'); + final client = StreamChatClient('b67pax5b2wdq'); /// Set the current user. In a production scenario, this should be done using /// a backend to generate a user token using our server SDK. @@ -12,14 +12,13 @@ Future main() async { /// https://getstream.io/chat/docs/ios_user_setup_and_tokens/ await client.connectUser( User( - id: 'salvatore', + id: 'cool-shadow-7', extraData: { - 'name': 'Salvatore Giordano', 'image': - 'https://avatars.githubusercontent.com/u/20601437?s=460&u=3f66c22a7483980624804054ae7f357cf102c784&v=4', + 'https://getstream.io/random_png/?id=cool-shadow-7&name=Cool+shadow', }, ), - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoic2FsdmF0b3JlIn0.pgiJz7sIc7iP29BHKFwe3nLm5-OaR_1l2P-SlgiC9a8', + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiY29vbC1zaGFkb3ctNyJ9.gkOlCRb1qgy4joHPaxFwPOdXcGvSPvp6QY0S4mpRkVo', ); runApp( From 89be28cadf096258154bc7a906fa1eaaaeadf553 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Wed, 21 Apr 2021 12:44:52 +0530 Subject: [PATCH 15/15] Fix dispose bug for controller --- packages/stream_chat_flutter_core/example/lib/main.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/stream_chat_flutter_core/example/lib/main.dart b/packages/stream_chat_flutter_core/example/lib/main.dart index de10574b..f5f803c8 100644 --- a/packages/stream_chat_flutter_core/example/lib/main.dart +++ b/packages/stream_chat_flutter_core/example/lib/main.dart @@ -307,8 +307,10 @@ class _MessageScreenState extends State { await channel.sendMessage( Message(text: _controller.value.text), ); - _controller.clear(); - _updateList(); + if (mounted) { + _controller.clear(); + _updateList(); + } } }, child: const Padding(