import 'dart:math'; import 'package:comunic/enums/report_target_type.dart'; import 'package:comunic/helpers/friends_helper.dart'; import 'package:comunic/helpers/users_helper.dart'; import 'package:comunic/lists/friends_list.dart'; import 'package:comunic/lists/users_list.dart'; import 'package:comunic/models/friend.dart'; import 'package:comunic/models/report_target.dart'; import 'package:comunic/ui/dialogs/report_dialog.dart'; import 'package:comunic/ui/tiles/accepted_friend_tile.dart'; import 'package:comunic/ui/tiles/pending_friend_tile.dart'; import 'package:comunic/ui/widgets/safe_state.dart'; import 'package:comunic/utils/conversations_utils.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; /// Friends list screen /// /// Display the list of friends of the current user /// /// @author Pierre HUBERT enum _ErrorsLevel { NONE, MINOR, MAJOR } class FriendsListScreen extends StatefulWidget { final bool showAppBar; const FriendsListScreen({Key? key, this.showAppBar = true}) : super(key: key); @override State createState() => _FriendsListScreenState(); } class _FriendsListScreenState extends SafeState { /// Helpers final _friendsHelper = FriendsHelper(); final _usersHelper = UsersHelper(); /// Widget members _ErrorsLevel _error = _ErrorsLevel.NONE; FriendsList? _friendsList; late UsersList _usersInfo; final _refreshIndicatorKey = GlobalKey(); /// Useful setters set error(_ErrorsLevel err) => setState(() => _error = err); void _gotError() { error = _friendsList == null ? _ErrorsLevel.MAJOR : _ErrorsLevel.MINOR; } @override void didChangeDependencies() { super.didChangeDependencies(); _getList(); } /// Initialize list retrieving Future _getList() async { await _loadList(false); await _loadList(true); } /// Refresh the list of friends Future _refreshList() async { if (_refreshIndicatorKey.currentState != null) await _refreshIndicatorKey.currentState!.show(); else await _getList(); } /// Load the list of friends Future _loadList(bool online) async { error = _ErrorsLevel.NONE; // Get the list of friends final list = await _friendsHelper.getList(online: online); // Check if there is no cache yet if (!online && list!.isEmpty) return; // Check for errors if (list == null) return _gotError(); // Get information about related users final users = await _usersHelper.getUsersInfo(list.usersId); // Check for errors if (users == null) return _gotError(); // Apply new information setState(() { _friendsList = list..sort(); _usersInfo = users; }); error = _ErrorsLevel.NONE; } /// Build and return loading error Widget _buildError() => buildErrorCard( tr("Could not load your list of friends!"), actions: [ TextButton( onPressed: _refreshList, child: Text( tr("Retry")!.toUpperCase(), style: TextStyle(color: Colors.white), ), ), ], ); @override Widget build(BuildContext context) => Scaffold( appBar: widget.showAppBar ? AppBar( title: Text(tr("Your friends list")!), ) : null, body: _buildBody(), ); Widget _buildBody() { if (_error == _ErrorsLevel.MAJOR) return _buildError(); if (_friendsList == null) return buildCenteredProgressBar(); return Column( children: [ // Check for errors Container(child: _error != _ErrorsLevel.NONE ? _buildError() : null), // List of friends Expanded( child: RefreshIndicator( key: _refreshIndicatorKey, onRefresh: () => _loadList(true), child: ListView.builder( physics: AlwaysScrollableScrollPhysics(), itemCount: max(_friendsList!.length, 1), itemBuilder: (c, i) { if (_friendsList!.isEmpty) return Padding( padding: const EdgeInsets.all(8.0), child: Center( child: Text(tr("You do not have any friend yet!")!)), ); return _friendsList![i].accepted ? AcceptedFriendTile( friend: _friendsList![i], user: _usersInfo.getUser(_friendsList![i].id), onOpenPrivateConversation: _openPrivateConversation, onSetFollowing: _setFollowingFriend, onRequestDelete: _deleteFriend, onReportFriend: _reportFriend, ) : PendingFriendTile( friend: _friendsList![i], user: _usersInfo.getUser(_friendsList![i].id), onRespond: _respondRequest, onReport: _reportFriend, ); }), ), ), ], ); } /// Respond to friendship request Future _respondRequest(Friend f, bool accept) async { if (!await _friendsHelper.respondRequest(f.id, accept)) showSimpleSnack(context, tr("Could not respond to friendship request!")!); // Load the list of friends again _refreshList(); } /// Update following status of a friend Future _setFollowingFriend(Friend friend, bool follow) async { if (!await _friendsHelper.setFollowing(friend.id, follow)) showSimpleSnack(context, tr("Could not update following status!")!); _refreshList(); } /// Handles deletion request of a friend Future _deleteFriend(Friend f) async { final choice = await showDialog( context: context, builder: (b) => AlertDialog( title: Text(tr("Delete friend")!), content: Text(tr( "Are you sure do you want to remove this friend from your list of friends ? A friendship request will have to be sent to get this user back to your list!")!), actions: [ TextButton( onPressed: () => Navigator.pop(context, false), child: Text(tr("Cancel")!.toUpperCase()), ), TextButton( onPressed: () => Navigator.pop(context, true), child: Text( tr("Confirm")!.toUpperCase(), style: TextStyle(color: Colors.red), ), ), ], ), ); if (choice == null || !choice) return; // Forward the request to the server if (!await _friendsHelper.removeFriend(f.id)) showSimpleSnack( context, tr("Could not delete this person from your friends list!")!); // Refresh list _refreshList(); } /// Report a friend Future _reportFriend(Friend friend) async => await showReportDialog( ctx: context, target: ReportTarget(ReportTargetType.User, friend.id)); /// Open a private conversation for a given [friend] Future _openPrivateConversation(Friend friend) async { await openPrivateConversation(context, friend.id); } }