From 18e69cd4bd7ba415d7ab52be504adb597f98318d Mon Sep 17 00:00:00 2001 From: Salvatore Giordano Date: Thu, 28 Apr 2022 14:53:00 +0200 Subject: [PATCH] update lists --- .../ios/Runner.xcodeproj/project.pbxproj | 2 + packages/stream_chat_v1/lib/channel_list.dart | 86 ++++++---- .../stream_chat_v1/lib/group_info_screen.dart | 136 +++++++-------- .../stream_chat_v1/lib/new_chat_screen.dart | 53 ++++-- .../lib/new_group_chat_screen.dart | 146 ++++++++-------- .../lib/user_mentions_page.dart | 157 +++++++++--------- 6 files changed, 317 insertions(+), 263 deletions(-) diff --git a/packages/stream_chat_v1/ios/Runner.xcodeproj/project.pbxproj b/packages/stream_chat_v1/ios/Runner.xcodeproj/project.pbxproj index 925a689..5b28f53 100644 --- a/packages/stream_chat_v1/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/stream_chat_v1/ios/Runner.xcodeproj/project.pbxproj @@ -264,6 +264,7 @@ "${BUILT_PRODUCTS_DIR}/sqlite3/sqlite3.framework", "${BUILT_PRODUCTS_DIR}/sqlite3_flutter_libs/sqlite3_flutter_libs.framework", "${BUILT_PRODUCTS_DIR}/url_launcher_ios/url_launcher_ios.framework", + "${BUILT_PRODUCTS_DIR}/video_compress/video_compress.framework", "${BUILT_PRODUCTS_DIR}/video_player_avfoundation/video_player_avfoundation.framework", "${BUILT_PRODUCTS_DIR}/video_thumbnail/video_thumbnail.framework", "${BUILT_PRODUCTS_DIR}/wakelock/wakelock.framework", @@ -292,6 +293,7 @@ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/sqlite3_flutter_libs.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/url_launcher_ios.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_compress.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_player_avfoundation.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/video_thumbnail.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/wakelock.framework", diff --git a/packages/stream_chat_v1/lib/channel_list.dart b/packages/stream_chat_v1/lib/channel_list.dart index 25813f0..5c8164e 100644 --- a/packages/stream_chat_v1/lib/channel_list.dart +++ b/packages/stream_chat_v1/lib/channel_list.dart @@ -20,9 +20,21 @@ class ChannelList extends StatefulWidget { class _ChannelList extends State { ScrollController _scrollController = ScrollController(); - TextEditingController? _controller; + late StreamMessageSearchListController _messageSearchListController = + StreamMessageSearchListController( + client: StreamChat.of(context).client, + filter: Filter.in_('members', [StreamChat.of(context).currentUser!.id]), + limit: 5, + searchQuery: '', + sort: [ + SortOption( + 'created_at', + direction: SortOption.ASC, + ), + ], + ); - String _channelQuery = ''; + TextEditingController? _controller; bool _isSearchActive = false; @@ -32,10 +44,11 @@ class _ChannelList extends State { if (_debounce?.isActive ?? false) _debounce!.cancel(); _debounce = Timer(const Duration(milliseconds: 350), () { if (mounted) { + _messageSearchListController.searchQuery = _controller!.text; setState(() { - _channelQuery = _controller!.text; - _isSearchActive = _channelQuery.isNotEmpty; + _isSearchActive = _controller!.text.isNotEmpty; }); + if (_isSearchActive) _messageSearchListController.doInitialLoad(); } }); } @@ -61,12 +74,12 @@ class _ChannelList extends State { _controller?.removeListener(_channelQueryListener); _controller?.dispose(); _scrollController.dispose(); + _channelListController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { - final user = StreamChat.of(context).currentUser; return WillPopScope( onWillPop: () async { if (_isSearchActive) { @@ -98,17 +111,7 @@ class _ChannelList extends State { ], body: _isSearchActive ? StreamMessageSearchListView( - showErrorTile: true, - messageQuery: _channelQuery, - filters: Filter.in_('members', [user!.id]), - sortOptions: [ - SortOption( - 'created_at', - direction: SortOption.ASC, - ), - ], - pullToRefresh: false, - limit: 30, + controller: _messageSearchListController, emptyBuilder: (_) { return LayoutBuilder( builder: (context, viewportConstraints) { @@ -139,24 +142,34 @@ class _ChannelList extends State { }, ); }, - onItemTap: (messageResponse) async { - FocusScope.of(context).requestFocus(FocusNode()); - 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, - ), + itemBuilder: ( + context, + messageResponses, + index, + defaultWidget, + ) { + return defaultWidget.copyWith( + onTap: () async { + final messageResponse = messageResponses[index]; + FocusScope.of(context).requestFocus(FocusNode()); + 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, + ), + ); + }, ); }, ) @@ -166,9 +179,10 @@ class _ChannelList extends State { onRefresh: _channelListController.refresh, child: StreamChannelListView( controller: _channelListController, - itemBuilder: (context, channel, tile) { + itemBuilder: (context, channels, index, defaultWidget) { final chatTheme = StreamChatTheme.of(context); final backgroundColor = chatTheme.colorTheme.inputBg; + final channel = channels[index]; final canDeleteChannel = channel.ownCapabilities .contains(PermissionType.deleteChannel); return Slidable( @@ -249,7 +263,7 @@ class _ChannelList extends State { ), ], ), - child: tile, + child: defaultWidget, ); }, onChannelTap: (channel) { diff --git a/packages/stream_chat_v1/lib/group_info_screen.dart b/packages/stream_chat_v1/lib/group_info_screen.dart index ba70916..e650b07 100644 --- a/packages/stream_chat_v1/lib/group_info_screen.dart +++ b/packages/stream_chat_v1/lib/group_info_screen.dart @@ -704,6 +704,28 @@ 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, @@ -721,85 +743,63 @@ class _GroupInfoScreenState extends State { ), clipBehavior: Clip.antiAlias, child: Scaffold( - body: UsersBloc( - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(16), - child: _buildTextInputSection(modalSetState), - ), - Expanded( - child: StreamUserListView( - selectedUsers: {}, - onUserTap: (user, _) async { - _searchController!.clear(); + body: Column( + children: [ + Padding( + padding: const EdgeInsets.all(16), + child: _buildTextInputSection(modalSetState), + ), + Expanded( + child: StreamUserListView( + controller: userListController, + onUserTap: (user) async { + _searchController!.clear(); - await channel.addMembers([user.id]); - Navigator.pop(context); - setState(() {}); - }, - crossAxisCount: 4, - 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, - ), - ], - 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()); } Widget _buildTextInputSection(modalSetState) { diff --git a/packages/stream_chat_v1/lib/new_chat_screen.dart b/packages/stream_chat_v1/lib/new_chat_screen.dart index ffd9554..1d57d29 100644 --- a/packages/stream_chat_v1/lib/new_chat_screen.dart +++ b/packages/stream_chat_v1/lib/new_chat_screen.dart @@ -19,6 +19,20 @@ class _NewChatScreenState extends State { late TextEditingController _controller; + late final userListController = StreamUserListController( + client: StreamChat.of(context).client, + limit: 25, + filter: Filter.and([ + Filter.notEqual('id', StreamChat.of(context).currentUser!.id), + ]), + sort: [ + SortOption( + 'name', + direction: 1, + ), + ], + ); + ChipInputTextFieldState? get _chipInputTextFieldState => _chipInputTextFieldStateKey.currentState; @@ -45,6 +59,12 @@ class _NewChatScreenState extends State { _userNameQuery = _controller.text; _isSearchActive = _userNameQuery.isNotEmpty; }); + userListController.filter = Filter.and([ + if (_userNameQuery.isNotEmpty) + Filter.autoComplete('name', _userNameQuery), + Filter.notEqual('id', StreamChat.of(context).currentUser!.id), + ]); + userListController.doInitialLoad(); }); } @@ -112,6 +132,7 @@ class _NewChatScreenState extends State { _controller.clear(); _controller.removeListener(_userNameListener); _controller.dispose(); + userListController.dispose(); super.dispose(); } @@ -289,10 +310,10 @@ class _NewChatScreenState extends State { onPanDown: (_) => FocusScope.of(context).unfocus(), child: UsersBloc( child: StreamUserListView( - selectedUsers: _selectedUsers, - groupAlphabetically: - _isSearchActive ? false : true, - onUserTap: (user, _) { + controller: userListController, + // groupAlphabetically: + // _isSearchActive ? false : true, + onUserTap: (user) { _controller.clear(); if (!_selectedUsers.contains(user)) { _chipInputTextFieldState @@ -302,19 +323,17 @@ class _NewChatScreenState extends State { _chipInputTextFieldState!.removeItem(user); } }, - limit: 25, - filter: Filter.and([ - if (_userNameQuery.isNotEmpty) - Filter.autoComplete('name', _userNameQuery), - Filter.notEqual('id', - StreamChat.of(context).currentUser!.id), - ]), - sort: [ - SortOption( - 'name', - direction: 1, - ), - ], + itemBuilder: ( + context, + users, + index, + defaultWidget, + ) { + return defaultWidget.copyWith( + selected: + _selectedUsers.contains(users[index]), + ); + }, emptyBuilder: (_) { return LayoutBuilder( builder: (context, viewportConstraints) { diff --git a/packages/stream_chat_v1/lib/new_group_chat_screen.dart b/packages/stream_chat_v1/lib/new_group_chat_screen.dart index a2a2ec5..ff0c175 100644 --- a/packages/stream_chat_v1/lib/new_group_chat_screen.dart +++ b/packages/stream_chat_v1/lib/new_group_chat_screen.dart @@ -23,6 +23,20 @@ class _NewGroupChatScreenState extends State { Timer? _debounce; + late final userListController = StreamUserListController( + client: StreamChat.of(context).client, + sort: [ + SortOption( + 'name', + direction: 1, + ), + ], + limit: 25, + filter: Filter.and([ + Filter.notEqual('id', StreamChat.of(context).currentUser!.id), + ]), + ); + void _userNameListener() { if (_debounce?.isActive ?? false) _debounce!.cancel(); _debounce = Timer(const Duration(milliseconds: 350), () { @@ -31,6 +45,12 @@ class _NewGroupChatScreenState extends State { _userNameQuery = _controller!.text; _isSearchActive = _userNameQuery.isNotEmpty; }); + userListController.filter = Filter.and([ + if (_userNameQuery.isNotEmpty) + Filter.autoComplete('name', _userNameQuery), + Filter.notEqual('id', StreamChat.of(context).currentUser!.id), + ]); + userListController.doInitialLoad(); } }); } @@ -46,6 +66,7 @@ class _NewGroupChatScreenState extends State { _controller?.clear(); _controller?.removeListener(_userNameListener); _controller?.dispose(); + userListController.dispose(); super.dispose(); } @@ -228,77 +249,66 @@ class _NewGroupChatScreenState extends State { body: GestureDetector( behavior: HitTestBehavior.opaque, onPanDown: (_) => FocusScope.of(context).unfocus(), - child: UsersBloc( - child: StreamUserListView( - selectedUsers: _selectedUsers, - pullToRefresh: false, - groupAlphabetically: _isSearchActive ? false : true, - onUserTap: (user, _) { - if (!_selectedUsers.contains(user)) { - setState(() { - _selectedUsers.add(user); - }); - } else { - setState(() { - _selectedUsers.remove(user); - }); - } - }, - limit: 25, - filter: Filter.and([ - if (_userNameQuery.isNotEmpty) - Filter.autoComplete('name', _userNameQuery), - Filter.notEqual( - 'id', StreamChat.of(context).currentUser!.id), - ]), - sort: [ - SortOption( - 'name', - direction: 1, - ), - ], - 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, - ), + child: StreamUserListView( + controller: userListController, + // groupAlphabetically: _isSearchActive ? false : true, + itemBuilder: (context, items, index, defaultWidget) { + return defaultWidget.copyWith( + selected: _selectedUsers.contains(items[index]), + ); + }, + onUserTap: (user) { + if (!_selectedUsers.contains(user)) { + setState(() { + _selectedUsers.add(user); + }); + } else { + setState(() { + _selectedUsers.remove(user); + }); + } + }, + 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, - style: StreamChatTheme.of(context) - .textTheme - .footnote - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - ), - ], - ), + ), + Text( + AppLocalizations.of(context) + .noUserMatchesTheseKeywords, + style: StreamChatTheme.of(context) + .textTheme + .footnote + .copyWith( + color: StreamChatTheme.of(context) + .colorTheme + .textLowEmphasis, + ), + ), + ], ), ), - ); - }, - ); - }, - ), + ), + ); + }, + ); + }, ), ), ), diff --git a/packages/stream_chat_v1/lib/user_mentions_page.dart b/packages/stream_chat_v1/lib/user_mentions_page.dart index 4d266a1..3dcbab6 100644 --- a/packages/stream_chat_v1/lib/user_mentions_page.dart +++ b/packages/stream_chat_v1/lib/user_mentions_page.dart @@ -5,85 +5,94 @@ import 'package:stream_chat_flutter/stream_chat_flutter.dart'; import 'channel_page.dart'; -class UserMentionsPage extends StatelessWidget { +class UserMentionsPage extends StatefulWidget { + @override + State createState() => _UserMentionsPageState(); +} + +class _UserMentionsPageState extends State { + late final controller = StreamMessageSearchListController( + client: StreamChat.of(context).client, + filter: Filter.in_('members', [StreamChat.of(context).currentUser!.id]), + messageFilter: Filter.custom( + operator: r'$contains', + key: 'mentioned_users.id', + value: StreamChat.of(context).currentUser!.id, + ), + sort: [ + SortOption( + 'created_at', + direction: SortOption.ASC, + ), + ], + limit: 20, + ); @override Widget build(BuildContext context) { - final user = StreamChat.of(context).currentUser!; - return MessageSearchBloc( - child: StreamMessageSearchListView( - filters: Filter.in_('members', [user.id]), - messageFilters: Filter.custom( - operator: r'$contains', - key: 'mentioned_users.id', - value: user.id, - ), - sortOptions: [ - SortOption( - 'created_at', - direction: SortOption.ASC, - ), - ], - limit: 20, - showResultCount: false, - 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.mentions( - size: 96, - color: - StreamChatTheme.of(context).colorTheme.disabled, - ), + return StreamMessageSearchListView( + controller: controller, + 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.mentions( + size: 96, + color: + StreamChatTheme.of(context).colorTheme.disabled, ), - Text( - AppLocalizations.of(context).noMentionsExistYet, - style: StreamChatTheme.of(context) - .textTheme - .body - .copyWith( - color: StreamChatTheme.of(context) - .colorTheme - .textLowEmphasis, - ), - ), - ], - ), + ), + Text( + AppLocalizations.of(context).noMentionsExistYet, + style: + StreamChatTheme.of(context).textTheme.body.copyWith( + color: StreamChatTheme.of(context) + .colorTheme + .textLowEmphasis, + ), + ), + ], ), ), - ); - }, - ); - }, - onItemTap: (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, - ), - ); - }, - ), + ), + ); + }, + ); + }, + 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, + ), + ); + }, ); } + + @override + void dispose() { + controller.dispose(); + super.dispose(); + } }