mirror of
https://gitlab.com/comunic/comunicmobile
synced 2025-06-19 16:25:17 +00:00
Integrate members screen as a tab
This commit is contained in:
319
lib/ui/screens/group_sections/group_members_screen.dart
Normal file
319
lib/ui/screens/group_sections/group_members_screen.dart
Normal file
@ -0,0 +1,319 @@
|
||||
import 'package:comunic/helpers/groups_helper.dart';
|
||||
import 'package:comunic/helpers/users_helper.dart';
|
||||
import 'package:comunic/lists/group_members_list.dart';
|
||||
import 'package:comunic/lists/users_list.dart';
|
||||
import 'package:comunic/models/group.dart';
|
||||
import 'package:comunic/models/group_membership.dart';
|
||||
import 'package:comunic/models/user.dart';
|
||||
import 'package:comunic/ui/dialogs/multi_choices_dialog.dart';
|
||||
import 'package:comunic/ui/dialogs/pick_user_dialog.dart';
|
||||
import 'package:comunic/ui/widgets/account_image_widget.dart';
|
||||
import 'package:comunic/ui/widgets/async_screen_widget.dart';
|
||||
import 'package:comunic/utils/account_utils.dart';
|
||||
import 'package:comunic/utils/intl_utils.dart';
|
||||
import 'package:comunic/utils/ui_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Group members screen
|
||||
///
|
||||
/// @author Pierre HUBERT
|
||||
|
||||
class GroupMembersSection extends StatefulWidget {
|
||||
final int groupID;
|
||||
|
||||
const GroupMembersSection({Key key, this.groupID})
|
||||
: assert(groupID != null),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
_GroupMembersSectionState createState() => _GroupMembersSectionState();
|
||||
}
|
||||
|
||||
class _GroupMembersSectionState extends State<GroupMembersSection> {
|
||||
final _key = GlobalKey<AsyncScreenWidgetState>();
|
||||
Group _group;
|
||||
GroupMembersList _members;
|
||||
UsersList _users;
|
||||
|
||||
Future<void> _refresh() async {
|
||||
final group = await GroupsHelper().getSingle(widget.groupID, force: true);
|
||||
final members = await GroupsHelper.getMembersList(widget.groupID);
|
||||
final users = await UsersHelper().getListWithThrow(members.usersID);
|
||||
|
||||
_group = group;
|
||||
_members = members;
|
||||
_users = users;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => AsyncScreenWidget(
|
||||
key: _key,
|
||||
onReload: _refresh,
|
||||
onBuild: _buildBodyContent,
|
||||
errorMessage: tr("Could not load the list of members of this group!"),
|
||||
showOldDataWhileUpdating: true,
|
||||
);
|
||||
|
||||
Widget _buildBodyContent() {
|
||||
return Stack(
|
||||
children: [
|
||||
ListView(children: _members.map(_buildGroupMemberTile).toList()),
|
||||
_buildInvitationFAB()
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildGroupMemberTile(GroupMembership membership) {
|
||||
final user = _users.getUser(membership.userID);
|
||||
|
||||
return _GroupMembershipTile(
|
||||
group: _group,
|
||||
membership: membership,
|
||||
user: user,
|
||||
onUpdated: () => _key.currentState.refresh(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInvitationFAB() {
|
||||
return Positioned(
|
||||
right: 20,
|
||||
bottom: 20,
|
||||
child: FloatingActionButton(
|
||||
onPressed: _inviteMember,
|
||||
child: Icon(Icons.add),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _inviteMember() async {
|
||||
try {
|
||||
final userID = await showPickUserDialog(context);
|
||||
if (userID == null) return;
|
||||
|
||||
await GroupsHelper.sendInvitation(widget.groupID, userID);
|
||||
} catch (e, s) {
|
||||
print("Could not invite a user! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not invite a user!"));
|
||||
}
|
||||
|
||||
_key.currentState.refresh();
|
||||
}
|
||||
}
|
||||
|
||||
List<MultiChoiceEntry<GroupMembershipLevel>> get _membershipLevels => [
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.ADMINISTRATOR,
|
||||
title: tr("Administrator"),
|
||||
subtitle: tr("Can change members privileges and group settings")),
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.MODERATOR,
|
||||
title: tr("Moderator"),
|
||||
subtitle: tr(
|
||||
"Can always create posts, invite users and respond to membership request")),
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.MEMBER,
|
||||
title: tr("Member"),
|
||||
subtitle: tr("Can access to all group posts")),
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.PENDING,
|
||||
title: tr("Requested"),
|
||||
hidden: true,
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.INVITED,
|
||||
title: tr("Invited"),
|
||||
hidden: true,
|
||||
),
|
||||
MultiChoiceEntry(
|
||||
id: GroupMembershipLevel.VISITOR,
|
||||
title: tr("Visitor"),
|
||||
hidden: true,
|
||||
),
|
||||
];
|
||||
|
||||
class _GroupMembershipTile extends StatefulWidget {
|
||||
final Group group;
|
||||
final GroupMembership membership;
|
||||
final User user;
|
||||
final void Function() onUpdated;
|
||||
|
||||
const _GroupMembershipTile({
|
||||
Key key,
|
||||
@required this.group,
|
||||
@required this.membership,
|
||||
@required this.user,
|
||||
@required this.onUpdated,
|
||||
}) : assert(group != null),
|
||||
assert(membership != null),
|
||||
assert(user != null),
|
||||
assert(onUpdated != null),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
__GroupMembershipTileState createState() => __GroupMembershipTileState();
|
||||
}
|
||||
|
||||
enum _MemberMenuOptions { CHANGE_LEVEL, DELETE }
|
||||
|
||||
class __GroupMembershipTileState extends State<_GroupMembershipTile> {
|
||||
int get groupID => widget.membership.groupID;
|
||||
|
||||
int get memberID => widget.membership.userID;
|
||||
|
||||
bool get _isCurrentUser => widget.membership.userID == userID();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListTile(
|
||||
leading: AccountImageWidget(
|
||||
user: widget.user,
|
||||
),
|
||||
title: Text(widget.user.displayName),
|
||||
subtitle: Text(_membershipLevels
|
||||
.firstWhere((f) => f.id == widget.membership.level)
|
||||
.title),
|
||||
trailing: _buildTrailing(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTrailing() {
|
||||
switch (widget.membership.level) {
|
||||
case GroupMembershipLevel.ADMINISTRATOR:
|
||||
case GroupMembershipLevel.MODERATOR:
|
||||
case GroupMembershipLevel.MEMBER:
|
||||
return _buildMemberCase();
|
||||
break;
|
||||
|
||||
case GroupMembershipLevel.INVITED:
|
||||
return _buildInvitedCase();
|
||||
break;
|
||||
|
||||
case GroupMembershipLevel.PENDING:
|
||||
return _buildRequestedCase();
|
||||
break;
|
||||
|
||||
case GroupMembershipLevel.VISITOR:
|
||||
// This case should never happen
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
Widget _buildMemberCase() {
|
||||
return PopupMenuButton<_MemberMenuOptions>(
|
||||
itemBuilder: (c) => [
|
||||
// Change membership level
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Change level")),
|
||||
enabled: !_isCurrentUser && widget.group.isAdmin,
|
||||
value: _MemberMenuOptions.CHANGE_LEVEL,
|
||||
),
|
||||
|
||||
// Remove membership
|
||||
PopupMenuItem(
|
||||
child: Text(tr("Remove")),
|
||||
value: _MemberMenuOptions.DELETE,
|
||||
enabled: !_isCurrentUser,
|
||||
),
|
||||
],
|
||||
onSelected: _handleMenu,
|
||||
);
|
||||
}
|
||||
|
||||
void _handleMenu(_MemberMenuOptions value) {
|
||||
switch (value) {
|
||||
case _MemberMenuOptions.CHANGE_LEVEL:
|
||||
_changeLevel();
|
||||
break;
|
||||
|
||||
case _MemberMenuOptions.DELETE:
|
||||
_removeMembership();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void _changeLevel() async {
|
||||
try {
|
||||
final newLevel = await showMultiChoicesDialog(
|
||||
context: context,
|
||||
choices: _membershipLevels,
|
||||
defaultChoice: widget.membership.level,
|
||||
title: tr("New membership level"),
|
||||
);
|
||||
|
||||
if (newLevel == null) return;
|
||||
|
||||
await GroupsHelper.setNewLevel(groupID, memberID, newLevel);
|
||||
} catch (e, s) {
|
||||
print("Could not change group membership level! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not change group membership level!"));
|
||||
}
|
||||
|
||||
widget.onUpdated();
|
||||
}
|
||||
|
||||
void _removeMembership() async {
|
||||
try {
|
||||
if (!await showConfirmDialog(
|
||||
context: context,
|
||||
message: tr("Do you really want to remove this membership ?")))
|
||||
return;
|
||||
|
||||
await GroupsHelper.removeMemberFromGroup(groupID, memberID);
|
||||
} catch (e, s) {
|
||||
print("Could not remove membership! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not remove this membership!"));
|
||||
}
|
||||
|
||||
widget.onUpdated();
|
||||
}
|
||||
|
||||
Widget _buildInvitedCase() {
|
||||
return MaterialButton(
|
||||
onPressed: _cancelMembership,
|
||||
child: Text(tr("Cancel").toUpperCase()),
|
||||
textColor: Colors.red,
|
||||
);
|
||||
}
|
||||
|
||||
void _cancelMembership() async {
|
||||
try {
|
||||
await GroupsHelper.cancelInvitation(groupID, memberID);
|
||||
} catch (e, s) {
|
||||
print("Could not cancel invitation! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not cancel invitation!"));
|
||||
}
|
||||
|
||||
widget.onUpdated();
|
||||
}
|
||||
|
||||
Widget _buildRequestedCase() {
|
||||
return IntrinsicWidth(
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
MaterialButton(
|
||||
onPressed: () => _respondRequest(false),
|
||||
child: Text(tr("Reject").toUpperCase()),
|
||||
textColor: Colors.red,
|
||||
),
|
||||
MaterialButton(
|
||||
onPressed: () => _respondRequest(true),
|
||||
child: Text(tr("Accept").toUpperCase()),
|
||||
textColor: Colors.green,
|
||||
)
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
void _respondRequest(bool accept) async {
|
||||
try {
|
||||
await GroupsHelper.respondRequest(groupID, memberID, accept);
|
||||
} catch (e, s) {
|
||||
print("Could not respond to membership request! $e\n$s");
|
||||
showSimpleSnack(context, tr("Could not respond to membership request!"));
|
||||
}
|
||||
|
||||
widget.onUpdated();
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user