From d178e8b1c31460dde18e9160d71081853e1c9ec8 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Fri, 23 Apr 2021 19:32:34 +0200 Subject: [PATCH] Can request to join a Forez group --- lib/forez/helpers/forez_group_helper.dart | 19 +++++ lib/forez/tour/forez_tour_builder.dart | 2 +- lib/forez/tour/join_group_pane.dart | 87 +++++++++++++++++---- lib/helpers/groups_helper.dart | 8 +- lib/helpers/preferences_helper.dart | 2 + lib/models/group.dart | 20 +++++ lib/ui/widgets/group_membership_widget.dart | 4 +- lib/ui/widgets/login_routes_theme.dart | 3 + 8 files changed, 122 insertions(+), 23 deletions(-) create mode 100644 lib/forez/helpers/forez_group_helper.dart diff --git a/lib/forez/helpers/forez_group_helper.dart b/lib/forez/helpers/forez_group_helper.dart new file mode 100644 index 0000000..dade2bc --- /dev/null +++ b/lib/forez/helpers/forez_group_helper.dart @@ -0,0 +1,19 @@ +import 'package:comunic/helpers/preferences_helper.dart'; + +/// Forez group helper +/// +/// Contains the ID of the currently selected Forez group +/// +/// @author Pierre Hubert + +class ForezGroupHelper { + static Future setId(int groupID) async { + (await PreferencesHelper.getInstance()) + .setInt(PreferencesKeyList.FOREZ_GROUP, groupID); + } + + static Future getId() async { + return (await PreferencesHelper.getInstance()) + .getInt(PreferencesKeyList.FOREZ_GROUP); + } +} diff --git a/lib/forez/tour/forez_tour_builder.dart b/lib/forez/tour/forez_tour_builder.dart index 8257da6..4ae9342 100644 --- a/lib/forez/tour/forez_tour_builder.dart +++ b/lib/forez/tour/forez_tour_builder.dart @@ -15,7 +15,7 @@ const _JOIN_GROUP_KEY_ID = 1; List buildTour(TourRouteState state) { if (!state.pubKeys.containsKey(_JOIN_GROUP_KEY_ID)) - state.pubKeys[_JOIN_GROUP_KEY_ID] = GlobalKey(); + state.pubKeys[_JOIN_GROUP_KEY_ID] = GlobalKey(); return [ FirstTourPane( diff --git a/lib/forez/tour/join_group_pane.dart b/lib/forez/tour/join_group_pane.dart index b0771cf..534994f 100644 --- a/lib/forez/tour/join_group_pane.dart +++ b/lib/forez/tour/join_group_pane.dart @@ -1,8 +1,13 @@ +import 'package:comunic/forez/helpers/forez_group_helper.dart'; import 'package:comunic/helpers/forez_groups_helper.dart'; +import 'package:comunic/helpers/groups_helper.dart'; import 'package:comunic/models/group.dart'; +import 'package:comunic/ui/dialogs/alert_dialog.dart'; import 'package:comunic/ui/widgets/async_screen_widget.dart'; import 'package:comunic/ui/widgets/tour/presentation_pane.dart'; import 'package:comunic/utils/intl_utils.dart'; +import 'package:comunic/utils/log_utils.dart'; +import 'package:comunic/utils/ui_utils.dart'; import 'package:flutter/material.dart'; /// Ask the user to join a Forez group @@ -12,14 +17,16 @@ import 'package:flutter/material.dart'; class JoinGroupPane extends PresentationPane { JoinGroupPane({ @required Function() onUpdated, - @required GlobalKey key, + @required GlobalKey key, }) : super( icon: Icons.login, - title: tr("Join a group"), + title: tr("Join a Forez group"), child: (c) => _JoinGroupPaneBody( key: key, onUpdated: onUpdated, ), + canGoNext: key?.currentState?.canGoNext ?? false, + onTapNext: (c) => key.currentState.validateChoice(), ); } @@ -33,14 +40,20 @@ class _JoinGroupPaneBody extends StatefulWidget { super(key: key); @override - __JoinGroupPaneBodyState createState() => __JoinGroupPaneBodyState(); + JoinGroupPaneBodyState createState() => JoinGroupPaneBodyState(); } -class __JoinGroupPaneBodyState extends State<_JoinGroupPaneBody> { +class JoinGroupPaneBodyState extends State<_JoinGroupPaneBody> { List _groups; + int _currChoice; + + bool get canGoNext => _currChoice != null && _currChoice > 0; + + Group get _currGroup => _groups.firstWhere((e) => e.id == _currChoice); Future _load() async { _groups = await ForezGroupsHelper.getForezGroups(); + _currChoice = _groups[0].id; } @override @@ -49,18 +62,60 @@ class __JoinGroupPaneBodyState extends State<_JoinGroupPaneBody> { onBuild: onBuild, errorMessage: tr("Failed to load the list of Forez groups!")); - List get _initialWidgets => [ - Text(tr("Please choose now the Forez group you want to join...")), - ]; + Widget onBuild() => ConstrainedBox( + constraints: BoxConstraints(maxWidth: 300), + child: Column( + children: [ + Text(tr("Please choose now the Forez group you want to join...")), + ]..addAll(_groups.map((e) => RadioListTile( + value: e.id, + groupValue: _currChoice, + onChanged: (v) => setState(() => _currChoice = _currChoice), + title: Text(e.name), + subtitle: Text(e.membershipText), + ))), + ), + ); - Widget onBuild() => Flexible( - child: ListView.builder( - itemCount: _initialWidgets.length + _groups.length, - itemBuilder: (c, i) { - if (i < _initialWidgets.length) return _initialWidgets[i]; + Future validateChoice() async { + try { + switch (_currGroup.membershipLevel) { + case GroupMembershipLevel.VISITOR: + if (!await GroupsHelper.sendRequest(_currGroup.id)) + throw Exception("Failed to send group membership request!"); + break; - final group = _groups[i - _initialWidgets.length]; - return ListTile(); - }, - )); + case GroupMembershipLevel.INVITED: + if (!await GroupsHelper.respondInvitation(_currGroup.id, true)) + throw Exception( + "Failed to respond to group membership invitation!"); + break; + + case GroupMembershipLevel.PENDING: + break; + + case GroupMembershipLevel.ADMINISTRATOR: + case GroupMembershipLevel.MODERATOR: + case GroupMembershipLevel.MEMBER: + break; + } + + // Check if the user can not access yet the group + if ((_currGroup.membershipLevel == GroupMembershipLevel.VISITOR && + _currGroup.registrationLevel != GroupRegistrationLevel.OPEN) || + _currGroup.membershipLevel == GroupMembershipLevel.PENDING) { + await alert(context, + tr("You can not access this group yet, please wait for a member of the group to accept your request. Hopefully this will not be too long. Please check back soon!")); + + return false; + } + + await ForezGroupHelper.setId(_currGroup.id); + return true; + } catch (e, s) { + logError(e, s); + snack(context, tr("Failed to register to group!")); + return false; + } + } } diff --git a/lib/helpers/groups_helper.dart b/lib/helpers/groups_helper.dart index 6825e5d..64004c9 100644 --- a/lib/helpers/groups_helper.dart +++ b/lib/helpers/groups_helper.dart @@ -141,9 +141,9 @@ class GroupsHelper { } /// Perform a simple membership request - Future _simpleMembershipRequest(int groupID, String uri, + static Future _simpleMembershipRequest(int groupID, String uri, {Map args}) async => - (await (APIRequest(uri: uri, needLogin: true) + (await (APIRequest.withLogin(uri) ..addInt("id", groupID) ..addArgs(args == null ? Map() : args)) .exec()) @@ -158,11 +158,11 @@ class GroupsHelper { _simpleMembershipRequest(groupID, "groups/cancel_request"); /// Send a new membership request - Future sendRequest(int groupID) async => + static Future sendRequest(int groupID) async => _simpleMembershipRequest(groupID, "groups/send_request"); /// Respond to a group membership invitation - Future respondInvitation(int groupID, bool accept) async => + static Future respondInvitation(int groupID, bool accept) async => _simpleMembershipRequest(groupID, "groups/respond_invitation", args: { "accept": accept ? "true" : "false", }); diff --git a/lib/helpers/preferences_helper.dart b/lib/helpers/preferences_helper.dart index a6f9cb7..2486621 100644 --- a/lib/helpers/preferences_helper.dart +++ b/lib/helpers/preferences_helper.dart @@ -15,6 +15,7 @@ enum PreferencesKeyList { SHOW_PERFORMANCE_OVERLAY, PUSH_NOTIFICATIONS_STATUS, IS_TOUR_SEEN, + FOREZ_GROUP, } const _PreferenceKeysName = { @@ -25,6 +26,7 @@ const _PreferenceKeysName = { PreferencesKeyList.SHOW_PERFORMANCE_OVERLAY: "perfs_overlay", PreferencesKeyList.PUSH_NOTIFICATIONS_STATUS: "push_notifications_status", PreferencesKeyList.IS_TOUR_SEEN: "is_tour_seen", + PreferencesKeyList.FOREZ_GROUP: "forez_group", }; class PreferencesHelper { diff --git a/lib/models/group.dart b/lib/models/group.dart index de448d7..15967cf 100644 --- a/lib/models/group.dart +++ b/lib/models/group.dart @@ -1,3 +1,4 @@ +import 'package:comunic/utils/intl_utils.dart'; import 'package:meta/meta.dart'; /// Group information @@ -71,6 +72,25 @@ class Group implements Comparable { (membershipLevel == GroupMembershipLevel.MEMBER && postCreationLevel == GroupPostCreationLevel.MEMBERS); + String get membershipText { + switch (membershipLevel) { + case GroupMembershipLevel.ADMINISTRATOR: + return tr("Administrator"); + case GroupMembershipLevel.MODERATOR: + return tr("Moderator"); + case GroupMembershipLevel.MEMBER: + return tr("Member"); + case GroupMembershipLevel.INVITED: + return tr("Invited"); + case GroupMembershipLevel.PENDING: + return tr("Requested"); + case GroupMembershipLevel.VISITOR: + return tr("Visitor"); + } + + throw new Exception("Unreachable statement!"); + } + @override int compareTo(Group other) => id.compareTo(other.id); } diff --git a/lib/ui/widgets/group_membership_widget.dart b/lib/ui/widgets/group_membership_widget.dart index cd7d194..ff47935 100644 --- a/lib/ui/widgets/group_membership_widget.dart +++ b/lib/ui/widgets/group_membership_widget.dart @@ -87,7 +87,7 @@ class _GroupMembershipWidgetState extends SafeState { message: tr("Do you really want to reject this invitation?"))) return; - if (!await GroupsHelper().respondInvitation(_id, accept)) { + if (!await GroupsHelper.respondInvitation(_id, accept)) { showSimpleSnack(context, tr("Could not respond to your invitation!")); if (this.widget.onError != null) this.widget.onError(); } else { @@ -147,7 +147,7 @@ class _GroupMembershipWidgetState extends SafeState { /// Create new membership request void _requestMembership() async { - if (!await GroupsHelper().sendRequest(_id)) { + if (!await GroupsHelper.sendRequest(_id)) { showSimpleSnack(context, tr("Could not send your membership request!")); if (this.widget.onError != null) this.widget.onError(); } else { diff --git a/lib/ui/widgets/login_routes_theme.dart b/lib/ui/widgets/login_routes_theme.dart index 5796e82..ed01e91 100644 --- a/lib/ui/widgets/login_routes_theme.dart +++ b/lib/ui/widgets/login_routes_theme.dart @@ -46,6 +46,9 @@ class LoginRoutesTheme extends StatelessWidget { accentColor: Colors.white, hintColor: Colors.white, textTheme: TextTheme(subtitle1: TextStyle(color: Colors.white)), + radioTheme: RadioThemeData( + fillColor: MaterialStateProperty.all(Colors.white), + ), colorScheme: ColorScheme( primary: Colors.white, primaryVariant: Colors.white,