mirror of
				https://gitlab.com/comunic/comunicmobile
				synced 2025-11-04 12:14:11 +00:00 
			
		
		
		
	Made search live
This commit is contained in:
		@@ -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/intl_utils.dart';
 | 
				
			||||||
 | 
					import 'package:comunic/utils/ui_utils.dart';
 | 
				
			||||||
import 'package:flutter/material.dart';
 | 
					import 'package:flutter/material.dart';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/// Global search field
 | 
					/// Global search field
 | 
				
			||||||
@@ -7,12 +17,37 @@ import 'package:flutter/material.dart';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const _MainSearchColor = Color(0xFF999990);
 | 
					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 {
 | 
					class GlobalSearchField extends StatefulWidget {
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  _GlobalSearchFieldState createState() => _GlobalSearchFieldState();
 | 
					  _GlobalSearchFieldState createState() => _GlobalSearchFieldState();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class _GlobalSearchFieldState extends State<GlobalSearchField> {
 | 
					class _GlobalSearchFieldState extends State<GlobalSearchField> {
 | 
				
			||||||
 | 
					  final _focusNode = FocusNode();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  final _controller = TextEditingController();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  _SearchResults _searchResultsList;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  OverlayEntry _overlayEntry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  @override
 | 
				
			||||||
 | 
					  void initState() {
 | 
				
			||||||
 | 
					    super.initState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    _focusNode.addListener(() {
 | 
				
			||||||
 | 
					      if (!_focusNode.hasFocus) _removeOverlay();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  @override
 | 
					  @override
 | 
				
			||||||
  Widget build(BuildContext context) {
 | 
					  Widget build(BuildContext context) {
 | 
				
			||||||
    return Padding(
 | 
					    return Padding(
 | 
				
			||||||
@@ -23,7 +58,10 @@ class _GlobalSearchFieldState extends State<GlobalSearchField> {
 | 
				
			|||||||
        child: Padding(
 | 
					        child: Padding(
 | 
				
			||||||
          padding: const EdgeInsets.only(left: 8.0),
 | 
					          padding: const EdgeInsets.only(left: 8.0),
 | 
				
			||||||
          child: TextField(
 | 
					          child: TextField(
 | 
				
			||||||
 | 
					            controller: _controller,
 | 
				
			||||||
 | 
					            focusNode: _focusNode,
 | 
				
			||||||
            textAlignVertical: TextAlignVertical.center,
 | 
					            textAlignVertical: TextAlignVertical.center,
 | 
				
			||||||
 | 
					            onChanged: (s) => _refreshSearch(),
 | 
				
			||||||
            decoration: InputDecoration(
 | 
					            decoration: InputDecoration(
 | 
				
			||||||
              hintText: tr("Search..."),
 | 
					              hintText: tr("Search..."),
 | 
				
			||||||
              hintStyle: TextStyle(color: _MainSearchColor),
 | 
					              hintStyle: TextStyle(color: _MainSearchColor),
 | 
				
			||||||
@@ -40,4 +78,94 @@ class _GlobalSearchFieldState extends State<GlobalSearchField> {
 | 
				
			|||||||
      ),
 | 
					      ),
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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!");
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user