From 0f7a16dc6d8dd5be8fb3e662406b641103a16894 Mon Sep 17 00:00:00 2001 From: Deven Joshi Date: Mon, 25 Jan 2021 19:29:32 +0530 Subject: [PATCH] feat: Added channel_list new implementation, fixed upper repo --- .../lib/src/channel_file_display_screen.dart | 1 - .../lib/src/channel_header.dart | 1 - .../lib/src/channel_list_view.dart | 346 +++++++---------- .../lib/src/channel_media_display_screen.dart | 1 - .../flutter_widgets/lib/src/channel_name.dart | 1 - .../lib/src/channels_bloc.dart | 185 --------- .../lib/src/full_screen_media.dart | 1 - .../flutter_widgets/lib/src/image_header.dart | 2 +- .../lib/src/lazy_load_scroll_view.dart | 148 -------- .../lib/src/media_list_view.dart | 1 - .../lib/src/message_actions_modal.dart | 2 +- .../lib/src/message_input.dart | 1 - .../lib/src/message_list_view.dart | 2 - .../lib/src/message_search_bloc.dart | 150 -------- .../lib/src/message_search_list_view.dart | 3 +- .../lib/src/stream_channel.dart | 356 ------------------ .../flutter_widgets/lib/src/stream_chat.dart | 73 +--- .../lib/src/typing_indicator.dart | 2 +- .../lib/src/user_list_view.dart | 41 -- .../flutter_widgets/lib/src/users_bloc.dart | 108 ------ .../lib/stream_chat_flutter.dart | 8 +- packages/flutter_widgets/pubspec.yaml | 4 +- .../lib/src/channel_list_core.dart | 20 +- 23 files changed, 160 insertions(+), 1297 deletions(-) delete mode 100644 packages/flutter_widgets/lib/src/channels_bloc.dart delete mode 100644 packages/flutter_widgets/lib/src/lazy_load_scroll_view.dart delete mode 100644 packages/flutter_widgets/lib/src/message_search_bloc.dart delete mode 100644 packages/flutter_widgets/lib/src/stream_channel.dart delete mode 100644 packages/flutter_widgets/lib/src/users_bloc.dart diff --git a/packages/flutter_widgets/lib/src/channel_file_display_screen.dart b/packages/flutter_widgets/lib/src/channel_file_display_screen.dart index d5aef38c..244729a4 100644 --- a/packages/flutter_widgets/lib/src/channel_file_display_screen.dart +++ b/packages/flutter_widgets/lib/src/channel_file_display_screen.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; class ChannelFileDisplayScreen extends StatefulWidget { diff --git a/packages/flutter_widgets/lib/src/channel_header.dart b/packages/flutter_widgets/lib/src/channel_header.dart index 8b920f23..73ee23a5 100644 --- a/packages/flutter_widgets/lib/src/channel_header.dart +++ b/packages/flutter_widgets/lib/src/channel_header.dart @@ -9,7 +9,6 @@ import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import '../stream_chat_flutter.dart'; import './channel_name.dart'; import 'channel_image.dart'; -import 'stream_channel.dart'; /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_header.png) /// ![screenshot](https://raw.githubusercontent.com/GetStream/stream-chat-flutter/master/screenshots/channel_header_paint.png) diff --git a/packages/flutter_widgets/lib/src/channel_list_view.dart b/packages/flutter_widgets/lib/src/channel_list_view.dart index 0cc7634b..617b7ce9 100644 --- a/packages/flutter_widgets/lib/src/channel_list_view.dart +++ b/packages/flutter_widgets/lib/src/channel_list_view.dart @@ -5,16 +5,13 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_slidable/flutter_slidable.dart'; import 'package:shimmer/shimmer.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter/src/channels_bloc.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/utils.dart'; +import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import '../stream_chat_flutter.dart'; import 'channel_bottom_sheet.dart'; import 'channel_preview.dart'; -import 'stream_channel.dart'; -import 'stream_chat.dart'; /// Callback called when tapping on a channel typedef ChannelTapCallback = void Function(Channel, Widget); @@ -158,168 +155,161 @@ class _ChannelListViewState extends State with WidgetsBindingObserver { final ScrollController _scrollController = ScrollController(); final SlidableController _slideController = SlidableController(); + final ChannelListController _channelListController = ChannelListController(); @override Widget build(BuildContext context) { - final channelsBloc = ChannelsBloc.of(context); - if (!widget.pullToRefresh) { - return _buildListView(channelsBloc); + _channelListController.loadData(); } return RefreshIndicator( onRefresh: () async { - return channelsBloc.queryChannels( - filter: widget.filter, - sortOptions: widget.sort, - paginationParams: widget.pagination, - options: widget.options, - ); + _channelListController.loadData(); }, - child: _buildListView(channelsBloc), + child: ChannelListCore( + channelListController: _channelListController, + listBuilder: (context, list) { + return _buildListView(list); + }, + emptyBuilder: (BuildContext context) { + return _buildEmptyWidget(); + }, + errorBuilder: (Error error) { + return _buildErrorWidget(context); + }, + loadingBuilder: (BuildContext context) { + return _buildLoadingWidget(); + }, + pagination: widget.pagination, + options: widget.options, + sort: widget.sort, + filter: widget.filter, + ), ); } - StreamBuilder> _buildListView( - ChannelsBlocState channelsBlocState, + Widget _buildListView( + List channels, ) { - return StreamBuilder>( - stream: channelsBlocState.channelsStream, - builder: (context, snapshot) { - var child; - if (snapshot.hasError) { - child = _buildErrorWidget( - snapshot, - context, - channelsBlocState, - ); - } else if (!snapshot.hasData) { - child = _buildLoadingWidget(); - } else { - final channels = snapshot.data; + var child; - if (channels.isEmpty && widget.emptyBuilder != null) { - child = widget.emptyBuilder(context); - } - - if (channels.isEmpty && widget.emptyBuilder == null) { - child = LayoutBuilder( - builder: (context, viewportConstraints) { - return SingleChildScrollView( - physics: AlwaysScrollableScrollPhysics(), - child: Stack( - children: [ - ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: StreamSvgIcon.message( - size: 136, - color: StreamChatTheme.of(context) - .colorTheme - .greyGainsboro, - ), - ), - Padding( - padding: const EdgeInsets.all(8.0), - child: Text( - 'Let’s start chatting!', - style: StreamChatTheme.of(context) - .textTheme - .headline, - ), - ), - Padding( - padding: const EdgeInsets.symmetric( - vertical: 8.0, - horizontal: 52, - ), - child: Text( - 'How about sending your first message to a friend?', - textAlign: TextAlign.center, - style: StreamChatTheme.of(context) - .textTheme - .body - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .grey, - ), - ), - ), - ], - ), - ), - if (widget.onStartChatPressed != null) - Positioned( - right: 0, - left: 0, - bottom: 32, - child: Center( - child: FlatButton( - onPressed: widget.onStartChatPressed, - child: Text( - 'Start a chat', - style: StreamChatTheme.of(context) - .textTheme - .bodyBold - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .accentBlue, - ), - ), - ), - ), - ), - ], - ), - ); - }, - ); - } - - if (channels.isNotEmpty) { - if (widget.crossAxisCount > 1) { - child = GridView.builder( - padding: widget.padding, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisCount: widget.crossAxisCount), - itemCount: channels.length, - physics: AlwaysScrollableScrollPhysics(), - controller: _scrollController, - itemBuilder: (context, index) { - return _gridItemBuilder(context, index, channels); - }, - ); - } else { - child = ListView.separated( - padding: widget.padding, - physics: AlwaysScrollableScrollPhysics(), - itemCount: - channels.isNotEmpty ? channels.length + 1 : channels.length, - separatorBuilder: (_, index) { - if (widget.separatorBuilder != null) { - return widget.separatorBuilder(context, index); - } - return _separatorBuilder(context, index); - }, - itemBuilder: (context, index) { - return _listItemBuilder(context, index, channels); - }, - ); + if (channels.isNotEmpty) { + if (widget.crossAxisCount > 1) { + child = GridView.builder( + padding: widget.padding, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: widget.crossAxisCount), + itemCount: channels.length, + physics: AlwaysScrollableScrollPhysics(), + controller: _scrollController, + itemBuilder: (context, index) { + return _gridItemBuilder(context, index, channels); + }, + ); + } else { + child = ListView.separated( + padding: widget.padding, + physics: AlwaysScrollableScrollPhysics(), + itemCount: + channels.isNotEmpty ? channels.length + 1 : channels.length, + separatorBuilder: (_, index) { + if (widget.separatorBuilder != null) { + return widget.separatorBuilder(context, index); } - } - } + return _separatorBuilder(context, index); + }, + itemBuilder: (context, index) { + return _listItemBuilder(context, index, channels); + }, + ); + } + } - return AnimatedSwitcher( - child: child, - duration: Duration(milliseconds: 500), + return AnimatedSwitcher( + child: child, + duration: Duration(milliseconds: 500), + ); + } + + Widget _buildEmptyWidget() { + if (widget.emptyBuilder != null) { + return widget.emptyBuilder(context); + } + + return LayoutBuilder( + builder: (context, viewportConstraints) { + return SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: Stack( + children: [ + ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: StreamSvgIcon.message( + size: 136, + color: StreamChatTheme.of(context) + .colorTheme + .greyGainsboro, + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: Text( + 'Let’s start chatting!', + style: StreamChatTheme.of(context).textTheme.headline, + ), + ), + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8.0, + horizontal: 52, + ), + child: Text( + 'How about sending your first message to a friend?', + textAlign: TextAlign.center, + style: StreamChatTheme.of(context) + .textTheme + .body + .copyWith( + color: + StreamChatTheme.of(context).colorTheme.grey, + ), + ), + ), + ], + ), + ), + if (widget.onStartChatPressed != null) + Positioned( + right: 0, + left: 0, + bottom: 32, + child: Center( + child: FlatButton( + onPressed: widget.onStartChatPressed, + child: Text( + 'Start a chat', + style: StreamChatTheme.of(context) + .textTheme + .bodyBold + .copyWith( + color: StreamChatTheme.of(context) + .colorTheme + .accentBlue, + ), + ), + ), + ), + ), + ], + ), ); }, ); @@ -443,27 +433,8 @@ class _ChannelListViewState extends State } Widget _buildErrorWidget( - AsyncSnapshot> snapshot, BuildContext context, - ChannelsBlocState channelsBlocState, ) { - if (snapshot.error is Error) { - print((snapshot.error as Error).stackTrace); - } - - if (widget.errorBuilder != null) { - return widget.errorBuilder(snapshot.error); - } - - var message = snapshot.error.toString(); - if (snapshot.error is DioError) { - final dioError = snapshot.error as DioError; - if (dioError.type == DioErrorType.RESPONSE) { - message = dioError.message; - } else { - message = 'Check your connection and retry'; - } - } return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -484,20 +455,9 @@ class _ChannelListViewState extends State ), style: Theme.of(context).textTheme.headline6, ), - Padding( - padding: const EdgeInsets.only( - top: 16.0, - ), - child: Text(message), - ), FlatButton( onPressed: () { - channelsBlocState.queryChannels( - filter: widget.filter, - sortOptions: widget.sort, - paginationParams: widget.pagination, - options: widget.options, - ); + _channelListController.loadData(); }, child: Text('Retry'), ), @@ -714,14 +674,7 @@ class _ChannelListViewState extends State if (_scrollController.position.maxScrollExtent == _scrollController.offset && _scrollController.offset != 0) { - channelsProvider.queryChannels( - filter: widget.filter, - sortOptions: widget.sort, - paginationParams: widget.pagination.copyWith( - offset: channelsProvider.channels?.length ?? 0, - ), - options: widget.options, - ); + _channelListController.paginateData(); } } @@ -759,12 +712,7 @@ class _ChannelListViewState extends State EventType.channelVisible, ) .listen((event) { - channelsBloc.queryChannels( - filter: widget.filter, - sortOptions: widget.sort, - paginationParams: widget.pagination, - options: widget.options, - ); + _channelListController.loadData(); }); } @@ -777,13 +725,7 @@ class _ChannelListViewState extends State widget.pagination?.toJson()?.toString() != oldWidget.pagination?.toJson()?.toString() || widget.options?.toString() != oldWidget.options?.toString()) { - final channelsBloc = ChannelsBloc.of(context); - channelsBloc.queryChannels( - filter: widget.filter, - sortOptions: widget.sort, - paginationParams: widget.pagination, - options: widget.options, - ); + _channelListController.loadData(); } } diff --git a/packages/flutter_widgets/lib/src/channel_media_display_screen.dart b/packages/flutter_widgets/lib/src/channel_media_display_screen.dart index 8b69fac8..85b571bc 100644 --- a/packages/flutter_widgets/lib/src/channel_media_display_screen.dart +++ b/packages/flutter_widgets/lib/src/channel_media_display_screen.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:video_player/video_player.dart'; diff --git a/packages/flutter_widgets/lib/src/channel_name.dart b/packages/flutter_widgets/lib/src/channel_name.dart index bf3eff22..1d51f3fc 100644 --- a/packages/flutter_widgets/lib/src/channel_name.dart +++ b/packages/flutter_widgets/lib/src/channel_name.dart @@ -3,7 +3,6 @@ import 'package:flutter/rendering.dart'; import 'package:stream_chat/stream_chat.dart'; import '../stream_chat_flutter.dart'; -import 'stream_channel.dart'; /// It shows the current [Channel] name using a [Text] widget. /// diff --git a/packages/flutter_widgets/lib/src/channels_bloc.dart b/packages/flutter_widgets/lib/src/channels_bloc.dart deleted file mode 100644 index c3c5c6f1..00000000 --- a/packages/flutter_widgets/lib/src/channels_bloc.dart +++ /dev/null @@ -1,185 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter/src/stream_chat.dart'; -import 'package:stream_chat_flutter/stream_chat_flutter.dart'; - -/// Widget dedicated to the management of a channel list with pagination -class ChannelsBloc extends StatefulWidget { - /// The widget child - final Widget child; - - /// Set this to true to prevent channels to be brought to the top of the list when a new message arrives - final bool lockChannelsOrder; - - /// Comparator used to sort the channels when a message.new event is received - 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; - - /// Instantiate a new ChannelsBloc - const ChannelsBloc({ - Key key, - this.child, - this.lockChannelsOrder = false, - this.channelsComparator, - this.shouldAddChannel, - }) : super(key: key); - - @override - ChannelsBlocState createState() => ChannelsBlocState(); - - /// Use this method to get the current [ChannelsBlocState] instance - static ChannelsBlocState of(BuildContext context) { - ChannelsBlocState streamChatState; - - streamChatState = context.findAncestorStateOfType(); - - if (streamChatState == null) { - throw Exception('You must have a ChannelsBloc widget as ancestor'); - } - - return streamChatState; - } -} - -/// The current state of the [ChannelsBloc] -class ChannelsBlocState extends State - with AutomaticKeepAliveClientMixin { - @override - Widget build(BuildContext context) { - super.build(context); - return widget.child; - } - - /// The current channel list - List get channels => _channelsController.value; - - /// The current channel list as a stream - Stream> get channelsStream => _channelsController.stream; - - final BehaviorSubject _queryChannelsLoadingController = - BehaviorSubject.seeded(false); - - final BehaviorSubject> _channelsController = BehaviorSubject(); - - /// The stream notifying the state of queryChannel call - Stream get queryChannelsLoading => - _queryChannelsLoadingController.stream; - - final List _hiddenChannels = []; - - /// Calls [client.queryChannels] updating [queryChannelsLoading] stream - Future queryChannels({ - Map filter, - List sortOptions, - PaginationParams paginationParams, - Map options, - bool onlyOffline = false, - }) async { - final client = StreamChat.of(context).client; - - if (client.state?.user == null || - _queryChannelsLoadingController.value == true) { - return; - } - _queryChannelsLoadingController.sink.add(true); - - try { - final clear = paginationParams == null || - paginationParams.offset == null || - paginationParams.offset == 0; - final oldChannels = List.from(channels ?? []); - final _channels = await client.queryChannels( - filter: filter, - sort: sortOptions, - options: options, - paginationParams: paginationParams, - onlyOffline: onlyOffline, - ); - - if (clear) { - _channelsController.add(_channels); - } else { - final l = oldChannels + _channels; - _channelsController.add(l); - } - _queryChannelsLoadingController.sink.add(false); - } catch (err, stackTrace) { - print(err); - print(stackTrace); - _queryChannelsLoadingController.addError(err, stackTrace); - } - } - - final List _subscriptions = []; - - @override - void initState() { - super.initState(); - - final client = StreamChat.of(context).client; - - 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); - if (index > -1) { - if (index > 0) { - final channel = newChannels.removeAt(index); - newChannels.insert(0, channel); - } - } else if (widget.shouldAddChannel != null && - widget.shouldAddChannel(e)) { - final hiddenIndex = _hiddenChannels.indexWhere((c) => c.cid == e.cid); - if (hiddenIndex > -1) { - 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 (widget.channelsComparator != null) { - newChannels.sort(widget.channelsComparator); - } - _channelsController.add(newChannels); - })); - } - - _subscriptions.add(client.on(EventType.channelHidden).listen((event) async { - final newChannels = List.from(channels ?? []); - final channelIndex = newChannels.indexWhere((c) => c.cid == event.cid); - if (channelIndex > -1) { - final channel = newChannels.removeAt(channelIndex); - _hiddenChannels.add(channel); - _channelsController.add(newChannels); - } - })); - - _subscriptions.add(client - .on(EventType.channelDeleted, EventType.notificationRemovedFromChannel) - .listen((e) { - final channel = e.channel; - _channelsController - .add(List.from(channels..removeWhere((c) => c.cid == channel.cid))); - })); - } - - @override - void dispose() { - _channelsController.close(); - _queryChannelsLoadingController.close(); - _subscriptions.forEach((s) => s.cancel()); - super.dispose(); - } - - @override - bool get wantKeepAlive => true; -} diff --git a/packages/flutter_widgets/lib/src/full_screen_media.dart b/packages/flutter_widgets/lib/src/full_screen_media.dart index 473c9a05..98ed3384 100644 --- a/packages/flutter_widgets/lib/src/full_screen_media.dart +++ b/packages/flutter_widgets/lib/src/full_screen_media.dart @@ -6,7 +6,6 @@ import 'package:photo_view/photo_view.dart'; import 'package:stream_chat_flutter/src/image_footer.dart'; import 'package:stream_chat_flutter/src/image_header.dart'; import 'package:video_player/video_player.dart'; -import 'stream_channel.dart'; import '../stream_chat_flutter.dart'; diff --git a/packages/flutter_widgets/lib/src/image_header.dart b/packages/flutter_widgets/lib/src/image_header.dart index 1694f0b7..f31ee3a6 100644 --- a/packages/flutter_widgets/lib/src/image_header.dart +++ b/packages/flutter_widgets/lib/src/image_header.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; +import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'image_actions_modal.dart'; -import 'stream_channel.dart'; class ImageHeader extends StatelessWidget implements PreferredSizeWidget { /// True if this header shows the leading back button diff --git a/packages/flutter_widgets/lib/src/lazy_load_scroll_view.dart b/packages/flutter_widgets/lib/src/lazy_load_scroll_view.dart deleted file mode 100644 index bc1f4d1b..00000000 --- a/packages/flutter_widgets/lib/src/lazy_load_scroll_view.dart +++ /dev/null @@ -1,148 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/widgets.dart'; - -enum _LoadingStatus { LOADING, STABLE } - -/// A widget that wraps a [Widget] and will trigger [onEndOfPage]/[onStartOfPage] when it -/// reaches the bottom/start of the list -class LazyLoadScrollView extends StatefulWidget { - /// The [Widget] that this widget watches for changes on - final Widget child; - - /// Called when the [child] reaches the start of the list - final AsyncCallback onStartOfPage; - - /// Called when the [child] reaches the end of the list - final AsyncCallback onEndOfPage; - - /// Called when the list scrolling starts - final VoidCallback onPageScrollStart; - - /// Called when the list scrolling ends - final VoidCallback onPageScrollEnd; - - /// Called every time the [child] is in-between the list - final VoidCallback onInBetweenOfPage; - - /// The offset to take into account when triggering [onEndOfPage]/[onStartOfPage] in pixels - final double scrollOffset; - - /// Used to determine if loading of new data has finished. You should use set this if you aren't using a FutureBuilder or StreamBuilder - final bool isLoading; - - /// Initiates a LazyLoadScrollView widget - const LazyLoadScrollView({ - Key key, - @required this.child, - this.onStartOfPage, - this.onEndOfPage, - this.onPageScrollStart, - this.onPageScrollEnd, - this.onInBetweenOfPage, - this.isLoading = false, - this.scrollOffset = 100, - }) : assert(child != null), - super(key: key); - - @override - State createState() => _LazyLoadScrollViewState(); -} - -class _LazyLoadScrollViewState extends State { - _LoadingStatus _loadMoreStatus = _LoadingStatus.STABLE; - double _scrollPosition = 0.0; - - @override - Widget build(BuildContext context) { - return NotificationListener( - child: widget.child, - onNotification: _onNotification, - ); - } - - bool _onNotification(Notification notification) { - if (notification is ScrollStartNotification) { - if (widget.onPageScrollStart != null) { - widget.onPageScrollStart(); - return true; - } - } - if (notification is ScrollEndNotification) { - if (widget.onPageScrollEnd != null) { - widget.onPageScrollEnd(); - return true; - } - } - if (notification is ScrollUpdateNotification) { - final pixels = notification.metrics.pixels; - final maxScrollExtent = notification.metrics.maxScrollExtent; - final minScrollExtent = notification.metrics.minScrollExtent; - final scrollOffset = widget.scrollOffset; - - if (pixels > (minScrollExtent + scrollOffset) && - pixels < (maxScrollExtent - scrollOffset)) { - if (widget.onInBetweenOfPage != null) { - widget.onInBetweenOfPage(); - return true; - } - } - - final extentBefore = notification.metrics.extentBefore; - final extentAfter = notification.metrics.extentAfter; - final scrollingDown = _scrollPosition < pixels; - - if (scrollOffset == null || scrollOffset == 0) { - if (extentAfter == 0) { - _onEndOfPage(); - } - if (extentBefore == 0) { - _onStartOfPage(); - } - } else { - if (scrollingDown) { - if (extentAfter <= scrollOffset) { - _onEndOfPage(); - } - } else { - if (extentBefore <= scrollOffset) { - _onStartOfPage(); - } - } - } - _scrollPosition = pixels; - return true; - } - if (notification is OverscrollNotification) { - if (notification.overscroll > 0) { - _onEndOfPage(); - } - if (notification.overscroll < 0) { - _onStartOfPage(); - } - return true; - } - return false; - } - - void _onEndOfPage() { - if (_loadMoreStatus != null && _loadMoreStatus == _LoadingStatus.STABLE) { - _loadMoreStatus = _LoadingStatus.LOADING; - if (widget.onEndOfPage != null) { - widget.onEndOfPage().whenComplete(() { - _loadMoreStatus = _LoadingStatus.STABLE; - }); - } - } - } - - void _onStartOfPage() { - if (_loadMoreStatus != null && _loadMoreStatus == _LoadingStatus.STABLE) { - _loadMoreStatus = _LoadingStatus.LOADING; - if (widget.onStartOfPage != null) { - widget.onStartOfPage().whenComplete(() { - _loadMoreStatus = _LoadingStatus.STABLE; - }); - } - } - } -} diff --git a/packages/flutter_widgets/lib/src/media_list_view.dart b/packages/flutter_widgets/lib/src/media_list_view.dart index 249348c4..d84a2759 100644 --- a/packages/flutter_widgets/lib/src/media_list_view.dart +++ b/packages/flutter_widgets/lib/src/media_list_view.dart @@ -4,7 +4,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:photo_manager/photo_manager.dart'; -import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import '../stream_chat_flutter.dart'; diff --git a/packages/flutter_widgets/lib/src/message_actions_modal.dart b/packages/flutter_widgets/lib/src/message_actions_modal.dart index 0cda4e9d..3cee6ba4 100644 --- a/packages/flutter_widgets/lib/src/message_actions_modal.dart +++ b/packages/flutter_widgets/lib/src/message_actions_modal.dart @@ -6,9 +6,9 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter/src/reaction_picker.dart'; -import 'package:stream_chat_flutter/src/stream_channel.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/utils.dart'; +import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import 'message_input.dart'; import 'message_widget.dart'; diff --git a/packages/flutter_widgets/lib/src/message_input.dart b/packages/flutter_widgets/lib/src/message_input.dart index 01ae3d3a..35a9441e 100644 --- a/packages/flutter_widgets/lib/src/message_input.dart +++ b/packages/flutter_widgets/lib/src/message_input.dart @@ -26,7 +26,6 @@ import 'package:video_compress/video_compress.dart'; import '../stream_chat_flutter.dart'; import 'extension.dart'; import 'quoted_message_widget.dart'; -import 'stream_channel.dart'; typedef FileUploader = Future Function(PlatformFile, Channel); typedef AttachmentThumbnailBuilder = Widget Function( diff --git a/packages/flutter_widgets/lib/src/message_list_view.dart b/packages/flutter_widgets/lib/src/message_list_view.dart index 9a0fe955..f14c332a 100644 --- a/packages/flutter_widgets/lib/src/message_list_view.dart +++ b/packages/flutter_widgets/lib/src/message_list_view.dart @@ -8,7 +8,6 @@ import 'package:rxdart/rxdart.dart'; import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter/src/info_tile.dart'; -import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart'; import 'package:stream_chat_flutter/src/message_widget.dart'; import 'package:stream_chat_flutter/src/stream_svg_icon.dart'; import 'package:stream_chat_flutter/src/system_message.dart'; @@ -16,7 +15,6 @@ import 'package:visibility_detector/visibility_detector.dart'; import '../stream_chat_flutter.dart'; import 'date_divider.dart'; -import 'stream_channel.dart'; import 'swipeable.dart'; import 'extension.dart'; diff --git a/packages/flutter_widgets/lib/src/message_search_bloc.dart b/packages/flutter_widgets/lib/src/message_search_bloc.dart deleted file mode 100644 index 67b6455d..00000000 --- a/packages/flutter_widgets/lib/src/message_search_bloc.dart +++ /dev/null @@ -1,150 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat/stream_chat.dart'; - -import 'stream_chat.dart'; - -/// Widget dedicated to the management of a message list with pagination -class MessageSearchBloc extends StatefulWidget { - /// The widget child - final Widget child; - - /// Instantiate a new MessageSearchBloc - const MessageSearchBloc({ - Key key, - @required this.child, - }) : super(key: key); - - @override - MessageSearchBlocState createState() => MessageSearchBlocState(); - - /// Use this method to get the current [MessageSearchBlocState] instance - static MessageSearchBlocState of(BuildContext context) { - MessageSearchBlocState state; - - state = context.findAncestorStateOfType(); - - if (state == null) { - throw Exception('You must have a MessageSearchBloc widget as ancestor'); - } - - return state; - } -} - -/// The current state of the [MessageSearchBloc] -class MessageSearchBlocState extends State - with AutomaticKeepAliveClientMixin { - /// The current messages list - List get messageResponses => _messageResponses.value; - - /// The current messages list as a stream - Stream> get messagesStream => - _messageResponses.stream; - - final BehaviorSubject> _messageResponses = - BehaviorSubject(); - - final BehaviorSubject _queryMessagesLoadingController = - BehaviorSubject.seeded(false); - - /// The stream notifying the state of queryUsers call - Stream get queryMessagesLoading => - _queryMessagesLoadingController.stream; - - /// Calls [Client.search] updating [messageResponses] stream - Future search({ - Map filter, - Map messageFilter, - List sort, - String query, - PaginationParams pagination, - }) async { - _messageResponses.add(null); - try { - final messages = await _search( - filter: filter, - messageFilter: messageFilter, - sort: sort, - query: query, - pagination: pagination, - ); - _messageResponses.add(messages.results); - } catch (err, stk) { - _messageResponses.addError(err, stk); - } - } - - /// Calls [Client.search] updating [queryMessagesLoading] stream - Future loadMore({ - Map filter, - Map messageFilter, - List sort, - String query, - PaginationParams pagination, - }) async { - if (_queryMessagesLoadingController.value == true) { - return; - } - _queryMessagesLoadingController.add(true); - try { - final clear = pagination == null || - pagination.offset == null || - pagination.offset == 0; - - final oldMessages = List.from(messageResponses ?? []); - - final messages = await _search( - filter: filter, - messageFilter: messageFilter, - sort: sort, - query: query, - pagination: pagination, - ); - - if (clear) { - _messageResponses.add(messages.results); - } else { - final temp = oldMessages + messages.results; - _messageResponses.add(temp); - } - - _queryMessagesLoadingController.add(false); - } catch (err, stackTrace) { - _queryMessagesLoadingController.addError(err, stackTrace); - } - } - - Future _search({ - Map filter, - Map messageFilter, - List sort, - String query, - PaginationParams pagination, - }) { - final client = StreamChat.of(context).client; - return client.search( - filter, - sort, - query, - pagination, - messageFilters: messageFilter, - ); - } - - @override - Widget build(BuildContext context) { - super.build(context); - return widget.child; - } - - @override - void dispose() { - _messageResponses.close(); - _queryMessagesLoadingController.close(); - super.dispose(); - } - - @override - bool get wantKeepAlive => true; -} diff --git a/packages/flutter_widgets/lib/src/message_search_list_view.dart b/packages/flutter_widgets/lib/src/message_search_list_view.dart index b176e148..7897d2ec 100644 --- a/packages/flutter_widgets/lib/src/message_search_list_view.dart +++ b/packages/flutter_widgets/lib/src/message_search_list_view.dart @@ -4,10 +4,9 @@ import 'package:flutter/material.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter/src/info_tile.dart'; import 'package:stream_chat_flutter/src/message_search_item.dart'; +import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; import '../stream_chat_flutter.dart'; -import 'lazy_load_scroll_view.dart'; -import 'message_search_bloc.dart'; /// Callback called when tapping on a user typedef MessageSearchItemTapCallback = void Function(GetMessageResponse); diff --git a/packages/flutter_widgets/lib/src/stream_channel.dart b/packages/flutter_widgets/lib/src/stream_channel.dart deleted file mode 100644 index 8b4b97ef..00000000 --- a/packages/flutter_widgets/lib/src/stream_channel.dart +++ /dev/null @@ -1,356 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/material.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat/stream_chat.dart'; - -enum QueryDirection { top, bottom } - -/// Widget used to provide information about the channel to the widget tree -/// -/// Use [StreamChannel.of] to get the current [StreamChannelState] instance. -class StreamChannel extends StatefulWidget { - const StreamChannel({ - Key key, - @required this.child, - @required this.channel, - this.showLoading = true, - this.initialMessageId, - }) : assert(child != null), - assert(channel != null), - super(key: key); - - final Widget child; - final Channel channel; - final bool showLoading; - - /// If passed the channel will load from this particular message. - final String initialMessageId; - - /// Use this method to get the current [StreamChannelState] instance - static StreamChannelState of(BuildContext context) { - StreamChannelState streamChannelState; - - streamChannelState = context.findAncestorStateOfType(); - - if (streamChannelState == null) { - throw Exception( - 'You must have a StreamChannel widget at the top of your widget tree'); - } - - return streamChannelState; - } - - @override - StreamChannelState createState() => StreamChannelState(); -} - -class StreamChannelState extends State { - /// Current channel - Channel get channel => widget.channel; - - /// InitialMessageId - String get initialMessageId => widget.initialMessageId; - - /// Current channel state stream - Stream get channelStateStream => - widget.channel.state.channelStateStream; - - final _queryTopMessagesController = BehaviorSubject.seeded(false); - final _queryBottomMessagesController = BehaviorSubject.seeded(false); - - /// The stream notifying the state of [_queryTopMessages] call - Stream get queryTopMessages => _queryTopMessagesController.stream; - - /// The stream notifying the state of [_queryBottomMessages] call - Stream get queryBottomMessages => _queryBottomMessagesController.stream; - - bool _topPaginationEnded = false; - bool _bottomPaginationEnded = false; - - Future _queryTopMessages({ - int limit = 20, - bool preferOffline = false, - }) async { - if (_topPaginationEnded || _queryTopMessagesController?.value == true) { - return; - } - _queryTopMessagesController.add(true); - - if (channel.state.messages.isEmpty) { - return _queryTopMessagesController.add(false); - } - - final oldestMessage = channel.state.messages.first; - - try { - final state = await queryBeforeMessage( - oldestMessage.id, - limit: limit, - preferOffline: preferOffline, - ); - if (state.messages.isEmpty || state.messages.length < limit) { - _topPaginationEnded = true; - } - _queryTopMessagesController.add(false); - } catch (e, stk) { - _queryTopMessagesController.addError(e, stk); - } - } - - Future _queryBottomMessages({ - int limit = 20, - bool preferOffline = false, - }) async { - if (_bottomPaginationEnded || - _queryBottomMessagesController?.value == true || - channel?.state?.isUpToDate == true) return; - _queryBottomMessagesController.add(true); - - if (channel.state.messages.isEmpty) { - return _queryBottomMessagesController.add(false); - } - - final recentMessage = channel.state.messages.last; - - try { - final state = await queryAfterMessage( - recentMessage.id, - limit: limit, - preferOffline: preferOffline, - ); - if (state.messages.isEmpty || state.messages.length < limit) { - _bottomPaginationEnded = true; - } - _queryBottomMessagesController.add(false); - } catch (e, stk) { - _queryBottomMessagesController.addError(e, stk); - } - } - - /// Calls [channel.query] updating [queryMessage] stream - Future queryMessages({QueryDirection direction = QueryDirection.top}) { - if (direction == QueryDirection.top) return _queryTopMessages(); - return _queryBottomMessages(); - } - - /// Calls [channel.getReplies] updating [queryMessage] stream - Future getReplies( - String parentId, { - int limit = 50, - bool preferOffline = false, - }) async { - if (_topPaginationEnded || _queryTopMessagesController.value) return; - _queryTopMessagesController.add(true); - - Message message; - if (channel.state.threads.containsKey(parentId)) { - final thread = channel.state.threads[parentId]; - if (thread.isNotEmpty) { - message = thread.first; - } - } - - try { - final response = await channel.getReplies( - parentId, - PaginationParams( - lessThan: message?.id, - limit: limit, - ), - preferOffline: preferOffline, - ); - if (response.messages.isEmpty || response.messages.length < limit) { - _topPaginationEnded = true; - } - _queryTopMessagesController.add(false); - } catch (e, stk) { - _queryTopMessagesController.addError(e, stk); - } - } - - /// 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, - ), - ); - } - - /// Loads channel at specific message - Future loadChannelAtMessage( - String messageId, { - int before = 20, - int after = 20, - bool preferOffline = false, - }) { - return queryAtMessage( - messageId: messageId, - before: before, - after: after, - preferOffline: preferOffline, - ); - } - - /// - Future queryAtMessage({ - String messageId, - int before = 20, - int after = 20, - bool preferOffline = false, - }) async { - if (channel.state == null) return; - channel.state.isUpToDate = false; - channel.state.truncate(); - - if (messageId == null) { - await channel.query( - messagesPagination: PaginationParams( - limit: before, - ), - preferOffline: preferOffline, - ); - channel.state.isUpToDate = true; - return; - } - - return Future.wait([ - queryBeforeMessage( - messageId, - limit: before, - preferOffline: preferOffline, - ), - queryAfterMessage( - messageId, - limit: after, - preferOffline: preferOffline, - ), - ]); - } - - /// - Future queryBeforeMessage( - String messageId, { - int limit = 20, - bool preferOffline = false, - }) { - return channel.query( - messagesPagination: PaginationParams( - lessThan: messageId, - limit: limit, - ), - preferOffline: preferOffline, - ); - } - - /// - Future queryAfterMessage( - String messageId, { - int limit = 20, - bool preferOffline = false, - }) async { - final state = await channel.query( - messagesPagination: PaginationParams( - greaterThanOrEqual: messageId, - limit: limit, - ), - preferOffline: preferOffline, - ); - if (state.messages.isEmpty || state.messages.length < limit) { - channel.state.isUpToDate = true; - } - return state; - } - - /// - Future getMessage(String messageId) async { - var message = channel.state.messages.firstWhere( - (it) => it.id == messageId, - orElse: () => null, - ); - if (message == null) { - final response = await channel.getMessagesById([messageId]); - message = response.messages.first; - } - return message; - } - - /// Reloads the channel with latest message - Future reloadChannel() => queryAtMessage(before: 30); - - List> _futures; - - Future get _loadChannelAtMessage async { - try { - await loadChannelAtMessage(initialMessageId); - return true; - } catch (e, stk) { - print('Error: $e\nStack: $stk'); - rethrow; - } - } - - @override - void initState() { - super.initState(); - _futures = [widget.channel.initialized]; - if (initialMessageId != null) { - _futures.add(_loadChannelAtMessage); - } - } - - @override - void dispose() { - _queryTopMessagesController.close(); - _queryBottomMessagesController.close(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - Widget child = FutureBuilder>( - future: Future.wait(_futures), - initialData: [ - channel.state != null, - if (initialMessageId != null) false, - ], - builder: (context, snapshot) { - if (snapshot.hasError) { - if (snapshot.error is Error) { - print((snapshot.error as Error).stackTrace); - } - var message = snapshot.error.toString(); - if (snapshot.error is DioError) { - final dioError = snapshot.error as DioError; - if (dioError.type == DioErrorType.RESPONSE) { - message = dioError.message; - } else { - message = 'Check your connection and retry'; - } - } - return Center( - child: Text(message), - ); - } - final initialized = snapshot.data[0]; - final dataLoaded = initialMessageId == null ? true : snapshot.data[1]; - if (widget.showLoading && (!initialized || !dataLoaded)) { - return Center( - child: CircularProgressIndicator(), - ); - } - return widget.child; - }, - ); - if (initialMessageId != null) { - child = Material(child: child); - } - return child; - } -} diff --git a/packages/flutter_widgets/lib/src/stream_chat.dart b/packages/flutter_widgets/lib/src/stream_chat.dart index 5d1b7484..ee7e049f 100644 --- a/packages/flutter_widgets/lib/src/stream_chat.dart +++ b/packages/flutter_widgets/lib/src/stream_chat.dart @@ -6,6 +6,7 @@ import 'package:flutter_app_badger/flutter_app_badger.dart'; import 'package:flutter_portal/flutter_portal.dart'; import 'package:stream_chat/stream_chat.dart'; import 'package:stream_chat_flutter/src/stream_chat_theme.dart'; +import 'package:stream_chat_flutter/stream_chat_flutter.dart'; /// Widget used to provide information about the chat to the widget tree /// @@ -61,9 +62,8 @@ class StreamChat extends StatefulWidget { } /// The current state of the StreamChat widget -class StreamChatState extends State with WidgetsBindingObserver { +class StreamChatState extends State { Client get client => widget.client; - Timer _disconnectTimer; @override Widget build(BuildContext context) { @@ -81,7 +81,10 @@ class StreamChatState extends State with WidgetsBindingObserver { accentColor: streamTheme.colorTheme.accentBlue, scaffoldBackgroundColor: streamTheme.colorTheme.white, ), - child: widget.child, + child: StreamChatCore( + child: widget.child, + client: client, + ), ); }, ), @@ -106,7 +109,6 @@ class StreamChatState extends State with WidgetsBindingObserver { @override void initState() { super.initState(); - WidgetsBinding.instance.addObserver(this); client.state?.totalUnreadCountStream?.listen((count) { if (count > 0) { FlutterAppBadger.updateBadgeCount(count); @@ -115,67 +117,4 @@ class StreamChatState extends State with WidgetsBindingObserver { } }); } - - StreamSubscription _newMessageSubscription; - - @override - void didChangeAppLifecycleState(AppLifecycleState state) { - if (client.state?.user != null) { - if (state == AppLifecycleState.paused) { - if (client.showLocalNotification != null) { - _newMessageSubscription = client - .on(EventType.messageNew) - .where((e) => e.user?.id != user.id) - .where((e) => e.message.silent != true) - .where((e) => e.message.shadowed != true) - .listen((event) async { - final channel = client.channel( - event.channelType, - id: event.channelId, - ); - - client.showLocalNotification( - event.message, - ChannelModel( - id: channel.id, - createdAt: channel.createdAt, - extraData: channel.extraData, - type: channel.type, - memberCount: channel.memberCount, - frozen: channel.frozen, - cid: channel.cid, - deletedAt: channel.deletedAt, - config: channel.config, - createdBy: channel.createdBy, - updatedAt: channel.updatedAt, - lastMessageAt: channel.lastMessageAt, - ), - ); - }); - _disconnectTimer = Timer(client.backgroundKeepAlive, () { - client.disconnect(); - }); - } else { - client.disconnect(); - } - } else if (state == AppLifecycleState.resumed) { - _newMessageSubscription?.cancel(); - if (_disconnectTimer?.isActive == true) { - _disconnectTimer.cancel(); - } else { - if (client.wsConnectionStatus.value == - ConnectionStatus.disconnected) { - NotificationService.handleIosMessageQueue(client); - client.connect(); - } - } - } - } - } - - @override - void dispose() { - WidgetsBinding.instance.removeObserver(this); - super.dispose(); - } } diff --git a/packages/flutter_widgets/lib/src/typing_indicator.dart b/packages/flutter_widgets/lib/src/typing_indicator.dart index 177e9e21..c9e579be 100644 --- a/packages/flutter_widgets/lib/src/typing_indicator.dart +++ b/packages/flutter_widgets/lib/src/typing_indicator.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:lottie/lottie.dart'; import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter/src/stream_channel.dart'; +import 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; /// Widget to show the current list of typing users class TypingIndicator extends StatelessWidget { diff --git a/packages/flutter_widgets/lib/src/user_list_view.dart b/packages/flutter_widgets/lib/src/user_list_view.dart index c5bac576..be34d3db 100644 --- a/packages/flutter_widgets/lib/src/user_list_view.dart +++ b/packages/flutter_widgets/lib/src/user_list_view.dart @@ -2,8 +2,6 @@ import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:stream_chat/stream_chat.dart'; -import 'package:stream_chat_flutter/src/lazy_load_scroll_view.dart'; -import 'package:stream_chat_flutter/src/users_bloc.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'user_item.dart'; @@ -513,42 +511,3 @@ class _UserListViewState extends State } } } - -abstract class ListItem { - String get key { - if (this is ListHeaderItem) { - final header = (this as ListHeaderItem).heading; - return 'HEADER-$header'; - } - if (this is ListUserItem) { - final user = (this as ListUserItem).user; - return 'USER-${user.id}'; - } - return null; - } - - Widget when({ - @required Widget Function(String heading) headerItem, - @required Widget Function(User user) userItem, - }) { - if (this is ListHeaderItem) { - return headerItem((this as ListHeaderItem).heading); - } - if (this is ListUserItem) { - return userItem((this as ListUserItem).user); - } - return SizedBox(); - } -} - -class ListHeaderItem extends ListItem { - final String heading; - - ListHeaderItem(this.heading); -} - -class ListUserItem extends ListItem { - final User user; - - ListUserItem(this.user); -} diff --git a/packages/flutter_widgets/lib/src/users_bloc.dart b/packages/flutter_widgets/lib/src/users_bloc.dart deleted file mode 100644 index 91cbaf80..00000000 --- a/packages/flutter_widgets/lib/src/users_bloc.dart +++ /dev/null @@ -1,108 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:rxdart/rxdart.dart'; -import 'package:stream_chat/stream_chat.dart'; - -import 'stream_chat.dart'; - -/// Widget dedicated to the management of a users list with pagination -class UsersBloc extends StatefulWidget { - /// The widget child - final Widget child; - - /// Instantiate a new UsersBloc - const UsersBloc({ - Key key, - @required this.child, - }) : super(key: key); - - @override - UsersBlocState createState() => UsersBlocState(); - - /// Use this method to get the current [UsersBlocState] instance - static UsersBlocState of(BuildContext context) { - UsersBlocState state; - - state = context.findAncestorStateOfType(); - - if (state == null) { - throw Exception('You must have a UsersBloc widget as ancestor'); - } - - return state; - } -} - -/// The current state of the [UsersBloc] -class UsersBlocState extends State - with AutomaticKeepAliveClientMixin { - /// The current users list - List get users => _usersController.value; - - /// The current users list as a stream - Stream> get usersStream => _usersController.stream; - - final BehaviorSubject> _usersController = BehaviorSubject(); - - final BehaviorSubject _queryUsersLoadingController = - BehaviorSubject.seeded(false); - - /// The stream notifying the state of queryUsers call - Stream get queryUsersLoading => _queryUsersLoadingController.stream; - - /// Calls [Client.queryUsers] updating [queryUsersLoading] stream - Future queryUsers({ - Map filter, - List sort, - Map options, - PaginationParams pagination, - }) async { - final client = StreamChat.of(context).client; - - if (client.state?.user == null || - _queryUsersLoadingController.value == true) { - return; - } - _queryUsersLoadingController.add(true); - try { - final clear = pagination == null || - pagination.offset == null || - pagination.offset == 0; - - final oldUsers = List.from(users ?? []); - - final usersResponse = await client.queryUsers( - filter: filter, - sort: sort, - options: options, - pagination: pagination, - ); - - if (clear) { - _usersController.add(usersResponse.users); - } else { - final temp = oldUsers + usersResponse.users; - _usersController.add(temp); - } - - _queryUsersLoadingController.add(false); - } catch (err, stackTrace) { - _queryUsersLoadingController.addError(err, stackTrace); - } - } - - @override - Widget build(BuildContext context) { - super.build(context); - return widget.child; - } - - @override - void dispose() { - _usersController.close(); - _queryUsersLoadingController.close(); - super.dispose(); - } - - @override - bool get wantKeepAlive => true; -} diff --git a/packages/flutter_widgets/lib/stream_chat_flutter.dart b/packages/flutter_widgets/lib/stream_chat_flutter.dart index df113234..c4e0e2e1 100644 --- a/packages/flutter_widgets/lib/stream_chat_flutter.dart +++ b/packages/flutter_widgets/lib/stream_chat_flutter.dart @@ -7,7 +7,6 @@ export 'src/channel_list_header.dart'; export 'src/channel_list_view.dart'; export 'src/channel_name.dart'; export 'src/channel_preview.dart'; -export 'src/channels_bloc.dart'; export 'src/date_divider.dart'; export 'src/deleted_message.dart'; export 'src/file_attachment.dart'; @@ -22,8 +21,6 @@ export 'src/message_text.dart'; export 'src/message_widget.dart'; export 'src/reaction_picker.dart'; export 'src/sending_indicator.dart'; -export 'src/stream_channel.dart'; -export 'src/stream_chat.dart'; export 'src/stream_chat_theme.dart'; export 'src/stream_neumorphic_button.dart'; export 'src/stream_svg_icon.dart'; @@ -35,11 +32,8 @@ export 'src/user_item.dart'; export 'src/user_item.dart'; export 'src/user_list_view.dart'; export 'src/user_list_view.dart'; -export 'src/users_bloc.dart'; -export 'src/users_bloc.dart'; export 'src/utils.dart'; export 'src/video_attachment.dart'; -export 'src/message_search_bloc.dart'; export 'src/message_search_item.dart'; export 'src/message_search_list_view.dart'; export 'src/unread_indicator.dart'; @@ -47,3 +41,5 @@ export 'src/option_list_tile.dart'; export 'src/channel_file_display_screen.dart'; export 'src/channel_media_display_screen.dart'; export 'src/info_tile.dart'; +export 'src/stream_chat.dart'; +export 'package:stream_chat_flutter_core/stream_chat_flutter_core.dart'; diff --git a/packages/flutter_widgets/pubspec.yaml b/packages/flutter_widgets/pubspec.yaml index 2b82aca2..059d4fb2 100644 --- a/packages/flutter_widgets/pubspec.yaml +++ b/packages/flutter_widgets/pubspec.yaml @@ -11,6 +11,9 @@ environment: dependencies: flutter: sdk: flutter + # TODO: Change over to main dependency when deployed + stream_chat_flutter_core: + path: ../stream_chat_flutter_core flutter_app_badger: ^1.1.2 photo_view: ^0.10.3 rxdart: ^0.24.1 @@ -29,7 +32,6 @@ dependencies: image_picker: ^0.6.7+17 flutter_keyboard_visibility: ^4.0.2 mime: ^0.9.7 - stream_chat: ^0.2.24 video_compress: ^2.1.1 visibility_detector: ^0.1.5 http_parser: ^3.1.4 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 627c5131..972de62f 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 @@ -49,7 +49,6 @@ class ChannelListCore extends StatefulWidget { this.options, this.sort, this.pagination, - this.pullToRefresh = true, @required this.errorBuilder, @required this.emptyBuilder, @required this.loadingBuilder, @@ -92,9 +91,6 @@ class ChannelListCore extends StatefulWidget { /// message_limit: how many messages should be included to each channel final PaginationParams pagination; - /// Set it to false to disable the pull-to-refresh widget - final bool pullToRefresh; - @override _ChannelListCoreState createState() => _ChannelListCoreState(); } @@ -105,21 +101,7 @@ class _ChannelListCoreState extends State Widget build(BuildContext context) { final channelsBloc = ChannelsBloc.of(context); - if (!widget.pullToRefresh) { - return _buildListView(channelsBloc); - } - - return RefreshIndicator( - onRefresh: () async { - return channelsBloc.queryChannels( - filter: widget.filter, - sortOptions: widget.sort, - paginationParams: widget.pagination, - options: widget.options, - ); - }, - child: _buildListView(channelsBloc), - ); + return _buildListView(channelsBloc); } StreamBuilder> _buildListView(