1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-11-25 22:39:22 +00:00

Can create group conversations

This commit is contained in:
Pierre HUBERT 2021-04-06 17:04:55 +02:00
parent 07b42df06a
commit 054b2a1d32
8 changed files with 135 additions and 6 deletions

View File

@ -1,3 +1,4 @@
import 'package:comunic/helpers/groups_helper.dart';
import 'package:comunic/helpers/serialization/conversation_message_serialization_helper.dart'; import 'package:comunic/helpers/serialization/conversation_message_serialization_helper.dart';
import 'package:comunic/helpers/serialization/conversations_serialization_helper.dart'; import 'package:comunic/helpers/serialization/conversations_serialization_helper.dart';
import 'package:comunic/helpers/users_helper.dart'; import 'package:comunic/helpers/users_helper.dart';
@ -238,6 +239,8 @@ class ConversationsHelper {
color: map["color"] == null ? null : HexColor(map["color"]), color: map["color"] == null ? null : HexColor(map["color"]),
logoURL: map["logo"], logoURL: map["logo"],
groupID: map["group_id"], groupID: map["group_id"],
groupMinMembershipLevel:
APIGroupsMembershipLevelsMap[map["group_min_membership_level"]],
members: map["members"] members: map["members"]
.cast<Map<String, dynamic>>() .cast<Map<String, dynamic>>()
.map(apiToConversationMember) .map(apiToConversationMember)

View File

@ -6,6 +6,7 @@ import 'package:comunic/models/advanced_group_info.dart';
import 'package:comunic/models/api_request.dart'; import 'package:comunic/models/api_request.dart';
import 'package:comunic/models/group.dart'; import 'package:comunic/models/group.dart';
import 'package:comunic/models/group_membership.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/api_utils.dart';
import 'package:comunic/utils/map_utils.dart'; import 'package:comunic/utils/map_utils.dart';
@ -13,7 +14,7 @@ import 'package:comunic/utils/map_utils.dart';
/// ///
/// @author Pierre HUBERT /// @author Pierre HUBERT
const _APIGroupsMembershipLevelsMap = { const APIGroupsMembershipLevelsMap = {
"administrator": GroupMembershipLevel.ADMINISTRATOR, "administrator": GroupMembershipLevel.ADMINISTRATOR,
"moderator": GroupMembershipLevel.MODERATOR, "moderator": GroupMembershipLevel.MODERATOR,
"member": GroupMembershipLevel.MEMBER, "member": GroupMembershipLevel.MEMBER,
@ -326,7 +327,22 @@ class GroupsHelper {
await APIRequest.withLogin("groups/update_membership_level") await APIRequest.withLogin("groups/update_membership_level")
.addInt("groupID", groupID) .addInt("groupID", groupID)
.addInt("userID", userID) .addInt("userID", userID)
.addString("level", invertMap(_APIGroupsMembershipLevelsMap)[level]) .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(); .execWithThrow();
/// Turn an API entry into a group object /// Turn an API entry into a group object
@ -336,7 +352,7 @@ class GroupsHelper {
name: map["name"], name: map["name"],
iconURL: map["icon_url"], iconURL: map["icon_url"],
numberMembers: map["number_members"], numberMembers: map["number_members"],
membershipLevel: _APIGroupsMembershipLevelsMap[map["membership"]], membershipLevel: APIGroupsMembershipLevelsMap[map["membership"]],
visibilityLevel: _APIGroupsVisibilityLevelsMap[map["visibility"]], visibilityLevel: _APIGroupsVisibilityLevelsMap[map["visibility"]],
registrationLevel: registrationLevel:
_APIGroupsRegistrationLevelsMap[map["registration_level"]], _APIGroupsRegistrationLevelsMap[map["registration_level"]],
@ -352,7 +368,7 @@ class GroupsHelper {
name: map["name"], name: map["name"],
iconURL: map["icon_url"], iconURL: map["icon_url"],
numberMembers: map["number_members"], numberMembers: map["number_members"],
membershipLevel: _APIGroupsMembershipLevelsMap[map["membership"]], membershipLevel: APIGroupsMembershipLevelsMap[map["membership"]],
visibilityLevel: _APIGroupsVisibilityLevelsMap[map["visibility"]], visibilityLevel: _APIGroupsVisibilityLevelsMap[map["visibility"]],
registrationLevel: registrationLevel:
_APIGroupsRegistrationLevelsMap[map["registration_level"]], _APIGroupsRegistrationLevelsMap[map["registration_level"]],
@ -373,6 +389,6 @@ class GroupsHelper {
userID: row["user_id"], userID: row["user_id"],
groupID: row["group_id"], groupID: row["group_id"],
timeCreate: row["time_create"], timeCreate: row["time_create"],
level: _APIGroupsMembershipLevelsMap[row["level"]], level: APIGroupsMembershipLevelsMap[row["level"]],
); );
} }

View File

@ -52,6 +52,8 @@ class ServerConfigurationHelper {
minLikesLifetime: dataConservationPolicy["min_likes_lifetime"], minLikesLifetime: dataConservationPolicy["min_likes_lifetime"],
), ),
conversationsPolicy: ConversationsPolicy( conversationsPolicy: ConversationsPolicy(
maxConversationNameLen:
conversationsPolicy["max_conversation_name_len"],
minMessageLen: conversationsPolicy["min_message_len"], minMessageLen: conversationsPolicy["min_message_len"],
maxMessageLen: conversationsPolicy["max_message_len"], maxMessageLen: conversationsPolicy["max_message_len"],
allowedFilesType: allowedFilesType:

View File

@ -1,4 +1,5 @@
import 'package:comunic/enums/likes_type.dart'; import 'package:comunic/enums/likes_type.dart';
import 'package:comunic/models/conversation.dart';
import 'package:comunic/models/like_element.dart'; import 'package:comunic/models/like_element.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@ -15,6 +16,7 @@ class AdvancedGroupInfo extends Group implements LikeElement {
String url; String url;
int likes; int likes;
bool userLike; bool userLike;
List<Conversation> conversations;
AdvancedGroupInfo({ AdvancedGroupInfo({
@required int id, @required int id,

View File

@ -4,6 +4,8 @@ import 'package:comunic/utils/account_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'group.dart';
/// Conversation model /// Conversation model
/// ///
/// @author Pierre HUBERT /// @author Pierre HUBERT
@ -17,6 +19,7 @@ class Conversation extends SerializableElement<Conversation> {
final Color color; final Color color;
final String logoURL; final String logoURL;
final int groupID; final int groupID;
final GroupMembershipLevel groupMinMembershipLevel;
final List<ConversationMember> members; final List<ConversationMember> members;
final bool canEveryoneAddMembers; final bool canEveryoneAddMembers;
final CallCapabilities callCapabilities; final CallCapabilities callCapabilities;
@ -29,6 +32,7 @@ class Conversation extends SerializableElement<Conversation> {
@required this.color, @required this.color,
@required this.logoURL, @required this.logoURL,
@required this.groupID, @required this.groupID,
@required this.groupMinMembershipLevel,
@required this.members, @required this.members,
@required this.canEveryoneAddMembers, @required this.canEveryoneAddMembers,
this.callCapabilities = CallCapabilities.NONE, this.callCapabilities = CallCapabilities.NONE,
@ -37,6 +41,7 @@ class Conversation extends SerializableElement<Conversation> {
assert(lastActivity != null), assert(lastActivity != null),
assert(members != null), assert(members != null),
assert(canEveryoneAddMembers != null), assert(canEveryoneAddMembers != null),
assert((groupID == null) == (groupMinMembershipLevel == null)),
assert(callCapabilities != null), assert(callCapabilities != null),
assert(isHavingCall != null); assert(isHavingCall != null);
@ -78,6 +83,9 @@ class Conversation extends SerializableElement<Conversation> {
color = map["color"] == null ? null : Color(map["color"]), color = map["color"] == null ? null : Color(map["color"]),
logoURL = map["logoURL"], logoURL = map["logoURL"],
groupID = map["groupID"], groupID = map["groupID"],
groupMinMembershipLevel = GroupMembershipLevel.values.firstWhere(
(element) => element.toString() == map["groupMinMembershipLevel"],
orElse: () => null),
lastActivity = map["lastActivity"], lastActivity = map["lastActivity"],
members = map["members"] members = map["members"]
.map((el) => ConversationMember.fromJSON(el)) .map((el) => ConversationMember.fromJSON(el))
@ -96,6 +104,7 @@ class Conversation extends SerializableElement<Conversation> {
"color": color?.value, "color": color?.value,
"logoURL": logoURL, "logoURL": logoURL,
"groupID": groupID, "groupID": groupID,
"groupMinMembershipLevel": groupMinMembershipLevel?.toString(),
"lastActivity": lastActivity, "lastActivity": lastActivity,
"members": members.map((e) => e.toJson()).toList(), "members": members.map((e) => e.toJson()).toList(),
"canEveryoneAddMembers": canEveryoneAddMembers, "canEveryoneAddMembers": canEveryoneAddMembers,

View File

@ -0,0 +1,21 @@
import 'package:comunic/models/group.dart';
import 'package:flutter/foundation.dart';
/// This class contains information about a conversation linked to a group
/// to create
///
/// @author Pierre Hubert
class NewGroupConversation {
final int groupID;
final String name;
final GroupMembershipLevel minMembershipLevel;
const NewGroupConversation({
@required this.groupID,
@required this.name,
@required this.minMembershipLevel,
}) : assert(groupID != null),
assert(name != null),
assert(minMembershipLevel != null);
}

View File

@ -58,6 +58,7 @@ class ServerDataConservationPolicy {
} }
class ConversationsPolicy { class ConversationsPolicy {
final int maxConversationNameLen;
final int minMessageLen; final int minMessageLen;
final int maxMessageLen; final int maxMessageLen;
final List<String> allowedFilesType; final List<String> allowedFilesType;
@ -72,6 +73,7 @@ class ConversationsPolicy {
final int maxLogoHeight; final int maxLogoHeight;
const ConversationsPolicy({ const ConversationsPolicy({
@required this.maxConversationNameLen,
@required this.minMessageLen, @required this.minMessageLen,
@required this.maxMessageLen, @required this.maxMessageLen,
@required this.allowedFilesType, @required this.allowedFilesType,
@ -84,7 +86,8 @@ class ConversationsPolicy {
@required this.maxThumbnailHeight, @required this.maxThumbnailHeight,
@required this.maxLogoWidth, @required this.maxLogoWidth,
@required this.maxLogoHeight, @required this.maxLogoHeight,
}) : assert(minMessageLen != null), }) : assert(maxConversationNameLen != null),
assert(minMessageLen != null),
assert(maxMessageLen != null), assert(maxMessageLen != null),
assert(allowedFilesType != null), assert(allowedFilesType != null),
assert(filesMaxSize != null), assert(filesMaxSize != null),

View File

@ -1,8 +1,10 @@
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:comunic/helpers/groups_helper.dart'; import 'package:comunic/helpers/groups_helper.dart';
import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/models/advanced_group_info.dart'; import 'package:comunic/models/advanced_group_info.dart';
import 'package:comunic/models/group.dart'; import 'package:comunic/models/group.dart';
import 'package:comunic/models/new_group_conversation.dart';
import 'package:comunic/ui/dialogs/input_user_password_dialog.dart'; import 'package:comunic/ui/dialogs/input_user_password_dialog.dart';
import 'package:comunic/ui/dialogs/multi_choices_dialog.dart'; import 'package:comunic/ui/dialogs/multi_choices_dialog.dart';
import 'package:comunic/ui/dialogs/virtual_directory_dialog.dart'; import 'package:comunic/ui/dialogs/virtual_directory_dialog.dart';
@ -17,6 +19,7 @@ import 'package:comunic/ui/widgets/settings/text_settings_edit_tile.dart';
import 'package:comunic/utils/files_utils.dart'; import 'package:comunic/utils/files_utils.dart';
import 'package:comunic/utils/input_utils.dart'; import 'package:comunic/utils/input_utils.dart';
import 'package:comunic/utils/intl_utils.dart'; import 'package:comunic/utils/intl_utils.dart';
import 'package:comunic/utils/log_utils.dart';
import 'package:comunic/utils/ui_utils.dart'; import 'package:comunic/utils/ui_utils.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:identicon/identicon.dart'; import 'package:identicon/identicon.dart';
@ -85,6 +88,7 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
HeadSpacerSection(), HeadSpacerSection(),
_buildGeneralSection(), _buildGeneralSection(),
_buildAccessRestrictions(), _buildAccessRestrictions(),
_buildConversationsArea(),
_buildGroupLogoArea(), _buildGroupLogoArea(),
_buildDangerZone(), _buildDangerZone(),
], ],
@ -256,6 +260,75 @@ class _GroupSettingsScreenState extends SafeState<GroupSettingsScreen> {
], ],
); );
List<MultiChoiceEntry<GroupMembershipLevel>>
get _conversationMinMembershipLevel => [
MultiChoiceEntry(
id: GroupMembershipLevel.ADMINISTRATOR,
title: tr("Administrators only"),
subtitle: tr(
"Only the administrators of the group can access the conversation"),
),
MultiChoiceEntry(
id: GroupMembershipLevel.MODERATOR,
title: tr("Moderators and administrators"),
subtitle: tr(
"Only moderators and administrators of the group can access the conversation"),
),
MultiChoiceEntry(
id: GroupMembershipLevel.MEMBER,
title: tr("All members"),
subtitle: tr(
"All the members of the group can access the conversation"),
),
];
SettingsSection _buildConversationsArea() => SettingsSection(
title: tr("Group conversations"),
tiles: [
SettingsTile(
title: tr("Create a new conversation"),
onPressed: _createNewGroupConversation,
),
],
);
void _createNewGroupConversation(BuildContext context) async {
try {
final name = await askUserString(
context: context,
title: tr("New conversation name"),
message: tr("Please give a name to the new conversation"),
defaultValue: "",
hint: tr("Name"),
minLength: 1,
maxLength: ServerConfigurationHelper
.config.conversationsPolicy.maxConversationNameLen,
);
if (name == null) return;
final visibility = await showMultiChoicesDialog(
context: context,
choices: _conversationMinMembershipLevel,
defaultChoice: GroupMembershipLevel.MEMBER,
title: tr("Conversation visibility"),
);
if (visibility == null) return;
await GroupsHelper.createGroupConversation(NewGroupConversation(
groupID: _groupSettings.id,
name: name,
minMembershipLevel: visibility,
));
_key.currentState.refresh();
} catch (e, s) {
logError(e, s);
snack(context, tr("Failed to create a conversation!"));
}
}
Widget _buildGroupLogoArea() { Widget _buildGroupLogoArea() {
return SettingsSection( return SettingsSection(
title: tr("Group logo"), title: tr("Group logo"),