diff --git a/lib/ui/widgets/tablet_mode/global_search_field.dart b/lib/ui/widgets/tablet_mode/global_search_field.dart index ed047fa..ab1ff95 100644 --- a/lib/ui/widgets/tablet_mode/global_search_field.dart +++ b/lib/ui/widgets/tablet_mode/global_search_field.dart @@ -1,4 +1,14 @@ +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/search_result.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'; /// Global search field @@ -7,12 +17,37 @@ import 'package:flutter/material.dart'; const _MainSearchColor = Color(0xFF999990); +class _SearchResults { + final SearchResultsList list; + final UsersList users; + final GroupsList groups; + + _SearchResults(this.list, this.users, this.groups); +} + class GlobalSearchField extends StatefulWidget { @override _GlobalSearchFieldState createState() => _GlobalSearchFieldState(); } class _GlobalSearchFieldState extends State { + final _focusNode = FocusNode(); + + final _controller = TextEditingController(); + + _SearchResults _searchResultsList; + + OverlayEntry _overlayEntry; + + @override + void initState() { + super.initState(); + + _focusNode.addListener(() { + if (!_focusNode.hasFocus) _removeOverlay(); + }); + } + @override Widget build(BuildContext context) { return Padding( @@ -23,7 +58,10 @@ class _GlobalSearchFieldState extends State { child: Padding( padding: const EdgeInsets.only(left: 8.0), child: TextField( + controller: _controller, + focusNode: _focusNode, textAlignVertical: TextAlignVertical.center, + onChanged: (s) => _refreshSearch(), decoration: InputDecoration( hintText: tr("Search..."), hintStyle: TextStyle(color: _MainSearchColor), @@ -40,4 +78,94 @@ class _GlobalSearchFieldState extends State { ), ); } + + void _refreshSearch() async { + if (_controller.text.length < 1) { + _removeOverlay(); + return; + } + + _showOverlay(); + + try { + final results = await SearchHelper().globalSearch(_controller.text); + final users = await UsersHelper().getListWithThrow(results.usersId); + final groups = await GroupsHelper().getListOrThrow(results.groupsId); + + _searchResultsList = _SearchResults(results, users, groups); + + if (_overlayEntry != null) _overlayEntry.markNeedsBuild(); + } catch (e, s) { + print("Could not perform search! $e\n$s"); + showSimpleSnack(context, tr("Could not perform search!")); + } + } + + void _showOverlay() { + if (_overlayEntry == null) { + _overlayEntry = _createOverlayEntry(); + Overlay.of(context).insert(_overlayEntry); + } + } + + void _removeOverlay() { + if (_overlayEntry != null) { + _overlayEntry.remove(); + _overlayEntry = null; + } + } + + OverlayEntry _createOverlayEntry() { + RenderBox renderBox = context.findRenderObject(); + var size = renderBox.size; + var offset = renderBox.localToGlobal(Offset.zero); + + return OverlayEntry( + builder: (context) => Positioned( + left: offset.dx, + top: offset.dy + size.height + 5.0, + width: size.width, + height: 300, + child: Material( + elevation: 4.0, + child: _SearchResultsWidget(results: _searchResultsList), + ), + )); + } +} + +class _SearchResultsWidget extends StatelessWidget { + final _SearchResults results; + + const _SearchResultsWidget({Key key, this.results}) : super(key: key); + + @override + Widget build(BuildContext context) { + if (results == null) return Container(); + return ListView.builder( + itemBuilder: _builder, + itemCount: results.list.length, + ); + } + + Widget _builder(BuildContext context, int index) { + final res = results.list[index]; + + switch (res.kind) { + case SearchResultKind.USER: + final user = results.users.getUser(res.id); + return ListTile( + leading: AccountImageWidget(user: user), + title: Text(user.displayName), + ); + + case SearchResultKind.GROUP: + final group = results.groups.getGroup(res.id); + return ListTile( + leading: GroupIcon(group: group), + title: Text(group.displayName), + ); + } + throw Exception("Unreachable statement!"); + } }