From 0a4f7c9d265125641a5828ba5c7e10c2bcc97fb4 Mon Sep 17 00:00:00 2001 From: TheOneWithTheBraid Date: Sun, 25 Dec 2022 13:08:51 +0100 Subject: [PATCH] fix: minor issues in room list - allow to discard focus of search field - properly circle the search field's progress indicator - always keep search sections visible in order to workaround annoying behavior: When quickly searching for a chat and one is fast at clicking on a room, it often happens that server side results just drop in at this moment and one clicks at the wrong item -> with a static height as now set, this no longer happens. Signed-off-by: TheOneWithTheBraid --- assets/l10n/intl_en.arb | 1 + lib/pages/chat_list/chat_list.dart | 16 +-- lib/pages/chat_list/chat_list_body.dart | 113 ++++++++++++---------- lib/pages/chat_list/chat_list_header.dart | 14 ++- lib/pages/chat_list/chat_list_view.dart | 57 ++++++----- lib/pages/chat_list/stories_header.dart | 6 +- 6 files changed, 115 insertions(+), 92 deletions(-) diff --git a/assets/l10n/intl_en.arb b/assets/l10n/intl_en.arb index c5c0579e..6f7da6d7 100644 --- a/assets/l10n/intl_en.arb +++ b/assets/l10n/intl_en.arb @@ -1450,6 +1450,7 @@ "type": "text", "placeholders": {} }, + "noSearchResult": "No matching search results.", "moderator": "Moderator", "@moderator": { "type": "text", diff --git a/lib/pages/chat_list/chat_list.dart b/lib/pages/chat_list/chat_list.dart index 1473f7bd..3465e6b2 100644 --- a/lib/pages/chat_list/chat_list.dart +++ b/lib/pages/chat_list/chat_list.dart @@ -55,6 +55,7 @@ enum ActiveFilter { class ChatList extends StatefulWidget { static BuildContext? contextForVoip; + const ChatList({Key? key}) : super(key: key); @override @@ -235,12 +236,15 @@ class ChatListController extends State _coolDown = Timer(const Duration(milliseconds: 500), _search); } - void cancelSearch() => setState(() { - searchController.clear(); - isSearchMode = false; - roomSearchResult = userSearchResult = null; - isSearching = false; - }); + void cancelSearch() { + setState(() { + searchController.clear(); + isSearchMode = false; + roomSearchResult = userSearchResult = null; + isSearching = false; + }); + FocusManager.instance.primaryFocus?.unfocus(); + } bool isTorBrowser = false; diff --git a/lib/pages/chat_list/chat_list_body.dart b/lib/pages/chat_list/chat_list_body.dart index 05dd24c4..922036f0 100644 --- a/lib/pages/chat_list/chat_list_body.dart +++ b/lib/pages/chat_list/chat_list_body.dart @@ -73,75 +73,81 @@ class ChatListViewBody extends StatelessWidget { return Column( mainAxisSize: MainAxisSize.min, children: [ - if (roomSearchResult != null) ...[ + if (controller.isSearchMode) ...[ SearchTitle( title: L10n.of(context)!.publicRooms, icon: const Icon(Icons.explore_outlined), ), - AnimatedContainer( - height: roomSearchResult.chunk.isEmpty ? 0 : 106, - duration: const Duration(milliseconds: 250), - clipBehavior: Clip.hardEdge, - decoration: const BoxDecoration(), - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: roomSearchResult.chunk.length, - itemBuilder: (context, i) => _SearchItem( - title: roomSearchResult.chunk[i].name ?? - roomSearchResult - .chunk[i].canonicalAlias?.localpart ?? - L10n.of(context)!.group, - avatar: roomSearchResult.chunk[i].avatarUrl, - onPressed: () => showModalBottomSheet( - context: context, - builder: (c) => PublicRoomBottomSheet( - roomAlias: roomSearchResult - .chunk[i].canonicalAlias ?? - roomSearchResult.chunk[i].roomId, - outerContext: context, - chunk: roomSearchResult.chunk[i], + SizedBox( + height: 106, + child: roomSearchResult == null || + roomSearchResult.chunk.isEmpty + ? Center( + child: + Text(L10n.of(context)!.noSearchResult), + ) + : ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: roomSearchResult.chunk.length, + itemBuilder: (context, i) => _SearchItem( + title: roomSearchResult.chunk[i].name ?? + roomSearchResult.chunk[i] + .canonicalAlias?.localpart ?? + L10n.of(context)!.group, + avatar: + roomSearchResult.chunk[i].avatarUrl, + onPressed: () => showModalBottomSheet( + context: context, + builder: (c) => PublicRoomBottomSheet( + roomAlias: roomSearchResult + .chunk[i].canonicalAlias ?? + roomSearchResult.chunk[i].roomId, + outerContext: context, + chunk: roomSearchResult.chunk[i], + ), + ), + ), ), - ), - ), - ), ), - ], - if (userSearchResult != null) ...[ SearchTitle( title: L10n.of(context)!.users, icon: const Icon(Icons.group_outlined), ), - AnimatedContainer( - height: userSearchResult.results.isEmpty ? 0 : 106, - duration: const Duration(milliseconds: 250), - clipBehavior: Clip.hardEdge, - decoration: const BoxDecoration(), - child: ListView.builder( - scrollDirection: Axis.horizontal, - itemCount: userSearchResult.results.length, - itemBuilder: (context, i) => _SearchItem( - title: - userSearchResult.results[i].displayName ?? - userSearchResult - .results[i].userId.localpart ?? - L10n.of(context)!.unknownDevice, - avatar: userSearchResult.results[i].avatarUrl, - onPressed: () => showModalBottomSheet( - context: context, - builder: (c) => ProfileBottomSheet( - userId: userSearchResult.results[i].userId, - outerContext: context, + SizedBox( + height: 106, + child: userSearchResult == null || + userSearchResult.results.isEmpty + ? Center( + child: + Text(L10n.of(context)!.noSearchResult), + ) + : ListView.builder( + scrollDirection: Axis.horizontal, + itemCount: userSearchResult.results.length, + itemBuilder: (context, i) => _SearchItem( + title: userSearchResult + .results[i].displayName ?? + userSearchResult + .results[i].userId.localpart ?? + L10n.of(context)!.unknownDevice, + avatar: + userSearchResult.results[i].avatarUrl, + onPressed: () => showModalBottomSheet( + context: context, + builder: (c) => ProfileBottomSheet( + userId: userSearchResult + .results[i].userId, + outerContext: context, + ), + ), + ), ), - ), - ), - ), ), - ], - if (controller.isSearchMode) SearchTitle( title: L10n.of(context)!.stories, icon: const Icon(Icons.camera_alt_outlined), ), + ], if (displayStoriesHeader) StoriesHeader( key: const Key('stories_header'), @@ -319,6 +325,7 @@ class _SearchItem extends StatelessWidget { title, maxLines: 2, textAlign: TextAlign.center, + overflow: TextOverflow.ellipsis, style: const TextStyle( fontSize: 12, ), diff --git a/lib/pages/chat_list/chat_list_header.dart b/lib/pages/chat_list/chat_list_header.dart index d9edbf33..fb1e1950 100644 --- a/lib/pages/chat_list/chat_list_header.dart +++ b/lib/pages/chat_list/chat_list_header.dart @@ -67,8 +67,18 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget { ), suffixIcon: controller.isSearchMode ? controller.isSearching - ? const CircularProgressIndicator.adaptive( - strokeWidth: 2, + ? const Align( + alignment: Alignment.centerRight, + child: Padding( + padding: EdgeInsets.symmetric( + vertical: 8.0, horizontal: 12), + child: SizedBox.square( + dimension: 24, + child: CircularProgressIndicator.adaptive( + strokeWidth: 2, + ), + ), + ), ) : TextButton( onPressed: controller.setServer, diff --git a/lib/pages/chat_list/chat_list_view.dart b/lib/pages/chat_list/chat_list_view.dart index 806c5e93..86850017 100644 --- a/lib/pages/chat_list/chat_list_view.dart +++ b/lib/pages/chat_list/chat_list_view.dart @@ -222,32 +222,37 @@ class ChatListView extends StatelessWidget { ), ], Expanded( - child: Scaffold( - appBar: ChatListHeader(controller: controller), - body: ChatListViewBody(controller), - bottomNavigationBar: controller.displayNavigationBar - ? NavigationBar( - height: 64, - selectedIndex: controller.selectedIndex, - onDestinationSelected: - controller.onDestinationSelected, - destinations: getNavigationDestinations(context), - ) - : null, - floatingActionButton: controller.filteredRooms.isNotEmpty && - selectMode == SelectMode.normal - ? KeyBoardShortcuts( - keysToPress: { - LogicalKeyboardKey.controlLeft, - LogicalKeyboardKey.keyN - }, - onKeysPressed: () => - VRouter.of(context).to('/newprivatechat'), - helpLabel: L10n.of(context)!.newChat, - child: StartChatFloatingActionButton( - controller: controller), - ) - : null, + child: GestureDetector( + onTap: FocusManager.instance.primaryFocus?.unfocus, + excludeFromSemantics: true, + behavior: HitTestBehavior.translucent, + child: Scaffold( + appBar: ChatListHeader(controller: controller), + body: ChatListViewBody(controller), + bottomNavigationBar: controller.displayNavigationBar + ? NavigationBar( + height: 64, + selectedIndex: controller.selectedIndex, + onDestinationSelected: + controller.onDestinationSelected, + destinations: getNavigationDestinations(context), + ) + : null, + floatingActionButton: controller.filteredRooms.isNotEmpty && + selectMode == SelectMode.normal + ? KeyBoardShortcuts( + keysToPress: { + LogicalKeyboardKey.controlLeft, + LogicalKeyboardKey.keyN + }, + onKeysPressed: () => + VRouter.of(context).to('/newprivatechat'), + helpLabel: L10n.of(context)!.newChat, + child: StartChatFloatingActionButton( + controller: controller), + ) + : null, + ), ), ), ], diff --git a/lib/pages/chat_list/stories_header.dart b/lib/pages/chat_list/stories_header.dart index 32fd103d..f07162bc 100644 --- a/lib/pages/chat_list/stories_header.dart +++ b/lib/pages/chat_list/stories_header.dart @@ -19,6 +19,7 @@ enum ContextualRoomAction { class StoriesHeader extends StatelessWidget { final String filter; + const StoriesHeader({required this.filter, Key? key}) : super(key: key); void _addToStoryAction(BuildContext context) => @@ -100,11 +101,6 @@ class StoriesHeader extends StatelessWidget { onTap: () => _addToStoryAction(context), ); } - if (client.storiesRooms.isEmpty || - !client.storiesRooms.any((room) => - room.displayname.toLowerCase().contains(filter.toLowerCase()))) { - return Container(); - } final ownStoryRoom = client.storiesRooms .firstWhereOrNull((r) => r.creatorId == client.userID); final stories = [