mirror of
https://gitlab.com/comunic/comunicmobile
synced 2024-11-22 21:09:21 +00:00
424 lines
14 KiB
Dart
424 lines
14 KiB
Dart
import 'dart:typed_data';
|
|
|
|
import 'package:comunic/helpers/conversations_helper.dart';
|
|
import 'package:comunic/lists/group_members_list.dart';
|
|
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/conversation.dart';
|
|
import 'package:comunic/models/group.dart';
|
|
import 'package:comunic/models/group_membership.dart';
|
|
import 'package:comunic/models/new_group_conversation.dart';
|
|
import 'package:comunic/utils/api_utils.dart';
|
|
import 'package:comunic/utils/map_utils.dart';
|
|
|
|
/// Groups helper
|
|
///
|
|
/// @author Pierre HUBERT
|
|
|
|
const APIGroupsMembershipLevelsMap = {
|
|
"administrator": GroupMembershipLevel.ADMINISTRATOR,
|
|
"moderator": GroupMembershipLevel.MODERATOR,
|
|
"member": GroupMembershipLevel.MEMBER,
|
|
"invited": GroupMembershipLevel.INVITED,
|
|
"pending": GroupMembershipLevel.PENDING,
|
|
"visitor": GroupMembershipLevel.VISITOR
|
|
};
|
|
|
|
const _APIGroupsVisibilityLevelsMap = {
|
|
"open": GroupVisibilityLevel.OPEN,
|
|
"private": GroupVisibilityLevel.PRIVATE,
|
|
"secrete": GroupVisibilityLevel.SECRETE
|
|
};
|
|
|
|
const _APIGroupsRegistrationLevelsMap = {
|
|
"open": GroupRegistrationLevel.OPEN,
|
|
"moderated": GroupRegistrationLevel.MODERATED,
|
|
"closed": GroupRegistrationLevel.CLOSED
|
|
};
|
|
|
|
const _APIGroupsPostsCreationLevelsMap = {
|
|
"moderators": GroupPostCreationLevel.MODERATORS,
|
|
"members": GroupPostCreationLevel.MEMBERS
|
|
};
|
|
|
|
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 {
|
|
final response = await APIRequest(
|
|
uri: "groups/get_multiple_info",
|
|
needLogin: true,
|
|
args: {"list": groups.join(",")},
|
|
).exec();
|
|
|
|
if (response.code != 200) return null;
|
|
|
|
final list = GroupsList();
|
|
|
|
response
|
|
.getObject()
|
|
.forEach((k, d) => list[int.parse(k)] = _getGroupFromAPI(d));
|
|
|
|
return list;
|
|
}
|
|
|
|
/// Get a list of groups from the server. In case of error, this method throws
|
|
/// an exception
|
|
Future<GroupsList> getListOrThrow(Set<int> groups,
|
|
{bool force = false}) async {
|
|
final list = await getList(groups, force: force);
|
|
|
|
if (list == null) throw Exception("Could not get the list of groups!");
|
|
|
|
return list;
|
|
}
|
|
|
|
/// Get a list of groups from the server
|
|
Future<GroupsList> getList(Set<int> groups, {bool force = false}) async {
|
|
final list = GroupsList();
|
|
|
|
// Check which groups information to download
|
|
final toDownload = Set<int>();
|
|
groups.forEach((groupID) {
|
|
if (!force && _groupsListCache.containsKey(groupID))
|
|
list[groupID] = _groupsListCache[groupID];
|
|
else
|
|
toDownload.add(groupID);
|
|
});
|
|
|
|
// Download required groups information
|
|
if (toDownload.length > 0) {
|
|
final downloaded = await _downloadList(toDownload);
|
|
|
|
if (downloaded == null) return null;
|
|
|
|
list.addAll(downloaded);
|
|
_groupsListCache.addAll(downloaded);
|
|
}
|
|
|
|
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())
|
|
.assertOk()
|
|
.getArray()
|
|
.map((f) => cast<int>(f))
|
|
.toSet();
|
|
|
|
/// Create a new group
|
|
///
|
|
/// Throws in case of failure
|
|
static Future<int> create(String name) async {
|
|
final result = await APIRequest.withLogin("groups/create")
|
|
.addString("name", name)
|
|
.execWithThrow();
|
|
|
|
return result.getObject()["id"];
|
|
}
|
|
|
|
/// Perform a simple membership request
|
|
Future<bool> _simpleMembershipRequest(int groupID, String uri,
|
|
{Map<String, String> args}) async =>
|
|
(await (APIRequest(uri: uri, needLogin: true)
|
|
..addInt("id", groupID)
|
|
..addArgs(args == null ? Map() : args))
|
|
.exec())
|
|
.isOK;
|
|
|
|
/// Remove group membership
|
|
Future<bool> removeMembership(int groupID) async =>
|
|
_simpleMembershipRequest(groupID, "groups/remove_membership");
|
|
|
|
/// Cancel membership request
|
|
Future<bool> cancelRequest(int groupID) async =>
|
|
_simpleMembershipRequest(groupID, "groups/cancel_request");
|
|
|
|
/// Send a new membership request
|
|
Future<bool> sendRequest(int groupID) async =>
|
|
_simpleMembershipRequest(groupID, "groups/send_request");
|
|
|
|
/// Respond to a group membership invitation
|
|
Future<bool> respondInvitation(int groupID, bool accept) async =>
|
|
_simpleMembershipRequest(groupID, "groups/respond_invitation", args: {
|
|
"accept": accept ? "true" : "false",
|
|
});
|
|
|
|
/// Update group following status
|
|
Future<bool> setFollowing(int groupID, bool follow) async =>
|
|
(await (APIRequest(uri: "groups/set_following", needLogin: true)
|
|
..addInt("groupID", groupID)
|
|
..addBool("follow", follow))
|
|
.exec())
|
|
.isOK;
|
|
|
|
/// 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!");
|
|
}
|
|
}
|
|
|
|
/// Get group settings
|
|
///
|
|
/// This function is currently a kind of alias, but it might
|
|
/// change in the future
|
|
///
|
|
/// Throws in case of error
|
|
Future<AdvancedGroupInfo> getSettings(int groupID) async {
|
|
final groupInfo = await getAdvancedInfo(groupID);
|
|
|
|
if (groupInfo.status != GetAdvancedInfoStatus.SUCCESS)
|
|
throw Exception("Could not get group information!");
|
|
|
|
return groupInfo.info;
|
|
}
|
|
|
|
/// Check the availability of a virtual directory
|
|
///
|
|
/// Throws in case of error
|
|
static Future<void> checkVirtualDirectoryAvailability(
|
|
int groupID, String dir) async =>
|
|
await APIRequest(uri: "groups/checkVirtualDirectory", needLogin: true)
|
|
.addInt("groupID", groupID)
|
|
.addString("directory", dir)
|
|
.execWithThrow();
|
|
|
|
/// Update (set) new group settings
|
|
///
|
|
/// Throws in case of error
|
|
static Future<void> setSettings(AdvancedGroupInfo settings) async {
|
|
await APIRequest(uri: "groups/set_settings", needLogin: true)
|
|
.addInt("id", settings.id)
|
|
.addString("name", settings.name)
|
|
.addString("virtual_directory", settings.virtualDirectory)
|
|
.addString("visibility",
|
|
invertMap(_APIGroupsVisibilityLevelsMap)[settings.visibilityLevel])
|
|
.addString(
|
|
"registration_level",
|
|
invertMap(
|
|
_APIGroupsRegistrationLevelsMap)[settings.registrationLevel])
|
|
.addString(
|
|
"posts_level",
|
|
invertMap(
|
|
_APIGroupsPostsCreationLevelsMap)[settings.postCreationLevel])
|
|
.addBool("is_members_list_public", settings.isMembersListPublic)
|
|
.addString("description", settings.description)
|
|
.addString("url", settings.url)
|
|
.execWithThrow();
|
|
}
|
|
|
|
/// Upload a new logo
|
|
///
|
|
/// Throws in case of failure
|
|
static Future<void> uploadNewLogo(int groupID, Uint8List bytes) async =>
|
|
await APIRequest(uri: "groups/upload_logo", needLogin: true)
|
|
.addInt("id", groupID)
|
|
.addBytesFile("logo", BytesFile("logo.png", bytes))
|
|
.execWithFilesAndThrow();
|
|
|
|
/// Delete group logo
|
|
///
|
|
/// Throws in case of error
|
|
static Future<void> deleteLogo(int groupID) async =>
|
|
await APIRequest(uri: "groups/delete_logo", needLogin: true)
|
|
.addInt("id", groupID)
|
|
.execWithThrow();
|
|
|
|
/// Delete a group
|
|
///
|
|
/// Throws in case of error
|
|
static Future<void> deleteGroup(int groupID, String password) async =>
|
|
await APIRequest(uri: "groups/delete", needLogin: true)
|
|
.addInt("groupID", groupID)
|
|
.addString("password", password)
|
|
.execWithThrow();
|
|
|
|
/// Get the list of members of the group
|
|
///
|
|
/// Throws in case of failure
|
|
static Future<GroupMembersList> getMembersList(int groupID) async =>
|
|
GroupMembersList()
|
|
..addAll((await APIRequest(uri: "groups/get_members", needLogin: true)
|
|
.addInt("id", groupID)
|
|
.execWithThrow())
|
|
.getArray()
|
|
.map((f) => _apiToGroupMembership(f))
|
|
.toList());
|
|
|
|
/// Invite a user to join a group
|
|
///
|
|
/// Throws an exception in case of failure
|
|
static Future<void> sendInvitation(int groupID, int userID) async =>
|
|
APIRequest.withLogin("groups/invite")
|
|
.addInt("group_id", groupID)
|
|
.addInt("userID", userID)
|
|
.execWithThrow();
|
|
|
|
/// Cancel a group membership invitation
|
|
///
|
|
/// Throws an exception in case of failure
|
|
static Future<void> cancelInvitation(int groupID, int userID) async =>
|
|
await APIRequest.withLogin("groups/cancel_invitation")
|
|
.addInt("groupID", groupID)
|
|
.addInt("userID", userID)
|
|
.execWithThrow();
|
|
|
|
/// Respond to a group membership request
|
|
///
|
|
/// Throws an exception in case of failure
|
|
static Future<void> respondRequest(
|
|
int groupID, int userID, bool accept) async =>
|
|
await APIRequest.withLogin("groups/respond_request")
|
|
.addInt("groupID", groupID)
|
|
.addInt("userID", userID)
|
|
.addBool("accept", accept)
|
|
.execWithThrow();
|
|
|
|
/// Remove a member from a group
|
|
///
|
|
/// Throws an exception in case of failure
|
|
static Future<void> removeMemberFromGroup(int groupID, int userID) async =>
|
|
APIRequest.withLogin("groups/delete_member")
|
|
.addInt("groupID", groupID)
|
|
.addInt("userID", userID)
|
|
.execWithThrow();
|
|
|
|
/// Change the membership level of a member of a group
|
|
///
|
|
/// Throws an exception in case of failure
|
|
static Future<void> 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();
|
|
|
|
/// Create a new group conversation
|
|
///
|
|
/// Throws in case of failure
|
|
static Future<void> createGroupConversation(
|
|
NewGroupConversation conv) async =>
|
|
await APIRequest.withLogin("groups/create_conversation")
|
|
.addInt("group_id", conv.groupID)
|
|
.addString(
|
|
"min_membership_level",
|
|
APIGroupsMembershipLevelsMap.entries
|
|
.firstWhere((e) => e.value == conv.minMembershipLevel)
|
|
.key)
|
|
.addString("name", conv.name)
|
|
.execWithThrow();
|
|
|
|
/// Set new conversation visibility level
|
|
///
|
|
/// Throws in case of failure
|
|
static Future<void> setConversationVisibility(
|
|
int convID, GroupMembershipLevel newLevel) async =>
|
|
await APIRequest.withLogin("groups/set_conversation_visibility")
|
|
.addInt("conv_id", convID)
|
|
.addString(
|
|
"min_membership_level",
|
|
APIGroupsMembershipLevelsMap.entries
|
|
.firstWhere((e) => e.value == newLevel)
|
|
.key)
|
|
.execWithThrow();
|
|
|
|
/// Delete a group's conversation
|
|
///
|
|
/// Throws in case of failure
|
|
static Future<void> deleteConversation(int convID) async =>
|
|
await APIRequest.withLogin("groups/delete_conversation")
|
|
.addInt("conv_id", convID)
|
|
.execWithThrow();
|
|
|
|
/// Turn an API entry into a group object
|
|
Group _getGroupFromAPI(Map<String, dynamic> map) {
|
|
return Group(
|
|
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: nullToEmpty(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"]],
|
|
isMembersListPublic: map["is_members_list_public"],
|
|
virtualDirectory: nullToEmpty(map["virtual_directory"]),
|
|
following: map["following"],
|
|
timeCreate: map["time_create"],
|
|
description: nullToEmpty(map["description"]),
|
|
url: nullToEmpty(map["url"]),
|
|
likes: map["number_likes"],
|
|
userLike: map["is_liking"],
|
|
conversations: map["conversations"]
|
|
.map((s) => ConversationsHelper.apiToConversation(s))
|
|
.cast<Conversation>()
|
|
.toList(),
|
|
isForezGroup: map["is_forez_group"],
|
|
);
|
|
|
|
/// Create [GroupMembership] object from API entry
|
|
static GroupMembership _apiToGroupMembership(Map<String, dynamic> row) =>
|
|
GroupMembership(
|
|
userID: row["user_id"],
|
|
groupID: row["group_id"],
|
|
timeCreate: row["time_create"],
|
|
level: APIGroupsMembershipLevelsMap[row["level"]],
|
|
);
|
|
}
|