diff --git a/lib/helpers/groups_helper.dart b/lib/helpers/groups_helper.dart index e225ff3..6e8adaf 100644 --- a/lib/helpers/groups_helper.dart +++ b/lib/helpers/groups_helper.dart @@ -306,6 +306,17 @@ class GroupsHelper { .addInt("userID", userID) .execWithThrow(); + /// Change the membership level of a member of a group + /// + /// Throws an exception in case of failure + static Future setNewLevel( + int groupID, int userID, GroupMembershipLevel level) async => + await APIRequest.withLogin("groups/update_membership_level") + .addInt("groupID", groupID) + .addInt("userID", userID) + .addString("level", invertMap(_APIGroupsMembershipLevelsMap)[level]) + .execWithThrow(); + /// Turn an API entry into a group object Group _getGroupFromAPI(Map map) { return Group( diff --git a/lib/ui/dialogs/multi_choices_dialog.dart b/lib/ui/dialogs/multi_choices_dialog.dart index 6cbf7bb..4606609 100644 --- a/lib/ui/dialogs/multi_choices_dialog.dart +++ b/lib/ui/dialogs/multi_choices_dialog.dart @@ -19,6 +19,8 @@ class MultiChoiceEntry { this.subtitle, }) : assert(id != null), assert(title != null); + + bool get hasSubtitle => subtitle != null; } /// Show multiple choices dialog @@ -81,7 +83,7 @@ class __MultiChoicesEntryDialogState onChanged: (v) => setState(() => _currChoice = v), ), title: Text(f.title), - subtitle: Text(f.subtitle), + subtitle: f.hasSubtitle ? Text(f.subtitle) : null, )) .toList(), ), diff --git a/lib/ui/screens/group_members_screen.dart b/lib/ui/screens/group_members_screen.dart index 9b8ef1c..a8b03cc 100644 --- a/lib/ui/screens/group_members_screen.dart +++ b/lib/ui/screens/group_members_screen.dart @@ -31,13 +31,16 @@ class GroupMembersScreen extends StatefulWidget { class _GroupMembersScreenState extends State { final _key = GlobalKey(); + Group _group; GroupMembersList _members; UsersList _users; Future _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; } @@ -74,6 +77,7 @@ class _GroupMembersScreenState extends State { final user = _users.getUser(membership.userID); return _GroupMembershipTile( + group: _group, membership: membership, user: user, onUpdated: () => _key.currentState.refresh(), @@ -110,8 +114,7 @@ List> get _membershipLevels => [ MultiChoiceEntry( id: GroupMembershipLevel.ADMINISTRATOR, title: tr("Administrator"), - subtitle: - tr("Can change members privileges and change group settings")), + subtitle: tr("Can change members privileges and group settings")), MultiChoiceEntry( id: GroupMembershipLevel.MODERATOR, title: tr("Moderator"), @@ -128,16 +131,19 @@ List> get _membershipLevels => [ ]; 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(membership != null), + }) : assert(group != null), + assert(membership != null), assert(user != null), assert(onUpdated != null), super(key: key); @@ -146,7 +152,7 @@ class _GroupMembershipTile extends StatefulWidget { __GroupMembershipTileState createState() => __GroupMembershipTileState(); } -enum _MemberMenuOptions { DELETE } +enum _MemberMenuOptions { CHANGE_LEVEL, DELETE } class __GroupMembershipTileState extends State<_GroupMembershipTile> { int get groupID => widget.membership.groupID; @@ -194,6 +200,13 @@ class __GroupMembershipTileState extends State<_GroupMembershipTile> { Widget _buildMemberCase() { return PopupMenuButton<_MemberMenuOptions>( itemBuilder: (c) => [ + // Change membership level + PopupMenuItem( + child: Text(tr("Change level")), + enabled: widget.group.isAdmin, + value: _MemberMenuOptions.CHANGE_LEVEL, + ), + // Remove membership PopupMenuItem( child: Text(tr("Remove")), @@ -206,12 +219,36 @@ class __GroupMembershipTileState extends State<_GroupMembershipTile> { 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, userID, 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(