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 <the-one@with-the-braid.cf>
This commit is contained in:
TheOneWithTheBraid 2022-12-25 13:08:51 +01:00
parent f9e4b9356a
commit 0a4f7c9d26
6 changed files with 115 additions and 92 deletions

View File

@ -1450,6 +1450,7 @@
"type": "text", "type": "text",
"placeholders": {} "placeholders": {}
}, },
"noSearchResult": "No matching search results.",
"moderator": "Moderator", "moderator": "Moderator",
"@moderator": { "@moderator": {
"type": "text", "type": "text",

View File

@ -55,6 +55,7 @@ enum ActiveFilter {
class ChatList extends StatefulWidget { class ChatList extends StatefulWidget {
static BuildContext? contextForVoip; static BuildContext? contextForVoip;
const ChatList({Key? key}) : super(key: key); const ChatList({Key? key}) : super(key: key);
@override @override
@ -235,12 +236,15 @@ class ChatListController extends State<ChatList>
_coolDown = Timer(const Duration(milliseconds: 500), _search); _coolDown = Timer(const Duration(milliseconds: 500), _search);
} }
void cancelSearch() => setState(() { void cancelSearch() {
setState(() {
searchController.clear(); searchController.clear();
isSearchMode = false; isSearchMode = false;
roomSearchResult = userSearchResult = null; roomSearchResult = userSearchResult = null;
isSearching = false; isSearching = false;
}); });
FocusManager.instance.primaryFocus?.unfocus();
}
bool isTorBrowser = false; bool isTorBrowser = false;

View File

@ -73,25 +73,29 @@ class ChatListViewBody extends StatelessWidget {
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [
if (roomSearchResult != null) ...[ if (controller.isSearchMode) ...[
SearchTitle( SearchTitle(
title: L10n.of(context)!.publicRooms, title: L10n.of(context)!.publicRooms,
icon: const Icon(Icons.explore_outlined), icon: const Icon(Icons.explore_outlined),
), ),
AnimatedContainer( SizedBox(
height: roomSearchResult.chunk.isEmpty ? 0 : 106, height: 106,
duration: const Duration(milliseconds: 250), child: roomSearchResult == null ||
clipBehavior: Clip.hardEdge, roomSearchResult.chunk.isEmpty
decoration: const BoxDecoration(), ? Center(
child: ListView.builder( child:
Text(L10n.of(context)!.noSearchResult),
)
: ListView.builder(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: roomSearchResult.chunk.length, itemCount: roomSearchResult.chunk.length,
itemBuilder: (context, i) => _SearchItem( itemBuilder: (context, i) => _SearchItem(
title: roomSearchResult.chunk[i].name ?? title: roomSearchResult.chunk[i].name ??
roomSearchResult roomSearchResult.chunk[i]
.chunk[i].canonicalAlias?.localpart ?? .canonicalAlias?.localpart ??
L10n.of(context)!.group, L10n.of(context)!.group,
avatar: roomSearchResult.chunk[i].avatarUrl, avatar:
roomSearchResult.chunk[i].avatarUrl,
onPressed: () => showModalBottomSheet( onPressed: () => showModalBottomSheet(
context: context, context: context,
builder: (c) => PublicRoomBottomSheet( builder: (c) => PublicRoomBottomSheet(
@ -105,43 +109,45 @@ class ChatListViewBody extends StatelessWidget {
), ),
), ),
), ),
],
if (userSearchResult != null) ...[
SearchTitle( SearchTitle(
title: L10n.of(context)!.users, title: L10n.of(context)!.users,
icon: const Icon(Icons.group_outlined), icon: const Icon(Icons.group_outlined),
), ),
AnimatedContainer( SizedBox(
height: userSearchResult.results.isEmpty ? 0 : 106, height: 106,
duration: const Duration(milliseconds: 250), child: userSearchResult == null ||
clipBehavior: Clip.hardEdge, userSearchResult.results.isEmpty
decoration: const BoxDecoration(), ? Center(
child: ListView.builder( child:
Text(L10n.of(context)!.noSearchResult),
)
: ListView.builder(
scrollDirection: Axis.horizontal, scrollDirection: Axis.horizontal,
itemCount: userSearchResult.results.length, itemCount: userSearchResult.results.length,
itemBuilder: (context, i) => _SearchItem( itemBuilder: (context, i) => _SearchItem(
title: title: userSearchResult
userSearchResult.results[i].displayName ?? .results[i].displayName ??
userSearchResult userSearchResult
.results[i].userId.localpart ?? .results[i].userId.localpart ??
L10n.of(context)!.unknownDevice, L10n.of(context)!.unknownDevice,
avatar: userSearchResult.results[i].avatarUrl, avatar:
userSearchResult.results[i].avatarUrl,
onPressed: () => showModalBottomSheet( onPressed: () => showModalBottomSheet(
context: context, context: context,
builder: (c) => ProfileBottomSheet( builder: (c) => ProfileBottomSheet(
userId: userSearchResult.results[i].userId, userId: userSearchResult
.results[i].userId,
outerContext: context, outerContext: context,
), ),
), ),
), ),
), ),
), ),
],
if (controller.isSearchMode)
SearchTitle( SearchTitle(
title: L10n.of(context)!.stories, title: L10n.of(context)!.stories,
icon: const Icon(Icons.camera_alt_outlined), icon: const Icon(Icons.camera_alt_outlined),
), ),
],
if (displayStoriesHeader) if (displayStoriesHeader)
StoriesHeader( StoriesHeader(
key: const Key('stories_header'), key: const Key('stories_header'),
@ -319,6 +325,7 @@ class _SearchItem extends StatelessWidget {
title, title,
maxLines: 2, maxLines: 2,
textAlign: TextAlign.center, textAlign: TextAlign.center,
overflow: TextOverflow.ellipsis,
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontSize: 12,
), ),

View File

@ -67,8 +67,18 @@ class ChatListHeader extends StatelessWidget implements PreferredSizeWidget {
), ),
suffixIcon: controller.isSearchMode suffixIcon: controller.isSearchMode
? controller.isSearching ? controller.isSearching
? const CircularProgressIndicator.adaptive( ? 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, strokeWidth: 2,
),
),
),
) )
: TextButton( : TextButton(
onPressed: controller.setServer, onPressed: controller.setServer,

View File

@ -222,6 +222,10 @@ class ChatListView extends StatelessWidget {
), ),
], ],
Expanded( Expanded(
child: GestureDetector(
onTap: FocusManager.instance.primaryFocus?.unfocus,
excludeFromSemantics: true,
behavior: HitTestBehavior.translucent,
child: Scaffold( child: Scaffold(
appBar: ChatListHeader(controller: controller), appBar: ChatListHeader(controller: controller),
body: ChatListViewBody(controller), body: ChatListViewBody(controller),
@ -250,6 +254,7 @@ class ChatListView extends StatelessWidget {
: null, : null,
), ),
), ),
),
], ],
), ),
); );

View File

@ -19,6 +19,7 @@ enum ContextualRoomAction {
class StoriesHeader extends StatelessWidget { class StoriesHeader extends StatelessWidget {
final String filter; final String filter;
const StoriesHeader({required this.filter, Key? key}) : super(key: key); const StoriesHeader({required this.filter, Key? key}) : super(key: key);
void _addToStoryAction(BuildContext context) => void _addToStoryAction(BuildContext context) =>
@ -100,11 +101,6 @@ class StoriesHeader extends StatelessWidget {
onTap: () => _addToStoryAction(context), onTap: () => _addToStoryAction(context),
); );
} }
if (client.storiesRooms.isEmpty ||
!client.storiesRooms.any((room) =>
room.displayname.toLowerCase().contains(filter.toLowerCase()))) {
return Container();
}
final ownStoryRoom = client.storiesRooms final ownStoryRoom = client.storiesRooms
.firstWhereOrNull((r) => r.creatorId == client.userID); .firstWhereOrNull((r) => r.creatorId == client.userID);
final stories = [ final stories = [