import 'package:comunic/helpers/conversations_helper.dart'; import 'package:comunic/helpers/friends_helper.dart'; import 'package:comunic/helpers/groups_helper.dart'; import 'package:comunic/helpers/users_helper.dart'; import 'package:comunic/helpers/webapp_helper.dart'; import 'package:comunic/lists/groups_list.dart'; import 'package:comunic/lists/memberships_list.dart'; import 'package:comunic/lists/users_list.dart'; import 'package:comunic/models/friend.dart'; import 'package:comunic/models/membership.dart'; import 'package:comunic/ui/routes/main_route/main_route.dart'; import 'package:comunic/ui/routes/main_route/page_info.dart'; import 'package:comunic/ui/widgets/account_image_widget.dart'; import 'package:comunic/ui/widgets/group_icon_widget.dart'; import 'package:comunic/ui/widgets/group_membership_widget.dart'; import 'package:comunic/ui/widgets/safe_state.dart'; import 'package:comunic/utils/date_utils.dart'; import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; /// Memberships panel /// /// @author Pierre Hubert class MembershipsPanel extends StatefulWidget { final PageInfo currentPage; const MembershipsPanel({ Key key, @required this.currentPage, }) : assert(currentPage != null), super(key: key); @override _MembershipsPanelState createState() => _MembershipsPanelState(); } const _MembershipIconsWidth = 30.0; class _MembershipsPanelState extends SafeState { final _refreshKey = GlobalKey(); MembershipList _membershipList; UsersList _usersList; GroupsList _groupsList; Future _refresh() async { try { final memberships = await WebAppHelper.getMemberships(); final users = await UsersHelper().getListWithThrow(memberships.usersId); final groups = await GroupsHelper().getListOrThrow(memberships.groupsId); setState(() { _membershipList = memberships; _usersList = users; _groupsList = groups; }); } catch (e, s) { print("Could not load the list of memberships! $e\n$s"); } } @override void initState() { super.initState(); setInterval(20, (t) => _refresh()); _refresh(); } @override Widget build(BuildContext context) { if (_membershipList == null) return buildCenteredProgressBar(); return Material( color: Colors.transparent, child: RefreshIndicator( key: _refreshKey, child: _buildMembershipsList(), onRefresh: _refresh), ); } Widget _buildMembershipsList() { return ListTileTheme( iconColor: IconTheme.of(context).color, child: ListView.builder( itemBuilder: _buildMembershipTile, itemCount: _membershipList.length, ), ); } Widget _buildMembershipTile(BuildContext context, int index) { final membership = _membershipList[index]; switch (membership.type) { case MembershipType.FRIEND: return _buildFriendMembership(membership); case MembershipType.GROUP: return _buildGroupMembership(membership); case MembershipType.CONVERSATION: return _buildConversationMembership(membership); } throw Exception("Unreachable statement!"); } // TODO : add private messages icon support Widget _buildFriendMembership(Membership membership) { final user = _usersList.getUser(membership.friend.id); final connected = membership.friend.isConnected; Widget subtitle; if (!membership.friend.accepted) { subtitle = RichText( text: TextSpan(children: [ WidgetSpan( child: _RespondFriendshipRequestButton( friend: membership.friend, accept: true, text: tr("Accept"), color: Colors.green, onTap: _respondFriendshipRequest, )), TextSpan(text: " "), WidgetSpan( child: _RespondFriendshipRequestButton( friend: membership.friend, accept: false, text: tr("Reject"), color: Colors.red, onTap: _respondFriendshipRequest, )), ])); } else subtitle = Text( connected ? tr("Online") : diffTimeFromNowToStr(membership.lastActive), style: TextStyle(color: connected ? Colors.green : null), ); return Container( color: widget.currentPage.type == PageType.USER_PAGE && widget.currentPage.id == user.id ? Colors.grey.shade800 : null, child: ListTile( leading: AccountImageWidget(user: user, width: _MembershipIconsWidth), title: Text(user.displayName), subtitle: subtitle, onTap: () => MainController.of(context).openUserPage(user.id), ), ); } Widget _buildGroupMembership(Membership membership) { final group = _groupsList.getGroup(membership.groupID); Widget subtitle; if (!group.isAtLeastMember) { subtitle = GroupMembershipWidget( group: group, onUpdated: () => _refreshKey.currentState.show(), onError: _onGroupMembershipUpdateError, ); } else { subtitle = Text(diffTimeFromNowToStr(membership.lastActive)); } return Container( color: widget.currentPage.type == PageType.GROUP_PAGE && widget.currentPage.id == group.id ? Colors.grey.shade800 : null, child: ListTile( leading: GroupIcon( group: group, width: _MembershipIconsWidth, ), title: Text(group.displayName), subtitle: subtitle, onTap: () => MainController.of(context).openGroup(group.id), ), ); } Widget _buildConversationMembership(Membership membership) { final conversation = membership.conversation; Color color; if (conversation.isHavingCall) color = Color(0xFF815d1d); else if (widget.currentPage.type == PageType.CONVERSATION_PAGE && widget.currentPage.id == conversation.id) color = Colors.grey.shade800; else if (!conversation.sawLastMessage) color = Color(0xFF1c443a); return Container( color: color, child: ListTile( dense: true, leading: Icon(Icons.message), title: Text( ConversationsHelper.getConversationName(conversation, _usersList)), subtitle: Text(diffTimeFromNowToStr(membership.lastActive) + (conversation.isHavingCall ? "\n" + tr("Ongoing call") : "")), onTap: () => MainController.of(context) .openConversation(conversation.id, fullScreen: true), trailing: conversation.isHavingCall ? FloatingActionButton( heroTag: null, child: Icon(Icons.call), onPressed: () => MainController.of(context).startCall(conversation.id), ) : null, ), ); } /// Respond to a friendship request Future _respondFriendshipRequest(Friend f, bool accept) async { try { if (!accept && !await showConfirmDialog( context: context, message: tr("Do you really want to reject this friendship request?"))) return; await FriendsHelper().respondRequest(f.id, accept); setState(() { if (accept) f.accepted = true; else _membershipList.removeFriend(f.id); }); _refreshKey.currentState.show(); } catch (e, s) { print("Could not respond to friendship request! $e\n$s"); showSimpleSnack(context, tr("Could not respond to friendship request!")); } } /// Handles the case of failure in group membership update void _onGroupMembershipUpdateError() { showSimpleSnack(context, tr("Could not update group membership!")); _refreshKey.currentState.show(); } } class _RespondFriendshipRequestButton extends StatelessWidget { final Friend friend; final bool accept; final String text; final Color color; final void Function(Friend, bool) onTap; const _RespondFriendshipRequestButton({ Key key, @required this.friend, @required this.accept, @required this.text, @required this.color, @required this.onTap, }) : super(key: key); @override Widget build(BuildContext context) { return InkWell( onTap: () => onTap(friend, accept), child: Text(text, style: TextStyle(color: color)), ); } }