OpenContacts/lib/widgets/user_search.dart

136 lines
4.5 KiB
Dart
Raw Normal View History

2023-05-04 12:16:19 -04:00
import 'dart:async';
import 'package:contacts_plus_plus/apis/user_api.dart';
2023-05-05 06:45:00 -04:00
import 'package:contacts_plus_plus/client_holder.dart';
import 'package:contacts_plus_plus/clients/messaging_client.dart';
2023-05-04 12:16:19 -04:00
import 'package:contacts_plus_plus/models/user.dart';
import 'package:contacts_plus_plus/widgets/default_error_widget.dart';
import 'package:contacts_plus_plus/widgets/user_list_tile.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
2023-05-04 12:16:19 -04:00
class SearchError {
final String message;
final IconData icon;
const SearchError({required this.message, required this.icon});
}
class UserSearch extends StatefulWidget {
const UserSearch({super.key});
2023-05-04 12:16:19 -04:00
@override
State<StatefulWidget> createState() => _UserSearchState();
}
class _UserSearchState extends State<UserSearch> {
final TextEditingController _searchInputController = TextEditingController();
Timer? _searchDebouncer;
2023-05-04 13:38:35 -04:00
late Future<List<User>?>? _usersFuture = _emptySearch;
2023-05-04 12:16:19 -04:00
2023-05-04 13:38:35 -04:00
Future<List<User>> get _emptySearch =>
Future(() =>
throw const SearchError(
message: "Start typing to search for users", icon: Icons.search)
);
2023-05-04 12:16:19 -04:00
void _querySearch(BuildContext context, String needle) {
if (needle.isEmpty) {
_usersFuture = _emptySearch;
return;
}
_usersFuture = UserApi.searchUsers(ClientHolder
2023-05-04 13:38:35 -04:00
.of(context)
.apiClient, needle: needle).then((value) {
2023-05-04 12:16:19 -04:00
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;
2023-05-04 13:38:35 -04:00
});
2023-05-04 12:16:19 -04:00
}
@override
Widget build(BuildContext context) {
final mClient = Provider.of<MessagingClient>(context, listen: false);
2023-05-04 12:16:19 -04:00
return Scaffold(
appBar: AppBar(
title: const Text("Find Users"),
),
body: Column(
children: [
Expanded(
child: FutureBuilder(
2023-05-04 13:38:35 -04:00
future: _usersFuture,
builder: (context, snapshot) {
if (snapshot.hasData) {
final users = snapshot.data as List<User>;
return ListView.builder(
itemCount: users.length,
itemBuilder: (context, index) {
final user = users[index];
return UserListTile(user: user, onChanged: () {
mClient.refreshFriendsList();
}, isFriend: mClient.getAsFriend(user.id) != null,);
2023-05-04 13:38:35 -04:00
},
);
} else if (snapshot.hasError) {
final err = snapshot.error;
if (err is SearchError) {
return DefaultErrorWidget(
title: err.message,
iconOverride: err.icon,
2023-05-04 12:16:19 -04:00
);
} else {
2023-05-04 13:38:35 -04:00
FlutterError.reportError(
FlutterErrorDetails(exception: snapshot.error!, stack: snapshot.stackTrace));
return DefaultErrorWidget(title: "${snapshot.error}",);
2023-05-04 12:16:19 -04:00
}
2023-05-04 13:38:35 -04:00
} else {
return Column(
children: const [
LinearProgressIndicator(),
],
);
2023-05-04 12:16:19 -04:00
}
2023-05-04 13:38:35 -04:00
},
2023-05-04 12:16:19 -04:00
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
child: TextField(
decoration: InputDecoration(
isDense: true,
hintText: "Search for users...",
contentPadding: const EdgeInsets.all(16),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(24)
)
),
autocorrect: false,
controller: _searchInputController,
onChanged: (String value) {
_searchDebouncer?.cancel();
if (value.isEmpty) {
setState(() {
_querySearch(context, value);
});
return;
}
setState(() {
2023-05-04 13:38:35 -04:00
_usersFuture = Future(() => null);
2023-05-04 12:16:19 -04:00
});
_searchDebouncer = Timer(const Duration(milliseconds: 300), () {
setState(() {
_querySearch(context, value);
});
});
},
),
),
],
),
);
}
}