diff --git a/packages/stream_chat_v1/lib/channel_file_display_screen.dart b/packages/stream_chat_v1/lib/channel_file_display_screen.dart index 743b3eb..8806f97 100644 --- a/packages/stream_chat_v1/lib/channel_file_display_screen.dart +++ b/packages/stream_chat_v1/lib/channel_file_display_screen.dart @@ -1,52 +1,45 @@ import 'package:example/localizations.dart'; +import 'package:example/routes/routes.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; +import 'package:video_player/video_player.dart'; + +import 'channel_page.dart'; class ChannelFileDisplayScreen extends StatefulWidget { - /// 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; - - /// 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; - - /// The builder used when the file list is empty. - final WidgetBuilder? emptyBuilder; + final StreamMessageThemeData messageTheme; const ChannelFileDisplayScreen({ - this.sortOptions, - this.paginationParams = const PaginationParams(limit: 20), - this.emptyBuilder, - }); + Key? key, + required this.messageTheme, + }) : super(key: key); @override - _ChannelFileDisplayScreenState createState() => + State createState() => _ChannelFileDisplayScreenState(); } class _ChannelFileDisplayScreenState extends State { - @override - void initState() { - super.initState(); - final messageSearchBloc = MessageSearchBloc.of(context); - messageSearchBloc.search( - filter: Filter.in_( - 'cid', - [StreamChannel.of(context).channel.cid!], + final Map controllerCache = {}; + + late final controller = StreamMessageSearchListController( + client: StreamChat.of(context).client, + filter: Filter.in_( + 'cid', + [StreamChannel.of(context).channel.cid!], + ), + messageFilter: Filter.in_( + 'attachments.type', + ['file'], + ), + sort: [ + SortOption( + 'created_at', + direction: SortOption.ASC, ), - messageFilter: Filter.in_( - 'attachments.type', - ['file'], - ), - sort: widget.sortOptions, - pagination: widget.paginationParams, - ); - } + ], + limit: 20, + ); @override Widget build(BuildContext context) { @@ -58,120 +51,109 @@ class _ChannelFileDisplayScreenState extends State { title: Text( AppLocalizations.of(context).files, style: TextStyle( - color: StreamChatTheme.of(context).colorTheme.textHighEmphasis, - fontSize: 16.0), - ), - leading: Center( - child: InkWell( - onTap: () { - Navigator.of(context).pop(); - }, - child: Container( - width: 24.0, - height: 24.0, - child: StreamSvgIcon.left( - color: StreamChatTheme.of(context).colorTheme.textHighEmphasis, - size: 24.0, - ), - ), + color: StreamChatTheme.of(context).colorTheme.textHighEmphasis, + fontSize: 16.0, ), ), + leading: StreamBackButton(), backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, ), - body: _buildMediaGrid(), - ); - } - - Widget _buildMediaGrid() { - final messageSearchBloc = MessageSearchBloc.of(context); - - return StreamBuilder>( - builder: (context, snapshot) { - if (snapshot.data == null) { - return Center( - child: const CircularProgressIndicator(), - ); - } - - if (snapshot.data!.isEmpty) { - if (widget.emptyBuilder != null) { - return widget.emptyBuilder!(context); - } - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamSvgIcon.files( - size: 136.0, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - SizedBox(height: 16.0), - Text( - AppLocalizations.of(context).noFiles, - style: TextStyle( - fontSize: 14.0, - color: - StreamChatTheme.of(context).colorTheme.textHighEmphasis, + body: ValueListenableBuilder( + valueListenable: controller, + builder: ( + BuildContext context, + PagedValue value, + Widget? child, + ) { + return value.when( + (items, nextPageKey, error) { + if (items.isEmpty) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + StreamSvgIcon.files( + size: 136.0, + color: StreamChatTheme.of(context).colorTheme.disabled, + ), + SizedBox(height: 16.0), + Text( + AppLocalizations.of(context).noFiles, + style: TextStyle( + fontSize: 14.0, + color: StreamChatTheme.of(context) + .colorTheme + .textHighEmphasis, + ), + ), + SizedBox(height: 8.0), + Text( + AppLocalizations.of(context).filesAppearHere, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14.0, + color: StreamChatTheme.of(context) + .colorTheme + .textHighEmphasis + .withOpacity(0.5), + ), + ), + ], ), - ), - SizedBox(height: 8.0), - Text( - AppLocalizations.of(context).filesAppearHere, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14.0, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - ], - ), - ); - } + ); + } + final media = {}; - final media = {}; + for (var item in items) { + item.message.attachments + .where((e) => e.type == 'file') + .forEach((e) { + media[e] = item.message; + }); + } - for (var item in snapshot.data!) { - item.message.attachments.where((e) => e.type == 'file').forEach((e) { - media[e] = item.message; - }); - } - - return LazyLoadScrollView( - onEndOfPage: () => messageSearchBloc.search( - filter: Filter.in_( - 'cid', - [StreamChannel.of(context).channel.cid!], - ), - messageFilter: Filter.in_( - 'attachments.type', - ['file'], - ), - sort: widget.sortOptions, - pagination: widget.paginationParams.copyWith( - offset: messageSearchBloc.messageResponses?.length ?? 0, - ), - ), - child: ListView.builder( - itemBuilder: (context, position) { - return Padding( - padding: const EdgeInsets.all(1.0), - child: Padding( - padding: const EdgeInsets.all(8.0), - child: StreamFileAttachment( - message: media.values.toList()[position], - attachment: media.keys.toList()[position], - ), + return LazyLoadScrollView( + onEndOfPage: () async { + if (nextPageKey != null) { + controller.loadMore(nextPageKey); + } + }, + child: ListView.builder( + itemBuilder: (context, position) { + return Padding( + padding: const EdgeInsets.all(1.0), + child: Padding( + padding: const EdgeInsets.all(8.0), + child: StreamFileAttachment( + message: media.values.toList()[position], + attachment: media.keys.toList()[position], + ), + ), + ); + }, + itemCount: media.length, ), ); }, - itemCount: media.length, - ), - ); - }, - stream: messageSearchBloc.messagesStream, + loading: () => Center( + child: const CircularProgressIndicator(), + ), + error: (_) => Offstage(), + ); + }, + ), ); } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } + + @override + void initState() { + controller.doInitialLoad(); + super.initState(); + } } diff --git a/packages/stream_chat_v1/lib/channel_list.dart b/packages/stream_chat_v1/lib/channel_list.dart index 5c8164e..dcc1e91 100644 --- a/packages/stream_chat_v1/lib/channel_list.dart +++ b/packages/stream_chat_v1/lib/channel_list.dart @@ -278,31 +278,33 @@ class _ChannelList extends State { emptyBuilder: (_) { return Center( child: Padding( - padding: EdgeInsets.all(8), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Expanded(child: StreamChannelListEmptyWidget()), - TextButton( - onPressed: () { - Navigator.pushNamed( - context, - Routes.NEW_CHAT, - ); - }, - child: Text( - 'Start a chat', - style: StreamChatTheme.of(context) - .textTheme - .bodyBold - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .accentPrimary, - ), - ), + padding: const EdgeInsets.all(8), + child: StreamScrollViewEmptyWidget( + emptyIcon: StreamSvgIcon.message( + size: 148, + color: StreamChatTheme.of(context) + .colorTheme + .disabled, + ), + emptyTitle: TextButton( + onPressed: () { + Navigator.pushNamed( + context, + Routes.NEW_CHAT, + ); + }, + child: Text( + 'Start a chat', + style: StreamChatTheme.of(context) + .textTheme + .bodyBold + .copyWith( + color: StreamChatTheme.of(context) + .colorTheme + .accentPrimary, + ), ), - ], + ), ), ), ); diff --git a/packages/stream_chat_v1/lib/channel_list_page.dart b/packages/stream_chat_v1/lib/channel_list_page.dart index af45104..6a9438f 100644 --- a/packages/stream_chat_v1/lib/channel_list_page.dart +++ b/packages/stream_chat_v1/lib/channel_list_page.dart @@ -100,9 +100,7 @@ class _ChannelListPageState extends State { body: IndexedStack( index: _currentIndex, children: [ - MessageSearchBloc( - child: ChannelList(), - ), + ChannelList(), UserMentionsPage(), ], ), diff --git a/packages/stream_chat_v1/lib/channel_media_display_screen.dart b/packages/stream_chat_v1/lib/channel_media_display_screen.dart index 0cfdde6..65c9a5d 100644 --- a/packages/stream_chat_v1/lib/channel_media_display_screen.dart +++ b/packages/stream_chat_v1/lib/channel_media_display_screen.dart @@ -1,61 +1,45 @@ import 'package:example/localizations.dart'; +import 'package:example/routes/routes.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'package:video_player/video_player.dart'; +import 'channel_page.dart'; + class ChannelMediaDisplayScreen extends StatefulWidget { - /// 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; - - /// 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; - - /// The builder used when the file list is empty. - final WidgetBuilder? emptyBuilder; - - final ShowMessageCallback? onShowMessage; - final StreamMessageThemeData messageTheme; const ChannelMediaDisplayScreen({ + Key? key, required this.messageTheme, - this.sortOptions, - this.paginationParams = const PaginationParams(limit: 20), - this.emptyBuilder, - this.onShowMessage, - }); + }) : super(key: key); @override - _ChannelMediaDisplayScreenState createState() => + State createState() => _ChannelMediaDisplayScreenState(); } class _ChannelMediaDisplayScreenState extends State { - Map controllerCache = {}; + final Map controllerCache = {}; - @override - void initState() { - super.initState(); - final messageSearchBloc = MessageSearchBloc.of(context); - messageSearchBloc.search( - filter: Filter.in_( - 'cid', - [StreamChannel.of(context).channel.cid!], + late final controller = StreamMessageSearchListController( + client: StreamChat.of(context).client, + filter: Filter.in_( + 'cid', + [StreamChannel.of(context).channel.cid!], + ), + messageFilter: Filter.in_( + 'attachments.type', + ['image', 'video'], + ), + sort: [ + SortOption( + 'created_at', + direction: SortOption.ASC, ), - messageFilter: Filter.in_( - 'attachments.type', - ['image', 'video'], - ), - sort: widget.sortOptions, - pagination: widget.paginationParams, - ); - } + ], + limit: 20, + ); @override Widget build(BuildContext context) { @@ -74,160 +58,169 @@ class _ChannelMediaDisplayScreenState extends State { leading: StreamBackButton(), backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, ), - body: _buildMediaGrid(), - ); - } - - Widget _buildMediaGrid() { - final messageSearchBloc = MessageSearchBloc.of(context); - - return StreamBuilder>( - builder: (context, snapshot) { - if (snapshot.data == null) { - return Center( - child: const CircularProgressIndicator(), - ); - } - - if (snapshot.data!.isEmpty) { - if (widget.emptyBuilder != null) { - return widget.emptyBuilder!(context); - } - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - StreamSvgIcon.pictures( - size: 136.0, - color: StreamChatTheme.of(context).colorTheme.disabled, - ), - SizedBox(height: 16.0), - Text( - AppLocalizations.of(context).noMedia, - style: TextStyle( - fontSize: 14.0, - color: - StreamChatTheme.of(context).colorTheme.textHighEmphasis, - ), - ), - SizedBox(height: 8.0), - Text( - AppLocalizations.of(context).photosOrVideosWillAppearHere, - textAlign: TextAlign.center, - style: TextStyle( - fontSize: 14.0, - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis - .withOpacity(0.5), - ), - ), - ], - ), - ); - } - - final media = <_AssetPackage>[]; - - for (var item in snapshot.data!) { - item.message.attachments - .where((e) => - (e.type == 'image' || e.type == 'video') && - e.ogScrapeUrl == null) - .forEach((e) { - VideoPlayerController? controller; - if (e.type == 'video') { - var cachedController = controllerCache[e.assetUrl]; - - if (cachedController == null) { - controller = VideoPlayerController.network(e.assetUrl!); - controller.initialize(); - controllerCache[e.assetUrl] = controller; - } else { - controller = cachedController; - } - } - media.add(_AssetPackage(e, item.message, controller)); - }); - } - - return LazyLoadScrollView( - onEndOfPage: () => messageSearchBloc.search( - filter: Filter.in_( - 'cid', - [StreamChannel.of(context).channel.cid!], - ), - messageFilter: Filter.in_( - 'attachments.type', - ['image', 'video'], - ), - sort: widget.sortOptions, - pagination: widget.paginationParams.copyWith( - offset: messageSearchBloc.messageResponses?.length ?? 0, - ), - ), - child: GridView.builder( - gridDelegate: - SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3), - itemBuilder: (context, position) { - var channel = StreamChannel.of(context).channel; - return Padding( - padding: const EdgeInsets.all(1.0), - child: InkWell( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => StreamChannel( - channel: channel, - child: StreamFullScreenMedia( - mediaAttachmentPackages: media - .map( - (e) => StreamAttachmentPackage( - attachment: e.attachment, - message: e.message, - ), - ) - .toList(), - startIndex: position, - userName: media[position].message.user!.name, - onShowMessage: widget.onShowMessage, - ), + body: ValueListenableBuilder( + valueListenable: controller, + builder: (BuildContext context, + PagedValue value, Widget? child) { + return value.when( + (items, nextPageKey, error) { + if (items.isEmpty) { + return Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + StreamSvgIcon.pictures( + size: 136.0, + color: StreamChatTheme.of(context).colorTheme.disabled, + ), + SizedBox(height: 16.0), + Text( + AppLocalizations.of(context).noMedia, + style: TextStyle( + fontSize: 14.0, + color: StreamChatTheme.of(context) + .colorTheme + .textHighEmphasis, ), ), + SizedBox(height: 8.0), + Text( + AppLocalizations.of(context) + .photosOrVideosWillAppearHere, + textAlign: TextAlign.center, + style: TextStyle( + fontSize: 14.0, + color: StreamChatTheme.of(context) + .colorTheme + .textHighEmphasis + .withOpacity(0.5), + ), + ), + ], + ), + ); + } + final media = <_AssetPackage>[]; + + for (var item in value.asSuccess.items) { + item.message.attachments + .where((e) => + (e.type == 'image' || e.type == 'video') && + e.ogScrapeUrl == null) + .forEach((e) { + VideoPlayerController? controller; + if (e.type == 'video') { + var cachedController = controllerCache[e.assetUrl]; + + if (cachedController == null) { + controller = VideoPlayerController.network(e.assetUrl!); + controller.initialize(); + controllerCache[e.assetUrl] = controller; + } else { + controller = cachedController; + } + } + media.add(_AssetPackage(e, item.message, controller)); + }); + } + + return LazyLoadScrollView( + onEndOfPage: () async { + if (nextPageKey != null) { + controller.loadMore(nextPageKey); + } + }, + child: GridView.builder( + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 3), + itemBuilder: (context, position) { + var channel = StreamChannel.of(context).channel; + return Padding( + padding: const EdgeInsets.all(1.0), + child: InkWell( + onTap: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => StreamChannel( + channel: channel, + child: StreamFullScreenMedia( + mediaAttachmentPackages: media + .map( + (e) => StreamAttachmentPackage( + attachment: e.attachment, + message: e.message, + ), + ) + .toList(), + startIndex: position, + userName: media[position].message.user!.name, + onShowMessage: (m, c) async { + final client = + StreamChat.of(context).client; + final message = m; + final channel = client.channel( + c.type, + id: c.id, + ); + if (channel.state == null) { + await channel.watch(); + } + Navigator.pushNamed( + context, + Routes.CHANNEL_PAGE, + arguments: ChannelPageArgs( + channel: channel, + initialMessage: message, + ), + ); + }, + ), + ), + ), + ); + }, + child: media[position].attachment.type == 'image' + ? IgnorePointer( + child: StreamImageAttachment( + attachment: media[position].attachment, + message: media[position].message, + showTitle: false, + size: Size( + MediaQuery.of(context).size.width * 0.8, + MediaQuery.of(context).size.height * 0.3, + ), + messageTheme: widget.messageTheme, + ), + ) + : VideoPlayer(media[position].videoPlayer!), + ), ); }, - child: media[position].attachment.type == 'image' - ? IgnorePointer( - child: StreamImageAttachment( - attachment: media[position].attachment, - message: media[position].message, - showTitle: false, - size: Size( - MediaQuery.of(context).size.width * 0.8, - MediaQuery.of(context).size.height * 0.3, - ), - messageTheme: widget.messageTheme, - ), - ) - : VideoPlayer(media[position].videoPlayer!), + itemCount: media.length, ), ); }, - itemCount: media.length, - ), - ); - }, - stream: messageSearchBloc.messagesStream, + loading: () => Center( + child: const CircularProgressIndicator(), + ), + error: (_) => Offstage(), + ); + }, + ), ); } @override void dispose() { + controller.dispose(); super.dispose(); - for (var c in controllerCache.values) { - c!.dispose(); - } + } + + @override + void initState() { + controller.doInitialLoad(); + super.initState(); } } diff --git a/packages/stream_chat_v1/lib/channel_page.dart b/packages/stream_chat_v1/lib/channel_page.dart index 6cf60e5..e2b2d42 100644 --- a/packages/stream_chat_v1/lib/channel_page.dart +++ b/packages/stream_chat_v1/lib/channel_page.dart @@ -35,7 +35,7 @@ class ChannelPage extends StatefulWidget { class _ChannelPageState extends State { FocusNode? _focusNode; - MessageInputController _messageInputController = MessageInputController(); + StreamMessageInputController _messageInputController = StreamMessageInputController(); @override void initState() { diff --git a/packages/stream_chat_v1/lib/chat_info_screen.dart b/packages/stream_chat_v1/lib/chat_info_screen.dart index 9556840..3860229 100644 --- a/packages/stream_chat_v1/lib/chat_info_screen.dart +++ b/packages/stream_chat_v1/lib/chat_info_screen.dart @@ -218,36 +218,7 @@ class _ChatInfoScreenState extends State { MaterialPageRoute( builder: (context) => StreamChannel( channel: channel, - child: MessageSearchBloc( - child: PinnedMessagesScreen( - messageTheme: widget.messageTheme, - sortOptions: [ - SortOption( - 'created_at', - direction: SortOption.ASC, - ), - ], - onShowMessage: (m, c) async { - final client = StreamChat.of(context).client; - final message = m; - final channel = client.channel( - c.type, - id: c.id, - ); - if (channel.state == null) { - await channel.watch(); - } - Navigator.pushNamed( - context, - Routes.CHANNEL_PAGE, - arguments: ChannelPageArgs( - channel: channel, - initialMessage: message, - ), - ); - }, - ), - ), + child: PinnedMessagesScreen(), ), ), ); @@ -278,35 +249,8 @@ class _ChatInfoScreenState extends State { MaterialPageRoute( builder: (context) => StreamChannel( channel: channel, - child: MessageSearchBloc( - child: ChannelMediaDisplayScreen( - messageTheme: widget.messageTheme, - sortOptions: [ - SortOption( - 'created_at', - direction: SortOption.ASC, - ), - ], - onShowMessage: (m, c) async { - final client = StreamChat.of(context).client; - final message = m; - final channel = client.channel( - c.type, - id: c.id, - ); - if (channel.state == null) { - await channel.watch(); - } - Navigator.pushNamed( - context, - Routes.CHANNEL_PAGE, - arguments: ChannelPageArgs( - channel: channel, - initialMessage: message, - ), - ); - }, - ), + child: ChannelMediaDisplayScreen( + messageTheme: widget.messageTheme, ), ), ), @@ -338,15 +282,8 @@ class _ChatInfoScreenState extends State { MaterialPageRoute( builder: (context) => StreamChannel( channel: channel, - child: MessageSearchBloc( - child: ChannelFileDisplayScreen( - sortOptions: [ - SortOption( - 'created_at', - direction: SortOption.ASC, - ), - ], - ), + child: ChannelFileDisplayScreen( + messageTheme: widget.messageTheme, ), ), ), diff --git a/packages/stream_chat_v1/lib/group_info_screen.dart b/packages/stream_chat_v1/lib/group_info_screen.dart index e650b07..bf5fec2 100644 --- a/packages/stream_chat_v1/lib/group_info_screen.dart +++ b/packages/stream_chat_v1/lib/group_info_screen.dart @@ -40,16 +40,52 @@ class _GroupInfoScreenState extends State { ValueNotifier mutedBool = ValueNotifier(false); + late final channel = StreamChannel.of(context).channel; + + late final userListController = StreamUserListController( + client: StreamChat.of(context).client, + limit: 25, + filter: Filter.and( + [ + if (_searchController!.text.isNotEmpty) + Filter.autoComplete('name', _userNameQuery), + Filter.notIn('id', [ + StreamChat.of(context).currentUser!.id, + ...channel.state!.members + .map(((e) => e.userId)) + .whereType(), + ]), + ], + ), + sort: [ + SortOption( + 'name', + direction: 1, + ), + ], + ); + void _userNameListener() { if (_searchController!.text == _userNameQuery) { return; } if (_debounce?.isActive ?? false) _debounce!.cancel(); _debounce = Timer(const Duration(milliseconds: 350), () { - if (mounted && modalSetStateCallback != null) { - modalSetStateCallback!(() { - _userNameQuery = _searchController!.text; - }); + if (mounted) { + _userNameQuery = _searchController!.text; + userListController.filter = Filter.and( + [ + if (_searchController!.text.isNotEmpty) + Filter.autoComplete('name', _userNameQuery), + Filter.notIn('id', [ + StreamChat.of(context).currentUser!.id, + ...channel.state!.members + .map(((e) => e.userId)) + .whereType(), + ]), + ], + ); + userListController.doInitialLoad(); } }); } @@ -57,25 +93,28 @@ class _GroupInfoScreenState extends State { @override void initState() { super.initState(); - var channel = StreamChannel.of(context); + _nameController = TextEditingController.fromValue( - TextEditingValue( - text: (channel.channel.extraData['name'] as String?) ?? ''), + TextEditingValue(text: (channel.extraData['name'] as String?) ?? ''), ); _searchController = TextEditingController()..addListener(_userNameListener); _nameController!.addListener(() { setState(() {}); }); - mutedBool = ValueNotifier(StreamChannel.of(context).channel.isMuted); + mutedBool = ValueNotifier(channel.isMuted); + } + + @override + void dispose() { + userListController.dispose(); + super.dispose(); } @override Widget build(BuildContext context) { - var channel = StreamChannel.of(context); - return StreamBuilder>( - stream: channel.channel.state!.membersStream, + stream: channel.state!.membersStream, builder: (context, snapshot) { if (!snapshot.hasData) { return Container( @@ -94,7 +133,7 @@ class _GroupInfoScreenState extends State { title: Column( children: [ StreamBuilder( - stream: channel.channelStateStream, + stream: channel.state?.channelStateStream, builder: (context, state) { if (!state.hasData) { return Text( @@ -131,7 +170,7 @@ class _GroupInfoScreenState extends State { height: 3.0, ), Text( - '${channel.channel.memberCount} ${AppLocalizations.of(context).members}, ${snapshot.data?.where((e) => e.user!.online).length ?? 0} ${AppLocalizations.of(context).online}', + '${channel.memberCount} ${AppLocalizations.of(context).members}, ${snapshot.data?.where((e) => e.user!.online).length ?? 0} ${AppLocalizations.of(context).online}', style: TextStyle( color: StreamChatTheme.of(context) .colorTheme @@ -144,7 +183,7 @@ class _GroupInfoScreenState extends State { ), centerTitle: true, actions: [ - if (channel.channel.ownCapabilities + if (channel.ownCapabilities .contains(PermissionType.updateChannelMembers)) StreamNeumorphicButton( child: InkWell( @@ -169,7 +208,7 @@ class _GroupInfoScreenState extends State { height: 8.0, color: StreamChatTheme.of(context).colorTheme.disabled, ), - if (channel.channel.ownCapabilities + if (channel.ownCapabilities .contains(PermissionType.updateChannel)) _buildNameTile(), _buildOptionListTiles(), @@ -336,7 +375,6 @@ class _GroupInfoScreenState extends State { } Widget _buildNameTile() { - var channel = StreamChannel.of(context).channel; var channelName = (channel.extraData['name'] as String?) ?? ''; return Material( @@ -412,7 +450,7 @@ class _GroupInfoScreenState extends State { size: 24.0, ), onTap: () { - StreamChannel.of(context).channel.update({ + channel.update({ 'name': _nameController!.text.trim(), }).catchError((err) { setState(() { @@ -432,8 +470,6 @@ class _GroupInfoScreenState extends State { } Widget _buildOptionListTiles() { - var channel = StreamChannel.of(context); - return Column( children: [ // OptionListTile( @@ -448,10 +484,9 @@ class _GroupInfoScreenState extends State { // ), // onTap: () {}, // ), - if (channel.channel.ownCapabilities - .contains(PermissionType.muteChannel)) + if (channel.ownCapabilities.contains(PermissionType.muteChannel)) StreamBuilder( - stream: StreamChannel.of(context).channel.isMutedStream, + stream: channel.isMutedStream, builder: (context, snapshot) { mutedBool.value = snapshot.data; @@ -482,9 +517,9 @@ class _GroupInfoScreenState extends State { mutedBool.value = val; if (snapshot.data!) { - channel.channel.unmute(); + channel.unmute(); } else { - channel.channel.mute(); + channel.mute(); } }, ); @@ -517,36 +552,7 @@ class _GroupInfoScreenState extends State { MaterialPageRoute( builder: (context) => StreamChannel( channel: channel, - child: MessageSearchBloc( - child: PinnedMessagesScreen( - messageTheme: widget.messageTheme, - sortOptions: [ - SortOption( - 'created_at', - direction: SortOption.ASC, - ), - ], - onShowMessage: (m, c) async { - final client = StreamChat.of(context).client; - final message = m; - final channel = client.channel( - c.type, - id: c.id, - ); - if (channel.state == null) { - await channel.watch(); - } - Navigator.pushNamed( - context, - Routes.CHANNEL_PAGE, - arguments: ChannelPageArgs( - channel: channel, - initialMessage: message, - ), - ); - }, - ), - ), + child: PinnedMessagesScreen(), ), ), ); @@ -578,36 +584,8 @@ class _GroupInfoScreenState extends State { MaterialPageRoute( builder: (context) => StreamChannel( channel: channel, - child: MessageSearchBloc( - child: ChannelMediaDisplayScreen( - messageTheme: widget.messageTheme, - sortOptions: [ - SortOption( - 'created_at', - direction: SortOption.ASC, - ), - ], - paginationParams: PaginationParams(limit: 20), - onShowMessage: (m, c) async { - final client = StreamChat.of(context).client; - final message = m; - final channel = client.channel( - c.type, - id: c.id, - ); - if (channel.state == null) { - await channel.watch(); - } - await Navigator.pushNamed( - context, - Routes.CHANNEL_PAGE, - arguments: ChannelPageArgs( - channel: channel, - initialMessage: message, - ), - ); - }, - ), + child: ChannelMediaDisplayScreen( + messageTheme: widget.messageTheme, ), ), ), @@ -640,25 +618,16 @@ class _GroupInfoScreenState extends State { MaterialPageRoute( builder: (context) => StreamChannel( channel: channel, - child: MessageSearchBloc( - child: ChannelFileDisplayScreen( - sortOptions: [ - SortOption( - 'created_at', - direction: SortOption.ASC, - ), - ], - paginationParams: PaginationParams(limit: 20), - ), + child: ChannelFileDisplayScreen( + messageTheme: widget.messageTheme, ), ), ), ); }, ), - if (!channel.channel.isDistinct && - channel.channel.ownCapabilities - .contains(PermissionType.leaveChannel)) + if (!channel.isDistinct && + channel.ownCapabilities.contains(PermissionType.leaveChannel)) StreamOptionListTile( tileColor: StreamChatTheme.of(context).colorTheme.appBg, separatorColor: StreamChatTheme.of(context).colorTheme.disabled, @@ -703,106 +672,81 @@ class _GroupInfoScreenState extends State { } void _buildAddUserModal(context) { - var channel = StreamChannel.of(context).channel; - final userListController = StreamUserListController( - client: channel.client, - limit: 25, - filter: Filter.and( - [ - if (_searchController!.text.isNotEmpty) - Filter.autoComplete('name', _userNameQuery), - Filter.notIn('id', [ - StreamChat.of(context).currentUser!.id, - ...channel.state!.members - .map(((e) => e.userId)) - .whereType(), - ]), - ], - ), - sort: [ - SortOption( - 'name', - direction: 1, - ), - ], - ); - showDialog( useRootNavigator: false, context: context, barrierColor: StreamChatTheme.of(context).colorTheme.overlay, builder: (context) { - return StatefulBuilder(builder: (context, modalSetState) { - modalSetStateCallback = modalSetState; - return Padding( - padding: EdgeInsets.only(top: 16.0, left: 8.0, right: 8.0), - child: Material( - borderRadius: BorderRadius.only( - topLeft: Radius.circular(16.0), - topRight: Radius.circular(16.0), - ), - clipBehavior: Clip.antiAlias, - child: Scaffold( - body: Column( - children: [ - Padding( - padding: const EdgeInsets.all(16), - child: _buildTextInputSection(modalSetState), - ), - Expanded( - child: StreamUserListView( - controller: userListController, - onUserTap: (user) async { - _searchController!.clear(); + return Padding( + padding: EdgeInsets.only(top: 16.0, left: 8.0, right: 8.0), + child: Material( + borderRadius: BorderRadius.only( + topLeft: Radius.circular(16.0), + topRight: Radius.circular(16.0), + ), + clipBehavior: Clip.antiAlias, + child: Scaffold( + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: _buildTextInputSection(), + ), + Expanded( + child: StreamUserGridView( + controller: userListController, + onUserTap: (user) async { + _searchController!.clear(); - await channel.addMembers([user.id]); - Navigator.pop(context); - setState(() {}); - }, - emptyBuilder: (_) { - return LayoutBuilder( - builder: (context, viewportConstraints) { - return SingleChildScrollView( - physics: AlwaysScrollableScrollPhysics(), - child: ConstrainedBox( - constraints: BoxConstraints( - minHeight: viewportConstraints.maxHeight, - ), - child: Center( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(24), - child: StreamSvgIcon.search( - size: 96, - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), + await channel.addMembers([user.id]); + Navigator.pop(context); + setState(() {}); + }, + emptyBuilder: (_) { + return LayoutBuilder( + builder: (context, viewportConstraints) { + return SingleChildScrollView( + physics: AlwaysScrollableScrollPhysics(), + child: ConstrainedBox( + constraints: BoxConstraints( + minHeight: viewportConstraints.maxHeight, + ), + child: Center( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(24), + child: StreamSvgIcon.search( + size: 96, + color: StreamChatTheme.of(context) + .colorTheme + .textLowEmphasis, ), - Text(AppLocalizations.of(context) - .noUserMatchesTheseKeywords), - ], - ), + ), + Text(AppLocalizations.of(context) + .noUserMatchesTheseKeywords), + ], ), ), - ); - }, - ); - }, - ), + ), + ); + }, + ); + }, ), - ], - ), + ), + ], ), ), - ); - }); + ), + ); }, - ).then((_) => userListController.dispose()); + ).whenComplete(() { + _searchController?.clear(); + }); } - Widget _buildTextInputSection(modalSetState) { + Widget _buildTextInputSection() { final theme = StreamChatTheme.of(context); return Column( children: [ @@ -862,7 +806,6 @@ class _GroupInfoScreenState extends State { } void _showUserInfoModal(User? user, bool isUserAdmin) { - var channel = StreamChannel.of(context).channel; final color = StreamChatTheme.of(context).colorTheme.barsBg; showModalBottomSheet( diff --git a/packages/stream_chat_v1/lib/pinned_messages_screen.dart b/packages/stream_chat_v1/lib/pinned_messages_screen.dart index 6c2eaec..7ab6d98 100644 --- a/packages/stream_chat_v1/lib/pinned_messages_screen.dart +++ b/packages/stream_chat_v1/lib/pinned_messages_screen.dart @@ -1,60 +1,34 @@ import 'package:example/localizations.dart'; +import 'package:example/routes/routes.dart'; import 'package:flutter/material.dart'; import 'package:stream_chat_flutter/stream_chat_flutter.dart'; -import 'package:video_player/video_player.dart'; + +import 'channel_page.dart'; class PinnedMessagesScreen extends StatefulWidget { - /// 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; - - /// 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; - - /// The builder used when the file list is empty. - final WidgetBuilder? emptyBuilder; - - final ShowMessageCallback? onShowMessage; - - final StreamMessageThemeData messageTheme; - - const PinnedMessagesScreen({ - required this.messageTheme, - this.sortOptions, - this.paginationParams = const PaginationParams(limit: 20), - this.emptyBuilder, - this.onShowMessage, - }); - @override - _PinnedMessagesScreenState createState() => _PinnedMessagesScreenState(); + State createState() => _PinnedMessagesScreenState(); } class _PinnedMessagesScreenState extends State { - Map controllerCache = {}; - - @override - void initState() { - super.initState(); - final messageSearchBloc = MessageSearchBloc.of(context); - messageSearchBloc.search( - filter: Filter.in_( - 'cid', - [StreamChannel.of(context).channel.cid!], + late final controller = StreamMessageSearchListController( + client: StreamChat.of(context).client, + filter: Filter.in_( + 'cid', + [StreamChannel.of(context).channel.cid!], + ), + messageFilter: Filter.equal( + 'pinned', + true, + ), + sort: [ + SortOption( + 'created_at', + direction: SortOption.ASC, ), - messageFilter: Filter.equal( - 'pinned', - true, - ), - sort: widget.sortOptions, - pagination: widget.paginationParams, - ); - } + ], + limit: 20, + ); @override Widget build(BuildContext context) { @@ -73,25 +47,9 @@ class _PinnedMessagesScreenState extends State { leading: StreamBackButton(), backgroundColor: StreamChatTheme.of(context).colorTheme.barsBg, ), - body: _buildMediaGrid(), - ); - } - - Widget _buildMediaGrid() { - final messageSearchBloc = MessageSearchBloc.of(context); - - return StreamBuilder>( - builder: (context, snapshot) { - if (snapshot.data == null) { - return Center( - child: const CircularProgressIndicator(), - ); - } - - if (snapshot.data!.isEmpty) { - if (widget.emptyBuilder != null) { - return widget.emptyBuilder!(context); - } + body: StreamMessageSearchListView( + controller: controller, + emptyBuilder: (_) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, @@ -140,74 +98,33 @@ class _PinnedMessagesScreenState extends State { ], ), ); - } - - var data = snapshot.data ?? []; - - return LazyLoadScrollView( - onEndOfPage: () => messageSearchBloc.search( - filter: Filter.in_( - 'cid', - [StreamChannel.of(context).channel.cid!], + }, + onMessageTap: (messageResponse) async { + final client = StreamChat.of(context).client; + final message = messageResponse.message; + final channel = client.channel( + messageResponse.channel!.type, + id: messageResponse.channel!.id, + ); + if (channel.state == null) { + await channel.watch(); + } + Navigator.pushNamed( + context, + Routes.CHANNEL_PAGE, + arguments: ChannelPageArgs( + channel: channel, + initialMessage: message, ), - messageFilter: Filter.equal( - 'pinned', - true, - ), - sort: widget.sortOptions, - pagination: widget.paginationParams.copyWith( - offset: messageSearchBloc.messageResponses?.length ?? 0, - ), - ), - child: ListView.builder( - itemBuilder: (context, position) { - var user = data[position].message.user!; - var attachments = data[position].message.attachments; - var text = data[position].message.text ?? ''; - - return ListTile( - leading: StreamUserAvatar( - user: user, - constraints: BoxConstraints.tightFor( - width: 40.0, - height: 40.0, - ), - borderRadius: BorderRadius.circular(28), - ), - title: Text( - user.name, - style: TextStyle( - color: StreamChatTheme.of(context) - .colorTheme - .textHighEmphasis, - fontWeight: FontWeight.bold), - ), - subtitle: Text( - text != '' - ? text - : (attachments.isNotEmpty - ? '${attachments.length} ${attachments.length > 1 ? AppLocalizations.of(context).attachments : AppLocalizations.of(context).attachment}' - : ''), - ), - onTap: () { - widget.onShowMessage?.call(data[position].message, - StreamChannel.of(context).channel); - }, - ); - }, - itemCount: snapshot.data!.length, - ), - ); - }, - stream: messageSearchBloc.messagesStream, + ); + }, + ), ); } @override void dispose() { + controller.dispose(); super.dispose(); - for (var c in controllerCache.values) { - c!.dispose(); - } } } diff --git a/packages/stream_chat_v1/lib/thread_page.dart b/packages/stream_chat_v1/lib/thread_page.dart index 6ffd5e3..3983cb7 100644 --- a/packages/stream_chat_v1/lib/thread_page.dart +++ b/packages/stream_chat_v1/lib/thread_page.dart @@ -19,12 +19,13 @@ class ThreadPage extends StatefulWidget { class _ThreadPageState extends State { FocusNode _focusNode = FocusNode(); - late MessageInputController _messageInputController; + late StreamMessageInputController _messageInputController; @override void initState() { super.initState(); - _messageInputController = MessageInputController(message: widget.parent); + _messageInputController = + StreamMessageInputController(message: widget.parent); } @override