mirror of
https://gitlab.com/comunic/comunicmobile
synced 2024-12-27 21:38:51 +00:00
320 lines
8.8 KiB
Dart
320 lines
8.8 KiB
Dart
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, required this.groupID})
|
|
: assert(groupID != null),
|
|
super(key: key);
|
|
|
|
@override
|
|
_GroupMembersSectionState createState() => _GroupMembersSectionState();
|
|
}
|
|
|
|
class _GroupMembersSectionState extends State<GroupMembersSection> {
|
|
final _key = GlobalKey<AsyncScreenWidgetState>();
|
|
late Group _group;
|
|
late GroupMembersList _members;
|
|
late 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()),
|
|
_group.isAtLeastModerator ? _buildInvitationFAB() : Container()
|
|
],
|
|
);
|
|
}
|
|
|
|
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: widget.group.isAtLeastModerator ? _buildTrailing() : null,
|
|
);
|
|
}
|
|
|
|
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();
|
|
}
|
|
}
|