mirror of
https://gitlab.com/comunic/comunicmobile
synced 2024-12-26 12:58:51 +00:00
Start to build groups page
This commit is contained in:
parent
c7d8843f06
commit
b10163575f
@ -1,4 +1,5 @@
|
||||
import 'package:comunic/lists/groups_list.dart';
|
||||
import 'package:comunic/models/advanced_group_info.dart';
|
||||
import 'package:comunic/models/api_request.dart';
|
||||
import 'package:comunic/models/group.dart';
|
||||
import 'package:comunic/utils/api_utils.dart';
|
||||
@ -35,6 +36,17 @@ const _APIGroupsPostsCreationLevelsMap = {
|
||||
|
||||
final _groupsListCache = GroupsList();
|
||||
|
||||
/// Callback for getting advanced user information
|
||||
enum GetAdvancedInfoStatus { SUCCESS, ACCESS_DENIED }
|
||||
|
||||
class GetAdvancedInfoResult {
|
||||
final GetAdvancedInfoStatus status;
|
||||
final AdvancedGroupInfo info;
|
||||
|
||||
GetAdvancedInfoResult(this.status, this.info) : assert(status != null);
|
||||
}
|
||||
|
||||
/// Groups helper
|
||||
class GroupsHelper {
|
||||
/// Download a list of groups information from the server
|
||||
Future<GroupsList> _downloadList(Set<int> groups) async {
|
||||
@ -92,6 +104,15 @@ class GroupsHelper {
|
||||
return list;
|
||||
}
|
||||
|
||||
/// Get information about a single group
|
||||
///
|
||||
/// Throws in case of failure
|
||||
Future<Group> getSingle(int groupID, {bool force = false}) async {
|
||||
return (await getListOrThrow(Set<int>()..add(groupID), force: force))
|
||||
.values
|
||||
.first;
|
||||
}
|
||||
|
||||
/// Get the list of groups of a user
|
||||
Future<Set<int>> getListUser() async =>
|
||||
(await APIRequest(uri: "groups/get_my_list", needLogin: true).exec())
|
||||
@ -127,6 +148,27 @@ class GroupsHelper {
|
||||
"accept": accept ? "true" : "false",
|
||||
});
|
||||
|
||||
/// Get advanced information about the user
|
||||
Future<GetAdvancedInfoResult> getAdvancedInfo(int groupID) async {
|
||||
// Get advanced information about the user
|
||||
final result =
|
||||
await (APIRequest(uri: "groups/get_advanced_info", needLogin: true)
|
||||
..addInt("id", groupID))
|
||||
.exec();
|
||||
|
||||
switch (result.code) {
|
||||
case 401:
|
||||
return GetAdvancedInfoResult(GetAdvancedInfoStatus.ACCESS_DENIED, null);
|
||||
|
||||
case 200:
|
||||
return GetAdvancedInfoResult(GetAdvancedInfoStatus.SUCCESS,
|
||||
_getAdvancedGroupInfoFromAPI(result.getObject()));
|
||||
|
||||
default:
|
||||
throw Exception("Could not get advanced group information!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn an API entry into a group object
|
||||
Group _getGroupFromAPI(Map<String, dynamic> map) {
|
||||
return Group(
|
||||
@ -142,4 +184,25 @@ class GroupsHelper {
|
||||
virtualDirectory: map["virtual_directory"],
|
||||
following: map["following"]);
|
||||
}
|
||||
|
||||
/// Get advanced group information
|
||||
AdvancedGroupInfo _getAdvancedGroupInfoFromAPI(Map<String, dynamic> map) =>
|
||||
AdvancedGroupInfo(
|
||||
id: map["id"],
|
||||
name: map["name"],
|
||||
iconURL: map["icon_url"],
|
||||
numberMembers: map["number_members"],
|
||||
membershipLevel: _APIGroupsMembershipLevelsMap[map["membership"]],
|
||||
visibilityLevel: _APIGroupsVisibilityLevelsMap[map["visibility"]],
|
||||
registrationLevel:
|
||||
_APIGroupsRegistrationLevelsMap[map["registration_level"]],
|
||||
postCreationLevel: _APIGroupsPostsCreationLevelsMap[map["posts_level"]],
|
||||
virtualDirectory: map["virtual_directory"],
|
||||
following: map["following"],
|
||||
timeCreate: map["time_create"],
|
||||
description: map["description"],
|
||||
url: map["url"],
|
||||
numberLikes: map["number_likes"],
|
||||
isLiking: map["is_liking"],
|
||||
);
|
||||
}
|
||||
|
43
lib/models/advanced_group_info.dart
Normal file
43
lib/models/advanced_group_info.dart
Normal file
@ -0,0 +1,43 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'group.dart';
|
||||
|
||||
/// Advanced group information
|
||||
///
|
||||
/// @author Pierre Hubert
|
||||
|
||||
class AdvancedGroupInfo extends Group {
|
||||
final int timeCreate;
|
||||
final String description;
|
||||
final String url;
|
||||
final int numberLikes;
|
||||
final bool isLiking;
|
||||
|
||||
AdvancedGroupInfo({
|
||||
@required int id,
|
||||
@required String name,
|
||||
@required String iconURL,
|
||||
@required int numberMembers,
|
||||
@required GroupMembershipLevel membershipLevel,
|
||||
@required GroupVisibilityLevel visibilityLevel,
|
||||
@required GroupRegistrationLevel registrationLevel,
|
||||
@required GroupPostCreationLevel postCreationLevel,
|
||||
@required String virtualDirectory,
|
||||
@required bool following,
|
||||
@required this.timeCreate,
|
||||
@required this.description,
|
||||
@required this.url,
|
||||
@required this.numberLikes,
|
||||
@required this.isLiking,
|
||||
}) : super(
|
||||
id: id,
|
||||
name: name,
|
||||
iconURL: iconURL,
|
||||
numberMembers: numberMembers,
|
||||
membershipLevel: membershipLevel,
|
||||
visibilityLevel: visibilityLevel,
|
||||
registrationLevel: registrationLevel,
|
||||
postCreationLevel: postCreationLevel,
|
||||
virtualDirectory: virtualDirectory,
|
||||
following: following);
|
||||
}
|
@ -53,4 +53,9 @@ class Group {
|
||||
assert(following != null);
|
||||
|
||||
get displayName => this.name;
|
||||
|
||||
bool get getIsAtLeastMember =>
|
||||
membershipLevel == GroupMembershipLevel.ADMINISTRATOR ||
|
||||
membershipLevel == GroupMembershipLevel.MODERATOR ||
|
||||
membershipLevel == GroupMembershipLevel.MEMBER;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import 'package:comunic/helpers/account_helper.dart';
|
||||
import 'package:comunic/ui/routes/app_settings_route.dart';
|
||||
import 'package:comunic/ui/screens/conversations_list_screen.dart';
|
||||
import 'package:comunic/ui/screens/friends_list_screen.dart';
|
||||
import 'package:comunic/ui/screens/group_screen.dart';
|
||||
import 'package:comunic/ui/screens/groups_list_screen.dart';
|
||||
import 'package:comunic/ui/screens/newest_posts.dart';
|
||||
import 'package:comunic/ui/screens/notifications_screen.dart';
|
||||
@ -21,6 +22,10 @@ import 'login_route.dart';
|
||||
class HomeRoute extends StatefulWidget {
|
||||
@override
|
||||
State<StatefulWidget> createState() => _HomeRouteState();
|
||||
|
||||
/// Get current instance of Home controller
|
||||
static HomeController of(BuildContext context) =>
|
||||
context.findAncestorStateOfType<HomeController>();
|
||||
}
|
||||
|
||||
class CurrPage {
|
||||
@ -28,9 +33,24 @@ class CurrPage {
|
||||
final Map<String, dynamic> args;
|
||||
|
||||
const CurrPage(this.action, {this.args}) : assert(action != null);
|
||||
|
||||
@override
|
||||
String toString() =>
|
||||
"CurrPage {\n\taction: " +
|
||||
this.action.toString() +
|
||||
"\n\targs: " +
|
||||
this.args.toString() +
|
||||
"\n}";
|
||||
}
|
||||
|
||||
class _HomeRouteState extends State<HomeRoute> {
|
||||
/// Public interface of home controller
|
||||
abstract class HomeController extends State<HomeRoute> {
|
||||
/// Open a specific group page specified by its [groupID]
|
||||
void openGroup(int groupID);
|
||||
}
|
||||
|
||||
/// Private implementation of HomeController
|
||||
class _HomeRouteState extends HomeController {
|
||||
CurrPage get _currTab => history.last;
|
||||
List<CurrPage> history = List();
|
||||
|
||||
@ -106,6 +126,9 @@ class _HomeRouteState extends State<HomeRoute> {
|
||||
case BarCallbackActions.OPEN_GROUPS:
|
||||
return GroupsListScreen();
|
||||
|
||||
case BarCallbackActions.OPEN_GROUP_PAGE:
|
||||
return GroupPageScreen(groupID: _currTab.args["groupID"]);
|
||||
|
||||
default:
|
||||
throw "Invalid tab : " + _currTab.toString();
|
||||
}
|
||||
@ -156,4 +179,10 @@ class _HomeRouteState extends State<HomeRoute> {
|
||||
return LoginRoute();
|
||||
}));
|
||||
}
|
||||
|
||||
@override
|
||||
void openGroup(int groupID) {
|
||||
_pushPage(CurrPage(BarCallbackActions.OPEN_GROUP_PAGE,
|
||||
args: {"groupID": groupID}));
|
||||
}
|
||||
}
|
||||
|
106
lib/ui/screens/group_access_denied_screen.dart
Normal file
106
lib/ui/screens/group_access_denied_screen.dart
Normal file
@ -0,0 +1,106 @@
|
||||
import 'package:comunic/helpers/groups_helper.dart';
|
||||
import 'package:comunic/models/group.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/intl_utils.dart';
|
||||
import 'package:comunic/utils/ui_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Group access denied screen
|
||||
///
|
||||
/// @author Pierre Hubert
|
||||
|
||||
class GroupAccessDeniedScreen extends StatefulWidget {
|
||||
final int groupID;
|
||||
final Function() onMembershipAcquired;
|
||||
|
||||
const GroupAccessDeniedScreen({
|
||||
Key key,
|
||||
@required this.groupID,
|
||||
@required this.onMembershipAcquired,
|
||||
}) : assert(groupID != null),
|
||||
assert(onMembershipAcquired != null),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
_GroupAccessDeniedScreenState createState() =>
|
||||
_GroupAccessDeniedScreenState();
|
||||
}
|
||||
|
||||
class _GroupAccessDeniedScreenState extends SafeState<GroupAccessDeniedScreen> {
|
||||
Group _group;
|
||||
|
||||
bool error = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_refresh();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (error)
|
||||
return buildErrorCard(tr("Could not get basic group information!"),
|
||||
actions: [
|
||||
MaterialButton(
|
||||
child: Text(tr("Try again").toUpperCase()),
|
||||
onPressed: () => this._refresh(),
|
||||
)
|
||||
]);
|
||||
|
||||
if (_group == null) return buildCenteredProgressBar();
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Spacer(
|
||||
flex: 5,
|
||||
),
|
||||
GroupIcon(group: _group),
|
||||
Spacer(),
|
||||
Text(
|
||||
_group.displayName,
|
||||
style: TextStyle(fontSize: 20),
|
||||
),
|
||||
Spacer(),
|
||||
Text(tr("A registration is required to access this group page.")),
|
||||
Spacer(),
|
||||
GroupMembershipWidget(
|
||||
group: _group,
|
||||
onUpdated: () => this._refresh(),
|
||||
onError: () => this._refresh(),
|
||||
),
|
||||
Spacer(
|
||||
flex: 5,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Get basic information about the groups
|
||||
Future<void> _refresh() async {
|
||||
try {
|
||||
setState(() {
|
||||
error = false;
|
||||
_group = null;
|
||||
});
|
||||
|
||||
// Get information about a single group
|
||||
final group = await GroupsHelper().getSingle(widget.groupID, force: true);
|
||||
|
||||
if (group.getIsAtLeastMember) widget.onMembershipAcquired();
|
||||
|
||||
setState(() => _group = group);
|
||||
} catch (e) {
|
||||
print(e);
|
||||
setState(() => error = true);
|
||||
}
|
||||
}
|
||||
}
|
80
lib/ui/screens/group_screen.dart
Normal file
80
lib/ui/screens/group_screen.dart
Normal file
@ -0,0 +1,80 @@
|
||||
import 'package:comunic/helpers/groups_helper.dart';
|
||||
import 'package:comunic/ui/screens/group_access_denied_screen.dart';
|
||||
import 'package:comunic/ui/widgets/safe_state.dart';
|
||||
import 'package:comunic/utils/intl_utils.dart';
|
||||
import 'package:comunic/utils/ui_utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// Group screen
|
||||
///
|
||||
/// @author Pierre Hubert
|
||||
|
||||
class GroupPageScreen extends StatefulWidget {
|
||||
final int groupID;
|
||||
|
||||
const GroupPageScreen({Key key, @required this.groupID})
|
||||
: assert(groupID != null),
|
||||
super(key: key);
|
||||
|
||||
@override
|
||||
_GroupPageScreenState createState() => _GroupPageScreenState();
|
||||
}
|
||||
|
||||
class _GroupPageScreenState extends SafeState<GroupPageScreen> {
|
||||
int get groupID => widget.groupID;
|
||||
|
||||
GetAdvancedInfoResult _getGroupResult;
|
||||
var error = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_refreshPage();
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (error)
|
||||
return buildErrorCard(tr("Could not load information about the group!"),
|
||||
actions: [
|
||||
MaterialButton(
|
||||
onPressed: () => _refreshPage(),
|
||||
child: Text(tr("Try again").toUpperCase()),
|
||||
)
|
||||
]);
|
||||
|
||||
// If we are still loading the page
|
||||
if (_getGroupResult == null) return buildCenteredProgressBar();
|
||||
|
||||
// Check if access to the group was denied
|
||||
if (_getGroupResult.status == GetAdvancedInfoStatus.ACCESS_DENIED)
|
||||
return GroupAccessDeniedScreen(
|
||||
groupID: groupID,
|
||||
onMembershipAcquired: () => _refreshPage(),
|
||||
);
|
||||
|
||||
// Create another screen for the group page
|
||||
}
|
||||
|
||||
/// Refresh the page
|
||||
Future<void> _refreshPage() async {
|
||||
try {
|
||||
setState(() {
|
||||
error = false;
|
||||
_getGroupResult = null;
|
||||
});
|
||||
|
||||
// Get information about the group
|
||||
final result = await GroupsHelper().getAdvancedInfo(groupID);
|
||||
|
||||
setState(() {
|
||||
_getGroupResult = result;
|
||||
});
|
||||
} catch (e) {
|
||||
print(e);
|
||||
setState(() {
|
||||
error = true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import 'package:comunic/helpers/groups_helper.dart';
|
||||
import 'package:comunic/lists/groups_list.dart';
|
||||
import 'package:comunic/models/group.dart';
|
||||
import 'package:comunic/ui/routes/home_route.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';
|
||||
@ -40,7 +41,7 @@ class _GroupsListScreenState extends SafeState<GroupsListScreen> {
|
||||
hide: !_error,
|
||||
actions: [
|
||||
MaterialButton(
|
||||
child: Text(tr("Try again".toUpperCase())),
|
||||
child: Text(tr("Try again").toUpperCase()),
|
||||
onPressed: () => _refreshList(),
|
||||
),
|
||||
],
|
||||
@ -66,6 +67,7 @@ class _GroupsListScreenState extends SafeState<GroupsListScreen> {
|
||||
trailing: IconButton(
|
||||
icon: Icon(Icons.delete),
|
||||
onPressed: () => _deleteGroup(g)),
|
||||
onTap: () => HomeRoute.of(context).openGroup(g.id),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
|
@ -13,11 +13,11 @@ import 'package:flutter/material.dart';
|
||||
class GroupMembershipWidget extends StatefulWidget {
|
||||
final Group group;
|
||||
final Function() onUpdated;
|
||||
final Function() onError;
|
||||
|
||||
const GroupMembershipWidget({
|
||||
@required this.group,
|
||||
this.onUpdated,
|
||||
}) : assert(group != null);
|
||||
const GroupMembershipWidget(
|
||||
{@required this.group, this.onUpdated, this.onError})
|
||||
: assert(group != null);
|
||||
|
||||
@override
|
||||
_GroupMembershipWidgetState createState() => _GroupMembershipWidgetState();
|
||||
@ -50,6 +50,9 @@ class _GroupMembershipWidgetState extends SafeState<GroupMembershipWidget> {
|
||||
|
||||
case GroupMembershipLevel.VISITOR:
|
||||
return _buildVisitorState();
|
||||
|
||||
default:
|
||||
throw Exception("Unkonwn grou pmembership level state: $_level");
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,9 +87,10 @@ class _GroupMembershipWidgetState extends SafeState<GroupMembershipWidget> {
|
||||
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!"));
|
||||
else {
|
||||
if (this.widget.onError != null) this.widget.onError();
|
||||
} else {
|
||||
// Refresh state
|
||||
group.membershipLevel =
|
||||
accept ? GroupMembershipLevel.MEMBER : GroupMembershipLevel.VISITOR;
|
||||
@ -114,9 +118,10 @@ class _GroupMembershipWidgetState extends SafeState<GroupMembershipWidget> {
|
||||
|
||||
/// Cancel group membership request
|
||||
void _cancelRequest() async {
|
||||
if (!await GroupsHelper().cancelRequest(_id))
|
||||
if (!await GroupsHelper().cancelRequest(_id)) {
|
||||
showSimpleSnack(context, tr("Could not cancel your membership request!"));
|
||||
else {
|
||||
if (this.widget.onError != null) this.widget.onError();
|
||||
} else {
|
||||
// Refresh state
|
||||
group.membershipLevel = GroupMembershipLevel.VISITOR;
|
||||
this.setState(() {});
|
||||
@ -138,9 +143,10 @@ class _GroupMembershipWidgetState extends SafeState<GroupMembershipWidget> {
|
||||
|
||||
/// 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!"));
|
||||
else {
|
||||
if (this.widget.onError != null) this.widget.onError();
|
||||
} else {
|
||||
// Refresh state
|
||||
group.membershipLevel = GroupMembershipLevel.PENDING;
|
||||
this.setState(() {});
|
||||
|
@ -16,6 +16,7 @@ enum BarCallbackActions {
|
||||
OPEN_FRIENDS,
|
||||
OPEN_MY_PAGE,
|
||||
OPEN_GROUPS,
|
||||
OPEN_GROUP_PAGE,
|
||||
OPEN_APP_SETTINGS,
|
||||
NONE,
|
||||
ACTION_LOGOUT
|
||||
|
Loading…
Reference in New Issue
Block a user