From 5b4f509840024ef6a1aca4b78e7991138ab56adf Mon Sep 17 00:00:00 2001 From: Nutcake Date: Thu, 4 May 2023 19:38:35 +0200 Subject: [PATCH] Improve user search logic --- lib/widgets/friends_list.dart | 19 +++++---- lib/widgets/user_search.dart | 79 ++++++++++++++++++----------------- 2 files changed, 52 insertions(+), 46 deletions(-) diff --git a/lib/widgets/friends_list.dart b/lib/widgets/friends_list.dart index 45e2a20..3baff7e 100644 --- a/lib/widgets/friends_list.dart +++ b/lib/widgets/friends_list.dart @@ -16,7 +16,7 @@ import 'package:flutter/material.dart'; class MenuItemDefinition { final String name; final IconData icon; - final void Function() onTap; + final Function() onTap; const MenuItemDefinition({required this.name, required this.icon, required this.onTap}); } @@ -100,15 +100,20 @@ class _FriendsListState extends State { padding: const EdgeInsets.only(right: 4), child: PopupMenuButton( icon: const Icon(Icons.more_vert), - onSelected: (MenuItemDefinition itemDef) { - itemDef.onTap(); + onSelected: (MenuItemDefinition itemDef) async { + await itemDef.onTap(); }, itemBuilder: (BuildContext context) => [ - MenuItemDefinition(name: "Settings", icon: Icons.settings, onTap: () { - Navigator.of(context).push(MaterialPageRoute(builder: (context) => const SettingsPage())); + MenuItemDefinition(name: "Settings", icon: Icons.settings, onTap: () async { + _autoRefresh?.cancel(); + await Navigator.of(context).push(MaterialPageRoute(builder: (context) => const SettingsPage())); + _autoRefresh = Timer(_autoRefreshDuration, () => setState(() => _refreshFriendsList())); }), - MenuItemDefinition(name: "Find Users", icon: Icons.person_add, onTap: () { - Navigator.of(context).push(MaterialPageRoute(builder: (context) => const UserSearch())); + MenuItemDefinition(name: "Find Users", icon: Icons.person_add, onTap: () async { + _autoRefresh?.cancel(); + await Navigator.of(context).push(MaterialPageRoute(builder: (context) => const UserSearch())); + _autoRefresh = Timer(_autoRefreshDuration, () => setState(() => _refreshFriendsList())); + }) ].map((item) => PopupMenuItem( diff --git a/lib/widgets/user_search.dart b/lib/widgets/user_search.dart index 9146063..f5f5567 100644 --- a/lib/widgets/user_search.dart +++ b/lib/widgets/user_search.dart @@ -25,12 +25,13 @@ class UserSearch extends StatefulWidget { class _UserSearchState extends State { final TextEditingController _searchInputController = TextEditingController(); Timer? _searchDebouncer; - late Future>? _usersFuture = _emptySearch; - bool _isLoading = false; + late Future?>? _usersFuture = _emptySearch; - Future> get _emptySearch => Future(() => throw const SearchError( - message: "Start typing to search for users", icon: Icons.search) - ); + Future> get _emptySearch => + Future(() => + throw const SearchError( + message: "Start typing to search for users", icon: Icons.search) + ); void _querySearch(BuildContext context, String needle) { if (needle.isEmpty) { @@ -38,60 +39,61 @@ class _UserSearchState extends State { return; } _usersFuture = UserApi.searchUsers(ClientHolder - .of(context).apiClient, needle: needle).then((value) { + .of(context) + .apiClient, needle: needle).then((value) { final res = value.toList(); if (res.isEmpty) throw SearchError(message: "No user found with username '$needle'", icon: Icons.search_off); res.sort( (a, b) => a.username.length.compareTo(b.username.length) ); return res; - }).whenComplete(() => setState(() => _isLoading = false)); - } - - @override - void initState() { - super.initState(); + }); } @override Widget build(BuildContext context) { + final mClient = ClientHolder + .of(context) + .messagingClient; return Scaffold( appBar: AppBar( title: const Text("Find Users"), ), body: Column( children: [ - if(_isLoading) const LinearProgressIndicator(), Expanded( child: FutureBuilder( - future: _usersFuture, - builder: (context, snapshot) { - if (snapshot.hasData) { - final users = (snapshot.data as List); - final mClient = ClientHolder.of(context).messagingClient; - return ListView.builder( - itemCount: users.length, - itemBuilder: (context, index) { - final user = users[index]; - return UserListTile(user: user, isFriend: mClient.getAsFriend(user.id) != null,); - }, + future: _usersFuture, + builder: (context, snapshot) { + if (snapshot.hasData) { + final users = snapshot.data as List; + return ListView.builder( + itemCount: users.length, + itemBuilder: (context, index) { + final user = users[index]; + return UserListTile(user: user, isFriend: mClient.getAsFriend(user.id) != null,); + }, + ); + } else if (snapshot.hasError) { + final err = snapshot.error; + if (err is SearchError) { + return DefaultErrorWidget( + title: err.message, + iconOverride: err.icon, ); - } else if (snapshot.hasError) { - final err = snapshot.error; - if (err is SearchError) { - return DefaultErrorWidget( - title: err.message, - iconOverride: err.icon, - ); - } else { - FlutterError.reportError( - FlutterErrorDetails(exception: snapshot.error!, stack: snapshot.stackTrace)); - return DefaultErrorWidget(title: "${snapshot.error}",); - } } else { - return const SizedBox.shrink(); + FlutterError.reportError( + FlutterErrorDetails(exception: snapshot.error!, stack: snapshot.stackTrace)); + return DefaultErrorWidget(title: "${snapshot.error}",); } + } else { + return Column( + children: const [ + LinearProgressIndicator(), + ], + ); } + }, ), ), Padding( @@ -111,13 +113,12 @@ class _UserSearchState extends State { _searchDebouncer?.cancel(); if (value.isEmpty) { setState(() { - _isLoading = false; _querySearch(context, value); }); return; } setState(() { - _isLoading = true; + _usersFuture = Future(() => null); }); _searchDebouncer = Timer(const Duration(milliseconds: 300), () { setState(() {