1
0
mirror of https://gitlab.com/comunic/comunicmobile synced 2024-12-25 04:18:51 +00:00

Start to build report dialog

This commit is contained in:
Pierre HUBERT 2022-03-18 17:54:19 +01:00
parent 512b058d34
commit 80a1c4e0c4
8 changed files with 274 additions and 79 deletions

View File

@ -0,0 +1,28 @@
/// What kind of content that can be reported
enum ReportTargetType {
Post,
Comment,
Conversation,
ConversationMessage,
User,
Group
}
extension ReportTargetExt on ReportTargetType {
String get apiId {
switch (this) {
case ReportTargetType.Post:
return "post";
case ReportTargetType.Comment:
return "comment";
case ReportTargetType.Conversation:
return "conversation";
case ReportTargetType.ConversationMessage:
return "conversation_message";
case ReportTargetType.User:
return "user";
case ReportTargetType.Group:
return "group";
}
}
}

View File

@ -23,77 +23,88 @@ class ServerConfigurationHelper {
final dataConservationPolicy = response["data_conservation_policy"];
final conversationsPolicy = response["conversations_policy"];
final accountInformationPolicy = response["account_info_policy"];
final reportPolicy = response["report_policy"];
_config = ServerConfig(
minSupportedMobileVersion:
Version.parse(response["min_supported_mobile_version"]),
termsURL: response["terms_url"],
privacyPolicyURL: response["privacy_policy_url"],
contactEmail: response["contact_email"],
playStoreURL: response["play_store_url"],
androidDirectDownloadURL: response["android_direct_download_url"],
banner: banner == null
? null
: Banner(
enabled: banner["enabled"],
expire: banner["expire"],
nature: BannerNatureExt.fromStr(banner["nature"]),
message: Map<String, dynamic>.from(banner["message"])
.map((key, value) => MapEntry(key, value.toString())),
link: banner["link"]),
notificationsPolicy: NotificationsPolicy(
hasFirebase: pushNotificationsPolicy["has_firebase"],
hasIndependent: pushNotificationsPolicy["has_independent"],
),
passwordPolicy: PasswordPolicy(
allowMailInPassword: passwordPolicy["allow_email_in_password"],
allowNameInPassword: passwordPolicy["allow_name_in_password"],
minPasswordLength: passwordPolicy["min_password_length"],
minNumberUpperCaseLetters:
passwordPolicy["min_number_upper_case_letters"],
minNumberLowerCaseLetters:
passwordPolicy["min_number_lower_case_letters"],
minNumberDigits: passwordPolicy["min_number_digits"],
minNumberSpecialCharacters:
passwordPolicy["min_number_special_characters"],
minCategoriesPresence: passwordPolicy["min_categories_presence"],
),
dataConservationPolicy: ServerDataConservationPolicy(
minInactiveAccountLifetime:
dataConservationPolicy["min_inactive_account_lifetime"],
minNotificationLifetime:
dataConservationPolicy["min_notification_lifetime"],
minCommentsLifetime: dataConservationPolicy["min_comments_lifetime"],
minPostsLifetime: dataConservationPolicy["min_posts_lifetime"],
minConversationMessagesLifetime:
dataConservationPolicy["min_conversation_messages_lifetime"],
minLikesLifetime: dataConservationPolicy["min_likes_lifetime"],
),
conversationsPolicy: ConversationsPolicy(
maxConversationNameLen:
conversationsPolicy["max_conversation_name_len"],
minMessageLen: conversationsPolicy["min_message_len"],
maxMessageLen: conversationsPolicy["max_message_len"],
allowedFilesType:
conversationsPolicy["allowed_files_type"].cast<String>(),
filesMaxSize: conversationsPolicy["files_max_size"],
writingEventInterval: conversationsPolicy["writing_event_interval"],
writingEventLifetime: conversationsPolicy["writing_event_lifetime"],
maxMessageImageWidth: conversationsPolicy["max_message_image_width"],
maxMessageImageHeight: conversationsPolicy["max_message_image_height"],
maxThumbnailWidth: conversationsPolicy["max_thumbnail_width"],
maxThumbnailHeight: conversationsPolicy["max_thumbnail_height"],
maxLogoWidth: conversationsPolicy["max_logo_width"],
maxLogoHeight: conversationsPolicy["max_logo_height"],
),
accountInformationPolicy: AccountInformationPolicy(
minFirstNameLength: accountInformationPolicy["min_first_name_length"],
maxFirstNameLength: accountInformationPolicy["max_first_name_length"],
minLastNameLength: accountInformationPolicy["min_last_name_length"],
maxLastNameLength: accountInformationPolicy["max_last_name_length"],
maxLocationLength: accountInformationPolicy["max_location_length"],
),
);
minSupportedMobileVersion:
Version.parse(response["min_supported_mobile_version"]),
termsURL: response["terms_url"],
privacyPolicyURL: response["privacy_policy_url"],
contactEmail: response["contact_email"],
playStoreURL: response["play_store_url"],
androidDirectDownloadURL: response["android_direct_download_url"],
banner: banner == null
? null
: Banner(
enabled: banner["enabled"],
expire: banner["expire"],
nature: BannerNatureExt.fromStr(banner["nature"]),
message: Map<String, dynamic>.from(banner["message"])
.map((key, value) => MapEntry(key, value.toString())),
link: banner["link"]),
notificationsPolicy: NotificationsPolicy(
hasFirebase: pushNotificationsPolicy["has_firebase"],
hasIndependent: pushNotificationsPolicy["has_independent"],
),
passwordPolicy: PasswordPolicy(
allowMailInPassword: passwordPolicy["allow_email_in_password"],
allowNameInPassword: passwordPolicy["allow_name_in_password"],
minPasswordLength: passwordPolicy["min_password_length"],
minNumberUpperCaseLetters:
passwordPolicy["min_number_upper_case_letters"],
minNumberLowerCaseLetters:
passwordPolicy["min_number_lower_case_letters"],
minNumberDigits: passwordPolicy["min_number_digits"],
minNumberSpecialCharacters:
passwordPolicy["min_number_special_characters"],
minCategoriesPresence: passwordPolicy["min_categories_presence"],
),
dataConservationPolicy: ServerDataConservationPolicy(
minInactiveAccountLifetime:
dataConservationPolicy["min_inactive_account_lifetime"],
minNotificationLifetime:
dataConservationPolicy["min_notification_lifetime"],
minCommentsLifetime: dataConservationPolicy["min_comments_lifetime"],
minPostsLifetime: dataConservationPolicy["min_posts_lifetime"],
minConversationMessagesLifetime:
dataConservationPolicy["min_conversation_messages_lifetime"],
minLikesLifetime: dataConservationPolicy["min_likes_lifetime"],
),
conversationsPolicy: ConversationsPolicy(
maxConversationNameLen:
conversationsPolicy["max_conversation_name_len"],
minMessageLen: conversationsPolicy["min_message_len"],
maxMessageLen: conversationsPolicy["max_message_len"],
allowedFilesType:
conversationsPolicy["allowed_files_type"].cast<String>(),
filesMaxSize: conversationsPolicy["files_max_size"],
writingEventInterval: conversationsPolicy["writing_event_interval"],
writingEventLifetime: conversationsPolicy["writing_event_lifetime"],
maxMessageImageWidth: conversationsPolicy["max_message_image_width"],
maxMessageImageHeight:
conversationsPolicy["max_message_image_height"],
maxThumbnailWidth: conversationsPolicy["max_thumbnail_width"],
maxThumbnailHeight: conversationsPolicy["max_thumbnail_height"],
maxLogoWidth: conversationsPolicy["max_logo_width"],
maxLogoHeight: conversationsPolicy["max_logo_height"],
),
accountInformationPolicy: AccountInformationPolicy(
minFirstNameLength: accountInformationPolicy["min_first_name_length"],
maxFirstNameLength: accountInformationPolicy["max_first_name_length"],
minLastNameLength: accountInformationPolicy["min_last_name_length"],
maxLastNameLength: accountInformationPolicy["max_last_name_length"],
maxLocationLength: accountInformationPolicy["max_location_length"],
),
reportPolicy: reportPolicy == null
? null
: ReportPolicy(
causes: List.from(reportPolicy["causes"]
.map((cause) => ReportCause(
id: cause["id"],
label: new Map<String, String>.from(cause["label"])))
.toList()),
maxCommentLength: reportPolicy["max_comment_length"],
));
}
/// Get current server configuration, throwing if it is not loaded yet
@ -109,4 +120,4 @@ class ServerConfigurationHelper {
/// Shortcut for server configuration
ServerConfig? get srvConfig => ServerConfigurationHelper.config;
bool get showBanner => srvConfig!.banner != null && srvConfig!.banner!.visible;
bool get showBanner => srvConfig!.banner != null && srvConfig!.banner!.visible;

View File

@ -0,0 +1,8 @@
import 'package:comunic/enums/report_target_type.dart';
class ReportTarget {
final ReportTargetType type;
final int targetId;
const ReportTarget(this.type, this.targetId);
}

View File

@ -1,4 +1,5 @@
import 'package:comunic/utils/date_utils.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:version/version.dart';
/// Server static configuration
@ -137,6 +138,23 @@ class Banner {
bool get visible => enabled && (expire == null || expire! > time());
}
class ReportCause {
final String id;
final Map<String, String> label;
const ReportCause({required this.id, required this.label});
String get localeLabel =>
label.containsKey(shortLang) ? label[shortLang]! : label["en"]!;
}
class ReportPolicy {
final List<ReportCause> causes;
final int maxCommentLength;
const ReportPolicy({required this.causes, required this.maxCommentLength});
}
class ServerConfig {
final Version minSupportedMobileVersion;
final String termsURL;
@ -150,6 +168,7 @@ class ServerConfig {
final ServerDataConservationPolicy dataConservationPolicy;
final ConversationsPolicy conversationsPolicy;
final AccountInformationPolicy accountInformationPolicy;
final ReportPolicy? reportPolicy;
const ServerConfig({
required this.minSupportedMobileVersion,
@ -164,5 +183,8 @@ class ServerConfig {
required this.dataConservationPolicy,
required this.conversationsPolicy,
required this.accountInformationPolicy,
required this.reportPolicy,
});
bool get isReportingEnabled => this.reportPolicy != null;
}

View File

@ -0,0 +1,80 @@
import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/models/report_target.dart';
import 'package:comunic/models/server_config.dart';
import 'package:comunic/ui/widgets/dialogs/cancel_dialog_button.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:flutter/material.dart';
enum _Status { ChooseCause, GiveComment, Sending }
/// Show a dialog to report some content
Future<void> showReportDialog({
required BuildContext ctx,
required ReportTarget target,
}) async {
await showDialog(
context: ctx,
builder: (ctx) => _ReportDialog(
target: target,
));
}
class _ReportDialog extends StatefulWidget {
final ReportTarget target;
const _ReportDialog({Key? key, required this.target}) : super(key: key);
@override
State<_ReportDialog> createState() => _ReportDialogState();
}
class _ReportDialogState extends State<_ReportDialog> {
var _cause = srvConfig!.reportPolicy!.causes.length - 1;
var _status = _Status.ChooseCause;
List<ReportCause> get _causes => srvConfig!.reportPolicy!.causes;
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text(tr("Report abuse")!),
content: Container(width: 100, height: 200, child: _buildContent()),
actions: _status == _Status.Sending
? []
: [
CancelDialogButton(),
MaterialButton(
onPressed: null,
child: Text(
(_status == _Status.ChooseCause ? tr("Next")! : tr("Send")!)
.toUpperCase()),
)
],
);
}
Widget _buildContent() {
if (_status == _Status.Sending)
return Center(child: CircularProgressIndicator());
if (_status == _Status.ChooseCause)
return Column(
children: [
Text(tr("Please choose the reason of your report (you can scroll to access all reasons):")!),
Expanded(
child: ListView.builder(
itemBuilder: (c, i) => RadioListTile(
dense: false,
title: Text(_causes[i].localeLabel),
value: i,
groupValue: _cause,
onChanged: (_i) => setState(() => _cause = i)),
itemCount: _causes.length,
),
),
],
);
throw Exception("todo");
}
}

View File

@ -1,10 +1,13 @@
import 'dart:math';
import 'package:comunic/enums/report_target_type.dart';
import 'package:comunic/helpers/friends_helper.dart';
import 'package:comunic/helpers/users_helper.dart';
import 'package:comunic/lists/friends_list.dart';
import 'package:comunic/lists/users_list.dart';
import 'package:comunic/models/friend.dart';
import 'package:comunic/models/report_target.dart';
import 'package:comunic/ui/dialogs/report_dialog.dart';
import 'package:comunic/ui/tiles/accepted_friend_tile.dart';
import 'package:comunic/ui/tiles/pending_friend_tile.dart';
import 'package:comunic/ui/widgets/safe_state.dart';
@ -151,11 +154,13 @@ class _FriendsListScreenState extends SafeState<FriendsListScreen> {
onOpenPrivateConversation: _openPrivateConversation,
onSetFollowing: _setFollowingFriend,
onRequestDelete: _deleteFriend,
onReportFriend: _reportFriend,
)
: PendingFriendTile(
friend: _friendsList![i],
user: _usersInfo.getUser(_friendsList![i].id),
onRespond: _respondRequest,
onReport: _reportFriend,
);
}),
),
@ -216,6 +221,10 @@ class _FriendsListScreenState extends SafeState<FriendsListScreen> {
_refreshList();
}
/// Report a friend
Future<void> _reportFriend(Friend friend) async => await showReportDialog(
ctx: context, target: ReportTarget(ReportTargetType.User, friend.id));
/// Open a private conversation for a given [friend]
Future<void> _openPrivateConversation(Friend friend) async {
await openPrivateConversation(context, friend.id);

View File

@ -1,3 +1,4 @@
import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/models/friend.dart';
import 'package:comunic/models/user.dart';
import 'package:comunic/ui/widgets/account_image_widget.dart';
@ -10,11 +11,17 @@ import 'package:flutter/material.dart';
///
/// @author Pierre HUBERT
enum _FriendMenuChoices { REMOVE, TOGGLE_FOLLOWING, PRIVATE_CONVERSATION }
enum _FriendMenuChoices {
REMOVE,
TOGGLE_FOLLOWING,
PRIVATE_CONVERSATION,
REPORT
}
typedef OnRequestDeleteFriend = void Function(Friend);
typedef OnSetFollowing = void Function(Friend, bool);
typedef OnOpenPrivateConversation = void Function(Friend);
typedef OnReportFriend = void Function(Friend);
class AcceptedFriendTile extends StatelessWidget {
final Friend friend;
@ -22,6 +29,7 @@ class AcceptedFriendTile extends StatelessWidget {
final OnRequestDeleteFriend onRequestDelete;
final OnSetFollowing onSetFollowing;
final OnOpenPrivateConversation onOpenPrivateConversation;
final OnReportFriend onReportFriend;
const AcceptedFriendTile({
Key? key,
@ -30,6 +38,7 @@ class AcceptedFriendTile extends StatelessWidget {
required this.onRequestDelete,
required this.onSetFollowing,
required this.onOpenPrivateConversation,
required this.onReportFriend,
}) : super(key: key);
@override
@ -75,7 +84,15 @@ class AcceptedFriendTile extends StatelessWidget {
child: Text(tr("Remove")!),
value: _FriendMenuChoices.REMOVE,
),
],
]..addAll(srvConfig!.isReportingEnabled
? [
// Report user
PopupMenuItem(
child: Text(tr("Report abuse")!),
value: _FriendMenuChoices.REPORT,
),
]
: []),
onSelected: _selectedMenuOption,
),
);
@ -97,6 +114,10 @@ class AcceptedFriendTile extends StatelessWidget {
case _FriendMenuChoices.REMOVE:
onRequestDelete(friend);
break;
case _FriendMenuChoices.REPORT:
onReportFriend(friend);
break;
}
}
}

View File

@ -1,5 +1,7 @@
import 'package:comunic/helpers/server_config_helper.dart';
import 'package:comunic/models/friend.dart';
import 'package:comunic/models/user.dart';
import 'package:comunic/ui/tiles/accepted_friend_tile.dart';
import 'package:comunic/ui/widgets/account_image_widget.dart';
import 'package:comunic/utils/intl_utils.dart';
import 'package:flutter/material.dart';
@ -14,13 +16,15 @@ class PendingFriendTile extends StatelessWidget {
final Friend friend;
final User user;
final RespondFriendshipRequestCallback onRespond;
final OnReportFriend onReport;
const PendingFriendTile(
{Key? key,
required this.friend,
required this.user,
required this.onRespond})
: super(key: key);
const PendingFriendTile({
Key? key,
required this.friend,
required this.user,
required this.onRespond,
required this.onReport,
}) : super(key: key);
@override
Widget build(BuildContext context) {
@ -56,7 +60,19 @@ class PendingFriendTile extends StatelessWidget {
style: ButtonStyle(
backgroundColor: MaterialStateProperty.all(Colors.red)),
onPressed: () => onRespond(friend, false),
)
),
// Report button
srvConfig!.isReportingEnabled
? IconButton(
visualDensity: VisualDensity.compact,
onPressed: () => onReport(friend),
icon: Icon(
Icons.flag,
size: 15.0,
),
)
: Container()
],
),
),