From 0494a9058d44e59cbf453fb49e3529933b2105f3 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Fri, 17 Apr 2020 10:30:29 +0200 Subject: [PATCH] Create search page --- lib/helpers/search_helper.dart | 23 ++++++ lib/lists/search_results_list.dart | 20 +++++ lib/models/search_result.dart | 18 +++++ lib/ui/routes/home_route.dart | 4 + lib/ui/screens/search_screen.dart | 118 +++++++++++++++++++++++++++++ lib/ui/widgets/navbar_widget.dart | 7 +- 6 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 lib/lists/search_results_list.dart create mode 100644 lib/models/search_result.dart create mode 100644 lib/ui/screens/search_screen.dart diff --git a/lib/helpers/search_helper.dart b/lib/helpers/search_helper.dart index 36c5b2d..eb3a340 100644 --- a/lib/helpers/search_helper.dart +++ b/lib/helpers/search_helper.dart @@ -1,6 +1,8 @@ import 'package:comunic/helpers/users_helper.dart'; +import 'package:comunic/lists/search_results_list.dart'; import 'package:comunic/lists/users_list.dart'; import 'package:comunic/models/api_request.dart'; +import 'package:comunic/models/search_result.dart'; import 'package:comunic/utils/api_utils.dart'; /// Search helper @@ -21,4 +23,25 @@ class SearchHelper { return await UsersHelper() .getUsersInfo(response.getArray().map((f) => cast(f)).toList()); } + + /// Perform a global search + Future globalSearch(String query) async { + final result = await APIRequest( + uri: "search/global", needLogin: true, args: {"query": query}).exec(); + + result.assertOk(); + + return SearchResultsList()..addAll(result.getArray().map((f) { + switch (f["kind"]) { + case "user": + return SearchResult(id: f["id"], kind: SearchResultKind.USER); + + case "group": + return SearchResult(id: f["id"], kind: SearchResultKind.GROUP); + + default: + throw Exception("Unkown search kind: ${f["kind"]}"); + } + }).toList()); + } } diff --git a/lib/lists/search_results_list.dart b/lib/lists/search_results_list.dart new file mode 100644 index 0000000..37f8c49 --- /dev/null +++ b/lib/lists/search_results_list.dart @@ -0,0 +1,20 @@ +import 'package:comunic/lists/abstract_list.dart'; +import 'package:comunic/models/search_result.dart'; + +/// List of result of a global search on the database +/// +/// @author Pierre Hubert + +class SearchResultsList extends AbstractList { + /// Get the list of users included in this search result + Set get usersId => this + .where((f) => f.kind == SearchResultKind.USER) + .map((f) => f.id) + .toSet(); + + /// Get the list of groups included in this search result + Set get groupsId => this + .where((f) => f.kind == SearchResultKind.GROUP) + .map((f) => f.id) + .toSet(); +} diff --git a/lib/models/search_result.dart b/lib/models/search_result.dart new file mode 100644 index 0000000..3a136a4 --- /dev/null +++ b/lib/models/search_result.dart @@ -0,0 +1,18 @@ +import 'package:flutter/cupertino.dart'; + +/// Single search result +/// +/// @author Pierre Hubert + +enum SearchResultKind { USER, GROUP } + +class SearchResult { + final int id; + final SearchResultKind kind; + + SearchResult({ + @required this.id, + @required this.kind, + }) : assert(id != null), + assert(kind != null); +} diff --git a/lib/ui/routes/home_route.dart b/lib/ui/routes/home_route.dart index be727b3..855da8c 100644 --- a/lib/ui/routes/home_route.dart +++ b/lib/ui/routes/home_route.dart @@ -9,6 +9,7 @@ import 'package:comunic/ui/screens/groups_list_screen.dart'; import 'package:comunic/ui/screens/newest_posts.dart'; import 'package:comunic/ui/screens/notifications_screen.dart'; import 'package:comunic/ui/screens/other_friends_lists_screen.dart'; +import 'package:comunic/ui/screens/search_screen.dart'; import 'package:comunic/ui/screens/user_access_denied_screen.dart'; import 'package:comunic/ui/screens/user_page_screen.dart'; import 'package:comunic/ui/widgets/navbar_widget.dart'; @@ -168,6 +169,9 @@ class _HomeRouteState extends HomeController { case BarCallbackActions.OPEN_USER_ACCESS_DENIED_PAGE: return UserAccessDeniedScreen(userID: _currTab.args["userID"]); + case BarCallbackActions.OPEN_SEARCH_PAGE: + return SearchScreen(); + case BarCallbackActions.OPEN_GROUPS: return GroupsListScreen(); diff --git a/lib/ui/screens/search_screen.dart b/lib/ui/screens/search_screen.dart new file mode 100644 index 0000000..0f1628d --- /dev/null +++ b/lib/ui/screens/search_screen.dart @@ -0,0 +1,118 @@ +import 'package:comunic/helpers/groups_helper.dart'; +import 'package:comunic/helpers/search_helper.dart'; +import 'package:comunic/helpers/users_helper.dart'; +import 'package:comunic/lists/groups_list.dart'; +import 'package:comunic/lists/search_results_list.dart'; +import 'package:comunic/lists/users_list.dart'; +import 'package:comunic/models/group.dart'; +import 'package:comunic/models/search_result.dart'; +import 'package:comunic/models/user.dart'; +import 'package:comunic/ui/routes/home_route.dart'; +import 'package:comunic/ui/widgets/account_image_widget.dart'; +import 'package:comunic/ui/widgets/group_icon_widget.dart'; +import 'package:comunic/utils/intl_utils.dart'; +import 'package:comunic/utils/ui_utils.dart'; +import 'package:flutter/material.dart'; + +/// Search screen +/// +/// @author Pierre Hubert + +class SearchScreen extends StatefulWidget { + @override + _SearchScreenState createState() => _SearchScreenState(); +} + +class _SearchScreenState extends State { + SearchResultsList _searchResultsList; + UsersList _usersList; + GroupsList _groupsList; + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + TextField( + onChanged: (s) => _newSearch(s), + ), + _searchResultsList == null + ? Container() + : Expanded( + child: ListView( + children: _searchResultsList + .map((f) => f.kind == SearchResultKind.USER + ? _SearchResultUser( + user: _usersList.getUser(f.id), + ) + : _SearchResultGroup( + group: _groupsList[f.id], + )) + .toList(), + ), + ) + ], + ), + ); + } + + /// Perform a new search + void _newSearch(String s) async { + try { + if (s.length < 2) return; + + final list = await SearchHelper().globalSearch(s); + final users = await UsersHelper().getListWithThrow(list.usersId); + final groups = await GroupsHelper().getListOrThrow(list.groupsId); + + setState(() { + _searchResultsList = list; + _usersList = users; + _groupsList = groups; + }); + } catch (e, stack) { + print(e); + print(stack); + + showSimpleSnack(context, tr("Could not peform search!")); + } + } +} + +class _SearchResultUser extends StatelessWidget { + final User user; + + const _SearchResultUser({Key key, this.user}) + : assert(user != null), + super(key: key); + + @override + Widget build(BuildContext context) { + return ListTile( + leading: AccountImageWidget( + user: user, + ), + title: Text(user.displayName), + onTap: () => HomeController.of(context).openUserPage(user.id), + ); + } +} + +class _SearchResultGroup extends StatelessWidget { + final Group group; + + const _SearchResultGroup({Key key, this.group}) + : assert(group != null), + super(key: key); + + @override + Widget build(BuildContext context) { + return ListTile( + leading: GroupIcon(group: group), + title: Text(group.displayName), + subtitle: Text(tr("Group")), + onTap: () => HomeController.of(context).openGroup(group.id), + ); + } +} diff --git a/lib/ui/widgets/navbar_widget.dart b/lib/ui/widgets/navbar_widget.dart index 4fa6176..2dd9b0e 100644 --- a/lib/ui/widgets/navbar_widget.dart +++ b/lib/ui/widgets/navbar_widget.dart @@ -16,6 +16,7 @@ enum BarCallbackActions { OPEN_NEWEST_POSTS, OPEN_FRIENDS, OPEN_MY_PAGE, + OPEN_SEARCH_PAGE, OPEN_GROUPS, OPEN_GROUP_PAGE, OPEN_USER_PAGE, @@ -91,7 +92,11 @@ final _menuActionsItem = <_ActionMenuItem>[ _ActionMenuItem( label: tr("My Page"), action: BarCallbackActions.OPEN_MY_PAGE), _ActionMenuItem(label: tr("Groups"), action: BarCallbackActions.OPEN_GROUPS), - _ActionMenuItem(label: tr("Account settings"), action: BarCallbackActions.OPEN_ACCOUNT_SETTINGS), + _ActionMenuItem( + label: tr("Search"), action: BarCallbackActions.OPEN_SEARCH_PAGE), + _ActionMenuItem( + label: tr("Account settings"), + action: BarCallbackActions.OPEN_ACCOUNT_SETTINGS), _ActionMenuItem( label: tr("App settings"), action: BarCallbackActions.OPEN_APP_SETTINGS), _ActionMenuItem(